Lewati ke konten utama

Gradient Checkpointing: Fine-Tuning Efisien dengan Unsloth dan NVIDIA

Pelajari bagaimana gradient checkpointing dan QLoRA yang dioptimalkan NVIDIA oleh Unsloth mengurangi penggunaan VRAM saat fine-tuning Qwen3.5 4B untuk OCR dokumen medis.
Diperbarui 14 Agu 2026  · 12 mnt baca

Jelajahi dengan AI

ChatGPTClaudePerplexity

Kolaborasi Unsloth dengan NVIDIA berfokus untuk mempercepat fine-tuning dengan mengurangi overhead tersembunyi dalam pelatihan. Alih-alih hanya mengandalkan GPU yang lebih besar atau model yang lebih kecil, peningkatan ini menargetkan hambatan di dalam proses pelatihan, seperti konstruksi metadata berulang, jeda pemuatan ulang aktivasi, dan routing token yang tidak efisien. Bagi pengguna, ini berarti pelatihan lebih cepat dan alur kerja fine-tuning yang lebih mulus pada GPU NVIDIA yang didukung.

Dalam panduan ini, kita akan mempelajari peningkatan performa baru Unsloth tersebut dan menerapkan alur kerja fine-tuning yang dioptimalkan Unsloth pada tugas vision-language yang praktis. Kita akan melakukan fine-tuning Qwen3.5 Vision 4B untuk OCR medis, di mana model belajar mengekstrak teks terstruktur dari gambar dokumen medis menggunakan subset kecil dari dataset OCR yang menyerupai dokumen medis.

Kita akan menggunakan:

  • Qwen3.5 4B (Vision) sebagai model dasar
  • QLoRA 4-bit untuk mengurangi penggunaan VRAM
  • Adapter LoRA untuk fine-tuning yang efisien
  • Unsloth gradient checkpointing untuk menghemat memori saat pelatihan
  • Subset 300 sampel dari dataset OCR medis
  • Praproses gambar berukuran tetap untuk pelatihan visi yang lebih mulus
  • Evaluasi sebelum-dan-sesudah untuk membandingkan keluaran model dasar dan yang telah di-fine-tune

Menggunakan Alur Kerja Fine-Tuning yang Dioptimalkan NVIDIA oleh Unsloth

Sebelum mulai fine-tuning, ada baiknya memahami apa yang ditingkatkan melalui kolaborasi Unsloth dengan NVIDIA dan bagaimana kaitannya dengan panduan ini.

Unsloth melaporkan bahwa kolaborasinya dengan NVIDIA membuat pelatihan LLM sekitar 25% lebih cepat tanpa kehilangan akurasi, di atas peningkatan kecepatan fine-tuning 2–5x yang sudah ada. Peningkatan ini berasal dari pengurangan overhead tersembunyi di sekitar proses pelatihan utama, bukan mengubah objektif pembelajaran model. Dengan kata lain, tujuannya adalah membuat fine-tuning lebih cepat dan efisien sambil menjaga akurasi tetap sama.

Sumber: Cara Membuat Pelatihan LLM Lebih Cepat dengan Unsloth dan NVIDIA

Sumber: How to Make LLM Training Faster with Unsloth and NVIDIA 

Peningkatan performa pelatihan

Kolaborasi ini melaporkan beberapa peningkatan performa, termasuk:

  • 14,3% lebih cepat per batch pada benchmark Qwen3-14B QLoRA SFT melalui caching metadata packed-sequence
  • Peningkatan 8,4% pada model 8B, 6,7% pada 14B, dan 4,6% pada 32B dari gradient checkpointing asinkron double-buffered
  • Sekitar 10–15% peningkatan kecepatan untuk pelatihan GPT-OSS MoE, dengan 23% lebih cepat pada forward dan 13% lebih cepat pada backward di jalur routing yang ditargetkan

Beberapa peningkatan performa terbesar dari kolaborasi Unsloth dan NVIDIA berlaku untuk pelatihan teks-only yang dipacking dan model Mixture-of-Experts. Kita tidak menggunakan itu dalam panduan ini karena alur kerja kita berfokus pada fine-tuning Qwen3.5 Vision untuk OCR.

Dalam panduan ini, kita menggunakan GPU NVIDIA RTX 3090, sehingga alur kerja dibangun di atas akselerasi GPU NVIDIA dan jalur fine-tuning yang dioptimalkan Unsloth. Kita tidak melakukan benchmarking Unsloth terhadap pelatih lain, jadi panduan ini tidak boleh dibaca sebagai bukti independen atas peningkatan kecepatan yang dilaporkan. Sebaliknya, kita menerapkan alur kerja fine-tuning Unsloth yang dioptimalkan pada tugas vision-language nyata.

