-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy path36_ocr_grpo_training.py
More file actions
185 lines (154 loc) · 6.09 KB
/
Copy path36_ocr_grpo_training.py
File metadata and controls
185 lines (154 loc) · 6.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
"""
OCR GRPO Training (Reinforcement Learning for OCR)
Uses Group Relative Policy Optimization to improve OCR accuracy using
character-level reward functions. Instead of standard SFT, GRPO generates
multiple transcription attempts per image and uses CER (Character Error Rate)
as the reward signal to reinforce more accurate transcriptions.
This is a novel approach: applying RL to OCR to optimize for character accuracy.
Model: Qwen3.5-0.8B (general VLM)
Dataset: unsloth/LaTeX_OCR
Reward: Combined CER + Exact Match reward
Usage:
python examples/36_ocr_grpo_training.py
"""
# ===========================================================================
# MLX-Tune OCR imports
# ===========================================================================
from mlx_tune import (
FastOCRModel,
OCR_MODELS,
cer_reward,
exact_match_reward,
combined_ocr_reward,
compute_ocr_metrics,
)
from mlx_tune.ocr import OCRGRPOTrainer, OCRGRPOConfig
# ===========================================================================
# Step 1: Load the model
# ===========================================================================
print("=" * 70)
print("Step 1: Loading Qwen3.5-0.8B for OCR GRPO Training")
print("=" * 70)
model, processor = FastOCRModel.from_pretrained(
"mlx-community/Qwen3.5-0.8B-bf16",
# Other models for OCR GRPO:
# "mlx-community/GLM-OCR-4bit" # 0.9B, dedicated OCR
# "mlx-community/DeepSeek-OCR-8bit" # 0.9B, dedicated OCR
# "mlx-community/Qwen2.5-VL-7B-Instruct-4bit" # 7B, strong vision
)
# ===========================================================================
# Step 2: Add LoRA
# ===========================================================================
print("\n" + "=" * 70)
print("Step 2: Adding LoRA Adapters")
print("=" * 70)
model = FastOCRModel.get_peft_model(
model,
finetune_vision_layers=True,
finetune_language_layers=True,
r=16,
lora_alpha=16,
)
# ===========================================================================
# Step 3: Prepare GRPO dataset
# ===========================================================================
print("\n" + "=" * 70)
print("Step 3: Loading LaTeX OCR Dataset for GRPO")
print("=" * 70)
from datasets import load_dataset
raw_dataset = load_dataset("unsloth/LaTeX_OCR", split="train[:20]")
print(f"Loaded {len(raw_dataset)} samples for GRPO training")
# GRPO dataset format: each sample has prompt (image + instruction) + answer
instruction = "Write the LaTeX representation for this image."
grpo_dataset = []
for sample in raw_dataset:
grpo_dataset.append({
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": instruction},
{"type": "image", "image": sample["image"]},
],
},
],
"answer": sample["text"], # Ground truth for reward computation
})
print(f"Prepared {len(grpo_dataset)} GRPO samples")
# ===========================================================================
# Step 4: Define reward function
# ===========================================================================
print("\n" + "=" * 70)
print("Step 4: Defining OCR Reward Functions")
print("=" * 70)
# Demonstrate individual rewards
sample_pred = r"\frac{1}{2}"
sample_ref = r"\frac{1}{2}"
print(f"Perfect match:")
print(f" CER reward: {cer_reward(sample_pred, sample_ref):.3f}")
print(f" EM reward: {exact_match_reward(sample_pred, sample_ref):.3f}")
print(f" Combined: {combined_ocr_reward(sample_pred, sample_ref):.3f}")
sample_pred_bad = r"\frac{1}{3}"
print(f"\nOne char error:")
print(f" CER reward: {cer_reward(sample_pred_bad, sample_ref):.3f}")
print(f" EM reward: {exact_match_reward(sample_pred_bad, sample_ref):.3f}")
print(f" Combined: {combined_ocr_reward(sample_pred_bad, sample_ref):.3f}")
# Custom reward function that weights format correctness
def latex_ocr_reward(response: str, ground_truth: str) -> float:
"""Custom reward: CER accuracy + bonus for matching LaTeX structure."""
base = cer_reward(response, ground_truth)
# Bonus for having matching LaTeX delimiters
structure_bonus = 0.0
for pair in [("{", "}"), (r"\frac", ""), (r"\sum", ""), (r"\int", "")]:
if pair[0] in ground_truth and pair[0] in response:
structure_bonus += 0.05
return min(1.0, base * 0.8 + structure_bonus + exact_match_reward(response, ground_truth) * 0.15)
# ===========================================================================
# Step 5: GRPO Training
# ===========================================================================
print("\n" + "=" * 70)
print("Step 5: GRPO Training with CER Reward")
print("=" * 70)
FastOCRModel.for_training(model)
trainer = OCRGRPOTrainer(
model=model,
train_dataset=grpo_dataset,
processor=processor,
reward_fn=latex_ocr_reward,
args=OCRGRPOConfig(
num_generations=2,
temperature=0.7,
max_completion_length=256,
learning_rate=1e-6,
max_steps=10,
logging_steps=1,
output_dir="ocr_grpo_outputs",
),
)
trainer.train()
# ===========================================================================
# Step 6: Post-training evaluation
# ===========================================================================
print("\n" + "=" * 70)
print("Step 6: Post-Training Evaluation")
print("=" * 70)
FastOCRModel.for_inference(model)
test_images = [raw_dataset[i]["image"] for i in range(min(5, len(raw_dataset)))]
test_refs = [raw_dataset[i]["text"] for i in range(min(5, len(raw_dataset)))]
metrics = model.evaluate(
images=test_images,
references=test_refs,
prompt=instruction,
max_tokens=256,
)
# ===========================================================================
# Step 7: Save
# ===========================================================================
print("\n" + "=" * 70)
print("Step 7: Saving")
print("=" * 70)
model.save_pretrained("ocr_grpo_lora")
print("GRPO-trained LoRA adapters saved to ocr_grpo_lora/")
print("\n" + "=" * 70)
print("Done! OCR GRPO training complete.")
print("=" * 70)