Ga naar hoofdinhoud

Fijn-afstemmen van GPT-OSS

Een stapsgewijze gids voor het fijn-afstemmen van het OpenAI GPT-OSS 20B-model op de medische vraag-en-antwoorddataset, voor zowel nauwkeurigheid als stijl-overname.
· 6 min lezen

Verken met AI

De GPT-OSS 20B is een open-weight taalmodel uit OpenAI’s gpt-oss-serie, ontworpen voor lage latentie en gespecialiseerde use-cases. Het heeft 21 miljard parameters (3,6B actief) en is geoptimaliseerd om efficiënt te draaien op consumentenhardware, met slechts ongeveer 16GB geheugen dankzij MXFP4-kwantisatie.

Ondanks dat het kleiner is dan de GPT-OSS 120B, ondersteunt het nog steeds geavanceerde functies zoals configureerbare redeneerniveaus (laag, midden, hoog), volledige chain-of-thought-toegang voor debuggen en transparantie, en agent-capaciteiten zoals function calling, web browsing en code-executie.

In deze gids neem ik je mee door het volledige proces van het opzetten en fijn-afstemmen van GPT-OSS 20B. Dit omvat:

  1. Dependencies installeren en de omgeving voorbereiden
  2. Het model en de tokenizer laden
  3. De promptstijl creëren met het nieuwe OpenAI Harmony Python-pakket
  4. De dataset laden en de Harmony-opmaak toepassen
  5. Een eenvoudige modelinference draaien vóór het fijn-afstemmen
  6. Het model configureren voor training
  7. Het model fijn-afstemmen op een medische Q&A-dataset
  8. Inference uitvoeren na het fijn-afstemmen om verbeteringen te evalueren

Onthoud dat deze tutorial is bedoeld voor onderzoek en onderwijs en niet voor patiëntdiagnose of -behandeling.

Afbeelding door de auteur

1. Je omgeving instellen

Hoewel het GPT-OSS-model 21 miljard parameters heeft, vereist het nog steeds aanzienlijke GPU-geheugen, minstens 80GB VRAM om het model comfortabel te verwerken. Een snelle manier om te beginnen is door een RunPod H100-instance of een vergelijkbare GPU-omgeving met veel geheugen te starten.

RunPod H100SXM machine

Zodra de omgeving klaar is, installeer je alle vereiste Python-pakketten voor datapreprocessing, model-fijn-afstemming en experimenttracking:

%%capture
%pip install -U accelerate 
%pip install -U peft 
%pip install -U trl 
%pip install -U bitsandbytes
%pip install -U transformers
%pip install -U trackio
%pip install -U openai-harmony
%pip install -U tiktoken
%pip install -U pyctcdecode

Log ten slotte in bij de Hugging Face Hub met je API-sleutel. Hiermee kun je het fijn-afgestemde model en de tokenizer rechtstreeks opslaan en delen met je Hugging Face-account:

from huggingface_hub import notebook_login

notebook_login()

2. GPT-OSS-model en tokenizer laden

Vervolgens laden we de GPT-OSS 20B met MXFP4-kwantisatie maar dekwantiseren we de gewichten direct bij het laden naar bfloat16, met eager attention, cache uitgeschakeld en automatische apparaattoewijzing.

import torch
from transformers import AutoModelForCausalLM, Mxfp4Config

quantization_config = Mxfp4Config(dequantize=True)
model_kwargs = dict(
    attn_implementation="eager",
    torch_dtype=torch.bfloat16,
    quantization_config=quantization_config,
    use_cache=False,
    device_map="auto",
)

model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs)

Naast het model hebben we ook de tokenizer uit dezelfde repository nodig. De tokenizer zet tekst om in tokens (ID’s) die het model kan verwerken en decodeert vervolgens de modeluitvoer terug naar leesbare tekst.

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

3. Promptstijl creëren met OpenAI Harmony

OpenAI Harmony is een gespecialiseerd responsformaat dat door de GPT-OSS-modellen wordt gebruikt om gesprekken, redeneeruitvoer en functieaanroepen op een consistente manier te structureren. 

Het definieert hoe berichten zijn georganiseerd tussen rollen (system, developer, user, assistant) en stelt het model in staat om over meerdere kanalen uit te voeren—bijvoorbeeld het scheiden van chain-of-thought-redenering, tool-calling-preambles en definitieve, op de gebruiker gerichte antwoorden.

In deze sectie maken we een functie die een vraag en antwoord neemt en ze formatteert tot een Harmony-stijl gespreksprompt. Het gebruikt de openai_harmony-bibliotheek om het gesprek op een gestructureerde manier te encoderen.

from openai_harmony import (
    load_harmony_encoding, HarmonyEncodingName,
    Conversation, Message, Role,
    SystemContent, DeveloperContent, ReasoningEffort
)

enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)

def render_pair_harmony(question, answer):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
        Message.from_role_and_content(Role.ASSISTANT, answer.strip()),
    ])
    tokens = enc.render_conversation(convo)
    text = enc.decode(tokens)
    return text

