Ga naar hoofdinhoud

Qwen3.6 fijn-afstemmen op een medische Q&A-dataset

Leer hoe je Qwen3.6 fijnstemt op een H100 NVL-GPU met SFT, van datasetvoorbereiding en 4-bit laden tot training en evaluatie.
Bijgewerkt 21 apr 2026  · 12 min lezen

Qwen3.6-35B-A3B is een lichtgewicht mixture-of-experts-model dat is gebouwd voor sterke codeer-, redeneer- en lang-contexttaken. Het gebruikt in totaal 35B parameters met 3B actieve parameters per token, waardoor het efficiënter draait en toch sterke prestaties levert. Het ondersteunt ook een contextvenster van 262K tokens, wat het handig maakt voor grotere codebases, lange documenten en meerstapsworkflows.

Het model presteert vooral goed op benchmarks voor coderen en agent-achtige taken, met sterke resultaten op SWE-bench, Terminal-Bench en Claw-Eval. Het blijft ook competitief op bredere redeneer- en kennisbenchmarks zoals GPQA, AIME 2026, MMLU-Pro en C-Eval, wat het een sterk basismodel maakt voor fine-tuningexperimenten die zowel praktische taakprestaties als algemene redeneercapaciteiten nodig hebben.

In deze gids richten we een RunPod H100 NVL-omgeving in, bereiden we het project voor, laden en schonen we een medische Q&A-dataset op, draaien we Qwen3.6 in 4-bit-kwantisatie, formatteren we prompts, bouwen we evaluatiehelpers, testen we het basismodel vóór het fijn-afstemmen, trainen en bewaren we de adapter en vergelijken we het model daarna opnieuw na het fijn-afstemmen.

Disclaimer: Deze tutorial demonstreert een technisch fijn-afstemmingsproces uitsluitend voor educatieve doeleinden. Het resulterende model is niet bedoeld voor medisch of klinisch gebruik en mag niet worden gebruikt voor diagnose, besluitvorming of patiëntenzorg.

Stap 1: Richt de trainingsomgeving in

In het eerste deel starten we een RunPod H100 NVL-instantie, installeren we de vereiste libraries, verbinden we met Hugging Face en definiëren we de kernconfiguratie van het project.

Huur een H100 NVL GPU-machine

Ga naar RunPod en selecteer een H100 NVL GPU-machine. Deze GPU heeft ongeveer 94 GB VRAM en 94 GB systeem-RAM, wat genoeg is voor deze Qwen3.6-fijn-afstemmingsworkflow.

Kies de nieuwste PyTorch-template en bewerk vervolgens de podconfiguratie vóór deployment. Stel de containerschijfgrootte in op 100 GB en de volumeschijfgrootte op 200 GB. Dit geeft je genoeg ruimte om het model te downloaden, dependencies te cachen en de fijn-afgestemde adapter lokaal op te slaan.

Editing the Pytorch template in the Runpod.

Je moet in deze fase ook een HF_TOKEN-omgevingsvariabele toevoegen. Hiermee kun je later inloggen bij Hugging Face, verbeter je de toegang tot modeldownloads en wordt het makkelijker om het fijn-afgestemde model na de training naar de Hub te pushen.

Controleer vóór het starten van de instantie de podsamenvatting om de geschatte kosten te bekijken. Als alles klopt, kun je de pod deployen.

Pod summary of the H100 NVL machine.

Installeer de vereiste libraries

Zodra de pod klaar is, open je de Jupyter Notebook-link vanuit het RunPod-dashboard om JupyterLab te starten. Maak een nieuw Python-notebook en installeer vervolgens de libraries die nodig zijn voor gekwantiseerde fijn-afstemming, het laden van datasets en supervised fine-tuning met TRL. 

pip install -q -U transformers accelerate peft trl bitsandbytes datasets sentencepiece

Log in bij Hugging Face

Log daarna in bij Hugging Face met de HF_TOKEN-omgevingsvariabele die je toevoegde bij het aanmaken van de pod. Zo voorkom je dat je token hardcodeert in het notebook en blijft de workflow schoner. 

import os
from huggingface_hub import login

hf_token = os.getenv("HF_TOKEN")
login(token=hf_token)

Importeer libraries en definieer de configuratie

Importeer nu de vereiste libraries en definieer de configuratiewaarden die we in de hele tutorial gaan gebruiken. Dit omvat de model-ID, datasetnaam, steekproefgroottes, outputdirectory en random seed. 

