Skip to main content

llm_sft: conversations

Supervised fine-tuning teaches a model to answer the way your conversations answer. Use it when you have dialogues, one turn or many, whose assistant turns are the replies you want. The model learns every assistant turn and only those: user and system turns are context.

Rows​

{"messages": [{"role": "user", "content": "What is 9 plus 2?"}, {"role": "assistant", "content": "9 plus 2 is 11."}]}

A row may start with a system turn, holds any number of user and assistant turns, and ends with an assistant turn. The base model's chat template formats them, so a Qwen3 model and a Qwen3.5 model see their own format.

Config​

configs/llm/sft.yaml
# Supervised fine-tuning on conversations: the loss is on every assistant turn.
# As shipped this is a minutes-long pilot on the sample rows; point `data.sources` at your rows and drop `max_steps`.
pipeline:
recipe: llm_sft
name: sft-qwen3.5-0.8b
seed: 20260920
output_dir: ./output
# stages: [data] # default: data, train, validate

model:
architecture: hybrid # Qwen3.5: linear-attention + full-attention layers, vision tower frozen
name: Qwen/Qwen3.5-0.8B # or Qwen/Qwen3.5-2B @ 15852e8c16360a2fea060d615a32b45270f8a8fc
revision: 2fc06364715b967f1860aea9cf38778875588b17

data:
sources:
- name: local_jsonl
path: samples/llm_sft.jsonl # rows: {"messages": [{"role": ..., "content": ...}, ...]}
validation: 0.1

training: {epochs: 1, lr: 1.0e-5, batch_size: 4, accumulation: 2, max_length: 512, max_steps: 4}

validation:
max: {loss: 5.0} # the validate stage fails the run above this held-out loss

configs/llm/sft_transformer.yaml is the same recipe on Qwen3-0.6B, and configs/llm/sft_lora.yaml trains a LoRA adapter instead of every weight.

Use​

uv run llmclick run configs/llm/sft.yaml
  • Gate: validation.max: {loss: ...} on the held-out conversations; perplexity is reported too.
  • Output: output/sft-qwen3.5-0.8b-<hash>/validate/checkpoint, a save_pretrained directory with its tokenizer.
  • Next: score it on your rows with evaluation_custom and against the base model with evaluation_compare.

Source: configs/llm/sft.yaml. This page is generated; edit the source and run npm run sync.