FAQ

Fine-tuning

Fine-tuning with Unsloth: what your dataset has to look like

The exact dataset shapes Unsloth accepts, the EOS token bug that makes models generate forever, and how to find data worth training on in the first place.

Unsloth is the fastest way most people get a LoRA trained on a single GPU. The speed is real; the part that decides whether the result is any good is the dataset you hand it. Here is the format contract, the failure modes, and where the data comes from.

What Unsloth actually is

Unsloth is a patching layer over the Hugging Face stack rather than a replacement for it. It hard-depends on transformers, trl, peft, accelerate and bitsandbytes, and replaces the hot paths — attention, the LoRA matmuls, cross-entropy, RMS norm, RoPE, SwiGLU — with hand-written Triton kernels and manually derived backward passes.

That matters for your data for one reason: the trainer is TRL's. Unsloth's examples import SFTTrainer and DPOTrainer straight from TRL, so the dataset contract you are writing against is TRL's contract. Data prepared for Unsloth is portable to Axolotl, LLaMA-Factory or plain TRL with little or no change.

It now also ships as Unsloth Studio, a no-code web UI that also installs as a desktop app, alongside the Python package that engineers mean when they say Unsloth. Everything below concerns the package.

The four dataset shapes it accepts

Unsloth's documentation describes four data shapes, and also covers loading from the Hub, which is a loader rather than a shape.

Raw text — a single text field per row, for continued pretraining where you want the model to absorb a domain rather than learn a behaviour.

Alpaca — instruction, optional input, output. One turn in, one turn out. Note that the docs render these field names capitalised while the working notebooks use lowercase keys; use lowercase.

ShareGPT — a conversations list of {from, value} objects alternating between human and gpt, for anything multi-turn.

ChatML — a messages list of {role, content} alternating user and assistant. This is the shape TRL treats as canonical.

Loading from the Hub is not a fifth format — load_dataset(...) returns data in one of the above, and you still have to know which.

The EOS token, and why models generate forever

This is the single most common data-preparation bug in the whole ecosystem, and Unsloth's own canonical notebook flags it in a code comment: the formatting function must append the tokenizer's EOS token to every example, "otherwise your generation will go on forever!"

The mechanism is simple. The model learns to stop by seeing a stop token at the end of training examples. Omit it and the model has never been shown where an answer ends, so at inference it keeps producing text until it hits your length limit. Nothing errors during training and the loss curve looks perfectly healthy.

Alpaca formatting, with the token that matters
EOS_TOKEN = tokenizer.eos_token          # must not be omitted

alpaca_prompt = """Below is an instruction describing a task, paired with an input.
Write a response that appropriately completes the request.

### Instruction:
{}

### Input:
{}

### Response:
{}"""

def formatting_prompts_func(examples):
    texts = []
    for ins, inp, out in zip(examples["instruction"],
                             examples["input"],
                             examples["output"]):
        texts.append(alpaca_prompt.format(ins, inp, out) + EOS_TOKEN)
    return {"text": texts}

dataset = dataset.map(formatting_prompts_func, batched=True)

print(repr(dataset[0]["text"][-80:]))    # look at it once - is the EOS there?
A dark terminal showing one training example decoded token by token, each token on its own coloured background. It begins 'User: Provide a detailed analysis of Candace Parker's defensive techniques in her recent games...', continues after 'Assistant:' with a sports-commentary script, and ends with the special token <|end_of_text|>.
One Tülu 3 SFT row as the trainer sees it, after tokenisation under open-instruct's simple_chat template, with every token coloured separately. The final token is <|end_of_text|>: printing one example like this is the half-minute check that the stop token survived your formatting function. Image: allenai/open-instruct · Apache-2.0 repository; sample row from an ODC-BY dataset

Multi-turn data, and training only on the answers

For conversational data the path is: normalise the field names, apply the model's own chat template, then mask the loss so only assistant turns contribute. Unsloth ships helpers for all three steps.

Two stacked bars from 0 to 100%. Zero-shot: excellent 45.2%, pass 19.1%, fail 35.7%. Dialogue fine-tuned: excellent 76.1%, pass 21.7%, fail 2.2%.
Ratings of LIMA's turns in 10 test conversations, before (zero-shot) and after adding 30 multi-turn dialogues to its training data. Excellent responses rose from 45.2% to 76.1% and failures fell from 35.7% to 2.2%. A few dozen genuine conversations were enough: collect real dialogues rather than stitching unrelated rows together. Image: Figure 7 of the paper · CC BY 4.0 · from Zhou et al. 2023, “LIMA: Less Is More for Alignment”, arXiv:2305.11206

standardize_sharegpt converts {from, value} into {role, content}. get_chat_template applies one of dozens of named templates — including per-family ones for Llama, Mistral, Qwen, Gemma and Phi — and can remap keys on the fly without rewriting your files. train_on_responses_only then masks the prompt tokens.

Get that last call wrong and you hit a distinctive failure: every label becomes -100 and your training loss is identically zero. Unsloth's troubleshooting notes name this explicitly — it means the response markers it was given do not match the template that model actually uses. A loss of exactly 0.0 is never a good sign.

the conversational path
from unsloth.chat_templates import (
    get_chat_template, standardize_sharegpt, train_on_responses_only)
from datasets import load_dataset

tokenizer = get_chat_template(tokenizer, chat_template="llama-3.1")

dataset = load_dataset("<org>/<dataset>", split="train")
dataset = standardize_sharegpt(dataset)      # {from,value} -> {role,content}

def fmt(examples):
    return {"text": [tokenizer.apply_chat_template(c, tokenize=False,
                                                   add_generation_prompt=False)
                     for c in examples["conversations"]]}

