Causal LM Architectures
Explore model architectures for causal LM in this LLM Finetuning tutorial. Understand key concepts, apply them in hands-on exercises, and get ready for the next lesson.
Focus: explore model architectures for causal lm
Ever picked a model off the Hugging Face Hub, slapped it into your training script, and realized too late that its architecture silently shaped everything — from memory usage to whether your labels even make sense? That's the hidden trap when you're fine-tuning causal language models. This lesson gives you the mental map and hands-on moves to inspect, compare, and choose the right architecture for your next fine-tuning run, so you stop guessing and start shipping.
The Problem This Lesson Solves
When you fine-tune a causal language model (LM), the architecture is not just a container for weights — it dictates how the model attends to tokens, how autoregressive generation behaves, and how much VRAM you'll burn. If you ignore it, you'll hit mysterious CUDA out of memory errors, see generations that look repetitive and broken, or waste hours training on a model that can't do the task you need.
The reality? Most developers grab the newest base checkpoint from the Hub without second-guessing the design. They then wonder why their chat dataset produces loss curves that plateau, or why the model repeats the same phrase forever. Architecture choices — decoder-only versus encoder-decoder, attention variants, token-to-loss mapping — ripple through every downstream step: data prep, training loop, and even how you evaluate.
By the end of this lesson, you'll be able to look at any causal LM checkpoint and understand its architecture, pick the right one for your use case, and inspect your training setup with targeted probes. No black boxes.
Core Concept / Mental Model
Think of a causal LM as a left-to-right storyteller. It reads your prompt token by token, and at each position, it only looks at what came before — never the future. This is the causal attention mask: a triangular matrix that zeros out future tokens. The transformer blocks process the sequence autoregressively, predicting the next token at each step.
Architecture families differ in how they structure this storytelling:
- Decoder-only: pure storyteller. GPT, Llama, Mistral, Phi. Ideal for generation, chat, and instruction following. Most generative LLMs you see fall here.
- Encoder-decoder: a two-act play. The encoder reads the whole input bidirectionally (like a full reader), then the decoder generates output causally. T5, BART. Great for translation, summarization where the source is fully understood before output.
- Causal decoder with prefix: a hybrid where part of the input is attended bidirectionally (the prefix), and the rest is causal. Used in some summarization and infilling models.
Inside any architecture, you also have configuration knobs: number of layers, heads, hidden size, and attention variant (e.g., flash_attention_2). These aren't just metadata — they determine whether your fine-tune fits on a GPU and how fast it runs.
Pro tip: The model card and
config.jsonare your first stop. They tell you the architecture family, the vocab size, and whether it was trained with a causal mask — no guessing.
How It Works Step by Step
When you instantiate a causal LM with Hugging Face AutoModelForCausalLM, here's what happens under the hood:
- Config parsing: The library reads
config.jsonand builds a model class matching thearchitecturesfield (e.g.,LlamaForCausalLM). - Embedding: Tokens become vectors — size
vocab_size x hidden_size. - Positional encoding: Rotary or learned, adds order info.
- Transformer blocks: Each block runs self-attention (with a causal mask) through a feed-forward network.
- Output head: The final hidden states map to
vocab_sizelogits.
Crucially, when you fine-tune, the loss is computed only over the target tokens (the ones after your prompt), because every position in the input gets a next-token prediction. If your labels don't align that way, you get silent training issues.
At inference, generation uses this same causal forward pass: you feed the prompt, sample the next token, append it, and repeat. That's why the architecture can't be swapped casually across tasks — the attention pattern is baked in.
Hands-on Walkthrough
Let's put this into practice. You'll inspect a model's architecture, verify its causal nature, and prepare a tiny training example.
1. Inspect the architecture
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
model_name = "gpt2" # classic small causal LM
config = AutoConfig.from_pretrained(model_name)
print(config.architectures) # -> ["GPT2LMHeadModel"]
print(config.model_type) # -> "gpt2"
print(config.n_layer, config.n_head, config.n_embd) # -> 12 12 768
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# Confirm the causal mask in action: logits depend only on past tokens.
import torch
inputs = tokenizer("The capital of France is", return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits # shape (1, seq_len, vocab_size)
# For each position, the logits predict the *next* token.
next_token_id = logits[0, -1].argmax().item()
print(tokenizer.decode(next_token_id)) # e.g., " Paris"
Expected output:
["GPT2LMHeadModel"]
gpt2
12 12 768
Paris
2. Build a causal training example
For fine-tuning, the model expects labels that are shifted right by one token.** In practice, you pass the same input as labels, and the library masks them internally.
from transformers import Trainer, TrainingArguments
# Minimal dataset with prompt/completion structure
train_data = [
{"text": "Question: What is 2+2?\nAnswer: 4"},
{"text": "Question: What is the sky?\nAnswer: Blue"},
]
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, max_length=128)
tokenized = tokenize_function({"text": [d["text"] for d in train_data]})
tokenized["labels"] = tokenized["input_ids"].copy()
# In the Trainer, each batch's labels are shifted by -1 automatically.
training_args = TrainingArguments(
output_dir="./gpt2-finetuned",
num_train_epochs=1,
per_device_train_batch_size=2,
learning_rate=5e-5,
logging_dir="./logs",
)
trainer = Trainer(model=model, args=training_args, train_dataset=tokenized)
# train() would work, but we'll keep it instructional
trainer.train()
Note: If you use
AutoModelForCausalLM, the loss is automatically computed only over the shifted labels — every position in the sequence gets a next-token prediction, and the loss is the mean negative log-likelihood over all positions.
3. Compare memory footprint across architectures
import torch
def count_params(model):
return sum(p.numel() for p in model.parameters())
models = {
"gpt2": AutoModelForCausalLM.from_pretrained("gpt2"),
"facebook/opt-125m": AutoModelForCausalLM.from_pretrained("facebook/opt-125m"),
}
for name, model in models.items():
print(f"{name}: {count_params(model)/1e6:.1f}M params")
Expected: roughly 124M for GPT-2, 125M for OPT — similar size, but different positional encodings (learned vs. learned, but different) and tokenizers.
Compare Options / When to Choose What
| Architecture | Best for | Pros | Cons | Example models |
|---|---|---|---|---|
| Decoder-only (causal) | Generation, chat, code, instruction | Simple, scalable, state-of-the-art | No bidirectional context for input | GPT, Llama, Mistral |
| Encoder-decoder | Translation, summarization | Can read full input bidirectionally | More complex, often slower decode | T5, BART |
| Prefix-LM | Infilling, controlled generation | Mix of bidirectional and causal | Rare, fewer pretrained checkpoints | UniLM, some T5 variants |
When to choose what
- Chat / assistant: decoder-only. It's the natural fit for autoregressive response generation.
- Document summarization: you might get better results with an encoder-decoder that reads the whole document first. But many strong modern summarization models are decoder-only (e.g., GPT-3.5 summary).
- Code completion: decoder-only, because you generate token-by-token.
Troubleshooting & Edge Cases
1. "ValueError: Unrecognized model in transformers"
You picked a model with a custom architecture not in your transformers version. Fix: upgrade transformers or pick a standard one.
2. Loss doesn't decrease
If your labels include padding tokens, the model is forced to predict them. Mask padding with -100 in labels:
labels = input_ids.clone()
labels[labels == tokenizer.pad_token_id] = -100
3. Generating repetitive loops
Causal decode can easily repeat. Adjust no_repeat_ngram_size or temperature.
4. CUDA out of memory on larger models
Architecture size matters. Switch to a smaller variant or use parameter-efficient fine-tuning (LoRA) — but that's for a later lesson.
What You Learned & What's Next
You've demystified explore model architectures for causal lm. You can now inspect a checkpoint's architecture, understand how the causal mask shapes training and generation, and choose between decoder-only and encoder-decoder families. You also built a simple fine-tuning data pipeline and spotted classic pitfalls like padding-token loss.
This is the foundation for the next lesson: choosing a base model for your fine-tuning task. There, you'll learn how to map your task requirements (domain, language, context length) to the best pretrained checkpoint — using the architecture knowledge you just gained.
Keep this mental model handy: always check config.json, know your attention mask, and align labels carefully. Your future fine-tunes will thank you.
Practice recap
Open a Hugging Face model card for a causal LM like gpt2 or microsoft/phi-2, inspect its config.json, and write a script that prints model type, parameters, and max sequence length. Then, prepare a tiny dataset and simulate a single training step to verify the loss drops — this cements the label-shifting and padding-masking concepts you just learned.
Common mistakes
- Forgetting to mask padding tokens in labels — the model learns to predict padding, and loss never converges.
- Ignoring the architecture family — using an encoder-decoder model when you need autoregressive generation, or vice versa.
- Assuming every causal LM has the same tokenizer or context length — check
config.jsonformax_position_embeddings. - Not verifying the causal mask is active when using custom model classes — some wrappers accidentally disable it.
- Relying on the model card instead of inspecting the actual config file — cards can be incomplete or outdated.
Variations
- Use
AutoModelForCausalLMfor easy architecture-agnostic fine-tuning, or drop toGPT2LMHeadModel/LlamaForCausalLMfor architecture-specific control. - Experiment with attention variants like
flash_attention_2to speed up training on long sequences — available in recenttransformersfor many decoder-only models. - Consider encoder-decoder alternatives (T5, BART) when your task benefits from full bidirectional understanding of the input before generation.
Real-world use cases
- Fine-tuning a decoder-only model like Mistral-7B to build a domain-specific chatbot for customer support.
- Adapting an encoder-decoder (T5) for abstractive summarization of legal documents in a legal-tech SaaS.
- Building a code completion assistant by fine-tuning a small causal model like GPT-2 on a repository's codebase.
Key takeaways
- Causal LMs (decoder-only) generate left-to-right using a causal attention mask — the architecture dictates generation behavior and loss computation.
- Always inspect
config.jsonto understand model type, layer counts, and context length before fine-tuning. - Different architecture families (decoder-only vs. encoder-decoder) suit different tasks — choose based on input comprehension needs.
- Labels must be aligned and padding masked with -100 to train correctly with
AutoModelForCausalLM. - Memory and speed depend heavily on architecture size and attention variants — plan your hardware accordingly.
- The next step is selecting a base model that fits your task and constraints, building on this architecture knowledge.
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.