Fixing EOS Token Errors: Why Your Fine-Tuned Model Won't Stop Generating
Quick Answer / TL;DR
If your model hallucinates a new "User:" prompt and starts talking to itself after answering, your tokenizer failed to append the correct End of Sequence (EOS) token during training. You must explicitly set tokenizer.pad_token = tokenizer.eos_token and ensure your chat_template maps to the correct model-specific token (e.g., <|eot_id|> for Llama 3 vs. <|im_end|> for Qwen/ChatML).
The "Infinite Conversation" Bug
You just spent 8 hours running Supervised Fine-Tuning (SFT) on an 8B parameter model. You load it up for inference and ask it a question. The model answers perfectly, but instead of stopping, it outputs a newline, writes a brand new question from a fabricated user, and proceeds to answer that too.
This happens because language models are fundamentally next-token predictors. When the model reaches the end of its intended answer, it needs a mathematical stop sign (the EOS token) to trigger the inference engine to halt. If your training dataset was improperly formatted, the model never learned to associate the end of its reasoning trace with that stop sign.
The Fix: Aligning Tokenizer Templates
Base models have different vocabularies. You cannot apply a generic ChatML format to a Llama 3 architecture without causing severe tokenization mismatches. Before passing your dataset into the SFTTrainer, you must enforce the correct special tokens.
from transformers import AutoTokenizer
model_id = "meta-llama/Meta-Llama-3-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 1. Verify what the model actually expects
print("Model EOS Token:", tokenizer.eos_token)
# Llama 3 outputs: <|eot_id|> (NOT <|end_of_text|>)
# 2. Fix the padding index (Crucial for batched SFT)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# Enforce right padding so the EOS token stays at the absolute end
tokenizer.padding_side = "right"Applying the Chat Template
Never manually concatenate strings like "Assistant: " + text + "\n". Always map your JSONL data through the tokenizer's native apply_chat_template method. This guarantees the correct EOS token is baked into the tensor arrays during the forward pass.
def format_dataset(examples):
# This automatically appends the correct EOS token per the model's config
texts = [
tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False
)
for messages in examples["messages"]
]
return {"text": texts}Related Cookbooks
The Math of DPO, Explained | SR Cookbooks
A step-by-step mathematical derivation of Direct Preference Optimization (DPO), showing how the partition function cancels and verifying the implicit reward gradient in Python.
PEFT Explained: LoRA vs. QLoRA | SR Cookbooks
Understand the architectural differences between LoRA and QLoRA, and learn when to use each Parameter-Efficient Fine-Tuning technique based on your VRAM limits.
How to Train Custom Tokens with LoRA | SR Cookbooks
Learn how to fix untrained embedding errors when adding custom tokens to an LLM vocabulary during PEFT and LoRA fine-tuning.