AI Engineering Academy · Lektion

LoRA-fine-tuning med Hugging Face PEFT

Konfigurér LoRA-rang, alpha og målmoduler, kør superviseret fine-tuning med TRL SFTTrainer, overvåg træningstabet, og gem checkpoints både med sammenflettede vægte og kun med adapteren.

Lektion 3 af 413 trin

LoRA-fine-tuning med Hugging Face PEFT er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvorfor LoRA i stedet for fuld finjustering?

Ved fuld finjustering opdateres alle parametre i en model. For en model med 7 milliarder parametre i float32-præcision kræver det cirka 28 GB GPU-hukommelse alene til vægtene, plus optimeringstilstande, gradienter og aktiveringer — i alt nemt 80-120 GB. LoRA (Low-Rank Adaptation) tilføjer i stedet et lille antal parametre, der kan trænes (typisk 0,1-1 % af det samlede antal), som lavrangs-matricer, der anvendes på udvalgte lag. Det reducerer GPU-kravet 10-50 gange, samtidig med at resultaterne er sammenlignelige.

# LoRA math intuition:
# Full fine-tuning: update W (large matrix, e.g., 4096 x 4096 = 16.7M parameters)
# LoRA: instead train W = W_0 + A @ B where:
#   A has shape (4096, r) - only r*4096 params
#   B has shape (r, 4096) - only r*4096 params
#   r (rank) is typically 4, 8, or 16 - much smaller than 4096

# Memory comparison for 7B model:
# Full fine-tuning: ~80GB GPU RAM
# LoRA (rank=8): ~12GB GPU RAM - fits on a single A100 or 3090
print('LoRA makes fine-tuning accessible without massive GPU clusters')

Installation af de nødvendige biblioteker

LoRA-finjustering med Hugging Face kræver tre biblioteker: transformers (indlæsning af modeller og tokenisering), peft (Parameter-Efficient Fine-Tuning, som implementerer LoRA) og trl (Transformer Reinforcement Learning, som leverer SFTTrainer til overvåget finjustering). Tilsammen giver de en abstraktion på højt niveau og en produktionsklar arbejdsgang til finjustering.

# pip install transformers peft trl accelerate bitsandbytes datasets

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer, SFTConfig
from datasets import Dataset
import torch

print('Libraries imported successfully')
print(f'GPU available: {torch.cuda.is_available()}')
if torch.cuda.is_available():
    print(f'GPU: {torch.cuda.get_device_name(0)}')
    print(f'GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB')

Indlæsning af grundmodellen med kvantisering

Ved de fleste LoRA-finjusteringer skal du indlæse grundmodellen med 4-bit-kvantisering ved hjælp af bitsandbytes. Det reducerer grundmodellens hukommelsesforbrug med 75 % (en 7B-model går fra cirka 14 GB til cirka 4 GB), samtidig med at det meste af modellens funktionalitet bevares. Kun LoRA-adapterlagene trænes med fuld præcision. Kombinationen af 4-bit-kvantisering og LoRA kaldes QLoRA og gør finjustering af 7B-modeller mulig på almindelige GPU'er.

from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch

MODEL_NAME = 'mistralai/Mistral-7B-Instruct-v0.2'  # or 'meta-llama/Llama-3.2-3B-Instruct'

# 4-bit quantization config (QLoRA)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,                         # quantize base model to 4-bit
    bnb_4bit_quant_type='nf4',                 # NF4 quantization type
    bnb_4bit_compute_dtype=torch.float16,      # compute in float16
    bnb_4bit_use_double_quant=True             # double quantization for extra savings
)

# Load quantized model
model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map='auto',                         # automatically distribute across GPUs
    trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
tokenizer.pad_token = tokenizer.eos_token    # required for training batches
print(f'Model loaded. Parameters: {model.num_parameters():,}')

Konfiguration af LoRA-hyperparametre

De tre vigtigste LoRA-hyperparametre er: r (rang) — dimensionen af lavrangs-matricerne; en højere rang betyder flere parametre, der kan trænes, og en mere udtryksfuld tilpasning, men også mere hukommelsesforbrug og større risiko for overtilpasning. lora_alpha — en skaleringsfaktor, der normalt sættes til det dobbelte af rangen. target_modules — hvilke vægtmatricer LoRA skal anvendes på; opmærksomhedslagene (q_proj, v_proj) er det mest almindelige valg. Start med r=8, og justér derfra.

from peft import LoraConfig, TaskType

lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,         # causal language modeling
    r=8,                                   # rank: 4, 8, 16, 32 — higher = more params
    lora_alpha=16,                         # scaling: usually 2*r
    lora_dropout=0.1,                      # dropout on LoRA layers for regularization
    target_modules=['q_proj', 'v_proj',    # which weight matrices to adapt
                    'k_proj', 'o_proj',    # common to target all attention projections
                    'gate_proj', 'up_proj', 'down_proj'],  # and FFN layers
    bias='none',                           # do not adapt bias parameters
)

# Wrap the model with LoRA adapters
from peft import get_peft_model, prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)  # prepares quantized model for training
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 3,752,071,168 || trainable%: 0.11%

