Full Fine-tuning vs. Parameter-Efficient Fine-Tuning (PEFT) in LLMs
Learning Objectives: By the end of this lesson, you will be able to:
- Explain the difference between full fine-tuning and parameter-efficient fine-tuning (PEFT) for large language models (LLMs)
- Identify the pros and cons of each approach
- Recognize scenarios where PEFT is preferable and understand its motivation
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
“Imagine you have a huge language model with billions of parameters. If you want to adapt it to a new task, do you think you need to update every single parameter? Why or why not?”
Lesson Overview
- Duration: Approximately 2.5 hours including all exercises (or 60–90 minutes when selected activities are assigned)
- Prerequisites: Understanding of LLM architectures (BERT, GPT), basics of model training and transfer learning
- Materials Needed: Python environment (Jupyter or Colab), Hugging Face Transformers library, sample datasets
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Full fine-tuning: what it is, how it works, and its limitations
- The motivation for parameter-efficient fine-tuning (PEFT)
- Overview of PEFT strategies
- Hands-on practice and exercises
- Common pitfalls and how to avoid them
Success Criteria:
By the end of this session, you should be able to describe and compare full fine-tuning and PEFT, and know when to use each.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
Full Fine-tuning
Full fine-tuning means updating all the parameters of a pre-trained model on a new dataset or task. This is the traditional approach for adapting models.
Real-World Analogy: Think of retraining a whole orchestra to play a new song, rather than just teaching a few new instruments.
Why This Matters: Full fine-tuning can yield the best performance, but is resource-intensive and requires storing a new copy of the entire model for each task.
Parameter-Efficient Fine-Tuning (PEFT)
PEFT refers to techniques that adapt a model to new tasks by updating only a small subset of parameters, or by adding small trainable modules, while keeping most of the original model frozen.
Real-World Analogy: Instead of retraining the whole orchestra, you just swap out or retrain a few musicians or add a new section.
Why This Matters: PEFT enables efficient adaptation of very large models, saving compute, memory, and storage.
Key Terminology
- Fine-tuning: Adapting a pre-trained model to a new task by further training on task-specific data.
- Frozen parameters: Model weights that are not updated during training.
- Adapters/LoRA: Examples of PEFT modules that are added to or modify the model for efficient adaptation.
4. Detailed Explanation and Examples (25-30 minutes)
Understanding Full Fine-tuning
Full fine-tuning involves unfreezing all model parameters and training them on a new dataset. This can lead to high accuracy but is expensive for large models.
Practical Applications
- Adapting a BERT or GPT model to a specific domain (e.g., legal, medical)
- Customizing a model for a proprietary dataset
Common Misconceptions
- Misconception: Full fine-tuning is always better. Reality: For large models, PEFT can sometimes approach full fine-tuning quality with much less compute and storage; validate this per task.
- Misconception: You must always update all parameters to adapt a model. Reality: Many tasks only require small changes, which PEFT can provide.
The Need for PEFT
As LLMs grow, full fine-tuning becomes impractical:
- Compute: Updating billions of parameters is slow and expensive.
- Storage: Each fine-tuned model is as large as the original.
- Deployment: Serving many task-specific models is costly.
PEFT addresses these by:
- Only updating a small number of parameters
- Adding small modules (e.g., adapters, LoRA)
- Allowing multiple tasks to share the same base model
5. Live Coding Demonstration (20-25 minutes)
Example 1: Full Fine-tuning a BERT Model
from transformers import AutoTokenizer, BertForSequenceClassification, Trainer, TrainingArguments
from datasets import load_dataset
# Load pre-trained BERT and dataset
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
dataset = load_dataset('imdb')
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
tokenized = dataset.map(
lambda batch: tokenizer(batch['text'], truncation=True, max_length=256),
batched=True,
remove_columns=['text'],
)
# Define training arguments
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=1,
per_device_train_batch_size=8,
evaluation_strategy="epoch",
save_strategy="epoch",
)
# Trainer for full fine-tuning
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized['train'].shuffle(seed=42).select(range(1000)),
eval_dataset=tokenized['test'].shuffle(seed=42).select(range(500)),
)
trainer.train()
Explanation:
- Lines 1-2: Import necessary libraries and load a pre-trained BERT model and dataset.
- Lines 5-13: Set up training arguments.
- Lines 15-22: Create a Trainer object for full fine-tuning.
- Line 24: Start training, updating all model parameters.
Example 2: Motivation for PEFT
Suppose you want to fine-tune a 7B parameter model for 10 different tasks. Full fine-tuning would require storing 10 copies (70B parameters). With PEFT, you only store small adapters or LoRA weights for each task (often <1% of the model size).
Example 3: PEFT with LoRA
from peft import LoraConfig, TaskType, get_peft_model
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=8,
lora_alpha=16,
target_modules=["query", "value"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
Key Points:
- Only the adapter parameters are updated; the rest of the model is frozen.
- Much less memory and compute required.
- Multiple adapters can be added for different tasks.
Debugging Demonstration
Show a common mistake: forgetting to freeze the base model when using adapters.
# `train()` changes module mode; it does not select trainable parameters.
model.train()
trainable = [name for name, p in model.named_parameters() if p.requires_grad]
assert trainable and all("lora_" in name or "modules_to_save" in name
for name in trainable)
print(trainable)
6. Hands-On Practice (30-40 minutes)
Exercise 1: Basic Practice (10 minutes)
Objective: Fine-tune a small model on a text classification task using full fine-tuning.
Instructions:
- Load a pre-trained DistilBERT model for sequence classification.
- Fine-tune it on a small dataset (e.g., IMDb, AG News).
- Evaluate the model on a test set.
Starter Code:
from transformers import DistilBertForSequenceClassification, Trainer, TrainingArguments
from datasets import load_dataset
model = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')
dataset = load_dataset('ag_news')
# ...set up Trainer and train as above...
Solution:
from transformers import DistilBertForSequenceClassification, Trainer, TrainingArguments
from datasets import load_dataset
model = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')
dataset = load_dataset('ag_news')
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=1,
per_device_train_batch_size=8,
evaluation_strategy="epoch",
save_strategy="epoch",
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset['train'].shuffle(seed=42).select(range(1000)),
eval_dataset=dataset['test'].shuffle(seed=42).select(range(500)),
)
trainer.train()
Exercise 2: Intermediate Challenge (15 minutes)
Objective: Compare the storage requirements of full fine-tuning vs. PEFT.
Requirements:
- Calculate the disk space needed to store 5 fully fine-tuned models (e.g., BERT-base, ~400MB each).
- Calculate the space needed for 5 adapter modules (typically <10MB each).
- Discuss the implications for deploying many models.
Hints:
- Use
os.path.getsize()in Python to check file sizes. - Consider the cost for 100 tasks.
Exercise 3: Creative Application (15 minutes)
Objective: Propose a scenario where PEFT would be essential.
Challenge: Describe a real-world application (e.g., a multilingual chatbot for 50 languages) and explain why PEFT is the only practical solution.
Extension Ideas:
- Sketch how you would organize adapters or LoRA modules for each language.
- Consider how you would update or add new tasks efficiently.
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
-
Problem: Accidentally updating all model parameters during PEFT. Cause: Not freezing the base model or using the wrong training method. Solution: Use the correct training API (e.g.,
train_adapter) and verify which parameters are trainable. -
Problem: Storage bloat from full fine-tuning. Cause: Saving entire model checkpoints for each task. Solution: Use PEFT to only store small task-specific modules.
Debugging Tips
- Always check which parameters are being updated (
model.parameters()and theirrequires_gradattribute). - Use model summaries to verify adapter/LoRA modules are present and active.
- Read error messages carefully when integrating PEFT libraries.
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Use PEFT for large models and multi-task scenarios.
- Document which modules are trained and how to load/apply them.
- Monitor performance: sometimes full fine-tuning is still needed for highly specialized tasks.
Next Steps in Your Learning Journey
- Explore specific PEFT methods (LoRA, Adapters, Prompt Tuning)
- Try Hugging Face’s
peftlibrary - Read recent papers on efficient model adaptation
Current PEFT Workflow and Expectations
Tokenize datasets into model fields (input_ids, attention_mask, and labels) before passing them to Trainer; raw text is not accepted automatically without a preprocessing transform or specialized trainer. With Hugging Face PEFT, create a task-appropriate configuration (for example LoraConfig), call get_peft_model, and verify the boundary with model.print_trainable_parameters() plus inspection of requires_grad. PEFT can approach full fine-tuning on some tasks, but comparable quality is an empirical result—not a guarantee.
9. Summary and Review (5 minutes)
Key Takeaways
- Full fine-tuning updates all model parameters; PEFT updates only a small subset or adds small modules.
- PEFT is essential for scaling LLM adaptation to many tasks or users.
- Choosing the right fine-tuning strategy depends on resources, deployment needs, and task requirements.
Quick Review Questions
- What is the main difference between full fine-tuning and PEFT?
- Why is PEFT important for large language models?
- Give an example scenario where PEFT is preferable to full fine-tuning.
For Instructors
Teaching Tips
- Emphasize the practical limitations of full fine-tuning at scale
- Use real-world analogies to clarify PEFT concepts
- Encourage students to experiment with both approaches
Assessment Ideas
- Quiz: Compare and contrast full fine-tuning and PEFT
- Project: Fine-tune a model using both methods and report on resource usage
- Peer review: Present a PEFT use case and critique each other’s solutions
Adaptation Notes
- For beginners: Focus on conceptual understanding and simple code examples
- For advanced students: Dive into the internals of PEFT libraries and custom module design
- Online: Use Colab for hands-on exercises; In-person: Pair programming and group discussions