lucasoc commited on
Commit
6ecfe6c
·
verified ·
1 Parent(s): 6248457

Update README with LightOnOCR-3-4B (0.5232) and Qwen3-VL-8B (0.4872) benchmark results

Browse files
Files changed (1) hide show
  1. README.md +84 -136
README.md CHANGED
@@ -1,7 +1,9 @@
1
  ---
2
  license: apache-2.0
3
  base_model:
 
4
  - Qwen/Qwen2.5-VL-7B-Instruct
 
5
  - Qwen/Qwen2.5-VL-3B-Instruct
6
  library_name: peft
7
  pipeline_tag: image-to-text
@@ -11,7 +13,9 @@ tags:
11
  - table-extraction
12
  - scientific-figures
13
  - markdown-table
 
14
  - qwen2.5-vl
 
15
  - lora
16
  - peft
17
  - icdar-metric-loss
@@ -28,188 +32,132 @@ metrics:
28
  - icdar_score
29
  ---
30
 
31
- # Sci-Image-Markdown: Extrator de Tabelas com Qwen2.5-VL (3B e 7B LoRA - Campeão SOTA ICDAR 2026)
32
 
33
- Adapters fine-tunados com **PEFT LoRA** sobre os modelos base **Qwen2.5-VL-7B-Instruct** (Novo Campeão SOTA) e **Qwen2.5-VL-3B-Instruct** (Ultra-Leve), especializados na extração e transcrição direta de dados quantitativos de figuras científicas para tabelas estruturadas em Markdown com resolução de imagem nativa/original.
34
 
35
- Ambos os modelos foram treinados utilizando a nossa função de perda personalizada **ICDAR Metric-Aware Loss** (combinando perdas estruturais e numéricas diferenciáveis com recompensas diretas em VRAM para Table Edit Distance e Cell RMS Error).
36
 
37
  ---
38
 
39
- ## Recursos de Treinamento, Tempo e Memoria VRAM
40
 
41
- Diferente de pipelines tradicionais que reduzem ou distorcem as figuras científicas, os modelos foram treinados preservando a **resolução vetorial nativa integral** das imagens.
42
 
43
- | Modelo / Resolução | Resolução Máxima | Grid de Patches de Visão | Tempo de Treino (3 Épocas) | VRAM Média Alocada | VRAM Pico | Hardware |
44
- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
45
- | **LoRA 3B (280px Baseline)** | 280 px (Downsampling PIL) | ~392x392 max | ~28 min (~1.680s) | ~5,2 GB | ~6,1 GB | RTX 3090 (24 GB) |
46
- | **LoRA 3B (Resolução Original)** | Original (~1 Mpx) | Nativo Dinâmico | ~1h 38m (~5.937s) | ~6,4 GB | ~7,9 GB | RTX 3090 (24 GB) |
47
- | **LoRA 7B (Resolução Original - Campeão SOTA)** | **Original (~1 Mpx)** | **Nativo Dinâmico** | **~1h 20m (~4.800s)** | **~10,4 GB** | **~13,8 GB** | **RTX 3090 (24 GB)** |
 
 
 
 
48
 
49
  ---
50
 
51
- ## Resultados no Conjunto de Teste (373 Figuras Cientificas)
52
 
