from openai_harmony import(
load_harmony_encoding, HarmonyEncodingName,
Conversation, Message, Role,
SystemContent, DeveloperContent, ReasoningEffort
)
enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)defrender_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技术支持
from datasets import load_dataset
defprompt_style_harmony(examples):
qs = examples["Open-ended Verifiable Question"]
ans = examples["Ground-True Answer"]
outputs ={"text":[]}for q, a inzip(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"])技术支持
数据集现已包含新列 "text",其中保存了 Harmony 格式的对话,包含:
开发者指令(例如,要求简洁给出诊断)
用户消息(医疗问题)
助手消息(标准答案)
['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|>技术支持
defrender_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技术支持
<|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技术支持
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()技术支持
<|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|>技术支持
<|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|>技术支持
结语
对开源 AI 社区而言,这是令人振奋的时期。尽管新的 GPT-OSS 模型尚未在各项基准上名列前茅,但它们展现出强劲潜力,尤其在各自的参数规模段内,小模型同样能够带来亮眼表现。
来自 OpenAI gpt-oss 系列的 GPT-OSS 20B 是一款开放权重的语言模型,专为低延迟与专业化场景而设计。它拥有 210 亿参数(其中 36 亿为激活参数),并通过MXFP4 量化优化,可在消费级硬件上高效运行,仅需约 16GB 内存。
尽管小于 GPT-OSS 120B,它仍支持诸如可配置推理级别(低、中、高)、用于调试与透明性的完整思维链访问,以及代理能力,包括函数调用、网页浏览与代码执行等高级特性。
在本指南中,我将带您完成设置与微调 GPT-OSS 20B 的完整流程,涵盖:
请注意,本教程仅用于研究与教育,不用于患者诊断或治疗。
图片由作者提供
1. 环境搭建
虽然 GPT-OSS 模型拥有 210 亿参数,但仍需要相当可观的 GPU 显存,至少 80GB VRAM 才能从容运行。一个快速入门方式是启动 RunPod H100 实例或类似的高显存 GPU 环境。
环境就绪后,安装用于数据处理、模型微调与实验追踪的所有必要 Python 包:
最后,使用您的 API 密钥登录 Hugging Face Hub。这样您就可以将微调后的模型和分词器直接保存并共享到您的 Hugging Face 账户:
2. 加载 GPT-OSS 模型与分词器
接下来,我们以 MXFP4 量化加载 GPT-OSS 20B,但在加载时立即将权重量化还原为 bfloat16,同时使用 eager 注意力,实现禁用缓存与自动设备分配。
除模型外,我们还需要同一仓库的分词器。分词器负责将文本转换为模型可处理的 token(ID),并在推理后将输出解码回人类可读文本。
3. 使用 OpenAI Harmony 创建提示风格
OpenAI Harmony 是 GPT-OSS 模型采用的专用响应格式,用于以一致的方式组织对话、推理输出与函数调用。
它定义了不同角色(system、developer、user、assistant)之间消息的组织方式,并使模型能够跨多通道输出——例如,将思维链推理、工具调用前言与最终面向用户的回复分离。
在本节中,我们将创建一个函数,接收问答对并将其格式化为 Harmony 风格的对话提示。它使用
openai_harmony库以结构化方式对对话进行编码。4. 加载与格式化数据集
我们现在将加载医疗问答数据集(FreedomIntelligence/medical-o1-verifiable-problem),并对每个样本应用 prompt_style_harmony 函数。转换后的文本存放在名为 "text" 的新列中,形成与开发者提示风格一致的简洁、指令驱动格式。
数据集现已包含新列 "text",其中保存了 Harmony 格式的对话,包含:
5. 微调前的模型推理
在训练模型之前,评估其基线表现对于后续对比非常重要。为此,我们定义辅助函数
render_inference_harmony,构造包含开发者严格指令与用户问题的 Harmony 风格提示,但留空助手回复以供模型补全。随后,我们取数据集的首个样本,使用该函数进行格式化,分词后送入模型生成回复:
在这次测试中,即使开发者指令明确要求简洁给出诊断(≤5 个词),模型输出仍明显更长、更具描述性。
模型并未直接回答“Gastric ulcer”(标准标签),而是给出了关于 NSAID 使用与溃疡出血的冗长解释。
6. 训练前的模型设置
为高效微调模型,我们使用LoRA(低秩适配),仅训练一小部分参数并冻结基座模型,从而大幅降低显存占用与训练成本。
我们首先定义一个
LoraConfig,指定秩、缩放系数以及插入 LoRA 适配器的目标层。可训练参数仅约 1500 万(约占全模型的 0.07%),适配器大小约 16 MB,与全量微调相比效率提升巨大。
接着,我们使用
trl库的SFTConfig配置训练,包括学习率、批大小、梯度累积、预热比例与调度器类型等超参数:7. 训练模型
数据集已准备、LoRA 适配器挂载完毕、训练参数配置完成后,我们即可启动微调流程。使用
trl库的SFTTrainer,可在同一处处理模型、分词器、数据集与训练循环:训练过程中,loss 逐步下降,表明模型正在学习与 Harmony 格式医疗问答数据的对齐。
训练完成后,我们将微调模型保存至本地,并推送到Hugging Face Hub。需注意,上传的仅为 LoRA 适配器权重(非完整 20B 模型),使得仓库更轻量、便于共享:
这将在 Hugging Face 上创建一个新的模型仓库:kingabzpro/gpt-oss-20b-medical-qa。
来源:kingabzpro/gpt-oss-20b-medical-qa
8. 微调后的模型推理
微调完成后,我们可以测试模型是否更严格地遵循开发者指令。为此,先加载基座 GPT-OSS 20B 模型,然后挂载微调好的 LoRA 适配器。
接着,我们对数据集中的一个样本进行推理。将问题格式化为 Harmony 风格、分词、送入模型并解码为文本:
微调后的模型现在给出了简短且准确的回答,更接近标准标签(“Gastric ulcer”),并遵循了 ≤5 个词的约束。
我们也可以测试另一个样本以验证一致性:
很好。微调后的模型能够生成简洁、准确且遵循指令的回复。
结语
对开源 AI 社区而言,这是令人振奋的时期。尽管新的 GPT-OSS 模型尚未在各项基准上名列前茅,但它们展现出强劲潜力,尤其在各自的参数规模段内,小模型同样能够带来亮眼表现。
在本教程中,我们使用 LoRA 适配器与 Harmony 提示风格,对 GPT-OSS 20B 进行了医疗问答方向的微调。结果令人鼓舞:微调模型能够输出与临床推理任务一致的简洁、准确答案。通过增加训练轮次、扩充数据集与优化系统提示,性能还有进一步提升空间。
如果您想通过实战课程熟悉微调流程,不妨查看Fine-Tuning With Llama 3。