← Kembali ke Blog

Fine-Tune LLM Lokal dengan Unsloth dan Jalankan di Ollama

Model kamu tahu fakta yang benar tapi terus jawab dengan tone yang salah. Harga nggak pernah keluar dengan format yang bisnis mau, dan format output yang tim ops kamu harapkan nggak pernah konsisten. Kamu tumpul-in system prompt, eh dalam beberapa turn dia balik lagi ke kelakuannya.

Ini masalah perilaku, bukan masalah pengetahuan. Dan RAG nggak memperbaiki perilaku. RAG nambahin fakta, bukan kebiasaan. Kalau masalahnya adalah gimana model nulis, format, atau ngikutin konvensi domain kamu, fine-tuning adalah alat yang tepat.

Kabar baiknya: ini nggak butuh GPU cluster. Dengan QLoRA kamu bisa adaptasi model 7B di satu GPU dengan VRAM 8-16GB. Colab T4 gratis, yang punya 16GB, udah cukup. Panduan ini ngasih pipeline lengkap: dataset kecil yang bersih, Unsloth buat training, export ke GGUF, lalu jalanin lokal lewat Ollama.

Prerequisites

  • Python 3.10+ dengan pip
  • GPU dengan minimal 8GB VRAM. Colab T4 gratis (16GB) bisa
  • Dataset training format JSONL, yang kamu bikin di langkah 2
  • Ollama terinstall di mesin kamu buat langkah terakhir

Langkah 1: Putuskan dulu apakah kamu beneran butuh fine-tuning

Fine-tuning mengubah perilaku dan format output. Dia nggak nambahin fakta baru ke model. Kalau masalah kamu adalah model nggak tahu informasi terbaru, pakai RAG atau search tool dan lewatin training run. Kalau masalahnya model nggak mau ngikutin tone, schema, atau konvensi penulisan domain kamu, fine-tune.

Ini cara pikir yang bikin saya nggak buang-buang training run:

  • Prompt engineering: perilakunya udah deket tapi masih goyah di tepi. Coba dulu, gratis.
  • RAG: model kurang fakta terbaru atau fakta privat. Tambah retrieval.
  • Fine-tuning: modelnya konsisten nolak nulis sesuai yang domain kamu mau, sekeras apa pun di-prompt. Train.

Langkah 2: Bikin dataset kecil dulu

SFT butuh pasangan prompt-completion. Jangan mulai dari seratus ribu baris hasil scrape yang berantakan. Beberapa ratus contoh yang beneran kamu periksa menang telak dari beberapa ribu contoh berisik di kebanyakan kasus domain kecil.

Bikin dataset.jsonl dengan satu objek JSON per baris:

{"instruction": "Ubah ID ini jadi tracking link: ORD-1042", "output": "Paket kamu sedang dalam perjalanan. Lacak di sini: https://portal.example.com/ORD-1042"}
{"instruction": "Ubah ID ini jadi tracking link: ORD-1199", "output": "Paket kamu sedang dalam perjalanan. Lacak di sini: https://portal.example.com/ORD-1199"}

Bikin 5-30 dari contoh itu jadi hard case yang sekarang masih sering salah, bukan cuma yang gampang. Model belajar sebanding dengan isi file, jadi perilaku yang paling sulit butuh contoh paling banyak.

Langkah 3: Install Unsloth

pip install unsloth

Unsloth itu pembungkus di atas Hugging Face Transformers dan PEFT. Dia nambahin custom kernels yang ngurangin VRAM dan mempercepat training LoRA dan QLoRA, itu yang bikin model 7B bisa training di GPU kelas konsumen.

Langkah 4: Load base model dan pasang LoRA

Load base model di 4-bit lalu tempel adapter LoRA. Set load_in_4bit=True inilah yang bikin ini jadi QLoRA:

import torch
from unsloth import FastLanguageModel, is_bfloat16_supported

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/Qwen2.5-7B",
    max_seq_length=2048,
    dtype=None,  # auto-deteksi fp16 di T4, bf16 di Ampere+
    load_in_4bit=True,
)

