course
Unsloth का NVIDIA सहयोग छिपे हुए ट्रेनिंग ओवरहेड को घटाकर फाइन-ट्यूनिंग को तेज करने पर केंद्रित है। केवल बड़े GPU या छोटे मॉडल पर निर्भर रहने के बजाय, सुधार ट्रेनिंग प्रक्रिया के भीतर की बाधाओं को लक्ष्य बनाते हैं, जैसे बार-बार मेटाडेटा बनाना, एक्टिवेशन रीलोड देरी, और अक्षम टोकन रूटिंग। उपयोगकर्ताओं के लिए इसका मतलब है समर्थित NVIDIA GPUs पर तेज ट्रेनिंग और अधिक सुगम फाइन-ट्यूनिंग वर्कफ़्लो।
इस गाइड में, हम इन नई Unsloth प्रदर्शन सुधारों के बारे में सीखेंगे और Unsloth के ऑप्टिमाइज़्ड फाइन-ट्यूनिंग वर्कफ़्लो को एक व्यावहारिक विज़न-लैंग्वेज कार्य पर लागू करेंगे। हम Qwen3.5 Vision 4B को मेडिकल OCR के लिए फाइन-ट्यून करेंगे, जहाँ मॉडल एक छोटे, मेडिकल-लुकिंग OCR डेटासेट सबसेट का उपयोग करते हुए मेडिकल डॉक्यूमेंट इमेज से संरचित टेक्स्ट निकालना सीखता है।
हम उपयोग करेंगे:
- Qwen3.5 4B (Vision) को बेस मॉडल के रूप में
- 4-बिट QLoRA VRAM उपयोग कम करने के लिए
- LoRA एडॉप्टर्स कुशल फाइन-ट्यूनिंग के लिए
- Unsloth ग्रेडिएंट चेकपॉइंटिंग ट्रेनिंग के दौरान मेमोरी बचाने के लिए
- मेडिकल OCR डेटासेट का 300-नमूनों का सबसेट
- स्मूद विज़न ट्रेनिंग के लिए फिक्स्ड-साइज़ इमेज प्रीप्रोसेसिंग
- पहले और बाद का मूल्यांकन ताकि बेस और फाइन-ट्यून किए गए मॉडल के आउटपुट की तुलना की जा सके
Unsloth का NVIDIA-ऑप्टिमाइज़्ड फाइन-ट्यूनिंग वर्कफ़्लो उपयोग करना
फाइन-ट्यूनिंग शुरू करने से पहले, यह समझना उपयोगी है कि Unsloth का NVIDIA सहयोग किन चीज़ों में सुधार करता है और यह इस गाइड से कैसे जुड़ा है।
Unsloth का कहना है कि इसका NVIDIA सहयोग LLM ट्रेनिंग को लगभग 25% तेज बनाता है, वह भी बिना सटीकता खोए, और यह इसके पहले से मौजूद 2–5x फाइन-ट्यूनिंग स्पीडअप के ऊपर है। ये लाभ मॉडल के लर्निंग उद्देश्य को बदले बिना मुख्य ट्रेनिंग प्रक्रिया के आस-पास के छिपे ओवरहेड को घटाने से आते हैं। दूसरे शब्दों में, लक्ष्य सटीकता बनाए रखते हुए फाइन-ट्यूनिंग को तेज और अधिक कुशल बनाना है।

स्रोत: How to Make LLM Training Faster with Unsloth and NVIDIA
सुधरी हुई ट्रेनिंग परफॉर्मेंस
सहयोग कई परफॉर्मेंस सुधारों की रिपोर्ट करता है, जिनमें शामिल हैं:
- पैक्ड-सीक्वेंस मेटाडेटा कैशिंग के जरिए Qwen3-14B QLoRA SFT बेंचमार्क पर प्रति बैच 14.3% तेज
- डबल-बफर्ड एसिंक ग्रेडिएंट चेकपॉइंटिंग से 8B मॉडलों पर 8.4% स्पीडअप, 14B पर 6.7%, और 32B पर 4.6%
- GPT-OSS MoE ट्रेनिंग के लिए लगभग 10–15% स्पीडअप, लक्षित रूटिंग पथ में 23% तेज फॉरवर्ड और 13% तेज बैकवर्ड
Unsloth और NVIDIA सहयोग से मिलने वाले सबसे बड़े प्रदर्शन लाभों में से कुछ पैक्ड टेक्स्ट-ओनली ट्रेनिंग और Mixture-of-Experts मॉडलों पर लागू होते हैं। हम इस गाइड में उनका उपयोग नहीं कर रहे हैं क्योंकि हमारा वर्कफ़्लो Qwen3.5 Vision OCR फाइन-ट्यूनिंग पर केंद्रित है।
इस गाइड में, हम NVIDIA RTX 3090 GPU का उपयोग कर रहे हैं, इसलिए वर्कफ़्लो NVIDIA GPU एक्सेलेरेशन और Unsloth के ऑप्टिमाइज़्ड फाइन-ट्यूनिंग पथ के इर्द-गिर्द बना है। हम Unsloth की किसी अन्य ट्रेनर से बेंचमार्किंग नहीं कर रहे हैं, इसलिए इस गाइड को रिपोर्ट किए गए स्पीडअप का स्वतंत्र प्रमाण न माना जाए। इसके बजाय, हम Unsloth का ऑप्टिमाइज़्ड फाइन-ट्यूनिंग वर्कफ़्लो एक वास्तविक विज़न-लैंग्वेज कार्य पर लागू कर रहे हैं।
ग्रेडिएंट चेकपॉइंटिंग
इस वर्कफ़्लो के लिए सबसे प्रासंगिक ऑप्टिमाइज़ेशन Unsloth की ग्रेडिएंट चेकपॉइंटिंग है। यह ट्रेनिंग के दौरान हर एक्टिवेशन को GPU मेमोरी में स्टोर करने की आवश्यकता से बचकर मेमोरी उपयोग घटाने में मदद करती है। यह विशेष रूप से विज़न-लैंग्वेज फाइन-ट्यूनिंग के लिए उपयोगी है, जहाँ मॉडल को इमेज इनपुट और टेक्स्ट आउटपुट दोनों को प्रोसेस करना पड़ता है।
1. तेज फाइन-ट्यूनिंग के लिए Unsloth सेट करना
इस गाइड को चलाने के लिए आपको NVIDIA GPU की आवश्यकता है। आप RunPod, Vast.ai, या किसी भी अन्य क्लाउड GPU प्रोवाइडर से एक GPU किराए पर ले सकते हैं। मैंने शुरू में RunPod का उपयोग करने की कोशिश की क्योंकि यह आमतौर पर तेज और भरोसेमंद है, लेकिन उस समय उपलब्ध RTX 3090 विकल्प सीमित थे। इसलिए मैंने इस वर्कफ़्लो के लिए Vast.ai RTX 3090 GPU मशीन का उपयोग किया।
विभिन्न प्लेटफ़ॉर्म की तुलना के लिए, हमारे गाइड को देखें: the best GPU cloud providers.