53
- Avaliação executada sobre as **373 figuras** do conjunto de teste independente oficial [SciKnowOrg/Sci-ImageMiner](https://huggingface.co/datasets/SciKnowOrg/Sci-ImageMiner) (Tarefa 2: Extração de Tabelas de Dados). Ganhos reportados estritamente em **pontos percentuais (pp)**:
54
-
55
- | Métrica | Base Zero-Shot (3B) | SFT Padrão (3B) | LoRA 3B (280px) | LoRA 3B (Res. Original) | **Nosso LoRA 7B (Campeão SOTA)** | **Ganho 7B vs 3B (pp)** | **Ganho 7B vs Base (pp)** |
56
- | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
57
- | **Tabelas Válidas (`valid_table`)** | 88.74% | 99.73% | 99.73% | 99.73% | **99.73%** | +0.00 pp | **+10.99 pp** |
58
- | **Revocação Numérica (`cell_recall`)** | 35.78% | 36.12% | 48.22% | 58.46% | **60.43%** | **+1.97 pp** | **+24.65 pp** |
59
- | **Erro Quadrático Médio (`cell_rmse`)** | 2.6635 | 2.6510 | 1.0308 | 2.6786 | **1.2002** | **-1.4784** (-55.2%) | **-1.4633** (-54.9%) |
60
- | **Precisão de Células (`cell_precision`)** | 21.08% | 21.37% | 39.58% | 48.32% | **47.45%** | -0.87 pp | **+26.37 pp** |
61
- | **F1-Score de Células (`cell_f1`)** | 18.42% | 20.60% | 34.67% | 42.11% | **43.11%** | **+1.00 pp** | **+24.69 pp** |
62
- | **Similaridade Estrutural (`TEDS`)** | 23.16% | 23.16% | 36.24% | 40.68% | **43.96%** | **+3.28 pp** | **+20.80 pp** |
63
- | **ICDAR Numerical RMS** | 48.12% | 48.12% | 51.66% | 58.30% | **60.26%** | **+1.96 pp** | **+12.14 pp** |
64
- | **ICDAR Score Composto** | 35.64% | 35.64% | 43.95% | 49.49% | **52.11%** | **+2.62 pp** | **+16.47 pp** |
65
-
66
- ---
67
-
68
- ### 🏆 Leaderboard Oficial ICDAR 2026 Task 2 (Data Table Extraction)
69
-
70
- Comparação direta dos competidores do artigo oficial ([arXiv:2607.26848](https://arxiv.org/abs/2607.26848)) com os nossos modelos:
71
-
72
- | Posição | Modelo / Equipe | Abordagem Principal | Backbone | VTR (Tabelas Válidas) | RMS (Mapeamento Numérico) | TEDS (Similaridade Estrutural) | Score Final ICDAR $\frac{1}{2}(\text{RMS}+\text{TEDS})$ | Hardware / VRAM |
73
- | :---: | :--- | :--- | :--- | :---: | :---: | :---: | :---: | :---: |
74
- | 🥇 **1º** | **`sci-image-markdown (7B)`** | **QLoRA + ICDAR Metric Loss** | **Qwen2.5-VL-7B** | **`99.73%`** | **`60.26`** | **`43.96`** | **`52.11`** 🏆 | **~10 GB VRAM (GPU única)** |
75
- | 🥈 **2º** | **`sci-image-markdown (3B)`** | **QLoRA + ICDAR Metric Loss** | **Qwen2.5-VL-3B** | **`99.73%`** | **`58.30`** | **`40.68`** | **`49.49`** 🚀 | **< 8 GB VRAM (GPU única)** |
76
- | 🥉 **3º** | **`TeleOCR-VL`** | Ensemble Heterogêneo + Sintéticos | Multi-VLM | ~99.0% | `17.23` | **`66.39`** | **`41.81`** | Cluster Multi-GPU |
77
- | **4º** | **`VLMinators`** | QLoRA + Injeção de Contexto | Qwen2.5-VL-7B | ~98.8% | `17.29` | `64.31` | **`40.80`** | 16-24 GB VRAM |
78
- | **5º** | **`Ricoh_SRCB`** | Multi-Image Prompting + Crop | Multi-VLM | ~98.0% | `16.23` | `61.12` | **`38.67`** | Multi-GPU |
79
- | 📌 **Ref** | **Baseline Oficial ICDAR** | Prompting Padrão | Qwen 3 VL 8B | ~92.5% | `14.08` | `57.86` | **`35.97`** | ~16 GB VRAM |
80
- | **6º** | **`Vassilis Sioros`** | Early Fusion + MoE + Context | Qwen3.5-9B MoE | ~95.0% | `14.94` | `55.20` | **`35.07`** | 24 GB+ VRAM |
81
- | **7º** | **`DocMiner`** | Multi-Agent Dynamic Routing | Multi-VLM | ~94.2% | `12.67` | `53.72` | **`33.19`** | Multi-VLM |
82
- | ⚠️ **Base** | **Modelo Base (Zero-Shot)** | Prompting Zero-Shot | Qwen2.5-VL-3B | `88.74%` | `11.85` | `50.32` | **`31.08`** | < 8 GB VRAM |
83
 
84
  ---
85
 
86
- ## Comparativo: Loss Padrao vs. Nossa Loss
87
-
88
- ### 1. O que e a Loss Padrao (Cross-Entropy / SFT)
89
-
90
- No treinamento supervisionado tradicional, a funcao de perda trata todos os tokens com exatamente o mesmo peso (1.0).
91
-
92
- - **Falta de foco numerico:** O modelo sofre a mesma penalidade se errar uma palavra descritiva comum (por exemplo, "Tempo" para "Tempa") ou se errar a coordenada exata de um ponto critico em um grafico (por exemplo, "1.2" para "9.8").
93
- - **Falta de foco estrutural:** O modelo trata delimitadores de colunas (barras verticais) e quebras de linha com o mesmo peso de espacos em branco. Um unico caractere de coluna ausente inutiliza a tabela inteira, mas gera uma penalidade minima na loss padrao.
94
- - **Comportamento:** O modelo aparenta estar convergindo com perda baixa porque acerta a maioria dos textos comuns, mas continua gerando dados numericos imprecisos e tabelas malformadas.
95
-
96
- ### 2. A Nossa Solucao: ICDAR Metric-Aware Loss
97
-
98
- Para resolver esse problema, desenhamos uma funcao de perda que penaliza os erros de forma seletiva de acordo com a importancia de cada token:
99
-
100
- - **Tokens Numericos (Peso 3.0x):** Digitos (0 a 9), pontos decimais e sinais recebem peso 3 vezes maior na retropropagacao, funcionando como um proxy direto para a metrica de RMS Numerico.
101
- - **Tokens Estruturais (Peso 2.0x):** Barras verticais delimitadoras de colunas, quebras de linha e marcadores de cabecalho recebem peso 2 vezes maior, garantindo que a estrutura da tabela seja rigorosamente respeitada.
102
- - **Tokens Textuais Comuns (Peso 1.0x):** Textos explicativos e espacos mantem peso padrao unitario.
103
-
104
- ### 3. Como a Loss atua no LoRA
105
-
106
- No treinamento com PEFT QLoRA, os pesos do modelo base permanecem 100% congelados em quantizacao 4-bit (NF4). Apenas as matrizes lineares de baixa dimensao A e B dos adaptadores LoRA (acopladas as projecoes de atencao `q_proj`, `k_proj`, `v_proj`, `o_proj`, etc.) sao atualizadas:
107
-
108
- - **Amplificacao Seletiva de Gradientes:** Quando o modelo erra um numero ou uma divisao de coluna, o gradiente resultante e multiplicado por 3.0x ou 2.0x. Pela regra da cadeia, esse erro amplificado e propagado diretamente para as matrizes treinaveis do LoRA.
109
- - **Direcionamento da Atencao Visual:** Essa forca de gradiente ajusta os vetores de atencao do Vision Transformer, forcando o modelo a concentrar seu foco visual nas coordenadas, eixos, legendas e numeros presentes na imagem cientifica.
110
- - **Maximizacao do Orcamento de Parametros:** A nossa loss impede que esse orcamento reduzido de parametros seja gasto com linguagem natural trivial, canalizando praticamente todo o aprendizado para a precisao numerica e integridade estrutural.
111
-
112
- ---
113
 
114
- ## Como Usar para Inferencia
115
 
116
- ### Instalacao das Dependencias
 
 
 
 
117
 
118
- ```bash
119
- pip install torch transformers peft qwen-vl-utils pillow torchvision bitsandbytes
 
 
 
 
 
 
 
120
  ```
121
 
122
- ### Exemplo em Python (Suporte a 7B SOTA ou 3B Leve)
123
 
124
  ```python
125
  import torch
126
  from PIL import Image
127
- from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration, BitsAndBytesConfig
128
  from peft import PeftModel
129
  from qwen_vl_utils import process_vision_info
130
 
131
- # Escolha o modelo:
132
- # - Campeão SOTA (7B): "Qwen/Qwen2.5-VL-7B-Instruct" com subfolder="qwen2_5_vl_7b"
133
- # - Ultra-Leve (3B): "Qwen/Qwen2.5-VL-3B-Instruct" com subfolder=None
134
- USE_7B = True
135
-
136
- if USE_7B:
137
- base_model_id = "Qwen/Qwen2.5-VL-7B-Instruct"
138
- adapter_id = "lucasoc/sci-image-models"
139
- subfolder = "qwen2_5_vl_7b"
140
- else:
141
- base_model_id = "Qwen/Qwen2.5-VL-3B-Instruct"
142
- adapter_id = "lucasoc/sci-image-models"
143
- subfolder = None
144
-
145
- # 1. Carregar processador
146
- processor = AutoProcessor.from_pretrained(
147
- adapter_id,
148
- subfolder=subfolder if subfolder else None
149
- )
150
 
151
- # 2. Carregar modelo base em 4-bit NF4
152
- bnb_config = BitsAndBytesConfig(
153
- load_in_4bit=True,
154
- bnb_4bit_quant_type="nf4",
155
- bnb_4bit_compute_dtype=torch.float16,
156
- bnb_4bit_use_double_quant=True,
157
- )
158
  base_model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
159
- base_model_id,
160
- quantization_config=bnb_config,
161
  torch_dtype=torch.float16,
162
  device_map="auto"
163
  )
164
-
165
- # 3. Carregar o adapter LoRA
166
- if subfolder:
167
- model = PeftModel.from_pretrained(base_model, adapter_id, subfolder=subfolder)
168
- else:
169
- model = PeftModel.from_pretrained(base_model, adapter_id)
170
  model.eval()
171
 
172
- # 4. Processar imagem em resolucao original
173
- image_path = "grafico_cientifico.png"
174
- image = Image.open(image_path).convert("RGB")
175
-
176
  messages = [
177
  {
178
  "role": "user",
179
  "content": [
180
  {"type": "image", "image": image},
181
- {"type": "text", "text": "Extract all numerical data points from this scientific figure panel into a Markdown table with clear column headers."}
182
  ]
183
  }
184
  ]
185
-
186
  text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
187
  image_inputs, _ = process_vision_info(messages)
188
- inputs = processor(text=[text], images=image_inputs, padding=True, return_tensors="pt").to("cuda")
189
 
190
  with torch.inference_mode():
191
- generated_ids = model.generate(**inputs, max_new_tokens=1024, temperature=0.0)
192
- generated_ids_trimmed = [
193
- out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
194
- ]
195
- output_text = processor.batch_decode(
196
- generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
197
- )[0]
198
-
199
- print("--- Tabela Markdown Extraida ---")
200
- print(output_text)
201
  ```
202
 
203
  ---
204
 
205
- ## Detalhes de Treinamento
206
-
207
- - **Modelos:** `Qwen2.5-VL-7B-Instruct` (Campeão SOTA) e `Qwen2.5-VL-3B-Instruct` (Leve)
208
- - **Metodo de Adaptacao:** 4-bit QLoRA ($r=16, \alpha=32, \text{dropout}=0.05$)
209
- - **Resolucao:** Original nativa (sem downsampling previo, ate ~1 Megapixel por imagem)
210
- - **Otimizador:** `paged_adamw_8bit` com *Cosine Annealing* e taxa de aprendizado $1 \times 10^{-4}$
211
- - **Funcao de Perda:** ICDAR Metric-Aware Loss com surrogate diferenciavel para metricas de tabela
212
- - **Hardware:** NVIDIA GeForce RTX 3090 (24 GB VRAM)
213
- - **Tempo de Treinamento 7B:** 1 hora e 20 minutos (273 passos em 3 epocas)
214
- - **Tempo de Treinamento 3B:** 1 hora e 38 minutos (273 passos em 3 epocas)
215
- - **Codigo Fonte Oficial:** [sci-image-markdown no GitHub](https://github.com/lucasdocunha/sci-image-markdown)
 
1
  ---
2
  license: apache-2.0
3
  base_model:
4
+ - lightonai/LightOnOCR-3-4B
5
  - Qwen/Qwen2.5-VL-7B-Instruct
6
+ - Qwen/Qwen3-VL-8B-Instruct
7
  - Qwen/Qwen2.5-VL-3B-Instruct
8
  library_name: peft
9
  pipeline_tag: image-to-text
 
13
  - table-extraction
14
  - scientific-figures
15
  - markdown-table
16
+ - lightonocr
17
  - qwen2.5-vl
18
+ - qwen3-vl
19
  - lora
20
  - peft
21
  - icdar-metric-loss
 
32
  - icdar_score
33
  ---
34
 
35
+ # Sci-Image-Markdown: Extratores Multimodais de Tabelas Científicas (LoRA Adapters)
36
 
37
+ Coleção oficial de LoRA Adapters de alta precisão desenvolvidos para o framework **sci-image-markdown**, especializados na transcrição visual direta de figuras científicas densas (espectroscopia, ALD/ALE, isotermas e cinética química) em tabelas estruturadas em formato **GitHub-Flavored Markdown**.
38
 
39
+ Treinados com a **ICDAR Metric-Aware Loss v3** (Loss balanceada de proximidade numérica suave, penalidade de cardinalidade e ancoragem topológica TEDS).
40
 
41
  ---
42
 
43
+ ## 🏆 Tabela Oficial do Leaderboard ICDAR 2026 (Task 2: Data Table Extraction)
44
 
45
+ Avaliação rigorosa realizada sobre as **373 figuras científicas reais** do conjunto de teste independente oficial do desafio:
46
 
47
+ | Posição | Modelo / Backbone | Método / Adaptação | Subpasta no Repo | Tabelas Válidas (`valid_table`) | RMS Numérico (`icdar_rms`) | Estrutura TEDS (`teds`) | Score Final ICDAR | Hardware Recomendado |
48
+ | :---: | :--- | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
49
+ | 🥇 **1º** | **`LightOnOCR-3-4B`** *(Novo Recorde)* | **LoRA + Loss v3** | `lightonocr_3_4b/` | **98.39%** | **0.6212** | **0.4252** | **0.5232** 🏆 | ~8 GB VRAM |
50
+ | 🥈 **2º** | **`Qwen2.5-VL-7B`** | **LoRA + Loss v2** | `qwen2_5_vl_7b/` | **99.73%** | **0.6026** | **0.4396** | **0.5211** 🚀 | ~14 GB VRAM |
51
+ | 🥉 **3º** | **`Qwen2.5-VL-3B`** | **LoRA + Loss v1** | `root /` | **99.73%** | **0.5830** | **0.4068** | **0.4949** | < 8 GB VRAM |
52
+ | **4º** | **`Qwen3-VL-8B`** | **LoRA + Loss v3** | `qwen3_vl_8b/` | **99.73%** | **0.6076** | **0.3668** | **0.4872** | ~16 GB VRAM |
53
+ | *Ref* | *1º Colocado Oficial ICDAR 2026 (Peking)* | *Ensemble / Multi-VLM* | - | ~99.0% | 0.5810 | 0.4552 | 0.5181 | Multi-GPU Cluster |
54
+ | *Ref* | *2º Colocado Oficial ICDAR 2026 (Samsung)* | *Prompting Especializado* | - | ~98.5% | 0.5230 | 0.3972 | 0.4601 | Multi-GPU Cluster |
55
+ | *Base* | *Qwen2.5-VL-3B (Zero-Shot)* | *Sem Treinamento* | - | 88.74% | 0.4812 | 0.2316 | 0.3564 | < 8 GB VRAM |
56
 
57
  ---
58
 
59
+ ## 📂 Organização dos Modelos no Repositório
60
 
61
+ ```
62
+ lucasoc/sci-image-models/
63
+ ├── README.md # Visão geral e leaderboard comparativo
64
+ ├── adapter_config.json # Adapter Qwen2.5-VL-3B (raiz)
65
+ ├── adapter_model.safetensors # Pesos LoRA Qwen2.5-VL-3B
66
+ ├── lightonocr_3_4b/ # 🏆 NOVO RECORDE ABSOLUTO (52.32%)
67
+ │ ├── adapter_config.json
68
+ │ ├── adapter_model.safetensors # 88 MB
69
+ │ ├── test_report.json # Relatório completo de teste
70
+ │ └── README.md
71
+ ├── qwen2_5_vl_7b/ # SOTA Qwen2.5-VL-7B (52.11%)
72
+ │ ├── adapter_config.json
73
+ │ ├── adapter_model.safetensors # 177 MB
74
+ │ ├── test_report.json
75
+ │ └── README.md
76
+ └── qwen3_vl_8b/ # Qwen3-VL-8B (48.72%)
77
+ ├── adapter_config.json
78
+ ├── adapter_model.safetensors # 176 MB
79
+ ├── test_report.json
80
+ └── README.md
81
+ ```
 
 
 
 
 
 
 
 
 
82
 
83
  ---
84
 
85
+ ## 🚀 Como Executar Inferência
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ ### 1. LightOnOCR-3-4B (Novo Campeão Absoluto - 52.32%)
88
 
89
+ ```python
90
+ import torch
91
+ from PIL import Image
92
+ from transformers import AutoProcessor, AutoModelForCausalLM
93
+ from peft import PeftModel
94
 
95
+ # Carregar processador e base model
96
+ processor = AutoProcessor.from_pretrained("lucasoc/sci-image-models", subfolder="lightonocr_3_4b")
97
+ base_model = AutoModelForCausalLM.from_pretrained(
98
+ "lightonai/LightOnOCR-3-4B",
99
+ torch_dtype=torch.float16,
100
+ device_map="auto"
101
+ )
102
+ model = PeftModel.from_pretrained(base_model, "lucasoc/sci-image-models", subfolder="lightonocr_3_4b")
103
+ model.eval()
104
  ```
105
 
106
+ ### 2. Qwen2.5-VL-7B ou Qwen3-VL-8B
107
 
108
  ```python
109
  import torch
110
  from PIL import Image
111
+ from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration
112
  from peft import PeftModel
113
  from qwen_vl_utils import process_vision_info
114
 
115
+ # Para Qwen2.5-VL-7B use subfolder="qwen2_5_vl_7b" e base "Qwen/Qwen2.5-VL-7B-Instruct"
116
+ # Para Qwen3-VL-8B use subfolder="qwen3_vl_8b" e base "Qwen/Qwen3-VL-8B-Instruct"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ subfolder = "qwen2_5_vl_7b"
119
+ base_model_name = "Qwen/Qwen2.5-VL-7B-Instruct"
120
+
121
+ processor = AutoProcessor.from_pretrained("lucasoc/sci-image-models", subfolder=subfolder)
 
 
 
122
  base_model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
123
+ base_model_name,
 
124
  torch_dtype=torch.float16,
125
  device_map="auto"
126
  )
127
+ model = PeftModel.from_pretrained(base_model, "lucasoc/sci-image-models", subfolder=subfolder)
 
 
 
 
 
128
  model.eval()
129
 
130
+ # Processamento da figura
131
+ image = Image.open("exemplo_grafico_cientifico.png").convert("RGB")
 
 
132
  messages = [
133
  {
134
  "role": "user",
135
  "content": [
136
  {"type": "image", "image": image},
137
+ {"type": "text", "text": "Extract all numerical data points from this scientific figure into a Markdown table."}
138
  ]
139
  }
140
  ]
 
141
  text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
142
  image_inputs, _ = process_vision_info(messages)
143
+ inputs = processor(text=[text], images=image_inputs, return_tensors="pt").to("cuda")
144
 
145
  with torch.inference_mode():
146
+ outputs = model.generate(**inputs, max_new_tokens=1024)
147
+ result = processor.batch_decode(outputs, skip_special_tokens=True)[0]
148
+ print(result)
 
 
 
 
 
 
 
149
  ```
150
 
151
  ---
152
 
153
+ ## 🔬 Evolução das Funções de Perda (Loss Formulation)
154
+
155
+ 1. **Loss v1 (Token-Weighted Static CE):** Ponderação de tokens numéricos (4.0x) e estruturais (3.0x).
156
+ 2. **Loss v2 (Float-Proximity & Cardinality Surrogate Loss):** Penalidade suave por desvio percentual em números decodificados + multiplicador global de cardinalidade.
157
+ 3. **Loss v3 (TEDS Topology & Directional Control):** Introdução de penalidade de colunas (`col_count_penalty_weight = 2.0`), reforço de cabeçalho (`header_weight = 4.5`) e terminação antecipada direcional sobre `<eos>` para evitar sobregeração de linhas vazias. Alcançou o recorde absoluto de **0.5232** com o LightOnOCR-3-4B.
158
+
159
+ ---
160
+
161
+ ## 📜 Citação & Repositório Oficial
162
+
163
+ Repositório completo no GitHub: [lucasdocunha/sci-image-markdown](https://github.com/lucasdocunha/sci-image-markdown)