name faion-finetuning-skill user-invocable false description
LLM Fine-tuning Mastery
Customize Language Models for Domain-Specific Tasks (2025-2026)
Quick Reference
Area Key Elements Techniques Full fine-tuning, LoRA, QLoRA, DoRA Frameworks LLaMA-Factory, Unsloth, Axolotl, TRL Datasets Alpaca, ShareGPT, Conversation, Instruction Alignment SFT, RLHF, DPO, ORPO Evaluation Perplexity, task benchmarks, human eval Deployment GGUF, vLLM, TGI, Ollama Cost GPU selection, cloud pricing, optimization
Technique Comparison
Technique GPU Memory Speed Quality Use Case Full FT 80GB+ Slow Best Large budgets, critical tasks LoRA 16-24GB Fast Good Most production cases QLoRA 8-12GB Medium Good Consumer GPUs, prototyping DoRA 16-24GB Fast Better LoRA successor (2024+) OpenAI FT N/A Fast Good API-only workflows
LoRA (Low-Rank Adaptation)
Core Concept
LoRA freezes base model weights and injects small trainable matrices into attention layers.
Original weight W (d x k)
↓
W' = W + BA where B (d x r), A (r x k)
↓
Only B and A are trained (r << min(d, k))
Key Parameters
Parameter Description Typical Values rank (r) Adapter matrix rank 8, 16, 32, 64 alpha Scaling factor Usually = rank target_modules Layers to adapt q_proj, v_proj, k_proj, o_proj dropout Regularization 0.0 - 0.1
Rank Selection Guide
Rank Memory Use Case 8 Minimal Simple tasks, prototyping 16 Low General fine-tuning 32 Medium Complex tasks 64 Higher Maximum quality 128+ High Rarely needed
Target Modules by Model
Model Recommended Modules LLaMA/Mistral q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj Qwen c_attn, c_proj Phi q_proj, k_proj, v_proj, dense GPT-NeoX query_key_value, dense
QLoRA (Quantized LoRA)
Memory Savings
QLoRA loads base model in 4-bit precision, trains LoRA adapters in fp16.
Model Size Full FT LoRA (fp16) QLoRA (4-bit) 7B 28GB 16GB 6-8GB 13B 52GB 28GB 10-12GB 70B 280GB 140GB 40-48GB
Key Components
NF4 Quantization - 4-bit NormalFloat
Double Quantization - Quantize quantization constants
Paged Optimizers - Handle memory spikes
fp16 LoRA - Full precision adapters
Configuration
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True ,
bnb_4bit_quant_type="nf4" ,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True ,
)
OpenAI Fine-tuning API
Supported Models
Model Training Cost Inference Cost gpt-4o-mini-2024-07-18 $3.00/1M tokens $12.00/1M output gpt-4o-2024-08-06 $25.00/1M tokens $100.00/1M output gpt-3.5-turbo-0125 $0.80/1M tokens $3.20/1M output
Training Data Format
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is LoRA?"}, {"role": "assistant", "content": "LoRA (Low-Rank Adaptation) is..."}]}
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "How to fine-tune?"}, {"role": "assistant", "content": "To fine-tune a model..."}]}
Python API
from openai import OpenAI
client = OpenAI()
with open ("training_data.jsonl" , "rb" ) as f:
file = client.files.create(file=f, purpose="fine-tune" )
job = client.fine_tuning.jobs.create(
training_file=file.id ,
model="gpt-4o-mini-2024-07-18" ,
hyperparameters={
"n_epochs" : 3 ,
"batch_size" : 4 ,
"learning_rate_multiplier" : 1.8
},
suffix="my-custom-model"
)
print (f"Job ID: {job.id } " )
print (f"Status: {job.status} " )
events = client.fine_tuning.jobs.list_events(job.id )
for event in events.data:
print (event.message)
response = client.chat.completions.create(
model="ft:gpt-4o-mini-2024-07-18:my-org::abc123" ,
messages=[{"role" : "user" , "content" : "Hello!" }]
)
Best Practices
Minimum 10 examples - but 50-100+ recommended
Consistent format - same system prompt across examples
Quality over quantity - clean, accurate examples
Validation set - 10-20% for monitoring
Suffix naming - descriptive, version-tracked
LLaMA-Factory
Overview
User-friendly framework with WebUI for training LLMs.
Installation
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"
python src/webui.py
Supported Models
Family Models LLaMA Llama-2, Llama-3, Llama-3.1, Llama-3.2 Mistral Mistral-7B, Mixtral-8x7B Qwen Qwen-1.5, Qwen-2, Qwen-2.5 Yi Yi-6B, Yi-34B Phi Phi-2, Phi-3, Phi-3.5 DeepSeek DeepSeek-V2, DeepSeek-V3
CLI Training
llamafactory-cli train \
--stage sft \
--model_name_or_path meta-llama/Llama-3.1-8B \
--dataset alpaca_en \
--template llama3 \
--finetuning_type lora \
--lora_rank 16 \
--lora_target q_proj,v_proj \
--output_dir output/llama3-lora \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 4 \
--learning_rate 5e-5 \
--num_train_epochs 3 \
--fp16
llamafactory-cli export \
--model_name_or_path meta-llama/Llama-3.1-8B \
--adapter_name_or_path output/llama3-lora \
--template llama3 \
--finetuning_type lora \
--export_dir merged_model
Dataset Formats
Alpaca Format:
[
{
"instruction" : "Summarize the following text" ,
"input" : "Long text here..." ,
"output" : "Summary here..."
}
]
ShareGPT Format:
[
{
"conversations" : [
{ "from" : "human" , "value" : "What is AI?" } ,
{ "from" : "gpt" , "value" : "AI stands for..." }
]
}
]
Unsloth
Overview
2x faster training with 80% less memory using custom CUDA kernels.
Installation
pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps trl peft accelerate bitsandbytes
Quick Start
from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Llama-3.2-1B" ,
max_seq_length=2048 ,
dtype=None ,
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=42 ,
)
training_args = TrainingArguments(
output_dir="./outputs" ,
per_device_train_batch_size=2 ,
gradient_accumulation_steps=4 ,
warmup_steps=10 ,
max_steps=60 ,
learning_rate=2e-4 ,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
logging_steps=1 ,
optim="adamw_8bit" ,
weight_decay=0.01 ,
lr_scheduler_type="linear" ,
seed=42 ,
)
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
dataset_text_field="text" ,
max_seq_length= ,
args=training_args,
)
trainer.train()
model.save_pretrained( )
tokenizer.save_pretrained( )
model.save_pretrained_merged( , tokenizer, save_method= )
model.save_pretrained_gguf( , tokenizer, quantization_method= )
Key Features
Feature Benefit RoPE Scaling 4x longer context Gradient Checkpointing 30% less VRAM 4-bit Loading Train 70B on 48GB Flash Attention 2 2x faster attention GGUF Export Direct Ollama deployment
Axolotl
Overview
Advanced framework for complex training scenarios.
Installation
pip install axolotl
git clone https://github.com/OpenAccess-AI-Collective/axolotl
cd axolotl
pip install -e ".[flash-attn,deepspeed]"
Configuration (YAML)
base_model: meta-llama/Llama-3.1-8B
model_type: LlamaForCausalLM
tokenizer_type: AutoTokenizer
load_in_8bit: false
load_in_4bit: true
strict: false
datasets:
- path: my_dataset.jsonl
type: alpaca
data_files:
- train.jsonl
dataset_prepared_path: prepared_data
val_set_size: 0.05
output_dir: ./outputs
adapter: lora
lora_r: 32
lora_alpha: 16
lora_dropout: 0.05
lora_target_modules:
- q_proj
- v_proj
- k_proj
- o_proj
- gate_proj
- down_proj
- up_proj
sequence_len: 4096
sample_packing: true
pad_to_sequence_len:
Training
accelerate launch -m axolotl.cli.train config.yaml
accelerate launch --config_file deepspeed.yaml -m axolotl.cli.train config.yaml
accelerate launch -m axolotl.cli.inference config.yaml --lora_model_dir ./outputs
Dataset Preparation
Format Comparison
Format Structure Use Case Alpaca instruction, input, output Single-turn tasks ShareGPT conversations array Multi-turn chat OpenAI messages array API fine-tuning Completion prompt, completion Text completion
Data Cleaning Pipeline
import json
from datasets import load_dataset
def clean_dataset (raw_data ):
cleaned = []
seen = set ()
for item in raw_data:
key = (item.get("instruction" , "" ), item.get("output" , "" ))
if key in seen:
continue
seen.add(key)
if not item.get("output" , "" ).strip():
continue
if len (item.get("output" , "" )) < 10 :
continue
if len (item.get("output" , "" )) > 4096 :
continue
cleaned.append(item)
return cleaned
with open ("raw_data.json" ) as f:
raw = json.load(f)
cleaned = clean_dataset(raw)
print (f"Cleaned: {len (raw)} -> {len (cleaned)} " )
( , ) f:
json.dump(cleaned, f, indent= )
Prompt Formatting
def format_alpaca (example ):
"""Format for instruction-following."""
if example.get("input" ):
return f"""### Instruction:
{example['instruction' ]}
### Input:
{example['input' ]}
### Response:
{example['output' ]} """
else :
return f"""### Instruction:
{example['instruction' ]}
### Response:
{example['output' ]} """
def format_chat (example ):
"""Format for chat models."""
messages = []
for msg in example["conversations" ]:
role = "user" if msg["from" ] == "human" else "assistant"
messages.append({"role" : role, "content" : msg["value" ]})
return messages
Quality Guidelines
Criterion Description Accuracy Factually correct responses Consistency Same format across examples Diversity Varied instructions and domains Length Appropriate response length No Toxicity Filter harmful content No PII Remove personal information
Training Configuration
Hyperparameters
Parameter Description Typical Range learning_rate Step size 1e-5 to 5e-4 batch_size Samples per step 1-8 (per GPU) epochs Full passes 1-5 warmup_ratio LR warmup 0.03-0.1 weight_decay Regularization 0.01-0.1 max_seq_length Token limit 512-8192
Learning Rate Schedules
Schedule Description Use Case constant Fixed LR Short training linear Linear decay General cosine Cosine decay Best for most cases cosine_with_restarts Multiple cycles Long training
Gradient Accumulation
training_args = TrainingArguments(
per_device_train_batch_size=2 ,
gradient_accumulation_steps=4 ,
)
Evaluation Metrics
Perplexity
Lower is better. Measures model uncertainty.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
def calculate_perplexity (model, tokenizer, text, device="cuda" ):
encodings = tokenizer(text, return_tensors="pt" ).to(device)
with torch.no_grad():
outputs = model(**encodings, labels=encodings.input_ids)
return torch.exp(outputs.loss).item()
ppl = calculate_perplexity(model, tokenizer, "Sample text here" )
print (f"Perplexity: {ppl:.2 f} " )
Task-Specific Benchmarks
Benchmark Tasks Metrics MMLU Multi-task knowledge Accuracy HellaSwag Commonsense reasoning Accuracy TruthfulQA Factual accuracy MC1, MC2 HumanEval Code generation Pass@1, Pass@10 GSM8K Math reasoning Accuracy BBH Hard tasks Accuracy
Using LM Evaluation Harness
pip install lm-eval
lm_eval --model hf \
--model_args pretrained=./my_model \
--tasks mmlu,hellaswag,truthfulqa \
--device cuda:0 \
--batch_size 8
Human Evaluation
Aspect Rating Scale Fluency 1-5 (grammatically correct) Relevance 1-5 (answers the question) Accuracy 1-5 (factually correct) Helpfulness 1-5 (useful response)
Model Merging
Merge Strategies
Method Description Use Case Linear Weighted average Simple blending SLERP Spherical interpolation Smooth transitions TIES Trimmed sparse averaging Reduce interference DARE Drop and rescale Better generalization
Using mergekit
pip install mergekit
slices:
- sources:
- model: base_model
layer_range: [0 , 32 ]
- model: finetuned_model
layer_range: [0 , 32 ]
merge_method: slerp
base_model: base_model
parameters:
t: 0.5
merge_method: slerp
dtype: float16
mergekit-yaml merge.yaml ./merged_output
LoRA Merging
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
base_model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B" ,
torch_dtype=torch.float16,
device_map="auto" ,
)
model = PeftModel.from_pretrained(base_model, "path/to/lora" )
merged = model.merge_and_unload()
merged.save_pretrained("merged_model" )
Alignment Methods
Supervised Fine-tuning (SFT)
Basic instruction following on curated data.
RLHF (Reinforcement Learning from Human Feedback)
SFT Model → Reward Model → PPO Training → RLHF Model
DPO (Direct Preference Optimization)
Simpler alternative to RLHF. No reward model needed.
from trl import DPOTrainer, DPOConfig
dpo_config = DPOConfig(
beta=0.1 ,
learning_rate=5e-7 ,
per_device_train_batch_size=4 ,
gradient_accumulation_steps=4 ,
num_train_epochs=1 ,
)
trainer = DPOTrainer(
model=model,
ref_model=ref_model,
args=dpo_config,
train_dataset=dataset,
tokenizer=tokenizer,
)
trainer.train()
ORPO (Odds Ratio Preference Optimization)
2024 method. Even simpler than DPO.
from trl import ORPOTrainer, ORPOConfig
orpo_config = ORPOConfig(
beta=0.1 ,
learning_rate=8e-6 ,
per_device_train_batch_size=2 ,
num_train_epochs=1 ,
)
trainer = ORPOTrainer(
model=model,
args=orpo_config,
train_dataset=dataset,
tokenizer=tokenizer,
)
Deployment
GGUF for llama.cpp / Ollama
model.save_pretrained_gguf("model" , tokenizer, quantization_method="q4_k_m" )
Create Ollama Model
# Modelfile
FROM ./model-q4_k_m.gguf
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>"""
PARAMETER stop "<|start_header_id|>"
PARAMETER stop "<|end_header_id|>"
PARAMETER stop "<|eot_id|>"
ollama create my-model -f Modelfile
ollama run my-model
vLLM Deployment
pip install vllm
python -m vllm.entrypoints.openai.api_server \
--model ./merged_model \
--port 8000 \
--tensor-parallel-size 1
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{"model": "merged_model", "prompt": "Hello", "max_tokens": 50}'
Text Generation Inference (TGI)
docker run --gpus all -p 8080:80 \
-v $PWD /model:/data \
ghcr.io/huggingface/text-generation-inference:latest \
--model-id /data \
--max-input-length 4096 \
--max-total-tokens 8192
Cost Optimization
GPU Selection
GPU VRAM Models Cloud Cost RTX 3090 24GB 7B full, 13B QLoRA $0.50/hr RTX 4090 24GB 7B full, 13B QLoRA $0.75/hr A10G 24GB 7B full, 13B QLoRA $1.00/hr A100 40GB 40GB 13B full, 70B QLoRA $3.50/hr A100 80GB 80GB 70B full $4.50/hr H100 80GB 70B full (fastest) $8.00/hr
Cost Reduction Strategies
Strategy Savings Trade-off QLoRA instead of LoRA 50-70% VRAM Slightly slower Gradient checkpointing 30% VRAM 20% slower Mixed precision (bf16) 50% VRAM None Smaller batch + grad accum Variable Same effective batch Shorter sequences Linear Less context Fewer epochs Linear Less convergence
Cloud Providers
Provider GPU Options Pricing Model RunPod A100, H100, 4090 Per hour Lambda Labs A100, H100 Per hour Vast.ai Various Auction-based Google Colab Pro+ A100, V100 Monthly ($50) AWS SageMaker p4d, p5 Per hour
Training Time Estimation
def estimate_training_time (
dataset_size: int ,
batch_size: int ,
gradient_accumulation: int ,
epochs: int ,
tokens_per_second: int = 1000 ,
avg_seq_length: int = 512
):
"""Estimate training time in hours."""
steps = (dataset_size // (batch_size * gradient_accumulation)) * epochs
total_tokens = steps * batch_size * gradient_accumulation * avg_seq_length
hours = total_tokens / tokens_per_second / 3600
return hours
hours = estimate_training_time(
dataset_size=10000 ,
batch_size=2 ,
gradient_accumulation=4 ,
epochs=3 ,
tokens_per_second=2000 ,
)
print (f"Estimated: {hours:.1 f} hours" )
Troubleshooting
Common Issues
Issue Solution OOM (Out of Memory) Reduce batch size, use QLoRA, gradient checkpointing Loss not decreasing Lower LR, check data quality, increase rank Overfitting Add dropout, reduce epochs, more data Catastrophic forgetting Lower LR, use regularization Slow training Use Flash Attention, optimize data loading NaN loss Lower LR, check for bad data, use bf16
Debugging Tips
import torch
print (f"Allocated: {torch.cuda.memory_allocated() / 1e9 :.2 f} GB" )
print (f"Reserved: {torch.cuda.memory_reserved() / 1e9 :.2 f} GB" )
from transformers import TrainerCallback
class LogCallback (TrainerCallback ):
def on_log (self, args, state, control, logs=None , **kwargs ):
if logs:
print (f"Step {state.global_step} : loss={logs.get('loss' , 'N/A' ):.4 f} " )
Quick Start Checklist
Choose technique - LoRA for most cases, QLoRA for limited GPU
Prepare dataset - Clean, format, deduplicate (min 100 examples)
Select base model - Llama 3.1, Mistral, Qwen 2.5
Configure training - LR 2e-4, rank 16, epochs 3
Monitor metrics - Loss, perplexity, validation loss
Evaluate - Perplexity, task benchmarks, human eval
Merge/Export - GGUF for local, vLLM for serving
Deploy - Ollama, vLLM, TGI
References
Tools
Last updated: 2026-01-18
Skill version: 1.0