स्रोत: Vast.ai | Console
इंस्टेंस लॉन्च करने के बाद, मैंने Jupyter Notebook खोला और एक नया नोटबुक बनाया। Vast.ai पर, मैंने उपलब्ध मुख्य एनवायरनमेंट कर्नेल चुना ताकि आवश्यक Python पैकेजों को नोटबुक एनवायरनमेंट में स्थापित कर सकूँ, बिना सिस्टम-स्तरीय डिपेंडेंसीज़ को प्रभावित किए।
आवश्यक पैकेज इंस्टॉल करना
सबसे पहले, Unsloth, PyTorch, विज़न मॉडल ट्रेनिंग, डेटासेट लोडिंग, और Hugging Face इंटेग्रेशन के लिए आवश्यक पैकेज इंस्टॉल करें:
!pip install --upgrade \
"torch>=2.8.0" "triton>=3.4.0" \
numpy pillow torchvision bitsandbytes \
unsloth "unsloth_zoo>=2026.4.6" \
"datasets>=4.0.0" huggingface_hub hf_transfer pandas \
transformers==5.2.0 torchcodec timm
ये पैकेज आधिकारिक Unsloth नोटबुक सेटअप पर आधारित हैं ��र Qwen3.5 Vision लोड करने, इमेज-टेक्स्ट डेटा तैयार करने, और Unsloth के साथ मॉडल को फाइन-ट्यून करने के लिए आवश्यक प्रमुख लाइब्रेरीज़ शामिल करते हैं।
CUDA डिवाइस कॉन्फ़िगर करना
अगले चरण में, हम CUDA डिवाइस कॉन्फ़िगर करते हैं और सत्यापित करते हैं कि सही NVIDIA GPU उपलब्ध है। चूँकि इस गाइड में RTX 3090 का उपयोग होता है, कोड जाँचता है कि CUDA सक्षम है या नहीं, चयनित GPU की पुष्टि करता है, CUDA और PyTorch वर्शन प्रिंट करता है, और सुनिश्चित करता है कि इस प्रयोग के लिए मशीन में पर्याप्त VRAM हो।
import os
import platform
CUDA_DEVICE_INDEX = 0
TARGET_GPU_NAME = "3090"
# Must be set before CUDA / Unsloth are initialized. Restart the kernel if you change these.
os.environ["CUDA_VISIBLE_DEVICES"] = str(CUDA_DEVICE_INDEX)
# RunPod + Qwen3.5 Vision OCR can hit Torch Dynamo fullgraph recompile limits.
# This disables Unsloth's torch.compile path while keeping Unsloth model loading,
# LoRA, gradient checkpointing, collator, and 8-bit optimizer benefits.
os.environ["UNSLOTH_COMPILE_DISABLE"] = "1"
os.environ["TORCH_COMPILE_DISABLE"] = "1"
import torch
DEVICE = torch.device("cuda:0")
print("Python:", platform.python_version())
print("PyTorch:", torch.__version__)
print("CUDA available:", torch.cuda.is_available())
if not torch.cuda.is_available():
raise RuntimeError("CUDA is not available. Select a GPU instance before continuing.")
torch.cuda.set_device(0)
props = torch.cuda.get_device_properties(0)
gpu_name = torch.cuda.get_device_name(0)
total_gpu_memory_gb = props.total_memory / 1024**3
print("Selected device:", DEVICE)
print("GPU:", gpu_name)
print("CUDA version:", torch.version.cuda)
print("BF16 supported:", torch.cuda.is_bf16_supported())
print("Total GPU memory:", round(total_gpu_memory_gb, 2), "GB")
if TARGET_GPU_NAME not in gpu_name:
raise RuntimeError(f"Expected an RTX {TARGET_GPU_NAME}, but CUDA device 0 is: {gpu_name}")
if total_gpu_memory_gb < 20:
raise RuntimeError(f"Expected a 24 GB class 3090, but only found {total_gpu_memory_gb:.2f} GB VRAM.")
मेरी सेटअप में, एनवायरनमेंट ने निम्न GPU कॉन्फ़िगरेशन लौटाया:
Python: 3.12.13
PyTorch: 2.12.0+cu130
CUDA available: True
Selected device: cuda:0
GPU: NVIDIA GeForce RTX 3090
CUDA version: 13.0
BF16 supported: True
Total GPU memory: 23.56 GB
यह पुष्टि करता है कि नोटबुक NVIDIA GeForce RTX 3090 पर पर्याप्त VRAM के साथ फाइन-ट्यूनिंग प्रयोग के लिए चल रहा है।
ट्रेनिंग सेटिंग्स और प्रॉम्प्ट्स परिभाषित करना
GPU सत्यापित करने के बाद, हम मॉडल, डेटासेट, ट्रेनिंग सेटिंग्स, आउटपुट डायरेक्टरीज़, इमेज साइज़, और OCR प्रॉम्प्ट्स परिभाषित करते हैं।
MODEL_NAME = "unsloth/Qwen3.5-4B"
DATASET_NAME = "naazimsnh02/medocr-vision-dataset"
SAMPLE_COUNT = 300
EVAL_INDEX = 0
MAX_LENGTH = 4096
MAX_STEPS = 30
PER_DEVICE_BATCH_SIZE = 4
GRADIENT_ACCUMULATION_STEPS = 2
LEARNING_RATE = 2e-4
SEED = 3407
OUTPUT_DIR = "outputs/qwen35_vision_medical_ocr"
ADAPTER_DIR = "qwen35-vision-medical-ocr-lora"
# Medical document images vary heavily in size. Fixed-size canvases avoid
# repeated Torch Dynamo recompiles during vision training.
# 768x1024 is a practical portrait-page compromise for a 24 GB 3090 smoke test.
FIXED_IMAGE_SIZE = (768, 1024)
# Official Unsloth Qwen3.5 Vision notebook uses False here for 16-bit LoRA.
# Set True only if you hit VRAM limits.
LOAD_IN_4BIT = True
SYSTEM_PROMPT = "You are a medical OCR transcription engine. Return only the exact text visible in the medical document image."
INSTRUCTION = "Extract all readable text from this medical document exactly. Preserve structure when possible. Return only the OCR text, with no explanation, no diagnosis, no medical advice, and no reasoning."
यहाँ, हम Unsloth से Qwen3.5 Vision 4B और मेडिकल OCR विज़न डेटासेट का उपयोग करते हैं। इस गाइड के लिए, हम 300 सैंपल चुनते हैं और 30 स्टेप्स तक ट्रेन करते हैं, जो रन को हल्का रखता है, फिर भी दिखाता है कि मॉडल लक्षित OCR फ़ॉर्मैट के अनुसार कैसे ढलता है।
768×1024 का फिक्स्ड इमेज साइज़ ट्रेनिंग के दौरान इमेज इनपुट को सुसंगत रखने में मदद करता है। मेडिकल दस्तावेज़ रेज़ोल्यूशन और आस्पेक्ट रेशियो में बहुत भिन्न हो सकते हैं, इसलिए उन्हें फिक्स्ड कैनवास में री-साइज़ करना वर्कफ़्लो को स्मूद बनाता है और विज़न-लैंग्वेज फाइन-ट्यूनिंग के दौरान शेप-संबंधी समस्याएँ घटाता है।
2. मॉडल लोड करना
अब जब एनवायरनमेंट तैयार है, हम Unsloth के FastVisionModel का उपयोग करके Qwen3.5 Vision 4B मॉडल लोड कर सकते हैं।
import unsloth
from unsloth import FastVisionModel
torch.cuda.set_device(0)
model, tokenizer = FastVisionModel.from_pretrained(
MODEL_NAME,
load_in_4bit=LOAD_IN_4BIT,
use_gradient_checkpointing="unsloth",
)
print("Loaded:", MODEL_NAME)
print("4-bit:", LOAD_IN_4BIT)
print("Model device:", next(model.parameters()).device)
मॉडल लोड करने के बाद, आउटपुट पुष्टि करता है कि सही मॉडल लोड हुआ है, 4-बिट मोड सक्षम है, और मॉडल GPU पर रखा गया है:
Loaded: unsloth/Qwen3.5-4B
4-bit: True
Model device: cuda:0
यहाँ, FastVisionModel.from_pretrained() विज़न-लैंग्वेज मॉडल लोड करता है और तेज तथा अधिक मेमोरी-कुशल फाइन-ट्यूनिंग के लिए Unsloth के ऑप्टिमाइज़ेशन लागू करता है। हम load_in_4bit भी सक्षम करते हैं, जो मॉडल को 4-बिट प्रिसीजन में लोड करके VRAM उपयोग घटाता है। यह RTX 3090 जैसे 24 GB GPU के साथ काम करते समय उपयोगी है।
हम Unsloth ग्रेडिएंट चेकपॉइंटिंग भी use_gradient_checkpointing="unsloth" के साथ सक्षम करते हैं।
यह ट्रेनिंग के दौरान मेमोरी उपयोग घटाने में मदद करता है, जो विशेष रूप से विज़न-लैंग्वेज मॉडलों के लिए महत्वपूर्ण है क्योंकि वे इमेज और टेक्स्ट, दोनों इनपुट प्रोसेस करते हैं।
3. LoRA एडॉप्टर्स जोड़ें
अगले चरण में, हम मॉडल में LoRA एडॉप्टर्स जोड़ते हैं। LoRA हमें पूरे मॉडल को अपडेट करने के बजाय छोटे सेट के ट्रेन करने योग्य पैरामीटर फाइन-ट्यून करने देता है। इससे ट्रेनिंग तेज, अधिक मेमोरी-कुशल, और एकल GPU पर चलाने में आसान हो जाती है।
model = FastVisionModel.get_peft_model(
model,
finetune_vision_layers=True,
finetune_language_layers=True,
finetune_attention_modules=True,
finetune_mlp_modules=True,
r=16,
lora_alpha=16,
lora_dropout=0,
bias="none",
random_state=SEED,
use_rslora=False,
loftq_config=None,
)
इस गाइड के लिए, एडॉप्टर्स मॉडल के विज़न और लैंग्वेज दोनों हिस्सों में जोड़े गए हैं। यह मॉडल को मेडिकल डॉक्यूमेंट इमेज पढ़ना और अपेक्षित संरचित OCR टेक्स्ट तैयार करना सीखने में मदद करता है। इस चरण के बाद, मॉडल मेडिकल OCR डेटासेट पर ट्रेन होने के लिए तैयार है।
4. मेडिकल OCR डेटासेट लोड करें
अब हम Hugging Face से मेडिकल OCR डेटासेट लोड करते हैं और फाइन-ट्यूनिंग के लिए एक छोटा सबसेट तैयार करते हैं।
from datasets import load_dataset
from PIL import Image
raw_dataset = load_dataset(DATASET_NAME, split="train")
MEDICAL_KEYWORDS = [
"doctor", "dr.", "clinic", "hospital", "patient", "medication",
"medications", "prescription", "signature", "department", "report",
"diagnosis", "lab", "laboratory", "blood", "hemoglobin", "mg", "dose",
"<s_ocr>",
]
डेटासेट में डॉक्यूमेंट इमेज और उसके अनुरूप OCR टेक्स्ट शामिल है। चूँकि हम इस गाइड के लिए केवल मेडिकल-स्टाइल OCR उदाहरण चाहते हैं, हम सरल कीवर्ड-आधारित तरीके से डेटासेट को फ़िल्टर करते हैं। कोड मेडिकल दस्तावेज़ों में आम तौर पर पाए जाने वाले शब्दों को खोजता है, जैसे doctor, clinic, patient, medication, prescription, diagnosis, और dosage-संबंधी शब्द।
def looks_medical(sample):
text = str(sample.get("text", "")).lower()
return any(keyword in text for keyword in MEDICAL_KEYWORDS)
medical_indices = []
for idx, sample in enumerate(raw_dataset):
if looks_medical(sample):
medical_indices.append(idx)
if len(medical_indices) >= SAMPLE_COUNT:
break
if not medical_indices:
raise RuntimeError("No medical-looking OCR samples found. Broaden MEDICAL_KEYWORDS or inspect the dataset text field.")
print(f"Selected {len(medical_indices)} medical-looking samples.")
यह हमें मेडिकल OCR कार्य के लिए प्रासंगिक दिखने वाले उदाहरणों का हल्का-फुल्का चयन करने का तरीका देता है। इस रन में, हम 300 मेडिकल-लुकिंग सैंपल्स चुनते हैं।
अगले चरण में, हम प्रत्येक इमेज को 768×1024 के फिक्स्ड कैनवास में नॉर्मलाइज़ करते हैं। मेडिकल डॉक्यूमेंट इमेज अलग-अलग साइज़ और आस्पेक्ट रेशियो की हो सकती हैं, इसलिए यह चरण ट्रेनिंग डेटा को अधिक सुसंगत बनान��� में मदद करता है। इमेज को उसके मूल आस्पेक्ट रेशियो को बनाए रखते हुए री-साइज़ किया जाता है, फिर उसे सफेद पृष्ठभूमि पर रखा जाता है।
def normalize_ocr_image(image, size=FIXED_IMAGE_SIZE):
image = image.convert("RGB")
target_w, target_h = size
scale = min(target_w / image.width, target_h / image.height)
new_w = max(1, int(image.width * scale))
new_h = max(1, int(image.height * scale))
resized = image.resize((new_w, new_h), Image.Resampling.LANCZOS)
canvas = Image.new("RGB", size, "white")
left = (target_w - new_w) // 2
top = (target_h - new_h) // 2
canvas.paste(resized, (left, top))
return canvas
datasets.map का उपयोग करने के बजाय, हम मैन्युअली एक सरल Python सूची बनाते हैं। यह कुछ क्लाउड नोटबुक एनवायरनमेंट में PIL इमेज फिर से लिखते समय संभावित हैंगिंग समस्याओं से बचाता है।
dataset = []
for idx in medical_indices:
sample = raw_dataset[idx]
dataset.append(
{
"image": normalize_ocr_image(sample["image"]),
"text": sample["text"],
}
)
print("Examples:", len(dataset))
print("Columns:", list(dataset[0].keys()))
print("Fixed image size:", dataset[EVAL_INDEX]["image"].size)
print("Sample text:", dataset[EVAL_INDEX]["text"])
प्रीप्रोसेसिंग के बाद, प्रत्येक उदाहरण में दो फ़ील्ड होते हैं: नॉर्मलाइज़्ड इमेज और लक्ष्य OCR टेक्स्ट।
Examples: 300
Columns: ['image', 'text']
Fixed image size: (768, 1024)
Sample text: <s_ocr> doctor_name: Dr. A. Smith clinic_name: Meadowview Health clinic_address: 45 Oak Ave. patient_name: John Doe patient_age: 35 date: 2024-12-16 medications: - Hydrochlorothiazide 25 mg - Before meals signature: Dr. A. Smith </s>
हम री-साइज़ किए गए उदाहरणों में से एक का प्रीव्यू भी देख सकते हैं:
dataset[EVAL_INDEX]["image"].resize((384, 512))
प्रीव्यू एक मेडिकल-स्टाइल डॉक्यूमेंट इमेज दिखाता है जिसमें क्लिनिक विवरण, डॉक्टर का नाम, रोगी की जानकारी, दवा, और हस्ताक्षर शामिल हैं। यह पुष्टि करता है कि डेटासेट OCR फाइन-ट्यूनिंग कार्य के लिए उपयुक्त है।

