$ init portfolio
R
Back to Blog
Fine-TuningLLMHugging Face

Fine-Tuning LLMs: A Practical Guide

How to fine-tune large language models for your specific use case without breaking the bank.

Raj Tiwari
Raj Tiwari
2026-07-106 min read
Fine-Tuning LLMs: A Practical Guide

When to Fine-Tune

Fine-tuning is NOT always the answer. Try these first:

  • Prompt engineering — Can you get better results with a better prompt?
  • RAG — Can you add relevant context?
  • Fine-tuning — Only when you need specific behavior, style, or domain knowledge.

Full Fine-Tuning vs LoRA

  • Full fine-tuning: Update all parameters. Expensive, needs lots of data.
  • LoRA (Low-Rank Adaptation): Update a small subset of parameters. Cheap, fast, almost as good.
python
class="text-purple-400 font-medium">from peft class="text-purple-400 font-medium">import LoraConfig, get_peft_model
class="text-purple-400 font-medium">from transformers class="text-purple-400 font-medium">import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(class="text-emerald-class="text-amber-300">400">"meta-llama/Llama-class="text-amber-300">2-7b")

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># LoRA config
lora_config = LoraConfig(
    r=class="text-amber-300">16,                    class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Rank
    lora_alpha=class="text-amber-300">32,           class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Scaling factor
    target_modules=[class="text-emerald-class="text-amber-300">400">"q_proj", class="text-emerald-class="text-amber-300">400">"v_proj", class="text-emerald-class="text-amber-300">400">"k_proj", class="text-emerald-class="text-amber-300">400">"o_proj"],
    lora_dropout=class="text-amber-300">0.05,
    bias=class="text-emerald-class="text-amber-300">400">"none",
    task_type=class="text-emerald-class="text-amber-300">400">"CAUSAL_LM"
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># trainable params: class="text-amber-300">4,class="text-amber-300">194,class="text-amber-300">304 || all params: class="text-amber-300">6,class="text-amber-300">742,class="text-amber-300">609,class="text-amber-300">920 || class="text-amber-300">0.06%

Dataset Preparation

python
class="text-purple-400 font-medium">from datasets class="text-purple-400 font-medium">import Dataset

class=class="text-emerald-class="text-amber-300">400">"text-gray-class="text-amber-300">500 italic"># Format your data as instruction-response pairs
data = {
    class="text-emerald-class="text-amber-300">400">"instruction": [
        class="text-emerald-class="text-amber-300">400">"Summarize this text:",
        class="text-emerald-class="text-amber-300">400">"Translate to French:",
        class="text-emerald-class="text-amber-300">400">"Fix this code:"
    ],
    class="text-emerald-class="text-amber-300">400">"input": [
        class="text-emerald-class="text-amber-300">400">"Long text here...",
        class="text-emerald-class="text-amber-300">400">"Hello, how are you?",
        class="text-emerald-class="text-amber-300">400">"class="text-purple-400 font-medium">def add(a b): return a+b"
    ],
    class="text-emerald-class="text-amber-300">400">"output": [
        class="text-emerald-class="text-amber-300">400">"Summary here...",
        class="text-emerald-class="text-amber-300">400">"Bonjour, comment allez-vous?",
        class="text-emerald-class="text-amber-300">400">"class="text-purple-400 font-medium">def add(a, b): return a + b"
    ]
}

dataset = Dataset.from_dict(data)

class="text-purple-400 font-medium">def format_prompt(example):
    return class="text-emerald-class="text-amber-300">400">f""class="text-emerald-class="text-amber-300">400">"class="text-gray-class="text-amber-300">500 italicclass="text-emerald-class="text-amber-300">400">">### Instruction:
{example[class="text-emerald-class="text-amber-300">400">'instruction']}

class="text-gray-class="text-amber-300">500 italicclass="text-emerald-class="text-amber-300">400">">### Input:
{example[class="text-emerald-class="text-amber-300">400">'input']}

class="text-gray-class="text-amber-300">500 italicclass="text-emerald-class="text-amber-300">400">">### Response:
{example[class="text-emerald-class="text-amber-300">400">'output']}"class="text-emerald-class="text-amber-300">400">""

dataset = dataset.map(lambda x: {class="text-emerald-class="text-amber-300">400">"text": format_prompt(x)})

Training with Hugging Face

python
class="text-purple-400 font-medium">from transformers class="text-purple-400 font-medium">import TrainingArguments, Trainer

training_args = TrainingArguments(
    output_dir=class="text-emerald-class="text-amber-300">400">"./results",
    num_train_epochs=class="text-amber-300">3,
    per_device_train_batch_size=class="text-amber-300">4,
    gradient_accumulation_steps=class="text-amber-300">4,
    learning_rate=2e-class="text-amber-300">4,
    weight_decay=class="text-amber-300">0.01,
    warmup_steps=class="text-amber-300">100,
    logging_steps=class="text-amber-300">10,
    save_steps=class="text-amber-300">500,
    fp16=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    tokenizer=tokenizer,
)

trainer.train()

Key Takeaways

  • Always try prompt engineering and RAG before fine-tuning
  • LoRA makes fine-tuning accessible on consumer GPUs
  • Quality data matters more than quantity — 1000 good examples beat 10,000 bad ones
  • Use fp16=True and gradient accumulation to train on limited VRAM
0