model = FastLanguageModel.get_peft_model(
    model,
    r=16,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    lora_alpha=16,
    lora_dropout=0,
    bias="none",
    use_gradient_checkpointing="unsloth",
    random_state=3407,
)

Pakai unsloth/Qwen2.5-1.5B atau varian 3B kalau 7B nggak muat di kartu kamu. r=16 itu default yang masuk akal. Naikin nilainya bukan jaminan hasil lebih bagus, cuma nambah parameter yang ditrain dan makan memori.

Langkah 5: Train pakai SFTTrainer dari TRL

Ubah JSONL kamu ke format percakapan, lalu serahin ke SFTTrainer dari TRL. Trainer otomatis nerapin chat template model:

from datasets import load_dataset

raw = load_dataset("json", data_files="dataset.jsonl")["train"]

def to_messages(example):
    return {
        "messages": [
            {"role": "user", "content": example["instruction"]},
            {"role": "assistant", "content": example["output"]},
        ]
    }

ds = raw.map(to_messages, remove_columns=raw.column_names)

from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=ds,
    args=SFTConfig(
        max_seq_length=2048,
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        num_train_epochs=3,
        learning_rate=2e-4,
        warmup_ratio=0.1,
        optim="adamw_8bit",
        logging_steps=10,
        output_dir="./outputs",
    ),
)

trainer.train()

Learning rate 2e-4 ini sekitar 10x dari rate full fine-tuning, yang emang dibutuhin LoRA karena dia cuma update sebagian kecil parameter. Di Colab T4, beberapa ratus contoh selesai dalam sekitar satu jam. Pantau training loss; kalau berhenti turun setelah beberapa ratus step, dataset kamu terlalu berisik atau terlalu repetitif.

Langkah 6: Simpan adapter dan test dulu sebelum lanjut

model.save_pretrained("lora_model")

Test adapter yang udah ditrain di prompt yang dulu sering gagal:

from unsloth import FastLanguageModel

ft_model, ft_tok = FastLanguageModel.from_pretrained("lora_model")
FastLanguageModel.for_inference(ft_model)

messages = [{"role": "user", "content": "Ubah ID ini jadi tracking link: ORD-1042"}]
inputs = ft_tok.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True, return_tensors="pt"
).to(model.device)

outputs = ft_model.generate(**inputs, max_new_tokens=512)
print(ft_tok.decode(outputs[0]))

Kalau outputnya masih nggak ngikutin format kamu, tambah contoh buat kasus yang gagal dan train ulang. Iterasi di beberapa ratus contoh itu cepat dan murah, jadi kerjain sekarang, bukan setelah kamu bikin wrapper di sekitar model yang masih salah.

Langkah 7: Export ke GGUF dan jalanin dengan Ollama

Export adapter sebagai file GGUF dan biar Ollama yang jalanin. GGUF itu format yang dipahami llama.cpp dan Ollama, jadi ini jalan di laptop CPU setelah training.

model.save_pretrained_gguf("lora_gguf", tokenizer, quantization_method="q8_0")

Unsloth otomatis nulis Modelfile di folder itu. Lalu daftarin dan jalanin:

ollama create qwen2.5-finetuned --model lora_gguf/Modelfile
ollama run qwen2.5-finetuned

Dari sini model fine-tune kamu jadi model biasa di Ollama. Aplikasi apa pun yang udah ngomong ke endpoint Ollama lokal bisa pakai tanpa ganti kode.

Kapan fine-tune vs alternatif lain

Yang mau kamu ubah Pakai Kenapa
Tone, schema, konvensi penulisan domain Fine-tune Prompt cepet kendor; training bikin perilaku baru jadi default
Model kurang fakta baru atau privat RAG Lebih murah, bisa diupdate, nggak perlu training
Perubahan phrasing kecil Prompt engineering Gratis, iterasi cepat

Langkah selanjutnya

  • Mulai dari satu domain sempit dan beberapa ratus contoh terperiksa sebelum diperluas
  • Coba DPO berikutnya buat data preferensi kalau kamu punya human feedback, ini menyetir output tanpa butuh jawaban pasti
  • Benchmark model fine-tune kamu lawan base model di contoh hold-out sendiri, bukan cuma dari kesan

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis