Skip to main content

LLM recipes

Four recipes tune a generative language model. They differ in what a row is and what the loss rewards, and share everything else: the base models, the training block, LoRA, resume and the validation gate.

RecipeA row isThe lossValidation metrics
llm_sfta conversation (messages)next-token loss on every assistant turnloss, perplexity
llm_instructioninstruction, optional input, outputnext-token loss on the output onlyloss, perplexity
llm_dpoprompt, chosen, rejectedprefer chosen more than the base model doesaccuracy, margin
llm_grpoprompt and what the reward reads (answer)reinforce sampled completions above their group's mean rewardreward

Base models​

model.architectureModelsWhat it is
transformerQwen3-0.6B, Qwen3-1.7Ba decoder-only transformer: full attention in every layer
hybridQwen3.5-0.8B, Qwen3.5-2BGated DeltaNet linear-attention layers mixed with full-attention layers; the vision tower stays frozen

Pin model.revision to a commit so a run today and a run next month load the same weights. model.init starts from a checkpoint of an earlier experiment instead (<experiment>/validate/checkpoint).

The training block​

training:
epochs: 1
lr: 1.0e-5
batch_size: 4
accumulation: 2 # optimizer step every 2 batches
max_length: 512 # every row is measured against it before the first step
eval_every: 50 # optional: held-out metrics during training
adapter: {name: lora, r: 16, alpha: 32, dropout: 0.05} # optional: omit to train every weight in FP32

With adapter only the LoRA weights train and the resume snapshot holds just them; the adapter is folded into the base weights when the checkpoint is written, so a LoRA checkpoint loads like a fully trained one. Which layers are adapted comes from the architecture (attention projections; for hybrid also the linear-attention projections) unless targets names others.

Run one​

uv run llmclick validate configs/llm/sft.yaml # experiment key and stage plan, nothing runs
uv run llmclick run configs/llm/sft.yaml # data, train, validate

The shipped configs/llm/ files are pilots: sample rows from samples/ and max_steps: 4. Point data.sources at your rows and remove max_steps for a real run. The checkpoint lands at output/<name>-<hash>/validate/checkpoint and loads with AutoModelForCausalLM.from_pretrained.

Training and validation run on MPS; device in the config (or auto-detection: cuda, then mps, then cpu) decides. Weights are trained in FP32: fully fine-tuning Qwen3.5-0.8B needs roughly 13 GB of unified memory at the shipped batch sizes, and training.adapter (LoRA) cuts that to little more than the weights themselves.

Source: README.md. This page is generated; edit the source and run npm run sync.