Hello! Welcome to your seventh lesson in the "Large Language Models and Generative AI" module.
In our last lesson, we gave our chatbots memory, allowing them to hold contextually rich conversations. We explored strategies like buffering, summarizing, and using persistent storage to remember past interactions. These techniques, along with RAG, are powerful ways to control a model's output without changing the model itself.
But what if the base model's inherent style, reasoning process, or knowledge domain is fundamentally mismatched with your task? In such cases, prompting alone isn't enough. Today, we'll dive into fine-tuning, a technique that directly modifies the model's weights to adapt its core behavior to a specific, custom task.
This lesson will teach you how to fine-tune a small language model on a custom dataset. You will learn:
- The difference between fine-tuning and pre-training, and when to fine-tune versus using RAG.
- Why fine-tuning large models is computationally expensive.
- How modern techniques like QLoRA make fine-tuning accessible on consumer hardware.
- The end-to-end process of preparing a dataset, configuring, and running a supervised fine-tuning job.
1. What is Fine-Tuning?
Large Language Models undergo a two-stage development process: pre-training and fine-tuning.
- Pre-training: A massive neural network is trained on a vast corpus of text from the internet. This is computationally intensive (costing millions of dollars) and results in a base model with general knowledge about language, facts, and reasoning.
- Fine-tuning: This base model is then further trained on a smaller, curated dataset to specialize it for a particular task, such as following instructions, engaging in dialogue, or adopting a specific persona.

In this lesson, we'll focus on Supervised Fine-Tuning (SFT), where we teach the model by showing it examples of high-quality inputs and desired outputs.
Supervised Fine-Tuning - Hugging Face LLM Course
To start, let's clarify when SFT is the right tool for the job. The Hugging Face LLM Course provides excellent guidance on this.
Read the sections 'Supervised Fine-Tuning' and 'When to Use SFT'. Focus on the distinction between fine-tuning for performance/style and using prompting/RAG for knowledge injection.
As you read, consider this rule of thumb:
- Use RAG when you need to provide the model with new knowledge (e.g., internal company documents, recent news).
- Use fine-tuning when you need to teach the model a new skill or style (e.g., generating output in a specific format like JSON, adopting a certain persona, or understanding niche jargon).
2. The Challenge of Fine-Tuning: Memory and Cost
Fine-tuning an LLM seems simple in theory, but the practical hardware requirements are staggering. A full fine-tuning run, where every model parameter is updated, requires a massive amount of GPU memory (VRAM).
Why so much? You need to store not only the model's weights but also the gradients (used to calculate updates), and the optimizer states (which track momentum during training).
QLoRA—How to Fine-tune an LLM on a Single GPU (w/ Python Code)
Let's break down the memory costs. This video from Shaw Talebi does an excellent job explaining why fine-tuning a 10 billion parameter model can require over 160 GB of VRAM—far beyond any consumer GPU.
Watch the first 4 minutes and 38 seconds (00:00 - 04:38). Pay attention to the three main components that consume VRAM: model parameters, gradients, and optimizer states.
These prohibitive costs led to the development of Parameter-Efficient Fine-Tuning (PEFT) methods, which aim to achieve the benefits of fine-tuning without the massive resource footprint.
3. QLoRA: Democratizing Fine-Tuning
The breakthrough that made fine-tuning accessible on single, consumer-grade GPUs is QLoRA (Quantized Low-Rank Adaptation). It combines several clever techniques to drastically reduce memory usage.
Let's break down its key components.
Low-Rank Adaptation (LoRA)
Instead of updating all the billions of parameters in the original model, LoRA freezes them and injects small, trainable "adapter" layers. These adapters consist of two much smaller matrices. During training, only these adapter weights are updated, which represents a tiny fraction of the total parameters.
How to Fine Tune your own LLM using LoRA (on a CUSTOM dataset!)
The core idea of LoRA is to represent the weight updates using a low-rank decomposition. This video explains the concept intuitively.
Watch from 02:20 to 03:16. Focus on the visual explanation of how a large weight update matrix is approximated by multiplying two smaller matrices (A and B), significantly reducing the number of trainable parameters.
Quantization and Other Optimizations
QLoRA builds on LoRA by adding more memory-saving tricks:
- 4-bit Quantization: The weights of the large, frozen base model are loaded in a 4-bit data type instead of the usual 16-bit or 32-bit. This reduces the memory required to store the model by 4x.
- Double Quantization: This technique saves even more memory by quantizing the constants used during the first quantization step.
- Paged Optimizers: This leverages CPU memory to handle occasional memory spikes during training, preventing out-of-memory errors on the GPU.
QLoRA—How to Fine-tune an LLM on a Single GPU (w/ Python Code)
Now, let's see how all the pieces of QLoRA fit together. We'll return to the video by Shaw Talebi for a detailed breakdown of these components.
Watch from 04:38 to 20:21. This is the most technical part of the lesson, but it's crucial for understanding how QLoRA works: (04:38 - 07:12): Understand the concept of quantization as 'bucketing' numbers. (07:12 - 13:45): Grasp the ideas of '4-bit Normal Float' and 'Double Quantization' at a high level. (13:45 - 18:11): Learn about 'Paged Optimizers' and get a recap of the LoRA technique. (18:11 - 20:21): See the final summary of how all four ingredients combine to drastically reduce memory requirements.
The combined effect is transformative.