Gradient checkpointing

Untuk alur kerja ini, optimisasi yang paling relevan adalah gradient checkpointing dari Unsloth. Ini membantu mengurangi penggunaan memori saat pelatihan dengan menghindari kebutuhan untuk menyimpan setiap aktivasi di memori GPU. Ini sangat berguna untuk fine-tuning vision-language, di mana model harus memproses masukan gambar dan keluaran teks secara bersamaan.

1. Menyiapkan Unsloth untuk Fine-Tuning yang Lebih Cepat

Untuk menjalankan panduan ini, Anda memerlukan akses ke GPU NVIDIA. Anda dapat menyewanya dari platform seperti RunPod, Vast.ai, atau penyedia cloud GPU lainnya. Saya awalnya mencoba menggunakan RunPod karena biasanya cepat dan andal, tetapi saat itu opsi RTX 3090 yang tersedia terbatas. Karena itu, saya menggunakan mesin GPU Vast.ai RTX 3090 untuk alur kerja ini.

Untuk perbandingan berbagai platform, lihat panduan kami tentang penyedia cloud GPU terbaik.

Instans Vast.ai RTX 3090

Sumber: Vast.ai | Console 

Setelah meluncurkan instans, saya membuka Jupyter Notebook dan membuat notebook baru. Di Vast.ai, saya memilih kernel lingkungan utama yang tersedia untuk menginstal paket Python yang diperlukan di lingkungan notebook tanpa memengaruhi dependensi tingkat sistem.

Menginstal paket yang diperlukan

Pertama, instal paket yang diperlukan untuk Unsloth, PyTorch, pelatihan model visi, pemuatan dataset, dan integrasi 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

Paket-paket ini didasarkan pada setelan notebook resmi Unsloth dan mencakup pustaka utama yang dibutuhkan untuk memuat Qwen3.5 Vision, menyiapkan data gambar-teks, serta melakukan fine-tuning model dengan Unsloth.

Mengonfigurasi perangkat CUDA

Selanjutnya, kita mengonfigurasi perangkat CUDA dan memastikan GPU NVIDIA yang benar tersedia. Karena panduan ini menggunakan RTX 3090, kode memeriksa apakah CUDA diaktifkan, mengonfirmasi GPU yang dipilih, mencetak versi CUDA dan PyTorch, serta memverifikasi bahwa mesin memiliki VRAM yang cukup untuk eksperimen ini.

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.")

Dalam pengaturan saya, lingkungan mengembalikan konfigurasi GPU berikut:

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

Ini mengonfirmasi bahwa notebook berjalan pada NVIDIA GeForce RTX 3090 dengan VRAM yang cukup untuk eksperimen fine-tuning.

Menetapkan pengaturan pelatihan dan prompt

Setelah memverifikasi GPU, kita menentukan model, dataset, pengaturan pelatihan, direktori keluaran, ukuran gambar, dan prompt 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."

Di sini, kita menggunakan Qwen3.5 Vision 4B dari Unsloth dan dataset visi OCR medis. Untuk panduan ini, kita memilih 300 sampel dan melatih selama 30 langkah, yang membuat proses tetap ringan sambil tetap menunjukkan bagaimana model beradaptasi dengan format OCR target.

Ukuran gambar tetap 768×1024 membantu menjaga konsistensi masukan gambar selama pelatihan. Dokumen medis dapat sangat bervariasi dalam resolusi dan rasio aspek, sehingga mengubah ukurannya ke kanvas tetap membuat alur kerja lebih mulus dan mengurangi masalah terkait bentuk selama fine-tuning vision-language.

2. Memuat Model

Setelah lingkungan siap, kita dapat memuat model Qwen3.5 Vision 4B menggunakan FastVisionModel milik Unsloth.

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)

Setelah model dimuat, keluaran mengonfirmasi bahwa model yang benar telah dimuat, mode 4-bit diaktifkan, dan model ditempatkan di GPU:

Loaded: unsloth/Qwen3.5-4B
4-bit: True
Model device: cuda:0

Di sini, FastVisionModel.from_pretrained() memuat model vision-language dan menerapkan optimisasi Unsloth untuk fine-tuning yang lebih cepat dan lebih hemat memori. Kita juga mengaktifkan load_in_4bit, yang mengurangi penggunaan VRAM dengan memuat model dalam presisi 4-bit. Ini berguna saat bekerja dengan GPU 24 GB seperti RTX 3090.

Kita juga mengaktifkan gradient checkpointing Unsloth dengan use_gradient_checkpointing="unsloth"

Ini membantu mengurangi penggunaan memori saat pelatihan, yang sangat penting untuk model vision-language karena mereka memproses masukan gambar dan teks.

3. Menambahkan Adapter LoRA

Selanjutnya, kita menambahkan adapter LoRA ke model. LoRA memungkinkan kita melakukan fine-tuning pada subset parameter yang dapat dilatih alih-alih memperbarui seluruh model. Ini membuat pelatihan lebih cepat, lebih hemat memori, dan lebih mudah dijalankan pada satu 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,
)

Untuk panduan ini, adapter ditambahkan ke bagian visi dan bahasa dari model. Ini membantu model belajar membaca gambar dokumen medis dan menghasilkan teks OCR terstruktur yang diharapkan. Setelah langkah ini, model siap dilatih pada dataset OCR medis.

4. Memuat Dataset OCR Medis

Sekarang kita memuat dataset OCR medis dari Hugging Face dan menyiapkan subset kecil untuk fine-tuning.

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>",
]

Dataset ini berisi gambar dokumen dan teks OCR yang bersesuaian. Karena kita hanya menginginkan contoh OCR bergaya medis untuk panduan ini, kita memfilter dataset menggunakan pendekatan sederhana berbasis kata kunci. Kode mencari istilah yang umum ditemukan dalam dokumen medis, seperti doctor, clinic, patient, medication, prescription, diagnosis, dan kata-kata terkait dosis.

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.")

Ini memberikan cara yang ringan untuk memilih contoh yang tampak relevan dengan tugas OCR medis. Untuk run ini, kita memilih 300 sampel yang tampak seperti dokumen medis.

Selanjutnya, kita menormalkan setiap gambar ke kanvas tetap 768×1024. Gambar dokumen medis dapat memiliki ukuran dan rasio aspek yang berbeda, jadi langkah ini membantu membuat data pelatihan lebih konsisten. Gambar diubah ukurannya sambil mempertahankan rasio aspek asli, lalu ditempatkan di latar belakang putih.

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

Alih-alih menggunakan datasets.map, kita secara manual membangun daftar Python sederhana. Ini menghindari potensi hang di beberapa lingkungan notebook cloud saat menulis ulang gambar 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"])

Setelah praproses, setiap contoh berisi dua field: gambar yang dinormalisasi dan teks OCR target.

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>

Kita juga dapat melakukan pratinjau salah satu contoh yang telah diubah ukurannya:

dataset[EVAL_INDEX]["image"].resize((384, 512))

Pratinjau menampilkan gambar dokumen bergaya medis dengan detail klinik, nama dokter, informasi pasien, obat, dan tanda tangan. Ini mengonfirmasi bahwa dataset sesuai untuk tugas fine-tuning OCR.

Resep dokter.

5. Mengonversi Sampel ke Percakapan Vision

Setelah dataset dimuat dan gambar dinormalisasi, kita perlu mengonversi setiap contoh ke format percakapan yang diharapkan oleh Qwen3.5 Vision.

Setiap sampel pelatihan harus mencakup tiga bagian:

  • Pesan sistem yang mendefinisikan peran model sebagai mesin transkripsi OCR medis
  • Pesan pengguna yang berisi gambar dan instruksi OCR
  • Pesan asisten yang berisi keluaran OCR yang diharapkan
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

Fungsi pembantu di atas membuat struktur pesan untuk pelatihan dan inferensi. Selama pelatihan, kita menyertakan teks OCR target sebagai respons asisten. Selama inferensi, kita hanya memberikan gambar dan instruksi, lalu meminta model menghasilkan teks OCR.

Selanjutnya, kita konversi setiap sampel dataset ke format percakapan ini:

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]

Setelah konversi, setiap sampel berisi daftar pesan. Contoh pertama mencakup system prompt, gambar dokumen medis, instruksi OCR, dan transkripsi OCR terstruktur yang diharapkan. Format ini memungkinkan model belajar memetakan gambar dan instruksi ke keluaran teks yang benar.