4. De dataset laden en opmaken

We laden nu de medische Q&A-dataset (FreedomIntelligence/medical-o1-verifiable-problem) en passen de functie prompt_style_harmony toe op elk voorbeeld. De getransformeerde tekst wordt opgeslagen in een nieuwe kolom met de naam "text", wat een duidelijke, instructiegedreven opmaak geeft die aansluit bij de promptstijl van de developer.

from datasets import load_dataset

def prompt_style_harmony(examples):
    qs = examples["Open-ended Verifiable Question"]
    ans = examples["Ground-True Answer"]
    outputs = {"text": []}
    for q, a in zip(qs, ans):
        rendered = render_pair_harmony(q, a)
        outputs["text"].append(rendered)
    return outputs

dataset = load_dataset(
    "FreedomIntelligence/medical-o1-verifiable-problem",
    split="train[0:1000]"
)
dataset = dataset.map(prompt_style_harmony, batched=True)
print(dataset.column_names)

print(dataset[0]["text"])

De dataset bevat nu een nieuwe kolom "text" met het gesprek in Harmony-opmaak. Dit omvat:

  • Developer-instructies (bijv. beknopt antwoorden met een diagnose)
  • User-bericht (de medische vraag)
  • Assistant-bericht (het ground-truth-antwoord)
['Open-ended Verifiable Question', 'Ground-True Answer', 'text']
<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|message|>Gastric ulcer<|end|>

5. Modelinference vóór het fijn-afstemmen

Voordat we het model trainen, is het belangrijk om de baseline-prestaties te evalueren voor vergelijking na het fijn-afstemmen. Hiervoor definiëren we een hulpfunctie render_inference_harmony die een Harmony-stijl prompt voorbereidt met de strikte instructies van de developer en de vraag van de user, maar de reactie van de assistant leeg laat zodat het model die kan aanvullen.

def render_inference_harmony(question):
    convo = Conversation.from_messages([
        Message.from_role_and_content(
            Role.DEVELOPER,
            DeveloperContent.new().with_instructions(
                "You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. "
                "Respond with ONLY the final diagnosis/cause in ≤5 words."
            )
        ),
        Message.from_role_and_content(Role.USER, question.strip()),
    ])
    tokens = enc.render_conversation_for_completion(convo, Role.ASSISTANT)
    text = enc.decode(tokens)
    return text

Vervolgens nemen we het eerste sample uit de dataset, formatteren dit met deze functie, tokenizen het en voeren het door het model om een antwoord te genereren:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

In deze test is de output van het model merkbaar langer en beschrijvender dan gevraagd, ook al vroegen de developer-instructies expliciet om een beknopte diagnose (≤5 woorden). 

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|>We have an old woman with NSAID use causing ulcer bleed. The question: "An 88

In plaats van simpelweg te antwoorden met “Gastric ulcer” (het ground-truth-label), gaf het model een breedsprakige uitleg over NSAID-gebruik en een ulcus-bloeding.

dataset[0]["Ground-True Answer"]
'Gastric ulcer'

6. Het model voorbereiden voor training

Om het model efficiënt fijn te stemmen, gebruiken we LoRA (Low-Rank Adaptation), waarmee we slechts een kleine subset van parameters trainen terwijl het basismodel bevroren blijft. Dit vermindert het geheugengebruik en de trainingskosten drastisch.

We beginnen met het definiëren van een LoraConfig die de rang, schaalfactor en de specifieke doel-lagen specificeert waar LoRA-adapters worden ingevoegd.

from peft import LoraConfig, get_peft_model

peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules="all-linear",
    target_parameters=[
        "7.mlp.experts.gate_up_proj",
        "7.mlp.experts.down_proj",
        "15.mlp.experts.gate_up_proj",
        "15.mlp.experts.down_proj",
        "23.mlp.experts.gate_up_proj",
        "23.mlp.experts.down_proj",
    ],
)
peft_model = get_peft_model(model, peft_config)
peft_model.print_trainable_parameters()

Er zijn slechts ~15M parameters (≈0,07% van het volledige model) trainbaar, en de adaptergrootte is rond 16 MB, een enorme efficiëntiewinst vergeleken met volledige fine-tuning.

trainable params: 15,040,512 || all params: 20,929,797,696 || trainable%: 0.0719

Vervolgens configureren we de trainingssetup met SFTConfig uit de trl-bibliotheek. Dit omvat hyperparameters zoals learning rate, batchgrootte, gradient accumulation, warmup-verhouding en het type scheduler:

from trl import SFTConfig

training_args = SFTConfig(
    learning_rate=2e-4,
    gradient_checkpointing=True,
    num_train_epochs=1,
    logging_steps=10,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_length=2048,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine_with_min_lr",
    lr_scheduler_kwargs={"min_lr_rate": 0.1},
    output_dir="gpt-oss-20b-medical-qa",
    report_to="trackio",
    push_to_hub=True,
)

