$npx -y skills add Jeffallan/claude-skills --skill fine-tuning-expertUse when fine-tuning LLMs, training custom models, or adapting foundation models for specific tasks. Invoke for configuring LoRA/QLoRA adapters, preparing JSONL training datasets, setting hyperparameters for fine-tuning runs, adapter training, transfer learning, finetuning with H
| 1 | # Fine-Tuning Expert |
| 2 | |
| 3 | Senior ML engineer specializing in LLM fine-tuning, parameter-efficient methods, and production model optimization. |
| 4 | |
| 5 | ## Core Workflow |
| 6 | |
| 7 | 1. **Dataset preparation** — Validate and format data; run quality checks before training starts |
| 8 | - Checkpoint: `python validate_dataset.py --input data.jsonl` — fix all errors before proceeding |
| 9 | 2. **Method selection** — Choose PEFT technique based on GPU memory and task requirements |
| 10 | - Use LoRA for most tasks; QLoRA (4-bit) when GPU memory is constrained; full fine-tune only for small models |
| 11 | 3. **Training** — Configure hyperparameters, monitor loss curves, checkpoint regularly |
| 12 | - Checkpoint: validation loss must decrease; plateau or increase signals overfitting |
| 13 | 4. **Evaluation** — Benchmark against the base model; test on held-out set and edge cases |
| 14 | - Checkpoint: collect perplexity, task-specific metrics (BLEU/ROUGE), and latency numbers |
| 15 | 5. **Deployment** — Merge adapter weights, quantize, measure inference throughput before serving |
| 16 | |
| 17 | ## Reference Guide |
| 18 | |
| 19 | Load detailed guidance based on context: |
| 20 | |
| 21 | | Topic | Reference | Load When | |
| 22 | |-------|-----------|-----------| |
| 23 | | LoRA/PEFT | `references/lora-peft.md` | Parameter-efficient fine-tuning, adapters | |
| 24 | | Dataset Prep | `references/dataset-preparation.md` | Training data formatting, quality checks | |
| 25 | | Hyperparameters | `references/hyperparameter-tuning.md` | Learning rates, batch sizes, schedulers | |
| 26 | | Evaluation | `references/evaluation-metrics.md` | Benchmarking, metrics, model comparison | |
| 27 | | Deployment | `references/deployment-optimization.md` | Model merging, quantization, serving | |
| 28 | |
| 29 | ## Minimal Working Example — LoRA Fine-Tuning with Hugging Face PEFT |
| 30 | |
| 31 | ```python |
| 32 | from datasets import load_dataset |
| 33 | from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments |
| 34 | from peft import LoraConfig, get_peft_model, TaskType |
| 35 | from trl import SFTTrainer |
| 36 | import torch |
| 37 | |
| 38 | # 1. Load base model and tokenizer |
| 39 | model_id = "meta-llama/Llama-3-8B" |
| 40 | tokenizer = AutoTokenizer.from_pretrained(model_id) |
| 41 | tokenizer.pad_token = tokenizer.eos_token |
| 42 | |
| 43 | model = AutoModelForCausalLM.from_pretrained( |
| 44 | model_id, |
| 45 | torch_dtype=torch.bfloat16, |
| 46 | device_map="auto", |
| 47 | ) |
| 48 | |
| 49 | # 2. Configure LoRA adapter |
| 50 | lora_config = LoraConfig( |
| 51 | task_type=TaskType.CAUSAL_LM, |
| 52 | r=16, # rank — increase for more capacity, decrease to save memory |
| 53 | lora_alpha=32, # scaling factor; typically 2× rank |
| 54 | target_modules=["q_proj", "v_proj"], |
| 55 | lora_dropout=0.05, |
| 56 | bias="none", |
| 57 | ) |
| 58 | model = get_peft_model(model, lora_config) |
| 59 | model.print_trainable_parameters() # verify: should be ~0.1–1% of total params |
| 60 | |
| 61 | # 3. Load and format dataset (Alpaca-style JSONL) |
| 62 | dataset = load_dataset("json", data_files={"train": "train.jsonl", "test": "test.jsonl"}) |
| 63 | |
| 64 | def format_prompt(example): |
| 65 | return {"text": f"### Instruction:\n{example['instruction']}\n\n### Response:\n{example['output']}"} |
| 66 | |
| 67 | dataset = dataset.map(format_prompt) |
| 68 | |
| 69 | # 4. Training arguments |
| 70 | training_args = TrainingArguments( |
| 71 | output_dir="./checkpoints", |
| 72 | num_train_epochs=3, |
| 73 | per_device_train_batch_size=4, |
| 74 | gradient_accumulation_steps=4, # effective batch size = 16 |
| 75 | learning_rate=2e-4, |
| 76 | lr_scheduler_type="cosine", |
| 77 | warmup_ratio=0.03, # always use warmup |
| 78 | fp16=False, |
| 79 | bf16=True, |
| 80 | logging_steps=10, |
| 81 | eval_strategy="steps", |
| 82 | eval_steps=100, |
| 83 | save_steps=200, |
| 84 | load_best_model_at_end=True, |
| 85 | ) |
| 86 | |
| 87 | # 5. Train |
| 88 | trainer = SFTTrainer( |
| 89 | model=model, |
| 90 | args=training_args, |
| 91 | train_dataset=dataset["train"], |
| 92 | eval_dataset=dataset["test"], |
| 93 | dataset_text_field="text", |
| 94 | max_seq_length=2048, |
| 95 | ) |
| 96 | trainer.train() |
| 97 | |
| 98 | # 6. Save adapter weights only |
| 99 | model.save_pretrained("./lora-adapter") |
| 100 | tokenizer.save_pretrained("./lora-adapter") |
| 101 | ``` |
| 102 | |
| 103 | **QLoRA variant** — add these lines before loading the model to enable 4-bit quantization: |
| 104 | ```python |
| 105 | from transformers import BitsAndBytesConfig |
| 106 | |
| 107 | bnb_config = BitsAndBytesConfig( |
| 108 | load_in_4bit=True, |
| 109 | bnb_4bit_quant_type="nf4", |
| 110 | bnb_4bit_compute_d |