{'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. Mengevaluasi Model Dasar Sebelum Fine-Tuning

Sebelum pelatihan, kita perlu menguji model dasar pada satu contoh OCR. Ini memberi titik acuan sehingga kita dapat membandingkan keluaran model sebelum dan sesudah fine-tuning.

Pertama, kita mendefinisikan fungsi pembantu untuk menerapkan chat template model. Beberapa versi tokenizer mendukung enable_thinking=False, sementara yang lain tidak, jadi fungsi ini menyertakan fallback agar kode tetap kompatibel.

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,
        )

Berikutnya, kita mendefinisikan fungsi generasi. Fungsi ini membangun prompt OCR, meneruskan gambar dan instruksi teks ke tokenizer, menghasilkan keluaran model, dan mendekode hanya token yang baru dihasilkan.

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]

Sekarang kita ubah model ke mode inferensi dan menghasilkan teks OCR untuk gambar evaluasi pertama:

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)

Keluaran model dasar dapat dibaca, tetapi belum mengikuti struktur target secara persis:

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

Ini merupakan titik awal yang berguna. Model dasar sudah dapat membaca sebagian besar dokumen, tetapi mengeluarkan teks dalam gaya OCR alami alih-alih format terstruktur yang digunakan di dataset. Fine-tuning seharusnya membantu menyelaraskan model dengan format target dan membuat responsnya lebih konsisten.

7. Melatih Model

Sekarang setelah dataset berada dalam format percakapan visi yang benar, kita dapat melatih model menggunakan SFTTrainer dari TRL dengan collator data visi milik Unsloth.

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()

Pertama, kita ubah model ke mode pelatihan dengan FastVisionModel.for_training(model). Lalu, kita membuat trainer menggunakan dataset OCR yang telah dikonversi.

Bagian penting di sini adalah UnslothVisionDataCollator. Karena ini adalah tugas vision-language, trainer perlu menangani gambar dokumen medis dan teks OCR target dengan benar. Collator menyiapkan contoh multimodal ini untuk diteruskan ke model selama supervised fine-tuning.

Untuk panduan ini, kita melatih selama 30 langkah dengan batch size per perangkat 4 dan akumulasi gradien 2, memberikan batch size efektif 8. Ini menjaga proses tetap ringan sambil tetap menunjukkan bagaimana model mulai beradaptasi dengan format OCR terstruktur.

Fine-tuning model bahasa Qwen 3.5 4b Vision

Selama pelatihan, Unsloth mencetak informasi berguna tentang setelan, termasuk jumlah contoh, langkah, dan batch, jumlah parameter yang dapat dilatih, serta fitur penghematan memori. Pada run ini, Unsloth melaporkan bahwa double buffering diaktifkan untuk backward pass, yang membantu mengurangi waktu tunggu selama gradient checkpointing.

8. Mengevaluasi Model yang Telah Di-Fine-Tune

Setelah pelatihan, kita ubah kembali model ke mode inferensi dan menghasilkan teks OCR untuk gambar evaluasi yang sama seperti sebelum fine-tuning.

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)

Setelah fine-tuning, keluaran model jauh lebih mendekati struktur target dataset:

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>

Ini menunjukkan bahwa model yang telah di-fine-tune telah mempelajari format respons OCR yang diharapkan. Model dasar sudah dapat mengekstrak sebagian besar teks yang terlihat, tetapi fine-tuning membantu menyelaraskan keluaran dengan format terstruktur yang digunakan dalam data pelatihan.

Kita juga dapat menguji model pada contoh lain dari dataset:

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)

Untuk contoh kedua ini, model mengikuti struktur yang diharapkan, tetapi membuat kesalahan OCR kecil dengan menghasilkan Amoxicillin alih-alih Amlodipine:

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>

Ini mengingatkan kita bahwa model semakin baik dalam penyelarasan format, namun akurasi OCR tetap bergantung pada kualitas data, kejelasan gambar, ukuran pelatihan, dan jumlah langkah fine-tuning. Untuk sistem OCR produksi, Anda akan melatih pada dataset yang lebih besar dan beragam serta mengevaluasi akurasi pada beragam contoh.

9. Menyimpan Adapter yang Telah Di-Fine-Tune

Setelah pelatihan selesai, kita menyimpan adapter LoRA dan tokenizer secara lokal.

model.save_pretrained(ADAPTER_DIR)
tokenizer.save_pretrained(ADAPTER_DIR)

print("Saved adapter to:", ADAPTER_DIR)

Keluaran mengonfirmasi bahwa adapter telah disimpan:

Saved adapter to: qwen35-vision-medical-ocr-lora

