Skip to content

Commit 2fcca9e

Browse files
Merge pull request #2931 from mhall119/mhall/spark-finetuning
Update pytorch-finetuning learning path to use new script and model
2 parents 8350329 + d895988 commit 2fcca9e

4 files changed

Lines changed: 47 additions & 79 deletions

File tree

content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/1-setup.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,11 @@ NVIDIA provides a collection of ready-to-use fine-tuning scripts optimized for D
102102
Clone the playbooks repository:
103103

104104
```bash
105-
git clone https://github.com/NVIDIA/dgx-spark-playbooks
106-
cd dgx-spark-playbooks
107-
git checkout e51dae47ec9233ccd722dd465be87a984fd97d61
108-
cd nvidia/pytorch-fine-tune/assets
105+
git clone https://github.com/mhall119/finetuning-scripts.git
106+
cd finetuning-scripts/nvidia
109107
```
110108

111-
The repository contains scripts for different model architectures and training strategies. The `assets` directory includes the fine-tuning scripts you'll use in the next steps. Each script is preconfigured with sensible defaults but also accepts command-line arguments for customization.
109+
The repository contains a fork of the scripts found in [NVIDIA's Playbook](https://github.com/NVIDIA/dgx-spark-playbooks/nvidia/pytorch-fine-tune/assets) including the fine-tuning scripts you'll use in the next steps. This script is preconfigured with sensible defaults but also accepts command-line arguments for customization.
112110

113111
## What you've accomplished and what's next
114112

@@ -117,6 +115,6 @@ In this section you:
117115
- Configured Docker permissions on DGX Spark
118116
- Pulled the NVIDIA PyTorch container and launched an interactive session
119117
- Installed fine-tuning libraries and authenticated with Hugging Face
120-
- Cloned the DGX Spark playbooks repository
118+
- Cloned the fine-tuning scripts repository
121119

122120
In the next section, you'll learn how supervised fine-tuning works and what makes it effective for adapting pre-trained models to specific tasks.

content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/2-finetuning.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ layout: learningpathall
88

99
## Why fine-tuning matters for domain knowledge
1010

11-
Pre-trained models like Llama 3.1 8B have broad language skills, but they don't know everything. Ask the base model about the maximum clock speed of the RP2350 microcontroller and it confidently answers "1.8 GHz," a completely fabricated number. The actual specification is 150 MHz.
11+
Pre-trained models like Llama 3.2 8B have broad language skills, but they don't know everything. Ask the base model about the maximum clock speed of the RP2350 microcontroller and it confidently answers "1.8 GHz," a completely fabricated number. The actual specification is 150 MHz.
1212

1313
Fine-tuning fixes this by training the model on real data from Raspberry Pi datasheets. After fine-tuning, the same model answers correctly: "The RP2350 supports up to 150 MHz." No hallucination, no guessing.
1414

1515
The process breaks down into three steps:
1616

17-
1. Patch the NVIDIA playbook's fine-tuning script to load a custom dataset, then run training
17+
1. Run the fine-tuning script with a custom dataset about Raspberry Pi hardware
1818
2. Serve both the original and fine-tuned models using vLLM
1919
3. Compare the outputs side by side to see factual accuracy improve
2020

@@ -74,7 +74,7 @@ Not every model fits entirely in GPU memory during training. The fine-tuning scr
7474

7575
**QLoRA (Quantized LoRA)** goes a step further by loading the frozen model weights in 4-bit precision. Combined with LoRA's parameter-efficient training, this lets you fine-tune 70B-class models that would otherwise exceed available memory.
7676

77-
The script you'll run in the next section uses full fine-tuning by default, but the playbook includes LoRA and QLoRA scripts for larger models.
77+
The script you'll run in the next section uses full fine-tuning by default, but NVIDIA's playbook includes LoRA and QLoRA scripts for larger models.
7878

7979
## What you've accomplished and what's next
8080

@@ -84,4 +84,4 @@ In this section you learned:
8484
- How Raspberry Pi datasheet Q&A pairs are structured in the Alpaca prompt format
8585
- The differences between full fine-tuning, LoRA, and QLoRA
8686

87-
In the next section, you'll walk through the fine-tuning script, patch it to load the Raspberry Pi dataset, and run training to produce your own fine-tuned model.
87+
In the next section, you'll walk through the fine-tuning script, load the Raspberry Pi dataset, and run training to produce your own fine-tuned model.

content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/3-pytorch.md

Lines changed: 33 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,8 @@ weight: 4
66
layout: learningpathall
77
---
88

9-
Now that you understand how fine-tuning works, it's time to look at the actual code. In this section, you'll walk through the key parts of the NVIDIA playbook's fine-tuning script, patch it to load the Raspberry Pi dataset, and run it to produce your own fine-tuned Llama model.
9+
Now that you understand how fine-tuning works, it's time to look at the actual code. In this section, you'll walk through the key parts of the `Llama3_3B_full_finetuning.py` script and run it with the Raspberry Pi dataset to produce your own fine-tuned Llama model.
1010

11-
## Review the fine-tuning scripts
12-
13-
The NVIDIA playbook provides four main fine-tuning scripts, each designed for different scenarios:
14-
15-
| Script | Approach | Best for |
16-
|--------|----------|----------|
17-
| `Llama3_3B_full_finetuning.py` | Full fine-tuning (all parameters) | Smaller models where GPU memory isn't a constraint |
18-
| `Llama3_8B_LoRA_finetuning.py` | LoRA (frozen base + small trainable adapters) | Mid-size models with reduced memory needs |
19-
| `Llama3_70B_LoRA_finetuning.py` | LoRA + FSDP (distributed across GPUs) | Large models that need multi-GPU sharding |
20-
| `Llama3_70B_qLoRA_finetuning.py` | QLoRA (LoRA + 4-bit quantization) | Very large models on memory-limited systems |
21-
22-
The file names refer to the default model each script uses, but you can pass a different model on the command line. This Learning Path uses `Llama3_3B_full_finetuning.py`. The key sections of that script are explained below.
2311

2412
## Imports and dataset preparation
2513

@@ -33,35 +21,37 @@ from trl import SFTConfig, SFTTrainer
3321
from transformers import AutoModelForCausalLM, AutoTokenizer
3422
```
3523

36-
The `ALPACA_PROMPT_TEMPLATE` defines the instruction-following format for training data with three fields: instruction, input, and response. Each training example is formatted using this template so the model learns to recognize the pattern and produce structured answers.
24+
The `DATASET_PROMPT_TEMPLATE` defines the instruction-following format for training data with three fields: instruction, input, and response. Each training example is formatted using this template so the model learns to recognize the pattern and produce structured answers.
3725

38-
The `get_alpaca_dataset()` function loads the Alpaca dataset from Hugging Face by default and formats each example using the template, appending the EOS (End of String) token. You'll patch this function later to load the Raspberry Pi dataset from a local JSONL file instead.
26+
The `get_dataset()` function loads the dataset from (Hugging Face by default) and formats each example using the template, appending the EOS (End of String) token. You'll pass in the Raspberry Pi dataset from a local JSONL file instead.
3927

4028
```python
4129
# Define prompt templates
42-
ALPACA_PROMPT_TEMPLATE = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
30+
DATASET_PROMPT_TEMPLATE = """Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.
4331
### Instruction: {}
4432
4533
### Input: {}
4634
4735
### Response: {}"""
4836

49-
def get_alpaca_dataset(eos_token, dataset_size=500):
37+
def get_dataset(dataset_name, dataset_dir, dataset_files, eos_token, dataset_size=512):
5038
# Preprocess the dataset
5139
def preprocess(x):
5240
texts = [
53-
ALPACA_PROMPT_TEMPLATE.format(instruction, input, output) + eos_token
41+
DATASET_PROMPT_TEMPLATE.format(instruction, input, output) + eos_token
5442
for instruction, input, output in zip(x["instruction"], x["input"], x["output"])
5543
]
5644
return {"text": texts}
5745

58-
dataset = load_dataset("tatsu-lab/alpaca", split="train").select(range(dataset_size)).shuffle(seed=42)
46+
dataset = load_dataset(dataset_name, data_dir=dataset_dir, data_files=dataset_files, split="train")
47+
if len(dataset) > dataset_size:
48+
dataset = dataset.select(range(dataset_size)).shuffle(seed=42)
5949
return dataset.map(preprocess, remove_columns=dataset.column_names, batched=True)
6050
```
6151

6252
## Model and tokenizer loading
6353

64-
The `from_pretrained()` method downloads and initializes a pre-trained language model from Hugging Face. The tokenizer is loaded alongside it, with the padding token set to match the EOS token (required for batched training).
54+
The `from_pretrained()` method downloads and initializes a pre-trained language model (from Hugging Face by default). The tokenizer is loaded alongside it, with the padding token set to match the EOS token (required for batched training).
6555

6656
```python
6757
# Load the model and tokenizer
@@ -78,12 +68,12 @@ The `from_pretrained()` method downloads and initializes a pre-trained language
7868

7969
## Dataset loading
8070

81-
With the model and tokenizer loaded, the script prepares the training data by calling `get_alpaca_dataset()` with the tokenizer's EOS token and the specified dataset size. By default the script downloads the Alpaca dataset from Hugging Face, but you'll patch this function to load the Raspberry Pi JSONL file instead.
71+
With the model and tokenizer loaded, the script prepares the training data by calling `get_dataset()` with the tokenizer's EOS token and the specified dataset size. By default the script downloads the Alpaca dataset from Hugging Face, but you'll pass in the Raspberry Pi JSONL file from the command-line instead.
8272

8373
```python
8474
# Load and preprocess the dataset
8575
print(f"Loading dataset with {args.dataset_size} samples...")
86-
dataset = get_alpaca_dataset(tokenizer.eos_token, args.dataset_size)
76+
dataset = get_dataset(args.dataset, args.dataset_dir, args.dataset_files, tokenizer.eos_token, args.dataset_size)
8777
```
8878

8979
## Training configuration
@@ -94,7 +84,7 @@ The training configuration controls how the SFT process runs. Notable parameters
9484
# Configure the SFT config
9585
config = {
9686
"per_device_train_batch_size": args.batch_size,
97-
"num_train_epochs": 0.01, # Warmup epoch
87+
"num_train_epochs": 0.05, # Warmup epoch
9888
"gradient_accumulation_steps": args.gradient_accumulation_steps,
9989
"learning_rate": args.learning_rate,
10090
"optim": "adamw_torch",
@@ -104,7 +94,6 @@ The training configuration controls how the SFT process runs. Notable parameters
10494
"dataset_text_field": "text",
10595
"packing": False,
10696
"max_length": args.seq_length,
107-
"torch_compile": False,
10897
"report_to": "none",
10998
"logging_dir": args.log_dir,
11099
"logging_steps": args.logging_steps,
@@ -114,22 +103,21 @@ The training configuration controls how the SFT process runs. Notable parameters
114103

115104
## Model compilation and training
116105

117-
If `torch.compile()` is enabled, the script first optimizes the model graph for faster execution on the hardware. A short warmup pass (0.01 epochs) triggers compilation so the overhead doesn't affect the actual training run. After warmup, the script creates an `SFTTrainer` with the full epoch count and calls `trainer.train()`. The returned `trainer_stats` object contains metrics like loss, throughput, and training time.
106+
The script first optimizes the model graph for faster execution on the hardware using the `torch.compile()` function. A short warmup pass (0.05 epochs) triggers compilation so the overhead doesn't affect the actual training run. After warmup, the script creates an `SFTTrainer` with the full epoch count and calls `trainer.train()`. The returned `trainer_stats` object contains metrics like loss, throughput, and training time.
118107

119108
```python
120-
# Compile model if requested
121-
if args.use_torch_compile:
122-
print("Compiling model with torch.compile()...")
123-
model = torch.compile(model)
124-
125-
# Warmup for torch compile
126-
print("Running warmup for torch.compile()...")
127-
SFTTrainer(
128-
model=model,
129-
processing_class=tokenizer,
130-
train_dataset=dataset,
131-
args=SFTConfig(**config),
132-
).train()
109+
# Compile model for faster training
110+
print("Compiling model with torch.compile()...")
111+
model = torch.compile(model)
112+
113+
# Warmup for torch compile
114+
print("Running warmup for torch.compile()...")
115+
SFTTrainer(
116+
model=model,
117+
processing_class=tokenizer,
118+
train_dataset=dataset,
119+
args=SFTConfig(**config),
120+
).train()
133121

134122
# Train the model
135123
print(f"\nStarting full fine-tuning for {args.num_epochs} epoch(s)...")
@@ -146,9 +134,9 @@ If `torch.compile()` is enabled, the script first optimizes the model graph for
146134
trainer_stats = trainer.train()
147135
```
148136

149-
## Patch the script for the Raspberry Pi dataset
137+
## Download the Raspberry Pi dataset
150138

151-
The script loads the Alpaca dataset from Hugging Face by default. You need to patch the dataset loading function to use the local Raspberry Pi JSONL file instead.
139+
The script loads the Alpaca dataset from Hugging Face by default. You need to point it to the local Raspberry Pi JSONL dataset file instead.
152140

153141
First, open a new terminal on the DGX Spark (not inside the container) and navigate to the directory where you launched the Docker container. This is the directory that gets mounted as `/workspace` inside the container. Download the dataset file:
154142

@@ -164,34 +152,17 @@ Back inside the container, copy the dataset into the script's working directory:
164152
cp /workspace/raspberry_pi_qa.jsonl .
165153
```
166154

167-
The following `sed` command replaces the `get_alpaca_dataset()` function to load from a local JSONL file instead of Hugging Face. The replacement function reads the Raspberry Pi Q&A pairs and formats them using the same Alpaca prompt template:
168-
169-
```bash
170-
sed -i '/^def get_alpaca_dataset/,/^ return dataset\.map/c\
171-
def get_alpaca_dataset(eos_token, dataset_size=500):\
172-
def preprocess(x):\
173-
texts = [\
174-
ALPACA_PROMPT_TEMPLATE.format(instruction, inp, output) + eos_token\
175-
for instruction, inp, output in zip(x["instruction"], x["input"], x["output"])\
176-
]\
177-
return {"text": texts}\
178-
dataset = load_dataset("json", data_files="raspberry_pi_qa.jsonl", split="train")\
179-
if len(dataset) > dataset_size:\
180-
dataset = dataset.select(range(dataset_size))\
181-
return dataset.map(preprocess, remove_columns=dataset.column_names, batched=True)' Llama3_3B_full_finetuning.py
182-
```
183-
184-
The key difference is `load_dataset("json", data_files="raspberry_pi_qa.jsonl", split="train")`, which reads the local file instead of downloading from Hugging Face. The function still applies the same Alpaca prompt template and EOS token.
185-
186155
## Run the fine-tuning
187156

188157
With the dataset patch applied, you're ready to run the fine-tuning. The command below trains the Llama 3.1 8B model using full fine-tuning on the Raspberry Pi dataset:
189158

190159
```bash
191160
python Llama3_3B_full_finetuning.py \
192-
--model_name "meta-llama/Llama-3.1-8B" \
161+
--model_name "meta-llama/Llama-3.2-3B-Instruct" \
162+
--dataset "json" \
163+
--dataset_files="/workspace/projects/pytorch-finetuning/raspberry_pi_qa.jsonl" \
193164
--dataset_size 300 \
194-
--output_dir "/workspace/models/Llama-3.1-8B-FineTuned"
165+
--output_dir "/workspace/models/Llama-3.2-3B-FineTuned"
195166
```
196167

197168
The `--dataset_size 300` flag tells the script to use all entries in the Raspberry Pi dataset (the default is 500, but a smaller, focused dataset can be more effective than a larger generic one). The `--output_dir` flag saves the fine-tuned model and tokenizer to the specified directory. Because you mounted your current directory into the container with `-v ${PWD}:/workspace`, the saved model is also accessible from the host system.
@@ -202,9 +173,8 @@ Training takes a few minutes on DGX Spark. When it completes, you'll see a summa
202173

203174
In this section you:
204175

205-
- Reviewed the available fine-tuning scripts and their approaches
206176
- Walked through each stage of the full fine-tuning script
207-
- Patched the dataset loading function to use Raspberry Pi datasheet Q&A pairs
177+
- Downloaded the Raspberry Pi datasheet Q&A pairs
208178
- Ran full fine-tuning and saved the resulting model with `--output_dir`
209179

210180
In the next section, you'll serve both the original and fine-tuned models and compare their responses to Raspberry Pi hardware questions.

content/learning-paths/laptops-and-desktops/pytorch-finetuning-on-spark/4-testing.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ Before testing your fine-tuned model, first observe how the original, unmodified
4343

4444
### Launch vLLM
4545

46-
Start the vLLM server with the original Llama 3.1 8B model:
46+
Start the vLLM server with the original Llama 3.2 3B Instruct model:
4747

4848
```bash
4949
python3 -m vllm.entrypoints.openai.api_server \
50-
--model "meta-llama/Llama-3.1-8B" --trust-remote-code \
50+
--model "meta-llama/Llama-3.2-3B-Instruct" --trust-remote-code \
5151
--tensor-parallel-size 1 --quantization fp8 \
5252
--gpu-memory-utilization 0.80
5353
```
@@ -87,7 +87,7 @@ The original model hallucinates an incorrect specification. The output is simila
8787
"id": "cmpl-91e070e2a34aaf01",
8888
"object": "text_completion",
8989
"created": 1770998840,
90-
"model": "meta-llama/Llama-3.1-8B",
90+
"model": "meta-llama/Llama-3.2-3B-Instruct",
9191
"choices": [
9292
{
9393
"index": 0,
@@ -113,7 +113,7 @@ Now test your fine-tuned model to see how training on Raspberry Pi datasheet con
113113
As of this writing, vLLM does not support version 5 of the `transformers` library that was used when fine-tuning the model, so you need to patch its `tokenizer_config.json`. Run the following command to update the `tokenizer_class` to `PreTrainedTokenizerFast`, which is compatible with the older `transformers` version bundled in the vLLM container:
114114

115115
```bash
116-
sed -i 's/"tokenizer_class": "TokenizersBackend"/"tokenizer_class": "PreTrainedTokenizerFast"/' /workspace/models/Llama-3.1-8B-FineTuned/tokenizer_config.json
116+
sed -i 's/"tokenizer_class": "TokenizersBackend"/"tokenizer_class": "PreTrainedTokenizerFast"/' /workspace/models/Llama-3.2-3B-FineTuned/tokenizer_config.json
117117
```
118118
{{% /notice %}}
119119

@@ -123,7 +123,7 @@ Start the vLLM server with your fine-tuned model:
123123

124124
```bash
125125
python3 -m vllm.entrypoints.openai.api_server \
126-
--model "/workspace/models/Llama-3.1-8B-FineTuned" --trust-remote-code \
126+
--model "/workspace/models/Llama-3.2-3B-FineTuned" --trust-remote-code \
127127
--tensor-parallel-size 1 --quantization fp8 \
128128
--gpu-memory-utilization 0.80
129129
```
@@ -152,7 +152,7 @@ The fine-tuned model produces a correct, datasheet-accurate response. The output
152152
"id": "cmpl-bad36ff5edddfb74",
153153
"object": "text_completion",
154154
"created": 1770999123,
155-
"model": "/workspace/models/Llama-3.1-8B-FineTuned",
155+
"model": "/workspace/models/Llama-3.2-3B-FineTuned",
156156
"choices": [
157157
{
158158
"index": 0,

0 commit comments

Comments
 (0)