5. सैंपल्स को विज़न कन्वर्सेशन्स में बदलें
अब जब डेटासेट लोड हो चुका है और इमेज नॉर्मलाइज़ हो चुकी हैं, हमें प्रत्येक उदाहरण को Qwen3.5 Vision द्वारा अपेक्षित कन्वर्सेशन फ़ॉर्मैट में बदलना है।
प्रत्येक ट्रेनिंग सैंपल में तीन हिस्से शामिल होने चाहिए:
- एक सिस्टम संदेश जो मॉडल की भूमिका को मेडिकल OCR ट्रांसक्रिप्शन इंजन के रूप में परिभाषित करता है
- एक यूज़र संदेश जिसमें इमेज और OCR निर्देश शामिल हों
- एक असिस्टेंट संदेश जिसमें अपेक्षित OCR आउटपुट हो
def build_ocr_messages(image=None, target_text=None, instruction=INSTRUCTION):
user_content = [
{"type": "image"},
{"type": "text", "text": instruction},
]
if image is not None:
user_content[0]["image"] = image
messages = [
{"role": "system", "content": [{"type": "text", "text": SYSTEM_PROMPT}]},
{"role": "user", "content": user_content},
]
if target_text is not None:
messages.append(
{
"role": "assistant",
"content": [{"type": "text", "text": target_text}],
}
)
return messages
ऊपर दिया गया हेल्पर फ़ंक्शन ट्रेनिंग और इंफ़रेंस दोनों के लिए संदेश संरचना बनाता है। ट्रेनिंग के दौरान, हम लक्ष्य OCR टेक्स्ट को असिस्टेंट प्रतिक्रिया के रूप में शामिल करते हैं। इंफ़रेंस के दौरान, हम केवल इमेज और निर्देश प्रदान करते हैं, फिर मॉडल से OCR टेक्स्ट जेनरेट करने को कहते हैं।
अगले चरण में, हम हर डेटासेट सैंपल को इस कन्वर्सेशन फ़ॉर्मैट में बदलते हैं:
def convert_to_conversation(sample):
return {
"messages": build_ocr_messages(
image=sample["image"],
target_text=sample["text"],
)
}
converted_dataset = [convert_to_conversation(sample) for sample in dataset]
converted_dataset[0]
कन्वर्ज़न के बाद, प्रत्येक सैंपल में संदेशों की सूची होती है। पहला उदाहरण सिस्टम प्रॉम्प्ट, मेडिकल डॉक्यूमेंट इमेज, OCR निर्देश, और अपेक्षित संरचित OCR ट्रांसक्रिप्शन शामिल करता है। यह फ़ॉर्मैट मॉडल को यह सीखने देता है कि इमेज और निर्देश को सही टेक्स्ट आउटपुट से कैसे मैप करना है।
{'messages': [{'role': 'system',
'content': [{'type': 'text',
'text': 'You are a medical OCR transcription engine. Return only the exact text visible in the medical document image.'}]},
{'role': 'user',
'content': [{'type': 'image',
'image': <PIL.Image.Image image mode=RGB size=768x1024>},
{'type': 'text',
'text': 'Extract all readable text from this medical document exactly. Preserve structure when possible. Return only the OCR text, with no explanation, no diagnosis, no medical advice, and no reasoning.'}]},
{'role': 'assistant',
'content': [{'type': 'text',
'text': '<s_ocr> doctor_name: Dr. A. Smith clinic_name: Meadowview Health clinic_address: 45 Oak Ave. patient_name: John Doe patient_age: 35 date: 2024-12-16 medications: - Hydrochlorothiazide 25 mg - Before meals signature: Dr. A. Smith </s>'}]}]}
6. फाइन-ट्यूनिंग से पहले बेस मॉडल का मूल्यांकन
ट्रेनिंग से पहले, हमें एक OCR उदाहरण पर बेस मॉडल का परीक्षण करना चाहिए। यह हमें एक रेफरेंस पॉइंट देता है ताकि हम फाइन-ट्यूनिंग से पहले और बाद के मॉडल आउटपुट की तुलना कर सकें।
सबसे पहले, हम मॉडल के चैट टेम्पलेट को लागू करने के लिए एक हेल्पर फ़ंक्शन परिभाषित करते हैं। कुछ टोकनाइज़र वर्शन enable_thinking=False को सपोर्ट करते हैं, जबकि अन्य नहीं करते, इसलिए फ़ंक्शन में एक फॉलबैक शामिल है ताकि कोड संगत बना रहे।
def render_ocr_chat_template(tokenizer, messages):
try:
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=False,
)
except TypeError:
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
)
अगले चरण में, हम जेनरेशन फ़ंक्शन परिभाषित करते हैं। यह OCR प्रॉम्प्ट बनाता है, टोकनाइज़र को इमेज और टेक्स्ट निर्देश दोनों पास करता है, मॉडल आउटपुट जेनरेट करता है, और केवल नए जेनरेट किए गए टोकन डिकोड करता है।
def generate_ocr_text(model, tokenizer, image, instruction=INSTRUCTION, max_new_tokens=512):
messages = build_ocr_messages(instruction=instruction)
input_text = render_ocr_chat_template(tokenizer, messages)
inputs = tokenizer(
images=image,
text=input_text,
add_special_tokens=False,
return_tensors="pt",
).to(DEVICE)
with torch.inference_mode():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
use_cache=True,
do_sample=False,
temperature=None,
top_p=None,
)
prompt_length = inputs["input_ids"].shape[-1]
generated_tokens = outputs[:, prompt_length:]
return tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
अब हम मॉडल को इंफ़रेंस मोड में स्विच करते हैं और पहले मूल्यांकन इमेज के लिए OCR टेक्स्ट जेनरेट करते हैं:
FastVisionModel.for_inference(model)
eval_image = dataset[EVAL_INDEX]["image"]
base_output = generate_ocr_text(model, tokenizer, eval_image)
print("Target:")
print(dataset[EVAL_INDEX]["text"])
print("\nBase model output:")
print(base_output)
बेस मॉडल आउटपुट पठनीय है, लेकिन यह लक्ष्य संरचना का ठीक-ठीक पालन नहीं करता:
Target:
<s_ocr> doctor_name: Dr. A. Smith clinic_name: Meadowview Health clinic_address: 45 Oak Ave. patient_name: John Doe patient_age: 35 date: 2024-12-16 medications: - Hydrochlorothiazide 25 mg - Before meals signature: Dr. A. Smith </s>
Base model output:
Meadowview Health
45 Oak Ave.
Prescribed by: Dr. A. Smith
Date: 2024-12-16
Patient: John Doe, Age: 35
Hydrochlorothiazide 25 mg - Before meals
Signature: Dr. A. Smith
यह एक उपयोगी शुरुआती बिंदु है। बेस मॉडल पहले से ही दस्तावेज़ का बहुत कुछ पढ़ सकता है, लेकिन यह डेटासेट में उपयोग किए गए संरचित फ़ॉर्मैट के बजाय नैचुरल OCR शैली में टेक्स्ट आउटपुट करता है। फाइन-ट्यूनिंग को लक्ष्य फ़ॉर्मैट के साथ मॉडल को संरेखित करने और प्रतिक्रियाओं को अधिक सुसंगत बनाने में मदद करनी चाहिए।
7. मॉडल को ट्रेन करना
अब जब डेटासेट सही विज़न-कन्वर्सेशन फ़ॉर्मैट में है, हम Unsloth के विज़न डेटा कोलेटर के साथ TRL के SFTTrainer का उपयोग करके मॉडल को ट्रेन कर सकते हैं।
from unsloth.trainer import UnslothVisionDataCollator
from trl import SFTTrainer, SFTConfig
FastVisionModel.for_training(model)
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
data_collator=UnslothVisionDataCollator(model, tokenizer),
train_dataset=converted_dataset,
args=SFTConfig(
per_device_train_batch_size=PER_DEVICE_BATCH_SIZE,
gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS,
warmup_steps=5,
max_steps=MAX_STEPS,
learning_rate=LEARNING_RATE,
logging_steps=1,
optim="adamw_8bit",
weight_decay=0.001,
lr_scheduler_type="linear",
seed=SEED,
output_dir=OUTPUT_DIR,
report_to="none",
remove_unused_columns=False,
dataset_text_field="",
dataset_kwargs={"skip_prepare_dataset": True},
max_length=MAX_LENGTH,
),
)
trainer_stats = trainer.train()
सबसे पहले, हम FastVisionModel.for_training(model) के साथ मॉडल को ट्रेनिंग मोड में स्विच करते हैं। फिर, हम कन्वर्टेड OCR डेटासेट का उपयोग करके ट्रेनर बनाते हैं।
यहाँ महत्वपूर्ण हिस्सा UnslothVisionDataCollator है। चूँकि यह विज़न-लैंग्वेज कार्य है, ट्रेनर को मेडिकल डॉक्यूमेंट इमेज और लक्ष्य OCR टेक्स्ट, दोनों को सही ढंग से संभालना होता है। कोलेटर इन मल्टीमोडल उदाहरणों को सुपरवाइज़्ड फाइन-ट्यूनिंग के लिए मॉडल को पास करने हेतु तैयार करता है।
इस गाइड में, हम 30 स्टेप्स के लिए ट्रेन करते हैं, प्रति-डिवाइस बैच साइज़ 4 और ग्रेडिएंट अक्यूम्यूलेशन 2 के साथ, जिससे प्रभावी बैच साइज़ 8 बनता है। यह रन को हल्का रखता है, फिर भी दिखाता है कि मॉडल संरचित OCR फ़ॉर्मैट के अनुसार ढलना कैसे शुरू करता है।