dataset = dataset.map(fmt, batched=True)

trainer = SFTTrainer(model=model, tokenizer=tokenizer, train_dataset=dataset, ...)

# mask the user turns - both markers must match the llama-3.1 template exactly
trainer = train_on_responses_only(
    trainer,
    instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
    response_part    = "<|start_header_id|>assistant<|end_header_id|>\n\n",
)

# after one step, assert the loss is not exactly zero
Mask everything except the assistant turn, and include the stop token. Train on every token and the model learns to produce user turns too; mask everything and there is no training signal at all, which the trainer reports as 0.0 or NaN.

How much data, honestly

Unsloth publishes row-count guidance for supervised fine-tuning and for GRPO but none for preference tuning, and it is worth being precise about which is which.

A table of mean 5-shot MMLU accuracy: rows for 50,000, 100,000 and 150,000 datapoints and a mean row; columns for 1, 2 and 3 epochs on Chip (about 34-35), Unnatural Instructions (about 37-42) and FLAN v2 (about 43-46), plus a mean column of 39.28, 39.16 and 40.02.
QLoRA's sweep of one LLaMA model fine-tuned on three instruction datasets at 50,000, 100,000 and 150,000 examples for one to three epochs, scored on 5-shot MMLU. Tripling the data moves the mean by under a point (39.28 to 40.02); FLAN v2 instead of Chip2 moves it by nearly ten. Choose the dataset before the size. Image (rendered from the arXiv HTML): Table 11 of the paper · CC BY 4.0 · from “QLoRA: Efficient Finetuning of Quantized LLMs”, arXiv:2305.14314

For SFT the documentation recommends "a bare minimum of at least 100 rows" with "over 1,000 rows" preferable, and 1–3 epochs. It also notes that a training loss below 0.2 suggests overfitting. As a rough rule of thumb, a loss that settles somewhere around 0.5 to 1.0 is usually healthy, depending on the task.

For GRPO, Unsloth's reinforcement-learning guide suggests at least 500 rows. For DPO, ORPO and KTO, neither Unsloth nor TRL publishes a row count. Anyone quoting you a specific number for preference tuning is giving you a rule of thumb, not a documented figure. The honest answer is that it depends on how much the behaviour you are correcting differs from the base model's, and that you should look at the reference datasets in the field for scale. The table gives Unsloth's published minimum VRAM for fine-tuning at each model size.

Model sizeQLoRA (4-bit)LoRA (16-bit)
3B3.5 GB8 GB
7B5 GB19 GB
8B6 GB22 GB
14B8.5 GB33 GB
27B22 GB64 GB
32B26 GB76 GB
70B41 GB164 GB
405B237 GB950 GB
During training a LoRA adapter never materialises ΔW: it stores the two thin factors and applies them as two small matrix multiplies. For deployment you keep them separate or merge BA into W once. Rank sets capacity, so a rank too low for the shift you are asking for cannot be fixed with more data or more epochs.

Finding the dataset before you format it

All of the above assumes you have data. In practice the slow part of a fine-tune is not the training — on a single GPU with Unsloth that may be twenty minutes — it is deciding what to train on, and discovering three days in that the promising dataset is research-only, or is 90% duplicates, or does not actually contain the field you need.

That is the problem Dataset Finder exists for: describe the model you are training and the behaviour you want, and get back datasets ranked by fit, with the licence, size and structure visible before you download anything. Instruction-tuning sets, preference pairs, domain corpora for continued pretraining — filtered by what you are actually allowed to ship.

A results table for GPT-2 medium and large on the E2E NLG Challenge, with a '# Trainable Parameters' column (354.92M for full fine-tuning versus 0.35M for LoRA) and BLEU, NIST, MET, ROUGE-L and CIDEr score columns, LoRA's row shown in bold.
The LoRA paper's E2E results with the trainable-parameter column beside the scores: 354.92M for full fine-tuning against 0.35M for LoRA, and LoRA scores the higher BLEU (70.4 against 68.2). The adapter is the cheap part — the cost moved to the data you feed it. Image: microsoft/LoRA · MIT repository

Questions people ask

Can Unsloth train on my own files rather than a Hub dataset?

Yes. Anything the datasets library can load works — point load_dataset at local JSON, JSONL, CSV or Parquet. The format contract is unchanged: you still need to end up with either a text field or a messages list, and you still need the EOS token.

Which learning rate should I use?

Unsloth's own examples use 2e-4 for LoRA SFT and 5e-6 for DPO — a fortyfold difference, which surprises people. Preference tuning makes small corrections to an already-aligned model, so it needs a much gentler rate. For continued pretraining the docs use 5e-5 with a separate, smaller embedding learning rate.

Do I need to include a system prompt in training data?

Only if you want the model to respond to one at inference. If every example carries an identical system prompt the model partly absorbs it into the weights. If you want it to generalise across different system prompts, vary them during training, including omitting them sometimes.

Is data prepared for Unsloth portable to other trainers?

Largely yes, because Unsloth's trainers are TRL's. A messages list with role/content is accepted by TRL directly, by Axolotl as its default, and by LLaMA-Factory through a column mapping. Store conversations in that shape and you keep your options open.

What about fine-tuning a model that is already fine-tuned?

Possible, but a second round of fine-tuning can erode what the first one taught: the earlier behaviour and some general knowledge can degrade. If you are stacking a second behaviour on an instruct model, keep the learning rate low, keep the dataset tight, and evaluate the original capabilities as well as the new one — regressions here are easy to miss.

Stop hunting. Start searching.

Tens of thousands of curated datasets, searchable in plain language — with the licence and size checked before you download anything.

Search Dataset Finder →