Forberedelse af datasættet

SFTTrainer forventer datasæt i et bestemt format. Det enkleste format er et datasæt med en enkelt text-kolonne, der indeholder den fuldt formaterede prompt- og svarstreng. Brug tokenizerens chat-skabelon til at formatere samtaleeksempler ensartet. SFTTrainer håndterer tokenisering, batchinddeling og maskering af tab (tab beregnes kun på assistentens svar, ikke på inputprompten).

from datasets import Dataset
import json

def load_and_format_dataset(jsonl_path: str, tokenizer) -> Dataset:
    examples = []
    with open(jsonl_path) as f:
        for line in f:
            ex = json.loads(line.strip())
            # Apply chat template to format as expected by the model
            formatted = tokenizer.apply_chat_template(
                ex['messages'],
                tokenize=False,
                add_generation_prompt=False
            )
            examples.append({'text': formatted})
    
    return Dataset.from_list(examples)

# Load training and validation datasets
train_dataset = load_and_format_dataset('train.jsonl', tokenizer)
val_dataset = load_and_format_dataset('validation.jsonl', tokenizer)

print(f'Train examples: {len(train_dataset)}')
print(f'Validation examples: {len(val_dataset)}')
print('Sample formatted text:')
print(train_dataset[0]['text'][:300])

Træning med SFTTrainer

SFTTrainer fra TRL omslutter Hugging Faces Trainer med standardindstillinger til overvåget finjustering. Konfigurér den med træningshyperparametre: antal epoker (1-3 er normalt tilstrækkeligt til instruktionsfinjustering), batchstørrelse, trin til gradientakkumulering (så du kan simulere større batches med begrænset hukommelse), læringsrate (2e-4 er et almindeligt udgangspunkt) og outputmappen til kontrolpunkter.

from trl import SFTTrainer, SFTConfig

training_args = SFTConfig(
    output_dir='./fine-tuned-model',
    num_train_epochs=2,                    # 2-3 epochs for instruction fine-tuning
    per_device_train_batch_size=4,         # increase if GPU memory allows
    gradient_accumulation_steps=4,         # effective batch size = 4*4 = 16
    learning_rate=2e-4,                    # typical LoRA learning rate
    warmup_ratio=0.05,                     # warmup for 5% of steps
    lr_scheduler_type='cosine',            # cosine decay learning rate schedule
    logging_steps=10,
    eval_strategy='steps',
    eval_steps=50,                         # evaluate on validation set every 50 steps
    save_steps=100,
    max_seq_length=2048,                   # max token length per example
    fp16=True,                             # mixed precision training
    report_to='none'                       # or 'wandb' for experiment tracking
)

trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
)

print('Starting LoRA fine-tuning...')
trainer.train()

Overvågning af træningsforløbet

Under træningen skal du holde øje med to vigtige målepunkter: træningstab bør falde jævnt. Valideringstab bør først falde og derefter flade ud eller stige en smule (overtilpasning). Hvis valideringstabet stiger markant, mens træningstabet fortsætter med at falde, skal du stoppe træningen tidligt — modellen husker træningseksemplerne i stedet for at generalisere. Det optimale stoptidspunkt er lige før valideringstabet begynder at stige.

# Reading training logs
# Training step logs look like:
# {'loss': 1.4523, 'grad_norm': 0.85, 'learning_rate': 0.0002, 'epoch': 0.2, 'step': 20}
# {'loss': 1.2341, 'grad_norm': 0.72, 'learning_rate': 0.00018, 'epoch': 0.4, 'step': 40}

# Validation results look like:
# {'eval_loss': 1.1823, 'eval_runtime': 12.3, 'eval_samples_per_second': 8.1, 'step': 50}
# {'eval_loss': 1.0923, 'eval_runtime': 12.1, 'eval_samples_per_second': 8.3, 'step': 100}
# {'eval_loss': 1.1234, 'eval_runtime': 12.4, 'eval_samples_per_second': 8.2, 'step': 150}
# ^ validation loss went UP at step 150 - overfitting starting

# Load the best checkpoint (lowest validation loss)
from transformers import TrainerCallback

print('Best model checkpoint is saved automatically by SFTTrainer (load_best_model_at_end=True)')

Lagring og sammenfletning af LoRA-adaptere

Efter træningen skal du gemme LoRA-adaptervægtene separat fra grundmodellen. Adapteren er lille (fra nogle få MB til nogle få hundrede MB) og kan anvendes på grundmodellen ved inferens. Til produktionsudrulning kan du også sammenflette LoRA-vægtene med grundmodellen og dermed oprette en enkelt modelfil, der ikke kræver PEFT-biblioteket ved inferens — det reducerer inferensoverhead.

# Save only the LoRA adapter (tiny - typically 10-100MB)
model.save_pretrained('./lora-adapter-only')
tokenizer.save_pretrained('./lora-adapter-only')

# Load the adapter for inference (requires base model + peft)
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16, device_map='auto')
model_with_adapter = PeftModel.from_pretrained(base_model, './lora-adapter-only')