import os
import random
import re

import torch
from datasets import load_dataset
from peft import LoraConfig, prepare_model_for_kbit_training
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    set_seed,
)
from trl import SFTConfig, SFTTrainer

MODEL_ID = "Qwen/Qwen3.6-35B-A3B"
DATASET_ID = "keivalya/MedQuad-MedicalQnADataset"
SAMPLE_SIZE = 240
EVAL_SIZE = 24
COMPARISON_SAMPLE_COUNT = 3
MAX_SEQ_LENGTH = 768
OUTPUT_DIR = "qwen36-medquad-quick"
SEED = 42

set_seed(SEED)
random.seed(SEED)


def supports_bf16() -> bool:
    return torch.cuda.is_available() and torch.cuda.get_device_capability(0)[0] >= 8


print(f"PyTorch: {torch.__version__}")
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
    print(f"GPU: {torch.cuda.get_device_name(0)}")
    print(
        f"GPU memory (GB): {torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f}"
    )

Als je deze cel uitvoert, zou je output moeten zien die bevestigt dat CUDA beschikbaar is en dat het notebook is verbonden met een NVIDIA H100 NVL-GPU. 

PyTorch: 2.8.0+cu128
CUDA available: True
GPU: NVIDIA H100 NVL
GPU memory (GB): 93.1

Stap 2: Laad en bereid de dataset voor

In dit deel laden we de MedQuad medische Q&A-dataset, schonen we de tekstvelden op, filteren we voorbeelden van lagere kwaliteit eruit en maken we kleine train- en evaluatiesplits voor snelle fijn-afstemming.

Laad de MedQuad-dataset

We beginnen met het laden van de trainingssplit van de MedQuad-dataset vanaf Hugging Face. Na het laden inspecteren we de datasetstructuur, kolomnamen en het eerste voorbeeld, zodat we begrijpen met wat voor soort medische vraag-antwoordparen we werken.

raw_dataset = load_dataset(DATASET_ID, split="train")
print(raw_dataset)
print("First example:", raw_dataset[0])

Hieruit blijkt dat de MedQuad-dataset 16.407 medische vraag-antwoordvoorbeelden bevat, verdeeld over drie velden: qtype, Question en Answer.

Dataset({
features: ['qtype', 'Question', 'Answer'],
num_rows: 16407
})
First example: {'qtype': 'susceptibility', 'Question': 'Who is at risk for Lymphocytic Choriomeningitis (LCM)? ?', 'Answer': 'LCMV infections can occur after exposure to fresh urine, droppings, saliva, or nesting materials from infected rodents. Transmission may also occur when these materials are directly introduced into broken skin, the nose, the eyes, or the mouth, or presumably, via the bite of an infected rodent. Person-to-person transmission has not been reported, with the exception of vertical transmission from infected mother to fetus, and rarely, through organ transplantation.'}

Schoon de tekstvelden op

Vervolgens definiëren we een kleine tekstopruimfunctie en een filterregel om samples van lage kwaliteit te verwijderen. De opschoningsstap normaliseert extra witruimte, terwijl de filterstap voorbeelden verwijdert met heel korte vragen, heel korte antwoorden, te lange antwoorden of generieke antwoordsjablonen die minder nuttig zijn voor training. 

def clean_text(text):
    text = re.sub(r"\s+", " ", str(text)).strip()
    return text


BAD_ANSWER_PREFIXES = (
    "These resources address",
    "These resources from MedlinePlus",
    "For more information, go to",
    "For more information go to",
)


def keep_example(example):
    question = clean_text(example["Question"])
    answer = clean_text(example["Answer"])

    if len(question) < 12 or len(answer) < 40:
        return False
    if len(answer) > 900:
        return False
    if any(answer.startswith(prefix) for prefix in BAD_ANSWER_PREFIXES):
        return False
    return True


filtered_dataset = raw_dataset.filter(keep_example)

Maak de train- en evaluatiesplit

Na het filteren schudden we de dataset, selecteren we een kleinere subset voor deze snelle tutorial en splitsen we die in trainings- en evaluatiesets. Zo blijft de run lichtgewicht, terwijl we het model toch vóór en na het fijn-afstemmen kunnen vergelijken. 