ट्रेनिंग के दौरान, Unsloth सेटअप के बारे में उपयोगी जानकारी प्रिंट करता है, जिनमें उदाहरणों, स्टेप्स और बैचों की संख्या, ट्रेन करने योग्य पैरामीटरों की संख्या, और मेमोरी-बचत सुविधाएँ शामिल हैं। इस रन में, Unsloth रिपोर्ट करता है कि बैकवर्ड पास के लिए डबल बफ़रिंग सक्षम है, जो ग्रेडिएंट चेकपॉइंटिंग के दौरान प्रतीक्षा समय घटाने में मदद करता है।
8. फाइन-ट्यून किए गए मॉडल का मूल्यांकन
ट्रेनिंग के बाद, हम मॉडल को फिर से इंफ़रेंस मोड में स्विच करते हैं और वही मूल्यांकन इमेज के लिए OCR टेक्स्ट जेनरेट करते हैं जिसका उपयोग फाइन-ट्यूनिंग से पहले किया गया था।
FastVisionModel.for_inference(model)
fine_tuned_output = generate_ocr_text(model, tokenizer, eval_image)
print("Target:")
print(dataset[EVAL_INDEX]["text"])
print("\nBase model output:")
print(base_output)
print("\nFine-tuned output:")
print(fine_tuned_output)
फाइन-ट्यूनिंग के बाद, मॉडल आउटपुट डेटासेट के लक्ष्य फ़ॉर्मैट के काफी करीब है:
Target:
<s_ocr> doctor_name: Dr. A. Smith clinic_name: Meadowview Health clinic_address: 45 Oak Ave. patient_name: John Doe patient_age: 35 date: 2024-12-16 medications: - Hydrochlorothiazide 25 mg - Before meals signature: Dr. A. Smith </s>
Base model output:
Meadowview Health
45 Oak Ave.
Prescribed by: Dr. A. Smith
Date: 2024-12-16
Patient: John Doe, Age: 35
Hydrochlorothiazide 25 mg - Before meals
Signature: Dr. A. Smith
Fine-tuned output:
<s_ocr> doctor_name: Dr. A. Smith clinic_name: Meadowview Health clinic_address: 45 Oak Ave. patient_name: John Doe patient_age: 35 date: 2024-12-16 medications: - Hydrochlorothiazide 25 mg - Before meals signature: Dr. A. Smith </s>
यह दिखाता है कि फाइन-ट्यून किए गए मॉडल ने अपेक्षित OCR प्रतिक्रिया फ़ॉर्मैट सीख लिया है। बेस मॉडल पहले से ही अधिकांश दृश्य टेक्स्ट निकाल सकता था, लेकिन फाइन-ट्यूनिंग ने आउटपुट को ट्रेनिंग डेटा में उपयोग किए गए संरचित फ़ॉर्मैट के अनुरूप संरेखित करने में मदद की।
हम मॉडल को डेटासेट के किसी अन्य उदाहरण पर भी टेस्ट कर सकते हैं:
EVAL_INDEX_2 = 35
eval_image_2 = dataset[EVAL_INDEX_2]["image"]
fine_tuned_output = generate_ocr_text(model, tokenizer, eval_image_2)
print("Target:")
print(dataset[EVAL_INDEX]["text"])
print("\nFine-tuned output:")
print(fine_tuned_output)
इस दूसरे उदाहरण में, मॉडल अपेक्षित संरचना का पालन करता है, लेकिन एक छोटा OCR गलती करता है—Amlodipine के स्थान पर Amoxicillin जेनरेट कर देता है:
Target:
<s_ocr> doctor_name: Dr. C. Rossi clinic_name: Riverside Clinic clinic_address: 45 Oak Ave. patient_name: Wei Li patient_age: 70 date: 2024-12-16 medications: - Acetaminophen 20 mg - Take twice daily - Amlodipine 20 mg - After meals signature: Dr. C. Rossi </s>
Fine-tuned output:
<s_ocr> doctor_name: Dr. C. Rossi clinic_name: Riverside Clinic clinic_address: 45 Oak Ave. patient_name: Wei Li patient_age: 70 date: 2024-12-16 medications: - Acetaminophen 20 mg - Take twice daily - Amoxicillin 20 mg - After meals signature: Dr. C. Rossi </s>
यह याद दिलाता है कि मॉडल फ़ॉर्मैट एलाइनमेंट में सुधर रहा है, लेकिन OCR सटीकता अभी भी डेटा गुणवत्ता, इमेज स्पष्टता, ट्रेनिंग साइज़, और फाइन-ट्यूनिंग स्टेप्स की संख्या पर निर्भर करती है। प्रोडक्शन OCR सिस्टम के लिए, आप बड़े, विविध डेटासेट पर ट्रेन करेंगे और व्यापक उदाहरणों पर सटीकता का मूल्यांकन करेंगे।
9. फाइन-ट्यून किए गए एडॉप्टर को सेव करना
ट्रेनिंग पूरा होने पर, हम LoRA एडॉप्टर और टोकनाइज़र को लोकल रूप से सेव करते हैं।
model.save_pretrained(ADAPTER_DIR)
tokenizer.save_pretrained(ADAPTER_DIR)
print("Saved adapter to:", ADAPTER_DIR)
आउटपुट पुष्टि करता है कि एडॉप्टर सेव हो गया है:
Saved adapter to: qwen35-vision-medical-ocr-lora
यह केवल फाइन-ट्यून किए गए एडॉप्टर वेट्स सेव करता है, बेस मॉडल की पूरी कॉपी नहीं। बाद में, आप बेस Qwen3.5-4B मॉडल को फिर से लोड कर सकते हैं और इस एडॉप्टर को लागू कर सकते हैं ताकि फाइन-ट्यू�� किए गए OCR व्यवहार का पुन: उपयोग किया जा सके। इससे सेव किया गया मॉडल हल्का, स्टोर, शेयर, या डिप्लॉय करने में आसान बनता है।
अंतिम विचार
ट्रेनिंग प्रक्रिया एकल NVIDIA RTX 3090 पर हल्की और व्यावहारिक थी। भले ही विज़न-लैंग्वेज फाइन-ट्यूनिंग आमतौर पर मेमोरी-इंटेंसिव होती है, रन ने अपेक्षा से कहीं कम VRAM का उपयोग किया। अधिकतम VRAM उपयोग लगभग 14 GB के आसपास था, जबकि औसत 9 GB के करीब रहा, जो Qwen3.5 Vision मॉडल को फाइन-ट्यून करने के लिए प्रभावशाली है।
मॉडल भी तेजी से अनुकूलित हुआ। केवल कुछ ट्रेनिंग स्टेप्स के बाद, आउटपुट लक्ष्य OCR संरचना के काफी करीब आ गया। बेस मॉडल दस्तावेज़ पढ़ सकता था, लेकिन फाइन-ट्यूनिंग के बाद उसने डेटासेट फ़ॉर्मैट का अधिक सुसंगत रूप से पालन किया।
यह कहा जाना चाहिए कि सेटअप अनुभव परिपूर्ण नहीं था। Unsloth इंस्टॉल करना बहुत ट्रायल-एंड-एरर वाला रहा। इसे सही ढंग से कॉन्फ़िगर करना मुश्किल हो सकता है, खासकर तब जब अलग-अलग लोकल एनवायरनमेंट, वर्चुअल एनवायरनमेंट, CUDA वर्शन, और क्लाउड GPU प्रोवाइडर्स के बीच काम कर रहे हों।
कुछ मामलों में, CUDA संगतता समस्याएँ एनवायरनमेंट को तोड़ सकती हैं, और उन समस्याओं को डिबग करना अपेक्षा से अधिक समय ले सकता है। यहाँ तक कि क्लाउड GPU प्लेटफ़ॉर्म पर Unsloth Docker इमेज से शुरू करना भी समय लेने वाला हो सकता है यदि एनवायरनमेंट बॉक्स से बाहर साफ़-सुथरे ढंग से काम नहीं करता।
एक और महत्वपूर्ण सीख यह है कि मॉडल टेम्पलेट मायने रखता है। यदि डेटासेट को सही चैट या विज़न-कन्वर्सेशन फ़ॉर्मैट में कन्वर्ट नहीं किया गया, तो मॉडल सही ढंग से सीख नहीं पाएगा। Qwen3.5 Vision के लिए, सही इमेज-टेक्स्ट मैसेज संरचना का उपयोग आवश्यक है। सही टेम्पलेट के बिना, ट्रेनिंग चल सकती है, लेकिन मॉडल वास्तव में कार्य के अनुरूप अनुकूलित नहीं हो सकता।
कुल मिलाकर, Unsloth सीमित GPU एक्सेस वाले उपयोगकर्ताओं के लिए एक मजबूत विकल्प है जो लोकल मशीनों या किराए के GPUs पर कुशलता से मॉडल फाइन-ट्यून करना चाहते हैं। यह मेमोरी उपयोग घटाता है, छोटे हार्डवेयर को अधिक उपयोगी बनाता है, और प्रयोगों को तेज कर सकता है। हालाँकि, जो उपयोगकर्ता नियमित रूप से मॉडल फाइन-ट्यून और ट्रेन करते हैं, उनके लिए सेटअप जटिलता निराशाजनक हो सकती है। मानक Transformers-आधारित ट्रेनिंग अक्सर अधिक स्थिर, इंस्टॉल करने में आसान, और एनवायरनमेंट्स के बीच दोहराने में सरल होती है।
यदि इंस्टॉलेशन की झंझट आपको हतोत्साहित करती है, तो मैं हमारा Unsloth Studio गाइड पढ़ने की सलाह देता हूँ, जो दिखाता है कि Unsloth के लोकल वेब UI में बिना मैनुअल एनवायरनमेंट सेटअप के Qwen3.5-9B को कैसे फाइन-ट्यून करें।