Ini hanya menyimpan bobot adapter hasil fine-tuning, bukan salinan penuh model dasar. Nantinya, Anda dapat memuat ulang model dasar Qwen3.5-4B dan menerapkan adapter ini untuk menggunakan kembali perilaku OCR yang telah di-fine-tune. Ini membuat model yang disimpan menjadi ringan dan lebih mudah untuk disimpan, dibagikan, atau diterapkan.

Pemikiran Akhir

Proses pelatihan ini ringan dan praktis pada satu NVIDIA RTX 3090. Meskipun fine-tuning vision-language biasanya intensif memori, run ini menggunakan VRAM jauh lebih sedikit dari perkiraan. Penggunaan VRAM maksimum sekitar 14 GB, sementara rata-rata mendekati 9 GB—cukup impresif untuk fine-tuning model Qwen3.5 Vision.

Model juga beradaptasi dengan cepat. Setelah hanya beberapa langkah pelatihan, keluaran menjadi jauh lebih dekat dengan struktur OCR target. Model dasar sudah bisa membaca dokumen, tetapi setelah fine-tuning, model mengikuti format dataset dengan lebih konsisten.

Namun demikian, pengalaman penyiapan tidak selalu mulus. Menginstal Unsloth membutuhkan banyak trial and error. Konfigurasinya bisa sulit dilakukan dengan benar, terutama saat bekerja lintas lingkungan lokal, lingkungan virtual, versi CUDA, dan penyedia cloud GPU.

Dalam beberapa kasus, masalah kompatibilitas CUDA dapat merusak lingkungan, dan men-debug masalah tersebut bisa memakan waktu lebih lama dari perkiraan. Bahkan memulai dengan image Docker Unsloth di platform GPU cloud bisa memakan waktu jika lingkungannya tidak berjalan mulus sejak awal.

Pelajaran penting lainnya adalah bahwa template model itu penting. Jika dataset tidak dikonversi ke format chat atau percakapan visi yang benar, model mungkin tidak belajar dengan semestinya. Untuk Qwen3.5 Vision, menggunakan struktur pesan gambar-teks yang tepat adalah hal yang esensial. Tanpa template yang benar, pelatihan mungkin berjalan, tetapi model belum tentu benar-benar beradaptasi dengan tugas.

Secara keseluruhan, Unsloth adalah opsi yang kuat bagi pengguna dengan akses GPU terbatas yang ingin melakukan fine-tuning model secara efisien pada mesin lokal atau GPU sewaan. Unsloth mengurangi penggunaan memori, membuat perangkat keras yang lebih kecil lebih bermanfaat, dan dapat mempercepat eksperimen. Namun, bagi pengguna yang secara rutin melakukan fine-tuning dan pelatihan model, kompleksitas penyiapan bisa membuat frustrasi. Pelatihan berbasis Transformers standar sering kali lebih stabil, lebih mudah diinstal, dan lebih sederhana direproduksi lintas lingkungan.

Jika gesekan saat instalasi adalah hal yang membuat Anda ragu, saya sarankan membaca panduan Unsloth Studio kami, yang menunjukkan cara melakukan fine-tuning Qwen3.5-9B tanpa penyiapan lingkungan manual di UI web lokal Unsloth.


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Sebagai data scientist tersertifikasi, saya bersemangat memanfaatkan teknologi mutakhir untuk menciptakan aplikasi machine learning yang inovatif. Dengan latar belakang kuat di pengenalan ucapan, analisis dan pelaporan data, MLOps, conversational AI, dan NLP, saya mengasah keterampilan dalam mengembangkan sistem cerdas yang berdampak nyata. Selain keahlian teknis, saya juga komunikator andal yang mampu menyederhanakan konsep kompleks menjadi bahasa yang jelas dan ringkas. Karena itu, saya menjadi blogger yang dicari di bidang data science, membagikan wawasan dan pengalaman kepada komunitas profesional data yang terus berkembang. Saat ini, saya berfokus pada pembuatan dan penyuntingan konten, bekerja dengan large language model untuk mengembangkan konten yang kuat dan menarik agar membantu bisnis dan individu memaksimalkan data mereka.

Topik

Kursus AI Teratas

Kursus

Pengantar Deep Learning dengan PyTorch

4 Hr
88.5K
Pelajari cara membangun neural network pertama Anda, menyesuaikan hyperparameter, dan mengatasi masalah klasifikasi serta regresi di PyTorch.
Lihat DetailRight Arrow
Mulai Kursus
Lihat Lebih BanyakRight Arrow
Terkait

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

Lihat Lebih BanyakLihat Lebih Banyak