subset = filtered_dataset.shuffle(seed=SEED).select(range(SAMPLE_SIZE))
split_dataset = subset.train_test_split(test_size=EVAL_SIZE, seed=SEED)

train_dataset = split_dataset["train"]
eval_dataset = split_dataset["test"]

print(f"Filtered dataset size: {len(filtered_dataset)}")
print(f"Train size: {len(train_dataset)}")
print(f"Eval size: {len(eval_dataset)}")

Na het filteren blijven 7.355 voorbeelden van hogere kwaliteit over, waarvan we 216 trainingsvoorbeelden en 24 evaluatievoorbeelden samplen voor deze snelle fijn-afstemmingsrun.

Filtered dataset size: 7355
Train size: 216
Eval size: 24

Stap 3: Laad Qwen3.6 met 4-bit-kwantisatie

In deze stap configureren we 4-bit-kwantisatie met BitsAndBytes, laden we de tokenizer van Qwen3.6 en laden we vervolgens het basismodel zodat het efficiënter kan draaien op de H100 NVL-GPU.

Configureer BitsAndBytes voor 4-bit laden

Qwen3.6 is nog steeds een groot model, dus het volledig in hoge precisie laden zou veel meer geheugen gebruiken. Om fijn-afstemming efficiënter te maken, laden we het model in 4-bit-precisie met BitsAndBytes. Dit verlaagt het geheugengebruik terwijl het model toch praktisch blijft voor QLoRA-gebaseerde fijn-afstemming.

Hier gebruiken we nf4 kwantisatie, schakelen we dubbele kwantisatie in en stellen we de compute-dtype in op bfloat16 wanneer de GPU dit ondersteunt.

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_use_double_quant=True,
    bnb_4bit_compute_dtype=torch.bfloat16 if supports_bf16() else torch.float16,
)

Laad de tokenizer

Vervolgens laden we de tokenizer voor Qwen3.6. We controleren ook of er al een paddingtoken is gedefinieerd. Als dat ontbreekt, hergebruiken we het end-of-sequence-token als paddingtoken, zodat batching later in training en generatie correct werkt. 

tokenizer = AutoTokenizer.from_pretrained(
    MODEL_ID,
    trust_remote_code=True,
    use_fast=False,
)

if tokenizer.pad_token is None:
    tokenizer.pad_token = tokenizer.eos_token

Laad het basismodel

Nu laden we het Qwen3.6-basismodel met de 4-bit-kwantisatie-instellingen die we eerder definieerden. We gebruiken device_map="auto" zodat het model automatisch op de beschikbare GPU wordt geplaatst, en we gebruiken opnieuw bfloat16 wanneer ondersteund voor efficiëntere berekening.

Na het laden van het model stellen we de padding token ID in en schakelen we het gebruik van cache uit. Dit is belangrijk omdat caching nuttig is voor inferentie, maar tijdens fijn-afstemming kan het het trainen verstoren en het geheugengebruik verhogen.

model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    quantization_config=bnb_config,
    device_map="auto",
    dtype=torch.bfloat16 if supports_bf16() else torch.float16,
    trust_remote_code=True,
)

model.config.pad_token_id = tokenizer.pad_token_id
model.config.use_cache = False

Stap 4: Format prompts en bouw een evaluatiehelper

In deze stap definiëren we de promptstructuur voor Qwen3.6, schonen we de modeloutputs op, maken we helperfuncties voor training en inferentie en bereiden we een paar voorbeeldsamples voor om het model vóór en na fijn-afstemming te vergelijken. 

Definieer de systeemprompt

We beginnen met het definiëren van een korte systeemprompt die het model vertelt hoe te antwoorden. Omdat dit een medische Q&A-taak is, willen we dat de antwoorden direct, feitelijk en beknopt zijn.

SYSTEM_PROMPT = "Answer the medical question directly in 2-4 factual sentences."

Pas de chattemplate toe

Qwen3.6 gebruikt een chat-stijl inputformaat, dus we hebben een helperfunctie nodig die onze berichten omzet naar de juiste promptstructuur. Deze functie past de chattemplate van de tokenizer toe en schakelt reasoning-output uit, zodat het model gefocust blijft op het eindantwoord. 

def apply_chat_template(messages, add_generation_prompt=False):
    kwargs = {
        "tokenize": False,
        "add_generation_prompt": add_generation_prompt,
    }
    try:
        return tokenizer.apply_chat_template(
            messages,
            enable_thinking=False,
            **kwargs,
        )
    except TypeError:
        return tokenizer.apply_chat_template(
            messages,
            chat_template_kwargs={"enable_thinking": False},
            **kwargs,
        )

