Fine-Tuning Mistral 7B using QLoRA with PyTorch pt. 1: The Model | ML Engineering

    


Hi All

Today we're working with a popular and slightly bigger model than our previous example. Mistral 7B is capable of chat and light coding tasks, for older hardware it's a winner for sure. 

Here's a complete, runnable example of fine-tuning Mistral 7B using QLoRA with the peft, transformers, and bitsandbytes libraries. This example assumes you're working with a single GPU (eg. an A100 or similar).

First install the required packages:


pip install -q bitsandbytes datasets accelerate peft transformers trl

View full script below, also available here:  

Full breakdown of the script above, block-by-block.

1.    Dataset Loading


dataset = load_dataset("timdettmers/openassistant-guanaco", split="train")

*    Loads a preprocessed instruction-following dataset (Guanco, derived from OpenAssistant).

*    split="train" selects the training portion

*    The dataset is in a conversational format (instruction & response pairs).

*    Each entry has a text field combining the prompt and response.

 

2. Model and Tokenizer Setup


model_id = "mistralai/Mistral-7B-v0.1"
bnb_config = BitsAndBytesConfig(...)
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb_config, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token

*    Configures the model and tokenizer with 4-bit quantization.

*    Key Components:

            *    bnb_config:

                        *    load_in_4bit=True: Enables 4-bit quantization.

                        *    bnb_4bit_quant_type="nf4": Uses NormalFloat (optimal for LLMs)

                        *     bnb_4bit_compute_dtype=torch.bfloat16: Uses BF16 for computations.

*    device_map="auto": Automatically distributes layers across GPU's (or uses CPU if needed).

*    Tokenizer: Sets the padding token to the EOS token for batching.   

        

3. LoRA Configuration


peft_config = LoraConfig(
    r=16,                # Rank of LoRA matrices
    lora_alpha=32,       # Scaling factor
    lora_dropout=0.05,   # Dropout for regularization
    bias="none",         # No bias adaptation
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # Targeted layers
)

3.1    Defines the LoRA (Low Rank Adaptation) parameters.

3.2    Key parameters:

        *    r: Rank of the low-rank matrices (smaller = more efficient)

        *    target_modules: Only adapts attention projection layers (nt MLP layers).

        *    task_type: Specifies causal language modelling (for autoregressive tasks).

 

4. Training Arguments  


training_args = TrainingArguments(
    output_dir="mistral-7b-qlora-finetuned",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    optim="paged_adamw_8bit",  # Memory-efficient AdamW
    learning_rate=2e-4,
    lr_scheduler_type="cosine",
    save_strategy="epoch",
    logging_steps=10,
    num_train_epochs=3,
    max_steps=100,  # For demo (remove in practice)
    fp16=True,      # Mixed precision training
)

*    Configures training hyperparameters and behaviour.

*    Key Parameters:

                    *    Batch Size: 4 per device, with gradient accumulation to simulate 16.

                    *    Optimizer: paged_adamw_8bit (8-bit AdamW with memory paging).

                    *    Learning Rate: 2e-4 (typical  for LoRA).

                    *    Scheduler: Cosine annealing for stable convergence.

                    *    Mixed Precision: fp16=True for faster training.

 

5. Trainer Initialization


trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    peft_config=peft_config,
    dataset_text_field="text",  # Column in dataset containing text
    tokenizer=tokenizer,
    packing=True,  # Packs sequences to max length for efficiency
)

*    Sets up the training loop with PEFT (Parameter-Efficient Fine-Tuning)  

*    Key Features:

            *    SFTTrainer: A subclass of Trainer optimized for instruction tuning.

            *    packing=True: Combines multiple short sequences into one long sequence (reduces padding overhead).

 

6. Training Execution

trainer.train()

*    Starts the fine-tuning process

*    Loads batches from dataset

*    Applies LoRA updates to the frozen base model.

*    Logs metrics (loss, etc.) every logging_steps. 

 

7. Saving the Model


trainer.model.save_pretrained("mistral-7b-qlora-finetuned")

*    Saves the fine-tuned model and LoRA weights.

*    Output: directory with:

        *    adapter_config.json (LoRA config).

        *    adapter_model.bin (LoRA weights)

        *    config.json (base model config). 

Final Notes:

1.    Quantization Impact:

        *    The base model is loaded in 4-bit, but gradients are computed in BF16 (via compute_dtype).

        *    LoRA adapters are trained in full precision (no quantization).

2.    Memory Savings:

        *    QLoRA reduces memory usage by ~90% compared to full fine-tuning (eg. 40GB vs 80GB for Mistral 7B).

3.    Customization:

        *    For your own dataset, replace Dataset_text_field with your column name.

        *    Adjust target_modules if you want to adapt other layers (eg. MLP).

 The full source is available here. Stay tuned for part 2 where we'll dive into some Ops on the Host (ahem Linux). That's all for now folks.

 

Later

 

-    Ed 

     

 

          

 

 

 

 

Comments

Popular posts from this blog

Deploying LoRA Optimised BERT as a FastApi service on GKE | ML Engineering & MLOps

Advanced Rust ML: Custom Modules with Tch-rs | ML Engineering