|
| 1 | +--- |
| 2 | +title: Fine Tuning with PyTorch and Hugging Face |
| 3 | +weight: 4 |
| 4 | + |
| 5 | +### FIXED, DO NOT MODIFY |
| 6 | +layout: learningpathall |
| 7 | +--- |
| 8 | + |
| 9 | +## Review fine-tuning scripts |
| 10 | + |
| 11 | +The NVIDIA playbook provides four main fine-tuning scripts, each designed for different scenarios: |
| 12 | + |
| 13 | +**Llama3_3B_full_finetuning.py** - Full supervised fine-tuning for Llama 3.2 3B |
| 14 | +This script trains all model parameters, providing maximum flexibility but requiring more GPU memory. Use this for smaller models when you have sufficient GPU resources and want the best possible fine-tuning results. |
| 15 | + |
| 16 | +**Llama3_8B_LoRA_finetuning.py** - LoRA fine-tuning for Llama 3.1 8B |
| 17 | +This script uses Low-Rank Adaptation (LoRA), which trains only a small number of additional parameters while keeping the base model frozen. This approach dramatically reduces memory requirements and training time while maintaining good performance. |
| 18 | + |
| 19 | +**Llama3_70B_LoRA_finetuning.py** - LoRA fine-tuning for Llama 3.1 70B with FSDP |
| 20 | +For larger 70B models, this script implements LoRA with Fully Sharded Data Parallel (FSDP) support, distributing the model across multiple GPUs to handle the increased memory requirements. |
| 21 | + |
| 22 | +**Llama3_70B_qLoRA_finetuning.py** - QLoRA fine-tuning for Llama 3.1 70B |
| 23 | +This script uses Quantized LoRA (QLoRA), which combines LoRA with 4-bit quantization. This is the most memory-efficient option, allowing you to fine-tune very large models even on systems with limited GPU memory. |
| 24 | + |
| 25 | + |
| 26 | +The file names only refer to the default model they will use if you don't specify one on the command line, you can use them to train other models of your choosing. This tutorial will use the `Llama3_3B_full_finetuning.py` script. Below are the key sections of that script. |
| 27 | + |
| 28 | +## Imports and dataset preparation |
| 29 | + |
| 30 | +This section sets up the foundation for finetuning. The imports bring in PyTorch, dataset loading utilities, and Hugging Face libraries for supervised fine-tuning. |
| 31 | + |
| 32 | +```python |
| 33 | + |
| 34 | +import torch |
| 35 | +import argparse |
| 36 | +from datasets import load_dataset |
| 37 | +from trl import SFTConfig, SFTTrainer |
| 38 | +from transformers import AutoModelForCausalLM, AutoTokenizer |
| 39 | +``` |
| 40 | + |
| 41 | +The `ALPACA_PROMPT_TEMPLATE` defines the instruction-following format for training data with three fields: instruction, input, and response. This is the format that the fine-tuned model will be taught to expect as input and produce as output. |
| 42 | + |
| 43 | +The `get_alpaca_dataset()` function loads the Alpaca dataset and formats each example using the template, appending the EOS (End of String) token. It selects a configurable number of samples (default 500) and shuffles them for training. |
| 44 | + |
| 45 | +```python |
| 46 | +# Define prompt templates |
| 47 | +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. |
| 48 | +### Instruction: {} |
| 49 | +
|
| 50 | +### Input: {} |
| 51 | +
|
| 52 | +### Response: {}""" |
| 53 | + |
| 54 | +def get_alpaca_dataset(eos_token, dataset_size=500): |
| 55 | + # Preprocess the dataset |
| 56 | + def preprocess(x): |
| 57 | + texts = [ |
| 58 | + ALPACA_PROMPT_TEMPLATE.format(instruction, input, output) + eos_token |
| 59 | + for instruction, input, output in zip(x["instruction"], x["input"], x["output"]) |
| 60 | + ] |
| 61 | + return {"text": texts} |
| 62 | + |
| 63 | + dataset = load_dataset("tatsu-lab/alpaca", split="train").select(range(dataset_size)).shuffle(seed=42) |
| 64 | + return dataset.map(preprocess, remove_columns=dataset.column_names, batched=True) |
| 65 | +``` |
| 66 | + |
| 67 | +## Model and tokenizer loading |
| 68 | + |
| 69 | +This section initializes the language model. The `from_pretrained()` method loads a pre-trained language model from Hugging Face. The tokenizer loads the corresponding tokenizer and sets the padding token to match the EOS token, which is necessary for batched training. |
| 70 | + |
| 71 | +```python |
| 72 | + # Load the model and tokenizer |
| 73 | + print(f"Loading model: {args.model_name}") |
| 74 | + model = AutoModelForCausalLM.from_pretrained( |
| 75 | + args.model_name, |
| 76 | + dtype=args.dtype, |
| 77 | + device_map="auto", |
| 78 | + trust_remote_code=True |
| 79 | + ) |
| 80 | + tokenizer = AutoTokenizer.from_pretrained(args.model_name, trust_remote_code=True) |
| 81 | + tokenizer.pad_token = tokenizer.eos_token |
| 82 | +``` |
| 83 | + |
| 84 | +## Dataset loading |
| 85 | + |
| 86 | +This section prepares the training data by calling the previously defined `get_alpaca_dataset()` function with the model's tokenizer EOS token and the specified dataset size. The formatted dataset is ready for the trainer to consume. |
| 87 | + |
| 88 | +```python |
| 89 | + # Load and preprocess the dataset |
| 90 | + print(f"Loading dataset with {args.dataset_size} samples...") |
| 91 | + dataset = get_alpaca_dataset(tokenizer.eos_token, args.dataset_size) |
| 92 | +``` |
| 93 | + |
| 94 | +## Training configuration |
| 95 | + |
| 96 | +This section defines parameters for supervised fine-tuning. Key parameters include `num_train_epochs` (set to 0.01 for a warmup epoch, later changed to full training), `gradient_accumulation_steps` (how many batches to accumulate before updating weights), `learning_rate` (step size for optimizer updates), and `max_length` (maximum sequence length for training samples). The logging parameters control where and how often training metrics are saved. |
| 97 | + |
| 98 | +```python |
| 99 | + # Configure the SFT config |
| 100 | + config = { |
| 101 | + "per_device_train_batch_size": args.batch_size, |
| 102 | + "num_train_epochs": 0.01, # Warmup epoch |
| 103 | + "gradient_accumulation_steps": args.gradient_accumulation_steps, |
| 104 | + "learning_rate": args.learning_rate, |
| 105 | + "optim": "adamw_torch", |
| 106 | + "save_strategy": 'no', |
| 107 | + "remove_unused_columns": False, |
| 108 | + "seed": 42, |
| 109 | + "dataset_text_field": "text", |
| 110 | + "packing": False, |
| 111 | + "max_length": args.seq_length, |
| 112 | + "torch_compile": False, |
| 113 | + "report_to": "none", |
| 114 | + "logging_dir": args.log_dir, |
| 115 | + "logging_steps": args.logging_steps, |
| 116 | + "gradient_checkpointing": args.gradient_checkpointing, # Save memory |
| 117 | + } |
| 118 | +``` |
| 119 | + |
| 120 | +## Model compilation and training |
| 121 | + |
| 122 | +This section handles optional PyTorch compilation and executes the training. If enabled, `torch.compile()` optimizes the model for faster execution on the hardware. A warmup training pass runs a short training session (0.01 epochs) to trigger compilation and avoid compilation overhead during the actual training run. The full training creates an `SFTTrainer` with the full epoch count, then executes the training with `trainer.train()`. The `trainer_stats` variable returns training metrics like loss, throughput, and training time. |
| 123 | + |
| 124 | +```python |
| 125 | + # Compile model if requested |
| 126 | + if args.use_torch_compile: |
| 127 | + print("Compiling model with torch.compile()...") |
| 128 | + model = torch.compile(model) |
| 129 | + |
| 130 | + # Warmup for torch compile |
| 131 | + print("Running warmup for torch.compile()...") |
| 132 | + SFTTrainer( |
| 133 | + model=model, |
| 134 | + processing_class=tokenizer, |
| 135 | + train_dataset=dataset, |
| 136 | + args=SFTConfig(**config), |
| 137 | + ).train() |
| 138 | + |
| 139 | + # Train the model |
| 140 | + print(f"\nStarting full fine-tuning for {args.num_epochs} epoch(s)...") |
| 141 | + config["num_train_epochs"] = args.num_epochs |
| 142 | + config["report_to"] = "tensorboard" |
| 143 | + |
| 144 | + trainer = SFTTrainer( |
| 145 | + model=model, |
| 146 | + processing_class=tokenizer, |
| 147 | + train_dataset=dataset, |
| 148 | + args=SFTConfig(**config), |
| 149 | + ) |
| 150 | + |
| 151 | + trainer_stats = trainer.train() |
| 152 | +``` |
| 153 | + |
| 154 | +## Model saving |
| 155 | + |
| 156 | +Finally, section saves the fine-tuned model and tokenizer to disk if an output directory is specified. The `trainer.save_model()` method saves the model weights and configuration, while `tokenizer.save_pretrained()` saves the tokenizer configuration and vocabulary. This allows you to load and use the fine-tuned model later for inference or further training. |
| 157 | + |
| 158 | +```python |
| 159 | + # Save model if requested |
| 160 | + if args.output_dir: |
| 161 | + print(f"Saving model to {args.output_dir}...") |
| 162 | + trainer.save_model(args.output_dir) |
| 163 | + tokenizer.save_pretrained(args.output_dir) |
| 164 | + print("Model saved successfully!") |
| 165 | +``` |
| 166 | + |
| 167 | +## Run the fine-tuning |
| 168 | + |
| 169 | +```bash |
| 170 | +python Llama3_3B_full_finetuning.py \ |
| 171 | +--model_name "meta-llama/Meta-Llama-3-8B" \ |
| 172 | +--output_dir workspace/Models/Llama-3-8B-FineTuned |
| 173 | +``` |
0 commit comments