Schoon de modeloutput op

Vervolgens definiëren we een kleine opschoningsfunctie om de gegenereerde tekst te normaliseren. Dit verwijdert extra witruimte en stript een eventueel leidend answer:-voorvoegsel als dat in de output verschijnt. 

def clean_answer_text(answer):
    answer = clean_text(answer)
    answer = re.sub(r"^answer\s*:\s*", "", answer, flags=re.IGNORECASE)
    return answer

Bouw de userprompt

We maken ook een helperfunctie voor de userprompt. Dit houdt het vraagformaat consistent tijdens training en evaluatie en vertelt het model om alleen het antwoord te geven zonder de vraag te herhalen. 

def make_user_prompt(question):
    return (
        f"Question: {clean_text(question)}\n\n"
        "Respond with only the answer. Do not restate the question."
    )

Formatteer voorbeelden voor supervised fine-tuning

Nu kunnen we elk datasetvoorbeeld formatteren tot een volledige chatconversatie met een systeembericht, een gebruikersvraag en het verwachte assistent-antwoord. Dit is de structuur die we gebruiken voor supervised fine-tuning. 

def format_training_example(example):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {
            "role": "user",
            "content": make_user_prompt(example["Question"]),
        },
        {
            "role": "assistant",
            "content": clean_answer_text(example["Answer"]),
        },
    ]
    return {"text": apply_chat_template(messages, add_generation_prompt=False)}

Genereer baseline-antwoorden

Voordat we starten met trainen, hebben we een helperfunctie nodig die antwoorden kan genereren met het basismodel. We gebruiken dit om de antwoorden van het model vóór en na het fijn-afstemmen te vergelijken.

De onderstaande functie bouwt de prompt, tokenizeert die, voert generatie uit en verwijdert vervolgens alle <think>-sporen en extra formattering uit de output.

@torch.inference_mode()
def generate_answer(question, max_new_tokens=160):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {
            "role": "user",
            "content": make_user_prompt(question),
        },
    ]
    prompt = apply_chat_template(messages, add_generation_prompt=True)
    model_inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

    generated = model.generate(
        **model_inputs,
        max_new_tokens=max_new_tokens,
        do_sample=False,
        pad_token_id=tokenizer.pad_token_id,
        eos_token_id=tokenizer.eos_token_id,
    )

    new_tokens = generated[0][model_inputs["input_ids"].shape[1] :]
    text = tokenizer.decode(new_tokens, skip_special_tokens=True).strip()
    return clean_answer_text(re.sub(r"<think>.*?</think>", " ", text, flags=re.DOTALL))

We voegen ook een kleine helperfunctie toe om lange outputs in te korten bij het printen van voorbeeldresultaten. 

def clip_text(text, max_chars=600):
    text = re.sub(r"<think>.*?</think>", " ", text, flags=re.DOTALL)
    text = re.sub(r"\s+", " ", text).strip()
    return text if len(text) <= max_chars else text[:max_chars] + "..."

Maak voorbeeldsamples voor vergelijking

Tot slot bouwen we een helperfunctie die een paar voorbeeldrijen naast elkaar maakt. Elke rij bevat de vraag, de voorspelling van het model en het referentieantwoord. Dit geeft ons een eenvoudige manier om de prestaties vóór en na het fijn-afstemmen te bekijken. 

def generate_preview_rows(dataset, sample_count=COMPARISON_SAMPLE_COUNT):
    sample_count = min(sample_count, len(dataset))
    subset = dataset.select(range(sample_count))
    previews = []

    for row in subset:
        previews.append(
            {
                "question": row["Question"],
                "prediction": generate_answer(row["Question"]),
                "reference": clean_answer_text(row["Answer"]),
            }
        )

    return previews


formatted_train_dataset = train_dataset.map(
    format_training_example,
    remove_columns=train_dataset.column_names,
)

comparison_examples = eval_dataset.shuffle(seed=SEED).select(
    range(COMPARISON_SAMPLE_COUNT)
)

print(formatted_train_dataset[0])

Wanneer je het eerste geformatteerde trainingsvoorbeeld print, zou je een chat-achtige sample moeten zien met de systeemprompt, de gebruikersvraag en het assistent-antwoord, al verpakt in Qwens templateformaat. 