7. Het model trainen

Met de dataset voorbereid, de LoRA-adapter gekoppeld en de trainingsargumenten geconfigureerd, kunnen we nu het fijn-afstemmingsproces starten. We gebruiken de SFTTrainer uit de trl-bibliotheek, die supervised fine-tuning vereenvoudigt door het model, de tokenizer, de dataset en de trainingslus op één plek te beheren:

from trl import SFTTrainer

trainer = SFTTrainer(
    model=peft_model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()

Tijdens de training daalt de loss geleidelijk, wat aangeeft dat het model leert om zijn reacties af te stemmen op de Harmony-geformatteerde medische Q&A-dataset.

Model training loss

Zodra de training is voltooid, slaan we het fijn-afgestemde model lokaal op en pushen het vervolgens naar de Hugging Face Hub. Belangrijk: alleen de LoRA-adaptergewichten (niet het volledige 20B-model) worden geüpload, waardoor de repository lichtgewicht en eenvoudig te delen is:

trainer.save_model(training_args.output_dir)
trainer.push_to_hub(dataset_name="kingabzpro/gpt-oss-20b-medical-qa")

Dit maakt een nieuwe modelrepository aan op Hugging Face onder: kingabzpro/gpt-oss-20b-medical-qa.

Bron: kingabzpro/gpt-oss-20b-medical-qa

8. Modelinference na het fijn-afstemmen

Na het fijn-afstemmen kunnen we het model testen om te verifiëren of het nu de instructies van de developer nauwkeuriger volgt. Hiervoor laden we eerst het basis GPT-OSS 20B-model en koppelen we vervolgens de fijn-afgestemde LoRA-adapters.

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

# Load the original model first
model_kwargs = dict(attn_implementation="eager", torch_dtype="auto", use_cache=True, device_map="auto")
base_model = AutoModelForCausalLM.from_pretrained("openai/gpt-oss-20b", **model_kwargs).cuda()

# Merge fine-tuned weights with the base model
peft_model_id = "gpt-oss-20b-medical-qa"
model = PeftModel.from_pretrained(base_model, peft_model_id)
model = model.merge_and_unload()

Vervolgens voeren we inference uit op een sample uit de dataset. De vraag wordt in Harmony-stijl opgemaakt, getokenized, door het model gehaald en terug naar tekst gedecodeerd:

question = dataset[0]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Het fijn-afgestemde model produceert nu een kort en accuraat antwoord. Dit ligt veel dichter bij het ground-truth-label (“Gastric ulcer”) en volgt de ≤5-woordenbeperking.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>An 88-year-old woman with osteoarthritis is experiencing mild epigastric discomfort and has vomited material resembling coffee grounds multiple times. Considering her use of naproxen, what is the most likely cause of her gastrointestinal blood loss?<|end|><|start|>assistant<|return|><|message|>Stomach ulcer<|end|><|return|>

We kunnen ook een ander sample testen om consistentie te bevestigen:

question = dataset[10]["Open-ended Verifiable Question"]

text = render_inference_harmony(question)

inputs = tokenizer(
    [text + tokenizer.eos_token], return_tensors="pt"
).to("cuda")
outputs = model.generate(
    input_ids=inputs.input_ids,
    attention_mask=inputs.attention_mask,
    max_new_tokens=20,
    eos_token_id=tokenizer.eos_token_id,
    use_cache=True,
)
response = tokenizer.batch_decode(outputs)
print(response[0])

Perfect. Het fijn-afgestemde model genereert bondige, nauwkeurige en instructievolgende reacties.

<|start|>developer<|message|># Instructions

You are a medical expert with advanced knowledge in clinical reasoning and diagnostics. Respond with ONLY the final diagnosis/cause in ≤5 words.<|end|><|start|>user<|message|>What substance is primarily filtered in the renal tubules with minimal secretion or re-absorption?<|end|><|start|>assistant<|return|><|message|>creatinine<|end|><|return|>

Conclusie

Het zijn spannende tijden voor de open-source AI-community. Ook al staan de nieuwe GPT-OSS-modellen misschien nog niet bovenaan de benchmarks, ze tonen sterke potentie, vooral binnen hun respectievelijke parameterklassen, waar kleinere modellen nog steeds indrukwekkende resultaten kunnen leveren.

In deze tutorial hebben we het GPT-OSS 20B-model fijn-afgestemd voor medische vraagbeantwoording met LoRA-adapters en de Harmony-promptstijl. De resultaten zijn bemoedigend: het fijn-afgestemde model produceert bondige, nauwkeurige antwoorden die aansluiten bij klinische redeneertaken. Met meer trainingsepochs, grotere datasets en verfijnde systeemprompts kan de performance nog verder verbeteren.

Als je op zoek bent naar een hands-on cursus om vertrouwd te raken met fijn-afstemmen, bekijk dan zeker Fine-Tuning With Llama 3.

Abid Ali Awan

Auteur

Abid Ali Awan

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

Gerelateerd