# Alternative: Merge adapter into base model (no PEFT needed at inference)
print('Merging LoRA weights into base model...')
merged_model = model_with_adapter.merge_and_unload()  # creates a regular model
merged_model.save_pretrained('./merged-model')
print('Merged model saved - can be used with standard transformers, no PEFT needed')

Kørsel af inferens på den finjusterede model

Test din finjusterede model på et sæt prompts, der er holdt ude af træningen, før du erklærer projektet for vellykket. Sammenlign output side om side med grundmodellens output på de samme prompts for at kontrollere, at finjusteringen har nået sine mål. Kontrollér både de tilfælde, som modellen skal håndtere bedre (målopgaven), og de tilfælde, som den stadig skal håndtere godt (generelle opgaver, som du ikke ønsker skal blive dårligere).

def generate(model, tokenizer, prompt: str, max_new_tokens=512) -> str:
    inputs = tokenizer(prompt, return_tensors='pt').to(model.device)
    with torch.no_grad():
        outputs = model.generate(
            **inputs,
            max_new_tokens=max_new_tokens,
            temperature=0.1,
            do_sample=True,
            pad_token_id=tokenizer.eos_token_id
        )
    # Decode only the new tokens (not the input prompt)
    new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
    return tokenizer.decode(new_tokens, skip_special_tokens=True)

# Compare base vs fine-tuned
test_prompt = 'Extract JSON from: "Alice Johnson, 28, software engineer in NYC since 2021"'
print('=== BASE MODEL ===')
print(generate(base_model, tokenizer, test_prompt))
print('\n=== FINE-TUNED MODEL ===')
print(generate(merged_model, tokenizer, test_prompt))

Oversigt over LoRAs vigtigste hyperparametre

For at tilpasse LoRA til dit anvendelsesområde skal du starte med disse standardværdier og justere én ad gangen. r=8 er et sikkert udgangspunkt — øg til 16 eller 32, hvis modellen har brug for større udtrykskapacitet. lora_alpha = 2*r er et stabilt valg. Læringsrate 2e-4 fungerer til de fleste instruktionsfinjusteringer; sænk den til 1e-4, hvis du ser et ustabilt træningstab. Epoker 1-3: stop, når valideringstabet ikke længere falder.

Hvornår skal du i stedet bruge OpenAI Fine-Tuning?

Hugging Face PEFT LoRA kræver en GPU. Hvis du ikke har en GPU-infrastruktur, er OpenAI's fine-tuning-API et administreret alternativ, der håndterer træningsinfrastrukturen for dig. Upload din JSONL-fil, kald API'et for at starte en træningskørsel, og modtag et model-id for den finjusterede model, som du kan bruge i API-kald. OpenAI fine-tuning understøtter GPT-4o-mini og GPT-3.5-turbo. Det er dyrere pr. token, men fjerner behovet for al infrastrukturadministration.

from openai import OpenAI

client = OpenAI()

# Upload training file
train_file = client.files.create(
    file=open('train.jsonl', 'rb'),
    purpose='fine-tune'
)
val_file = client.files.create(
    file=open('validation.jsonl', 'rb'),
    purpose='fine-tune'
)

# Create fine-tuning job
job = client.fine_tuning.jobs.create(
    training_file=train_file.id,
    validation_file=val_file.id,
    model='gpt-4o-mini',  # base model to fine-tune
    hyperparameters={
        'n_epochs': 3,
        'batch_size': 'auto',
        'learning_rate_multiplier': 'auto'
    }
)
print(f'Fine-tuning job created: {job.id}')
# Monitor: client.fine_tuning.jobs.retrieve(job.id)
# Use: client.chat.completions.create(model=job.fine_tuned_model, ...)

Hurtigt tjek

Test din forståelse af LoRA-finjustering ud fra denne lektion.

Opsummering af lektionen

I denne lektion har du lært, at LoRA reducerer GPU-kravene til finjustering 10-50 gange ved kun at træne små lavrangs-adaptermatricer i stedet for alle modellens parametre, at QLoRA (4-bit-kvantisering og LoRA) gør finjustering af 7B-modeller mulig på almindelige GPU'er med cirka 12 GB VRAM, og at SFTTrainer fra TRL leverer et API på højt niveau, der håndterer tokenisering, batchinddeling, maskering af tab og lagring af kontrolpunkter. Næste skridt er at evaluere og udrulle den finjusterede model.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “LoRA-fine-tuning med Hugging Face PEFT” gratis?

Ja — hele teksten til “LoRA-fine-tuning med Hugging Face PEFT” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “LoRA-fine-tuning med Hugging Face PEFT”?

Konfigurér LoRA-rang, alpha og målmoduler, kør superviseret fine-tuning med TRL SFTTrainer, overvåg træningstabet, og gem checkpoints både med sammenflettede vægte og kun med adapteren. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på AI Engineering Academy?

Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “LoRA-fine-tuning med Hugging Face PEFT”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?

Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvornår fine-tuning slår prompting
  2. Forberedelse af et træningsdatasæt af høj kvalitet
  3. LoRA-fine-tuning med Hugging Face PEFT
  4. Evaluering og implementering af Deres fine-tunede model
← Tilbage til AI Engineering Academy