{'text': '<|im_start|>system\nAnswer the medical question directly in 2-4 factual sentences.<|im_end|>\n<|im_start|>user\nQuestion: What is (are) COPD ?\n\nRespond with only the answer. Do not restate the question.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\nMore information on COPD is available at: What is COPD? and at the Learn More, Breathe Better Campaign For information on quitting smoking, visit http://www.surgeongeneral.gov/tobacco/ or Smokefree.gov. For information on the H1N1 flu and COPD, go to The Centers for Disease Control and Prevention.<|im_end|>\n'}

Stap 5: Evalueer het basismodel vóór fijn-afstemming

We testen het Qwen3.6-basismodel op een paar evaluatievoorbeelden vóór de training. Dit geeft ons een nulmeting zodat we later kunnen vergelijken hoe het model verandert na het fijn-afstemmen.

We beginnen met het genereren van voorbeeldantwoorden voor een paar gesamplede voorbeelden uit de evaluatieset. 

Elke preview bevat de vraag, het antwoord van het model vóór fijn-afstemming en het referentieantwoord uit de dataset. 

baseline_previews = generate_preview_rows(comparison_examples)

for idx, sample in enumerate(baseline_previews, start=1):
    print(f"\n--- Preview {idx} ---")
    print("Question:", sample["question"])
    print("Model answer (before fine-tuning):", clip_text(sample["prediction"]))
    print("Reference answer:", clip_text(sample["reference"]))

Hier zijn de vergelijkingen naast elkaar tussen de huidige respons van het model en het referentieantwoord. Deze voorbeelden helpen ons het startpunt van het model te begrijpen vóór taak-specifieke training.

Evaluate the Base Model Before Fine-Tuning

Uit deze voorbeelden zien we al dat het basismodel in staat is medische, relevante en over het algemeen correcte antwoorden te geven. 

Toch komen de reacties niet altijd overeen met de stijl, het detailniveau of de bewoording van de referentieantwoorden in de dataset. In sommige gevallen geeft het model een bredere of algemenere uitleg, terwijl het referentieantwoord specifieker is en beter aansluit op het doelformaat.

Stap 6: Configureer QLoRA voor fijn-afstemming

In dit deel definiëren we de instellingen voor de LoRA-adapter, configureren we de supervised fine-tuning-argumenten en initialiseren we de trainer die het QLoRA-trainingsproces afhandelt.

Definieer de LoRA-configuratie

In plaats van alle modelparameters te updaten, stemt QLoRA een veel kleinere set trainbare adaptergewichten fijn af. Dit maakt trainen veel geheugenefficiënter, terwijl het model zich nog steeds kan aanpassen aan de nieuwe taak.

Hier definiëren we een LoRA-configuratie met een rank van 8, een schaalfactor van 16 en een kleine dropoutwaarde. We targeten ook alle lineaire lagen zodat de adapters breed over het model worden toegepast.

lora_config = LoraConfig(
    r=8,
    lora_alpha=16,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules="all-linear",
)

Stel de trainer-argumenten in 

Vervolgens definiëren we de trainingsconfiguratie met SFTConfig. Deze instellingen bepalen hoe de supervised fine-tuning-run zich gedraagt, inclusief batchgrootte, gradientaccumulatie, learning rate, logfrequentie en precisie.

Deze tutorial gebruikt een lichtgewicht setup met één epoch en een kleine batchgrootte, zodat de run praktisch blijft voor demonstratiedoeleinden. We schakelen ook gradient checkpointing en paged 8-bit AdamW in om het geheugengebruik tijdens training te verminderen.

trainer_config = SFTConfig(
    output_dir=OUTPUT_DIR,
    dataset_text_field="text",
    max_length=MAX_SEQ_LENGTH,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=2,
    num_train_epochs=1,
    learning_rate=1e-4,
    warmup_steps=5,
    logging_steps=5,
    save_strategy="epoch",
    report_to="none",
    packing=False,
    gradient_checkpointing=True,
    optim="paged_adamw_8bit",
    bf16=supports_bf16(),
    fp16=not supports_bf16(),
)

Initialiseer de SFTTrainer

Met de adapterinstellingen en trainingsargumenten op hun plaats kunnen we nu de SFTTrainer initialiseren. Deze trainer brengt het basismodel, de geformatteerde trainingsdataset, de LoRA-configuratie en de tokenizer samen zodat het model klaar is voor supervised fine-tuning.  

