Tokenize Your Data for Training
Master tokenization for LLM fine-tuning: learn how to convert raw text into model-ready tokens, handle padding and truncation, and avoid common pitfalls. Includes a hands-on exercise.
Focus: tokenize your data for training
You've cleaned the data, formatted it into instruction-response pairs, and split it into train and validation sets. But if you feed raw strings directly into your model, the training loop will crash with cryptic tensor errors. The missing step: tokenize your data for training. Tokenization converts your text into the numeric IDs your model actually consumes, and if you get it wrong, your fine-tune silently underperforms or fails outright. In this lesson, you'll master tokenization with Hugging Face's tokenizers and transformers libraries, so your dataset is truly model-ready.
The Problem This Lesson Solves
Every transformer model operates on integers, not words. A tokenizer splits your text into subword units—like "finetuning" into ["fin", "etun", "ing"]—and maps each to a unique ID in the model's vocabulary. If your dataset contains raw strings, your training loop will throw errors like TypeError: expected Tensor as element 1 in argument 0, but got str. Even if you manually encode each row, subtle mismatches—wrong padding, inconsistent lengths, or forgetting attention masks—will produce silent degradation.
Consider a real failure: you tokenize your dataset but forget to set return_tensors, so you pass lists of lists to the trainer. The model interprets ragged sequences as a batch, and you get a ValueError deep in the loss computation. Or you use a tokenizer from a different model (e.g., BERT's tokenizer for a GPT-2 model), and the vocabulary IDs are meaningless—your fine-tune learns garbage. This lesson shows you how to avoid these traps.
Core Concept / Mental Model
Think of a tokenizer as a bilingual dictionary between human-readable text and a model's numeric language. The model only understands IDs (e.g., 1623 for "the", 2514 for "token"), so the tokenizer must be consistent between training and inference—same vocabulary, same special tokens, same truncation/padding rules.
A key mental model is the tokenization pipeline:
- Normalization: Lowercasing, Unicode cleanup, stripping accents (optional).
- Pre-tokenization: Splitting text on spaces and punctuation to create initial word boundaries.
- Model: Applying the subword algorithm—WordPiece, BPE, or Unigram—to break words into subword units.
- Post-processing: Adding special tokens like
[CLS],[SEP], or<s>and</s>. - Encoding: Converting token list to a tensor of integer IDs, plus an attention mask.
For fine-tuning, you also add two critical steps: truncation to max length and padding to the longest sequence in a batch. Truncation prevents memory blowups; padding ensures all sequences in a batch have the same shape.
Pro tip: The tokenizer you use must match the model's pretraining exactly. If you load
huggy/llama-2-7bbut usegpt2's tokenizer, every ID will be wrong. Always pairAutoTokenizerwith the same model name you're loading.
How It Works Step by Step
Here's the logical sequence to tokenize your dataset correctly:
- Load a tokenizer that matches your base model.
- Define your maximum sequence length (e.g., 512 tokens) based on your data's length distribution and hardware limits.
- Write a tokenization function that takes a batch of text examples, applies truncation and padding, and returns input IDs, attention masks, and possibly labels.
- Map the function over your dataset using
dataset.map(..., batched=True)for efficiency. - Convert outputs to PyTorch tensors (or TensorFlow) so the trainer can consume them.
- Verify the tokenized output by decoding a sample and checking input IDs.
For causal language modeling (e.g., generating text), your labels are the same as input IDs but shifted by one; many trainers handle this internally. For sequence classification, your dataset already has a label column that you don't tokenize.
If your dataset is huge, avoid processing all rows at once; use map with batched=True and possibly num_proc to parallelize. Also, save the tokenized dataset to disk so you don't recompute it on every run.
Hands-On Walkthrough
Let's tokenize a small dataset for fine-tuning a text-generation model. We'll use a sample instruction dataset and the GPT-2 tokenizer (but the same steps apply to Llama or BERT).
Step 1: Load the tokenizer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
# Add a padding token if missing (GPT-2 has none by default)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
Step 2: Create a toy dataset
from datasets import Dataset
data = {
"instruction": ["What is 2+2?", "Explain quantum computing"],
"response": ["4", "Quantum computing uses qubits"]
}
dataset = Dataset.from_dict(data)
Step 3: Write a tokenization function
def tokenize_function(examples):
# Combine instruction and response into a single text
texts = [instr + " " + resp for instr, resp in zip(examples["instruction"], examples["response"])]
# Tokenize with truncation and padding (padding='max_length' is fine for small demos)
encodings = tokenizer(
texts,
truncation=True,
max_length=64,
padding="max_length",
return_tensors="pt" # returns PyTorch tensors
)
return {
"input_ids": encodings["input_ids"].squeeze(), # remove batch dim
"attention_mask": encodings["attention_mask"].squeeze(),
"labels": encodings["input_ids"].squeeze() # for causal LM
}
Step 4: Map over the dataset
tokenized_dataset = dataset.map(tokenize_function, batched=True)
print(tokenized_dataset[0])
Expected output:
{'instruction': 'What is 2+2?', 'response': '4', 'input_ids': tensor([...]), 'attention_mask': tensor([...]), 'labels': tensor([...])}
Notice that input_ids are tensors of length 64 (padded with the EOS token, which is also the pad token).
Step 5: Verify by decoding
sample = tokenized_dataset[0]["input_ids"]
decoded = tokenizer.decode(sample, skip_special_tokens=True)
print(decoded)
Expected output: 'What is 2+2? 4' — the padding tokens are skipped.
Pro tip: In production, use
padding=Truewith aDataCollatorForLanguageModelingto pad dynamically per batch, which is more memory-efficient than padding to max_length for every example.
Compare Options / When to Choose What
| Strategy | Padding Approach | When to Use | Pros | Cons |
|---|---|---|---|---|
padding='max_length' |
Pad every example to a fixed length | Small datasets, simple demos | Easy to reason about; no need for collator | Wastes memory; slower training on varied lengths |
padding='longest' |
Pad only to the longest sequence in the batch | Most training scenarios | Memory-efficient; good performance | Requires a data collator to handle batch-level padding |
padding='do_not_pad' |
No padding; batches must be uniform | When you pre-pad or use fixed-length chunks | Minimal overhead | Hard to batch variable-length sequences |
If you're fine-tuning a causal LM (like GPT), you typically use padding='longest' with a DataCollatorForLanguageModeling. For sequence classification (BERT-style), a simple DataCollatorWithPadding works.
If your sequences are near uniform length, padding='max_length' is simpler and doesn't hurt. But for realistic datasets with high variance, switch to dynamic padding to save GPU memory.
Troubleshooting & Edge Cases
-
ValueError: Asking to pad but the tokenizer does not have a padding token— Add one, typically the EOS token:python tokenizer.pad_token = tokenizer.eos_tokenFor BERT-style models, they usually have a[PAD]token. -
Tensor shape mismatch when using
return_tensors="pt"insidemap— The returned tensors include a batch dimension; use.squeeze()or reshape appropriately, or omitreturn_tensorsand let the data collator handle tensor conversion later. -
Decoded text contains special tokens like
<|endoftext|>— That's expected if you don't skip them in decode. Useskip_special_tokens=Truefor human-readable inspection. -
Truncating too aggressively — If max_length is too short, you cut off crucial context. Plot your token length distribution and choose a max length covering the 95th percentile.
-
Forgetting attention masks — When you manually encode, always return
attention_mask; otherwise the model may attend to padding tokens, degrading quality. -
Using the wrong tokenizer — Always verify that the tokenizer ID matches the model ID. A quick check: decode a known word and confirm the split matches the model's known behavior.
What You Learned & What's Next
You've learned the core idea behind tokenization and how it maps text to model-ready IDs. You can now load a tokenizer, define a tokenization function with truncation and padding, and map it over your dataset, while understanding the trade-offs between padding strategies and avoiding common pitfalls. This hands-on skill directly fulfills the objective of converting raw data into tensors for training.
Next in the track, you'll learn how to set up your training loop with Hugging Face's Trainer or a custom PyTorch loop, where you'll feed these tokenized batches into the model. You'll also explore evaluation strategies to measure your fine-tuned model's performance.
Now practice: take your own instruction dataset (or use the sample above) and tokenize it with a different model, like BERT, and observe the difference in token IDs and special tokens. Print a few decoded samples to ensure correctness.
Practice recap
Try tokenizing your own dataset with a small model. Change the max_length and see how truncation affects decoded text. Experiment with padding='longest' and a data collator, then print batch shapes to confirm they're uniform. This hands-on practice will cement your understanding before moving to the training loop.
Common mistakes
- Using a tokenizer from a different model than the one you're fine-tuning, producing meaningless IDs
- Forgetting to add a padding token when the tokenizer lacks one, crashing training
- Padding every example to max_length on large datasets, wasting GPU memory and slowing training
- Not setting truncation, causing OOM errors or loss of context
- Passing lists instead of tensors to the trainer, causing shape errors
Variations
- Dynamic padding with a data collator (e.g., DataCollatorWithPadding) for memory efficiency
- Tokenizing with batched=True and multiple workers to speed up large datasets
- Using the tokenizers library directly to train a custom tokenizer on your domain-specific corpus
Real-world use cases
- Fine-tuning a chatbot on customer support logs to generate accurate responses
- Adapting a sentiment analysis model to financial news using domain-specific vocabulary
- Creating a code completion model by tokenizing a dataset of code snippets with appropriate special tokens
Key takeaways
- Tokenization bridges raw text and model integers; the tokenizer must match your model
- Always include truncation and padding to handle variable-length sequences
- Choose padding strategy based on dataset size and memory constraints
- Leverage dataset.map with batched=True for efficient tokenization
- Verify tokenized output by decoding samples to catch silent errors
- Troubleshoot common issues like missing pad tokens and tensor shape mismatches
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.