{"record":{"id":"df043f5d3808352c","repo":"hiyouga/LlamaFactory","slug":"stage-does-not-supported-stage-df043f","errorCode":null,"errorMessage":"Stage does not supported: {stage}.","messagePattern":"Stage does not supported: (.+?)\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"scripts/stat_utils/cal_ppl.py","lineNumber":104,"sourceCode":"        )\n    )\n    tokenizer_module = load_tokenizer(model_args)\n    tokenizer = tokenizer_module[\"tokenizer\"]\n    template = get_template_and_fix_tokenizer(tokenizer, data_args)\n    trainset = get_dataset(template, model_args, data_args, training_args, stage, **tokenizer_module)[\"train_dataset\"]\n    model = load_model(tokenizer, model_args, finetuning_args, is_trainable=False)\n    if stage == \"pt\":\n        data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)\n    elif stage == \"sft\":\n        data_collator = MultiModalDataCollatorForSeq2Seq(\n            template=template, tokenizer=tokenizer, label_pad_token_id=IGNORE_INDEX\n        )\n    elif stage == \"rm\":\n        data_collator = PairwiseDataCollatorWithPadding(\n            template=template, tokenizer=tokenizer, label_pad_token_id=IGNORE_INDEX, train_on_prompt=train_on_prompt\n        )\n    else:\n        raise NotImplementedError(f\"Stage does not supported: {stage}.\")\n\n    dataloader = DataLoader(trainset, batch_size, shuffle=False, collate_fn=data_collator, pin_memory=True)\n    criterion = torch.nn.CrossEntropyLoss(reduction=\"none\")\n    total_ppl = 0\n    perplexities = []\n    batch: dict[str, torch.Tensor]\n    with torch.no_grad():\n        for batch in tqdm(dataloader, desc=\"Computing perplexities\"):\n            batch = batch.to(model.device)\n            outputs = model(**batch)\n            shift_logits: torch.Tensor = outputs[\"logits\"][..., :-1, :]\n            shift_labels: torch.Tensor = batch[\"labels\"][..., 1:]\n            loss_mask = shift_labels != IGNORE_INDEX\n            flatten_logits = shift_logits.contiguous().view(shift_labels.size(0) * shift_labels.size(1), -1)\n            flatten_labels = shift_labels.contiguous().view(-1)\n            token_logps: torch.Tensor = criterion(flatten_logits, flatten_labels)\n            token_logps = token_logps.contiguous().view(shift_logits.size(0), -1)\n            sentence_logps = (token_logps * loss_mask).sum(-1) / loss_mask.sum(-1)","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/scripts/stat_utils/cal_ppl.py#L86-L122","documentation":"In KTO's concatenated_forward (trainer.py:192), per-sequence logps must align 1:1 with the batch's kto_tags boolean mask used to split chosen/rejected. If len(target_logps) != len(batch['kto_tags']) the split would silently misindex, so the trainer raises ValueError. A mismatch means the tokenized inputs and the kto_tag labels were produced from inconsistent batch shapes — almost always a data/collation problem, not model logic.","triggerScenarios":"KTO dataset rows missing or misaligned kto_tag fields, a custom data collator that pads/duplicates inputs but not kto_tags, or preprocessing that drops sequences (e.g. over-long samples filtered on one side only), so the collated batch has N logps but M tags.","commonSituations":"Hand-built KTO datasets where kto_tag length differs from input length; mixing a non-KTO collator into the KTO stage; cutoff_len filtering applied after tagging; corrupted/sharegpt-format conversions where some rows lack the tag.","solutions":["Validate the dataset offline: every KTO example must have exactly one kto_tag (bool) matching one input sequence — check `len(tokenizer(ex['prompt']+ex['response']).input_ids)` rows against tags","Regenerate the dataset with the documented KTO format (prompt/response/kto_tag columns) via dataset_info.json rather than a custom script","If using a custom collator, ensure it never changes batch size between inputs and kto_tags","Re-run tokenization after changing cutoff_len or template so filtering is applied consistently"],"exampleFix":"# before: custom KTO rows with drifting tags\n[\n  {\"prompt\": \"...\", \"response\": \"...\", \"kto_tag\": true},\n  {\"prompt\": \"...\"}  # missing tag -> misaligned batch\n]\n\n# after: every row tagged, standard dataset_info entry\n[\n  {\"prompt\": \"...\", \"response\": \"...\", \"kto_tag\": true},\n  {\"prompt\": \"...\", \"response\": \"...\", \"kto_tag\": false}\n]","handlingStrategy":"validation","validationCode":"# Offline KTO alignment check before training\nbad = [\n    i for i, ex in enumerate(dataset)\n    if not isinstance(ex.get('kto_tag'), bool) or not ex.get('prompt') or not ex.get('response')\n]\nassert not bad, f'Rows with missing/invalid kto_tag: {bad[:5]}'\nassert all(len(dataset[i]['prompt']) + len(dataset[i]['response']) > 0 for i in range(len(dataset)))","typeGuard":"def is_valid_kto_example(example: dict) -> bool:\n    return (\n        isinstance(example.get('kto_tag'), bool)\n        and isinstance(example.get('prompt'), str)\n        and isinstance(example.get('response'), str)\n    )","tryCatchPattern":"try:\n    trainer.train()\nexcept ValueError as e:\n    if 'Mismatched shape' in str(e):\n        raise SystemExit('KTO dataset misaligned: verify one kto_tag per sequence and collator batch handling') from e\n    raise","preventionTips":["Generate KTO datasets with the documented prompt/response/kto_tag schema; never hand-edit rows","Smoke-run one batch through the collator and assert batch['input_ids'].shape[0] == batch['kto_tags'].shape[0]","Avoid custom collators that pad or drop samples asymmetrically"],"tags":["kto","dataset","data-quality","collator"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}