trainer = SFTTrainer(
    model=model,
    train_dataset=formatted_train_dataset,
    peft_config=lora_config,
    args=trainer_config,
    processing_class=tokenizer,
)

Voor je met trainen begint, is het handig om te bevestigen hoeveel parameters er daadwerkelijk worden geüpdatet. Een van de belangrijkste voordelen van QLoRA is dat slechts een heel klein deel van het model trainbaar wordt.

trainer.model.print_trainable_parameters()

Dit laat zien dat slechts ongeveer 0,03% van alle modelparameters wordt getraind. Dat maakt QLoRA zo'n praktische aanpak voor het fijn-afstemmen van grote taalmodellen op beperkte hardware.

trainable params: 11,238,720 || all params: 34,671,849,408 || trainable%: 0.0324

Stap 7: Train het model en sla de adapter op

In deze sectie starten we de supervised fine-tuning-run, slaan we de getrainde adapter lokaal op en pushen we die vervolgens naar Hugging Face, zodat hij later opnieuw gebruikt kan worden.

Start het fijn-afstemmen

Nu het model, de dataset en de trainer klaar zijn, kunnen we het fijn-afstemmingsproces starten. De trainer doorloopt de geformatteerde trainingsset en werkt alleen de LoRA-adaptergewichten bij in plaats van het volledige model. 

train_result = trainer.train()
train_result

Tijdens het trainen zou je de loss geleidelijk moeten zien dalen. Dit is een goed teken dat het model het taakformaat leert en zich aanpast aan de medische vraag-antwoorddata. 

Training loss reducing with each step.

Sla de fijn-afgestemde gewichten op

Als de training klaar is, sla je de fijn-afgestemde adapter en tokenizer lokaal op. Dit levert een herbruikbaar checkpoint op dat je later opnieuw kunt laden zonder de volledige training te herhalen.

trainer.model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)

Push het model naar Hugging Face

Zodra de adapter lokaal is opgeslagen, kun je hem uploaden naar Hugging Face. Dit maakt delen, versies beheren en het fijn-afgestemde model opnieuw laden in toekomstige notebooks of deploymentworkflows makkelijker. 

HF_REPO_ID = "kingabzpro/qwen36-medquad-quick"

trainer.model.push_to_hub(HF_REPO_ID)
tokenizer.push_to_hub(HF_REPO_ID)

Fine-tuned model adopter pushed Hugging face repository.Na het uploaden is de fijn-afgestemde adapter beschikbaar in de repository.

Fine-tuned model repository on Hugging face.

Bron: kingabzpro/qwen36-medquad-quick

Stap 8: Evalueer het model na fijn-afstemming

Nu de training voltooid is, kunnen we het fijn-afgestemde model evalueren op dezelfde vergelijkingsexamples die we eerder gebruikten. Dit helpt ons een directe voor-en-na-vergelijking te maken op exact dezelfde vragen. 

model.eval()

fine_tuned_previews = generate_preview_rows(comparison_examples)

for idx, (before, after) in enumerate(
    zip(baseline_previews, fine_tuned_previews), start=1
):
    print(f"\n{'=' * 80}")
    print(f"Sample {idx}")
    print(f"{'=' * 80}")
    print("Question:", before["question"])
    print("\nBefore fine-tuning:")
    print(clip_text(before["prediction"]))
    print("\nAfter fine-tuning:")
    print(clip_text(after["prediction"]))
    print("\nReference answer:")
    print(clip_text(before["reference"]))

Je zou voor elk sample het baseline-antwoord, het fijn-afgestemde antwoord en het referentieantwoord moeten zien. Zo wordt het makkelijker om te bekijken hoe de adapter de stijl en inhoud van de respons van het model heeft veranderd.

Comparing the baseline answer, the fine-tuned answer, and the reference answer for each sample.

Deze voorbeelden laten zien dat fijn-afstemming het model heeft geholpen zich beter aan te passen aan de stijl van de doeldataset, vooral bij kortere feitelijke medische vragen. 

Tegelijkertijd laten de resultaten ook zien dat dit slechts een kleine fijn-afstemmingsrun was op een beperkte subset van de dataset, dus verbetering bij elk sample is niet gegarandeerd. Sommige antwoorden sluiten beter aan bij de trainingsdistributie, terwijl andere nuttige details kunnen verliezen of te smal worden.

