Zweck
LoRA-Adapter für die Transkription von Tabellen aus deutschsprachigen Amtsdokument-Seitenbildern in kanonisches TEDS-HTML-Zellgrid (table/thead/tbody/tr/td/th, colspan/rowspan als Ganzzahl-Attribute). V1-Arbeitspaket der Verifikations-Lane des AI:AT-Doc-VLM-Programms (doc-vlm Epic #48, Issue #52): unabhängige Reproduktion des S1-Tinker-Rezepts auf EU-souveräner CUDA-Hardware (MeluXina, EuroHPC). Methoden-Beweis — kein Gate-Artefakt.
Training
- Methode: QLoRA (NF4, double-quant, bf16-Compute) auf eingefrorener Base; LoRA rank 32, alpha 64, dropout 0,
target_modules: q_proj, k_proj, v_proj, o_proj → 6 881 280 trainierbare Parameter (0,0196 %).
- Rezept (S1-Parität): 100 Optimizer-Steps = 800 Samples × 2 Epochen, effektiver Batch 16 (Gradient-Accumulation), LR 4.9908e-4 (
tinker_cookbook.hyperparam_utils.get_lr(base, is_lora=True)), max_length 4096, Gradient-Clipping 1.0, PagedAdamW8bit.
- Prompt-Variante: System-Prompt + Seitenbild + Instruktion aus
aiat_doc_vlm.sft.train_dataset (Repo-Import, byte-gleich zum S1-Pfad); Chat-Template mit enable_thinking=False (Pendant zum S1-Renderer qwen3_5_disable_thinking). Loss nur auf dem Assistant-Target (Label-Maske −100 auf dem Prompt-Präfix).
- Daten:
manifests/sft-tables-v1.jsonl (frozen) — 800 vollsynthetische bgbl-synth-Tabellen-Samples, Seitenbilder 1191×1684 px (pypdfium2 scale 2.0), Pixel-Cap 1,15 M px.
- Loss-Verlauf: 0.0666 (Step 1) → 0.000015 (Step 100); auskonvergiert ab ~Step 40. Vollständiges Log je Step im Run-Verzeichnis (
loss-log.jsonl).
- Trainingskosten: ~71 A100-GPU-Stunden (1 Node × 4× A100-40GB, 17:41 h Wallclock, Pipeline-Parallel
device_map=auto), EuroHPC-Allokation p201354 — kein Per-Token-Billing. Zum Vergleich: S1 auf Tinker $2.66.
Eval (screening)
Checkpoint-Provenienz: Slurm-Run 4885157 (MeluXina, Node mel2088, 2026-07-25/26), finaler Checkpoint nach Step 100 = dieses Adapter-Verzeichnis. Eval-Run 4895649 (2026-07-26), transformers-generate (greedy) auf 4-bit-Base — label: screening, kein CUDA/vLLM-Serving-Gate (RESULTS-CONTRACT §5).
Sanierter Heldout (n=15, manifests/sft-tables-heldout-eval.jsonl, seed 777 — Grid- UND Byte-disjunkt zum Training, CI-Gate tests/test_manifest_disjointness.py, doc-vlm #49):
Table with columns: Variante, teds_mean, teds_structure_mean, Coverage| Variante | teds_mean | teds_structure_mean | Coverage |
|---|
| Base (Qwen3.6-35B-A3B, 4-bit) | 0.8530 | 0.8533 | 15/15 |
| Base + dieser Adapter | 0.9996 | 1.0000 | 15/15 |
| Delta | +0.1466 | +0.1467 | — |
Vorregistriertes Warnsignal dokumentiert: Die V-Lane-Erwartung (»LoRA > Base, aber nicht 1,0«) wird mit 0.9996/1.0 fast exakt am Alarmwert getroffen. #49-Kontamination ist mechanisch ausgeschlossen (Byte-/Grid-Overlap 0/0); die Erklärung ist die zweite vorregistrierte: Der Heldout teilt die Layout-Familien (borderless, colspan_rowspan, gebuehren, multiline, paragraphen) mit dem Training und misst damit Within-Family-Generalisierung auf neue Zellwerte — die ist perfekt (u. a. paragraphen-Familie 0.665 → 0.998). Er misst nicht den Transfer auf fremde Layouts oder echte RIS-Tabellen.
Grenzen
- Synthetik-only: Training und Eval ausschließlich auf generierten Tabellen; kein Beleg für echte DE-Legal-Tabellen (das ist V2 auf der frozen Bench v1.1 gegen paddleocr-vl-1.6, mit realen P3-Daten).
- n=15 screening, transformers-generate statt vLLM-Serving — keine Gate-Zahl.
- Serving: MoE (35B-A3B) + LoRA in vLLM ist experimentell; für den Serving-Gate-Pfad empfiehlt das Epic 27B-Dense.
- Adapter erwartet den Trainings-Prompt (System-Prompt + Instruktion s. o.); andere Prompts sind unerprobt.
Daten-Herkunft (non-PII)
100 % synthetisch generierte Tabellen (bgbl-synth-Generator des aiat-doc-vlm-Repos), public-domain. Kein NDA-/Klartext-Material, keine personenbezogenen Daten. Frozen-Bench-doc_ids (bench/v1/BENCH-DOC-IDS.txt) waren per Leakage-Gate ausgeschlossen.
Usage
import torch
from peft import PeftModel
from transformers import AutoModelForImageTextToText, AutoProcessor, BitsAndBytesConfig
base = "Qwen/Qwen3.6-35B-A3B"
processor = AutoProcessor.from_pretrained(base)
model = AutoModelForImageTextToText.from_pretrained(
base,
quantization_config=BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16),
dtype=torch.bfloat16, device_map="auto",
)
model = PeftModel.from_pretrained(model, "AIAT-BernhardG/qwen3.6-35b-a3b-doc-table-v1-lora")
Framework-Versionen
- python 3.12 · torch 2.9.1 (conda-forge, CUDA 12.9-Build) · transformers 5.14.1 · peft 0.19.1 · bitsandbytes 0.49 · flash-linear-attention 0.5.1 · causal-conv1d (conda-forge)
- Environment reproduzierbar via
pixi.lock (MeluXina $WORK/klartext/v1-train), Lauf-Config in config.json des Runs.