SFT (Supervised Fine-Tuning)
SFT (supervised fine-tuning) applies to labeled prompt → response data. On MinT it runs the standard training loop with loss_fn='cross_entropy', and the training target is a LoRA low-rank adapter.
Building a Datum
Prompt tokens get a loss weight of 0 and do not contribute to the gradient; completion tokens get a weight of 1.0. The whole thing goes through a teacher-forcing shift: input is all_tokens[:-1], target is all_tokens[1:], and weights are all_weights[1:].
def process_sft_example(prompt_text, completion_text, tokenizer):
prompt_ids = tokenizer.encode(prompt_text, add_special_tokens=True)
completion_ids = tokenizer.encode(f" {completion_text}", add_special_tokens=False)
completion_ids.append(tokenizer.eos_token_id)
all_tokens = prompt_ids + completion_ids
all_weights = [0.0] * len(prompt_ids) + [1.0] * len(completion_ids)
return types.Datum(
model_input=types.ModelInput.from_ints(tokens=all_tokens[:-1]),
loss_fn_inputs={
"target_tokens": all_tokens[1:],
"weights": all_weights[1:],
},
)For chat-style data, we recommend using apply_chat_template(..., add_generation_prompt=True) for the prompt part.
Using the Renderer (recommended)
mint.recipe provides a Renderer that automatically handles the chat template, loss masking, and the teacher-forcing shift:
from mint import recipe
renderer = recipe.renderers.get_renderer(
recipe.get_recommended_renderer_name("Qwen/Qwen3-0.6B"), tokenizer
)
messages = [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."},
]
model_input, weights = renderer.build_supervised_example(messages)
datum = recipe.datum_from_model_input_weights(model_input, weights, max_length=2048)For multi-turn conversations, by default loss is computed only on the last assistant message; to compute it on every assistant message, pass train_on_what=TrainOnWhat.ALL_ASSISTANT_MESSAGES.
Parameters
| Parameter | Type | Default | Meaning |
|---|---|---|---|
base_model | str | "Qwen/Qwen3-0.6B" | Base model ID |
rank | int | 16 | LoRA rank; higher means more expressive but more memory, typically 8–64 |
train_mlp | bool | True | Train the MLP layers |
train_attn | bool | True | Train the attention layers |
train_unembed | bool | True | Train the unembedding (output) layer |
loss_fn | str | "cross_entropy" | SFT always uses cross_entropy; no other option |
learning_rate | float | 5e-5 | Adam learning rate; instruction tuning is typically 1e-5 to 1e-4 |
weight_decay | float | 0.0 | L2 regularization; LoRA is typically 0.0–0.01 |
Tinker compatibility notes
- Do not call
zero_grad_async(); the MinT server zeroes gradients automatically. loss_fngoes toforward_backward(...), not toAdamParams.save_weights_for_sampler(...)andsave_weights_and_get_sampling_client(...)are equivalent for serializing LoRA weights.
Which algorithm to choose
SFT fits "labeled prompt → response." For preference-pair data, see DPO; for scenarios with reward / verifier / environment feedback, see RL (GRPO); for the two-stage pipeline, see the SFT + RL combined Recipe.
Start Training
After data processing, move on to the training module. MinT Enterprise offers panel-based training-job configuration and also lets you submit the same job via an SDK script. Every training job centers on LoRA fine-tuning.
DPO (Preference Optimization)
DPO (Direct Preference Optimization) applies to chosen / rejected preference-pair data, training the model to favor the chosen response. On MinT it is implemented with forward_backward_custom plus a client-side custom Bradley-Terry loss.