Om betere resultaten te krijgen, zou de volgende stap zijn om op de volledige dataset te trainen in plaats van op een kleine subset en het fijn-afstemmingsproces ten minste 3 epochs te laten draaien. Je kunt ook de LoRA-rang verhogen om de adapter meer leercapaciteit te geven en de systeemprompt verder verfijnen zodat het model consequenter de gewenste medische antwoordstijl volgt. 

Deze veranderingen zullen waarschijnlijk stabielere verbeteringen opleveren over een breder scala aan vragen.

Tot slot 

Een modern taalmodel fijn-afstemmen is niet zo simpel als het misschien lijkt. 

Hoewel Qwen3.6-35B-A3B een efficiënt mixture-of-experts-model is met in totaal 35 miljard parameters, vereist het nog steeds serieuze rekenkracht om het zelfs maar in 4-bit-modus te laden en klaar te maken voor training. De training zelf blijft ook kostbaar, vooral zodra je verder gaat dan een kleine experimentele subset en werkt met een grotere dataset of meerdere epochs.

Hardware is ook erg bepalend. In deze tutorial gebruikten we een H100 NVL om de workflow praktisch te houden. Op een oudere GPU kan de trainingstijd sterk toenemen. Zelfs op een A100 kan een soortgelijke setup gemakkelijk ongeveer een uur duren, en zwakkere GPU's verlengen dat nog verder. 

Dus hoewel QLoRA en 4-bit laden fijn-afstemming toegankelijker maken, maken ze het niet lichtgewicht in de alledaagse betekenis. Het is ook de moeite waard om je af te vragen of fijn-afstemming überhaupt nodig is. Voor veel basistaken zou ik niet aanraden om direct met fijn-afstemming te beginnen. 

Als je tijd steekt in het verbeteren van de prompt, het beter structureren van de workflow en het gebruiken van tools, MCP's of retrieval-gebaseerde methodes, kun je vaak dicht bij het gewenste gedrag komen zonder te trainen. In veel gevallen brengt dat je tot circa 80% van het gewenste resultaat, terwijl het veel sneller, goedkoper en eenvoudiger is om op itereren.

Fijn-afstemming wordt veel waardevoller wanneer je het model consequent een zeer specifieke responsstijl, domeingedrag of institutioneel format wilt laten leren. Dán begint het zin te hebben. Als je wilt dat het model op exact dezelfde manier antwoordt als een bepaald ziekenhuis, medisch systeem of specialistische workflow op vragen reageert, dan is fijn-afstemming de betere weg. 

Met andere woorden: gebruik eerst prompting en workflowdesign, en grijp naar fijn-afstemming wanneer je echt wilt dat het model een gespecialiseerde manier van antwoorden internaliseert.


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Als gecertificeerd data scientist haal ik met passie het maximale uit de nieuwste technologie om innovatieve machinelearning-toepassingen te bouwen. Met een sterke achtergrond in spraakherkenning, data-analyse en -rapportage, MLOps, conversationele AI en NLP heb ik mijn vaardigheden aangescherpt in het ontwikkelen van intelligente systemen die echt impact maken. Naast mijn technische expertise ben ik ook een sterke communicator met een talent om complexe concepten terug te brengen tot heldere, beknopte taal. Daardoor ben ik uitgegroeid tot een veelgelezen blogger over data science, waar ik mijn inzichten en ervaringen deel met een groeiende community van data-professionals. Op dit moment richt ik me op contentcreatie en redactie, waarbij ik met large language models werk aan krachtige en aansprekende content die zowel bedrijven als individuen helpt het beste uit hun data te halen.

Onderwerpen

Top AI-cursussen

Leerpad

Associate AI Engineer voor ontwikkelaars

26 Hr
Leer hoe je AI in softwareapplicaties kunt integreren met behulp van API's en open-sourcebibliotheken. Begin vandaag nog aan je reis om AI-ingenieur te worden!
Bekijk detailsRight Arrow
Begin met de cursus
Meer zienRight Arrow
Gerelateerd

blog

AI vanaf nul leren in 2026: een complete gids van de experts

Ontdek alles wat je moet weten om in 2026 AI te leren, van tips om te beginnen tot handige resources en inzichten van industrie-experts.
Adel Nehme's photo

Adel Nehme

15 min

Meer zienMeer zien