Views
No views yet
Qwen/Qwen2-VL-2B-Instruct for reading handwritten
fields from Indian university OMR answer sheets (Part-D: registration
number, roll number, course code).| base model | Qwen/Qwen2-VL-2B-Instruct |
| method | LoRA (r=16, α=32, dropout=0.05) on q_proj,k_proj,v_proj,o_proj |
| starting weights | continued from prior adapter (v1) — not from scratch |
| dataset | 1500 OMR sheets × 3 fields, human-corrected via Django labeling tool |
| split | 80/20 by sheet (no leakage) → 3414 train / 859 eval rows |
| epochs | 3 |
| batch size | 1 (per-device) × 8 grad-accum = effective 8 |
| learning rate | 5e-5 (lower than v1 since starting from a trained adapter) |
| warmup | 0.03 |
| precision | fp16 on Apple Silicon MPS |
| gradient checkpointing | on |
| total steps | 1281 |
| final train loss | 0.014 (running avg) |
| wall-clock | ~6 hours on M-series MPS |
1from peft import PeftModel
2from transformers import AutoProcessor, Qwen2VLForConditionalGeneration
3from PIL import Image
4import torch
5
6BASE = "Qwen/Qwen2-VL-2B-Instruct"
7ADAPTER = "kshitizjangra/qwen2vl-omr-lora-v2"
8
9processor = AutoProcessor.from_pretrained(BASE, trust_remote_code=True)
10base = Qwen2VLForConditionalGeneration.from_pretrained(BASE, dtype=torch.float16, trust_remote_code=True)
11model = PeftModel.from_pretrained(base, ADAPTER).to("mps").eval()
12
13img = Image.open("path/to/roll_no.jpg").convert("RGB")
14messages = [{"role": "user", "content": [
15 {"type": "image", "image": img},
16 {"type": "text", "text": "Read the handwritten value. Output only the value."},
17]}]
18text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
19inputs = processor(text=[text], images=[[img]], return_tensors="pt").to("mps")
20with torch.no_grad():
21 out = model.generate(**inputs, max_new_tokens=64, do_sample=False)
22print(processor.tokenizer.decode(out[0][inputs.input_ids.shape[1]:], skip_special_tokens=True))