With QLoRA, it's possible to fine-tune a 7-billion-parameter model on a GPU with as little as 12 GB of VRAM.
4. A Practical Guide to Fine-Tuning with QLoRA
Now, let's walk through the end-to-end process of fine-tuning a small model. We'll use Hugging Face's powerful ecosystem of libraries: transformers, datasets, peft (for LoRA), and trl (Transformer Reinforcement Learning library, which includes the SFTTrainer).
Step 1: Prepare the Dataset
This is the most important step. Your model's performance will be a direct reflection of your data quality. For supervised fine-tuning, you need a dataset of instruction-response pairs. This dataset should be formatted in a way the model understands, typically as a list of messages.
# Example of a single data point in the required format
{
"messages": [
{"role": "user", "content": "What are the ingredients for a Margherita pizza?"},
{"role": "system", "content": "A Margherita pizza requires tomatoes, fresh mozzarella, fresh basil, salt, and extra-virgin olive oil."}
]
}
You can create this dataset manually, use an existing one from the Hugging Face Hub, or generate it synthetically using a more powerful LLM. A highly effective (but advanced) technique is to generate a large synthetic dataset and then use an LLM to filter for high-quality examples, as data quality often matters more than quantity.
Fully Fine-tune a Small Language Model with Hugging Face Tutorial
This tutorial provides a clear example of loading a custom dataset and formatting it for fine-tuning.
Read section '11.1 Format the dataset into LLM-style inputs/outputs'. Notice how a Python function is used to map the raw data into the conversational messages format and then split into training and testing sets.
Step 2: Set Up the Training Environment
You'll need a GPU for this, which you can access for free on platforms like Google Colab. The main libraries to install are:!pip install -q transformers datasets accelerate peft trl bitsandbytes
Step 3: Load the Quantized Model and Tokenizer
Here, we load our base model but instruct transformers to quantize it to 4-bit using the bitsandbytes library.
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
model_id = "mistralai/Mistral-7B-v0.1" # A popular small model
# Configuration for 4-bit quantization
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16 # Use bfloat16 for computation
)
# Load the model with quantization
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=quantization_config,
device_map="auto" # Automatically distribute across GPUs if available
)
# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token # Set padding token
Step 4: Configure and Apply LoRA
Next, we define our LoRA configuration using the peft library and apply it to our quantized model. This tells the trainer which parts of the model to adapt.
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# Prepare the model for k-bit training (enables gradient checkpointing)
model = prepare_model_for_kbit_training(model)
# LoRA configuration
lora_config = LoraConfig(
r=16, # Rank of the update matrices. Higher rank means more parameters.
lora_alpha=32, # Scaling factor for the LoRA weights.
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # Apply LoRA to attention layers
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# Get the PEFT model
peft_model = get_peft_model(model, lora_config)
Step 5: Configure and Run the Trainer
The SFTTrainer from the trl library handles the entire training loop. We just need to provide it with the model, datasets, tokenizer, and training arguments.
Fully Fine-tune a Small Language Model with Hugging Face Tutorial
Let's examine how to configure the trainer and start the fine-tuning process. This next resource walks through setting up SFTConfig and SFTTrainer.
Read section '12 Fine-tuning our model'. Pay close attention to the SFTConfig parameters like num_train_epochs, learning_rate, and per_device_train_batch_size. Then see how these are passed to the SFTTrainer before calling trainer.train().
During training, it's crucial to monitor the train_loss and validation_loss.
- If both are decreasing, training is going well.
- If
train_lossdecreases butvalidation_lossstarts increasing, the model is overfitting. You should stop training or adjust hyperparameters (e.g., reduce the learning rate).
Step 6: Test the Fine-Tuned Model
After training, you can save your LoRA adapter and use it for inference. The real test is to compare its output on a specific prompt against the original base model.
How to Fine Tune your own LLM using LoRA (on a CUSTOM dataset!)
This final video segment shows the reward for all our hard work: a model that performs significantly better on our target task.
Watch from 55:45 to 01:01:06. The presenter deploys the improved, re-trained model and tests it on the same baseline questions from the beginning. Notice how the answers are now accurate and conform to the domain-specific knowledge from the training data.
The difference is often dramatic. The fine-tuned model will have learned the specific style and structure of your training data, producing outputs that are far more aligned with your goal than the generic base model could ever achieve through prompting alone.
Conclusion
Today you've unlocked one of the most powerful capabilities in modern AI development. You've moved beyond simply using LLMs to actively shaping their behavior.
Key Takeaways:
- Fine-tuning adapts a model's skills and style, while RAG provides it with factual knowledge.
- Full fine-tuning is computationally expensive, requiring massive amounts of VRAM.
- QLoRA is a game-changing technique that combines quantization and low-rank adaptation to make fine-tuning accessible on a single consumer GPU.
- The fine-tuning workflow involves preparing a high-quality dataset, loading a quantized base model, applying a LoRA configuration, and using the
SFTTrainerto manage the training process. - Data quality is paramount. A smaller, high-quality dataset is often better than a larger, noisy one.
Preview of the Next Lesson:
Now that we can customize models to generate specific content, the responsibility to ensure that content is safe and appropriate becomes critical. In our next lesson, we will explore content moderation and safety guardrails, learning how to implement systems that prevent the generation of harmful, biased, or undesirable text, a crucial step for any production-ready LLM application.