医疗大模型这两年从论文里的概念快速落到了工程现场,身边不少做医疗信息化、临床科研甚至医院信息科的同行都在问同一个问题:手里有脱敏病历、有指南文献、有科室积累的问答对,怎么把它们真正喂进一个开源基座里,做出一个能回答专科问题、能跟着最新指南走的模型?MedicalGPT 这套流程就是冲着这个需求来的——它把增量预训练、监督微调、奖励建模、强化学习对齐这几段串成了一条可复现的流水线,基座可以换,数据可以换,但骨架是稳定的。我前后用 Qwen-7B 做基座跑过两轮完整流程,中间踩的坑比想象中多,尤其是数据配比和显存这两块。下面这篇就把从零构建 MedicalGPT 的全流程拆开讲,包括每一步为什么这么做、参数怎么算、哪些地方最容易翻车,适合有一定 Python 和 PyTorch 基础、想自己动手做医疗领域微调的读者参考。
1. 整体方案设计与技术选型思路
1.1 为什么医疗领域不能直接拿通用大模型上
通用大模型在开放域问答上表现不错,但一进医疗场景就露怯。我拿同一个问题测过好几个通用模型,问“二甲双胍在 eGFR 低于 45 的患者中如何使用”,有的模型直接给出常规剂量,完全忽略了肾功能不全的禁忌调整。这不是模型笨,而是它的训练语料里医学科普和临床指南的密度太低,模型学到的是“大众认知里的医疗”,而不是“临床决策里的医疗”。
医疗文本有几个很鲜明的特点。第一是术语密度高且缩写多,像“NSCLC”“COPD”“eGFR”这类缩写在不同科室含义可能不同,通用分词器切出来的 token 往往把缩写切碎,语义就散了。第二是长尾分布严重,罕见病、特定药物相互作用、最新指南更新这些内容在通用语料里几乎见不到。第三是容错率极低,通用场景答错一句无伤大雅,医疗场景答错一句可能涉及用药安全。这三点决定了医疗大模型必须经过领域数据的继续训练,而不是靠 prompt 工程糊弄过去。
MedicalGPT 的价值就在于它把这条领域适配路径标准化了。它不是某个具体的模型,而是一套训练流程和代码框架,核心思路是:先用海量医疗文本做增量预训练,让基座“补医学课”;再用高质量的问答对做监督微调,让它“学会按医生的方式回答”;如果追求更高的一致性,还可以继续做奖励建模和强化学习对齐。这套流程和通用大模型的训练范式是一致的,只是数据换成了医疗领域。
1.2 基座选型:为什么我最终落在 Qwen-7B 上
基座选择是第一个岔路口。市面上可选的中文基座不少,我实际对比过 Qwen-7B、Baichuan2-7B、InternLM-7B 这几个同量级的。选 Qwen-7B 主要基于三点考虑。
第一是中文医学语料的 token 效率。Qwen 的分词器对中文和常见英文医学术语的切分比较合理,同样一段病历文本,Qwen 切出来的 token 数通常比一些以英文为主的基座少 15% 到 25%。这意味着同样的显存能塞进更长的上下文,对处理长病历和指南文档很关键。
第二是生态成熟度。Qwen 系列的微调工具链、量化方案、推理部署支持都比较完整,遇到问题容易找到参考。做医疗项目最怕的是卡在一个冷门基座的坑里没人踩过,时间成本太高。
第三是 7B 这个量级的性价比。13B 以上对显存要求陡增,单卡 24G 很难舒服地跑全参数微调;7B 配合 LoRA 或者 QLoRA,单张 24G 卡就能跑起来,对大多数团队和个人开发者更现实。如果你的场景对效果要求极高且有 A100 集群,可以考虑 14B 或 32B,但流程是一样的。
提示:基座选型不要只看榜单分数。医疗场景更看重领域 token 效率和长文本处理能力,建议拿自己科室的真实文本做一次分词测试,对比 token 数和切分质量,比看排行榜靠谱。
1.3 四阶段流程的分工与取舍
MedicalGPT 的完整流程包含四个阶段:增量预训练、监督微调、奖励建模、强化学习对齐。很多人一上来就想跑全流程,我的建议是先想清楚自己的目标。
增量预训练解决的是“知识注入”问题,让模型见过足够多的医学文本,建立领域语感。监督微调解决的是“指令跟随”问题,让模型学会按问答格式输出。奖励建模和强化学习对齐解决的是“偏好对齐”问题,让模型的回答更符合人类医生的偏好,比如更严谨、更少幻觉。
如果你的目标只是做一个能回答科室常见问题的助手,增量预训练加监督微调基本够用。奖励建模和强化学习对齐对数据质量和算力要求都更高,收益在医疗场景下不一定线性增长,我建议先把前两阶段做扎实。下面重点讲前两阶段的完整实现,后两阶段给出思路和关键点。
2. 数据准备与增量预训练实操
2.1 医疗语料的来源与清洗要点
增量预训练的数据质量直接决定模型的知识底子。医疗语料大致分几类:公开医学教材和指南、脱敏病历文本、医学文献摘要、药品说明书、科室积累的问答记录。我实际用下来,教材和指南的性价比最高,因为结构清晰、术语规范、错误率低;病历文本信息密度高但噪声大,需要重点清洗。
清洗这一步我踩过最大的坑是“看起来干净其实有毒”。早期我直接拿一批病历文本去训练,结果模型学会了一堆模板化的套话和重复表述,因为病历里大量存在“患者一般情况可”“无特殊不适”这类高频重复句。后来我加了几道过滤:按段落去重、过滤长度过短或过长的段落、过滤包含大量数字和符号的表格残留、用困惑度过滤掉语义混乱的文本。
具体操作上,我写了一个清洗脚本,核心逻辑是分句、去重、长度过滤、困惑度打分。困惑度过滤用一个小的参考模型算,把明显不通顺的句子筛掉。这一步会损失一部分数据量,但留下来的质量明显更高。实测下来,清洗后 10G 高质量文本的效果,比 30G 未清洗文本要好。
import re from collections import Counter def clean_medical_corpus(texts, min_len=50, max_len=2000): seen = set() cleaned = [] for t in texts: # 按句号、换行切分 sents = re.split(r'[。\n]', t) for s in sents: s = s.strip() if len(s) < min_len or len(s) > max_len: continue # 过滤数字符号占比过高的段落 digit_ratio = sum(c.isdigit() for c in s) / max(len(s), 1) if digit_ratio > 0.3: continue # 去重 key = s[:50] if key in seen: continue seen.add(key) cleaned.append(s) return cleaned2.2 增量预训练的数据配比与打包策略
数据配比是个经验活。纯医疗语料训练会让模型在通用能力上退化,出现“只会说医学术语,日常对话都不会了”的情况。我的做法是医疗语料和通用中文语料按 7:3 到 8:2 混合,既保证领域知识注入,又保留基座的通用语言能力。
打包策略上,增量预训练通常用固定长度拼接,比如把多条短文本拼成 2048 或 4096 token 的序列,减少 padding 浪费。这里要注意的是拼接时不要跨文档破坏语义边界,我一般会在文档之间插入分隔符,并在 attention mask 上做处理,避免模型把两篇不相关的文档当成连续上下文。
训练参数方面,7B 模型增量预训练我用的配置是:学习率 1e-5 到 2e-5,cosine 衰减,warmup 比例 0.03,batch size 通过梯度累积凑到 128 左右,训练 1 到 2 个 epoch。学习率不能太高,否则容易把基座原有的能力冲垮;epoch 也不能太多,医疗语料重复训练容易过拟合。
注意:增量预训练阶段建议用全参数训练或者较大 rank 的 LoRA。小 rank 的 LoRA 在这个阶段注入知识的能力有限,因为知识注入需要改动较多参数。如果显存实在不够,可以先用 QLoRA 做一版,但效果会打折扣。
2.3 显存估算与训练加速技巧
7B 模型全参数训练,光模型权重加优化器状态就要 7B × (2 + 2 + 4 + 4) 字节左右,粗算下来接近 80G,单卡根本放不下。所以实际落地要么用多卡,要么用 LoRA/QLoRA。
我用的是 QLoRA 方案,4-bit 量化后模型权重只占约 4G,加上 LoRA 适配器和优化器状态,单张 24G 卡能跑起来。关键参数是 LoRA rank 设 64、alpha 设 128、target modules 覆盖 q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj、down_proj 这些线性层。rank 设大一点是因为增量预训练需要更强的表达能力。
加速技巧上,flash attention 2 一定要开,长序列训练能省不少显存和时间。gradient checkpointing 也建议开,用时间换显存。另外数据加载用多进程,避免 GPU 等数据。我实测下来,这些优化加起来能让训练速度提升 40% 以上。
| 配置项 | 全参数训练 | QLoRA 训练 |
|---|---|---|
| 显存需求 | 80G+ | 20G 左右 |
| 单卡可行性 | 不可行 | 24G 卡可行 |
| 知识注入效果 | 最好 | 良好 |
| 训练速度 | 快 | 中等 |
| 推荐场景 | 多卡集群 | 单卡/个人 |
3. 监督微调与数据构造核心细节
3.1 医疗问答对的质量标准
监督微调的数据质量比数量重要得多。我见过太多人拿几万条粗糙问答去微调,结果模型学会了胡说八道。医疗问答对有几条硬标准:答案必须有依据,不能是模型编的;问题要覆盖真实场景,不能全是教科书式的标准问法;答案长度要适中,太短信息不足,太长模型学不会抓重点。
我构造数据时遵循一个原则:每条问答都要能追溯到来源。来源可以是临床指南、药品说明书、权威教材,也可以是有经验的医生审核过的科室问答。没有来源的问答我宁可不用。另外我会刻意构造一些“边界问题”,比如“这个药孕妇能不能用”“这个症状要不要立即就医”,让模型学会在不确定时给出谨慎的回答,而不是硬编一个答案。
数据格式上,MedicalGPT 用的是标准的指令格式,包含 instruction、input、output 三个字段。对于纯问答,input 可以留空;对于带上下文的任务,比如“根据以下病历回答问题”,input 放病历文本。格式统一很重要,否则模型学出来的输出格式会很乱。
{ "instruction": "患者男,58岁,2型糖尿病,eGFR 38,询问二甲双胍是否可用", "input": "", "output": "根据患者 eGFR 38 的情况,二甲双胍使用需谨慎。当 eGFR 低于 45 时,不建议新启用二甲双胍;若已在用,需评估获益风险并考虑减量或换药。建议结合患者整体情况,考虑改用对肾功能影响较小的降糖方案,并密切监测肾功能。具体用药调整请遵医嘱。" }3.2 数据配比与多样性控制
监督微调的数据配比同样讲究。全是问答对会让模型变得只会答题,通用对话能力下降。我的配比是医疗问答占 70%,通用指令数据占 20%,剩下的 10% 放一些安全对齐数据,比如拒绝回答超出能力范围的问题、提醒就医等。
多样性控制上,我会统计问题的类型分布,确保覆盖问诊、用药、检查解读、健康科普、就医建议等不同类别。如果某一类占比过高,模型就会偏科。我一般用简单的关键词分类做统计,发现偏斜就补充数据。
还有一个容易被忽略的点是答案的风格多样性。如果所有答案都是同一种句式,模型输出会很死板。我会刻意保留不同医生的回答风格,有的简洁直接,有的详细解释,让模型学到更自然的表达。
3.3 LoRA 微调的关键参数设置
监督微调阶段我用 LoRA 就够了,不需要全参数。LoRA rank 设 32 到 64,alpha 设 rank 的两倍,dropout 设 0.05 到 0.1 防止过拟合。学习率比增量预训练高一些,用 1e-4 到 2e-4,cosine 衰减,warmup 比例 0.03。
target modules 的选择上,我建议覆盖 attention 和 MLP 的所有线性层,只调 attention 效果会差一些。batch size 根据显存尽量设大,配合梯度累积。训练 epoch 一般 2 到 3 个,医疗问答对数据量不大的话可以多跑几个 epoch,但要盯着验证集 loss,一旦回升就停。
实操心得:LoRA 微调时保存多个 checkpoint,最后用验证集挑最好的那个,不要只看最后一个。我遇到过最后一个 epoch 过拟合,中间某个 checkpoint 效果反而更好。
4. 奖励建模与强化学习对齐思路
4.1 奖励建模的数据与训练要点
奖励建模的目标是让模型学会判断哪个回答更好。数据形式是同一个问题的多个回答,人工标注偏好排序。医疗场景下,偏好维度包括准确性、安全性、完整性、表达清晰度。标注成本很高,所以这一步通常只在有明确需求时才做。
训练上,奖励模型一般基于微调后的模型加一个打分头,用 pairwise 的 ranking loss 训练。数据量不需要特别大,几千到几万条高质量偏好对就能有不错的效果,但标注质量必须高。我建议至少让有医学背景的人参与标注,纯众包标注在医疗场景下风险太大。
4.2 强化学习对齐的落地难点
强化学习对齐常用 PPO 或 DPO。PPO 工程复杂度高,需要同时维护策略模型、参考模型、奖励模型,显存和调参难度都大。DPO 相对简单,直接用偏好数据优化策略模型,不需要单独的奖励模型,在医疗场景下我更推荐 DPO。
DPO 的关键是 beta 参数,控制模型偏离参考模型的程度。beta 太小模型会过度优化偏好数据,出现 reward hacking;beta 太大则对齐效果不明显。我一般从 0.1 开始试,根据验证集表现调整。医疗场景下我倾向于保守一点,宁可对齐弱一些,也不要让模型为了迎合偏好而牺牲准确性。
注意:强化学习对齐阶段最容易出现的问题是模型学会“讨好”而不是“正确”。医疗场景下这个风险尤其高,因为偏好标注本身可能有偏差。建议对齐后做一轮严格的事实性评估,确认模型没有为了流畅而编造内容。
5. 常见问题排查与避坑经验
5.1 训练不收敛与 loss 异常排查
训练不收敛最常见的原因是学习率太大或数据格式有问题。我遇到过 loss 一开始就飙到很高然后不降,排查下来是数据里混入了大量空文本和乱码。所以训练前一定要抽样检查数据,确认格式统一、内容干净。
另一个常见问题是 loss 正常下降但模型输出乱码。这通常是 tokenizer 和模型不匹配,或者保存模型时没保存 tokenizer。建议训练脚本里显式保存 tokenizer,推理时用同一个。
如果 loss 震荡厉害,检查 batch size 是不是太小,或者梯度累积设置有没有问题。医疗数据分布不均,batch 里如果全是某一类样本,梯度方向会偏,适当 shuffle 能缓解。
5.2 显存溢出与训练中断处理
显存溢出是单卡训练的家常便饭。除了前面说的 QLoRA 和 gradient checkpointing,还可以降低 max_seq_length、减小 batch size、用 8-bit 优化器。我一般先把 max_seq_length 从 4096 降到 2048 试试,很多场景下 2048 够用了。
训练中断后恢复要注意 checkpoint 的完整性。我习惯每几百步存一次,并且存的时候同时保存优化器状态和 scheduler 状态,这样恢复后能接着原来的学习率曲线走,不会因为重启导致学习率跳变。
5.3 模型幻觉与安全性问题
医疗大模型最怕的就是幻觉。模型会用很自信的语气说出错误信息,用户很难分辨。缓解幻觉有几个手段:训练数据里加入“不知道就说不知道”的样本;推理时用较低的温度参数;对高风险问题加规则兜底,比如涉及用药剂量、急症判断的问题,强制模型给出就医建议而不是具体方案。
我还会在部署时加一层输出审核,用关键词匹配加小模型分类,把明显有风险的输出拦下来。这层审核不能完全依赖大模型自己,因为大模型可能被绕过。
| 常见问题 | 可能原因 | 排查方向 |
|---|---|---|
| loss 不下降 | 学习率过大、数据脏 | 降学习率、抽样检查数据 |
| 输出乱码 | tokenizer 不匹配 | 确认保存和加载同一 tokenizer |
| 显存溢出 | 序列过长、batch 过大 | 降 max_seq_length、用 QLoRA |
| 模型幻觉严重 | 数据质量差、温度过高 | 清洗数据、降温度、加兜底规则 |
| 通用能力退化 | 医疗数据占比过高 | 调整配比,混入通用数据 |
5.4 评估环节的实操建议
评估是很多人偷懒的环节,但医疗场景下评估比训练还重要。我建议至少做三层评估:自动指标看 perplexity 和 BLEU/ROUGE,只能作为参考;人工评估抽一批真实问题让医生打分,看准确性和安全性;对抗评估专门构造边界问题,看模型会不会胡说。
评估集要独立于训练集,而且要覆盖不同科室和问题类型。我一般留 500 到 1000 条高质量问答做评估,每条都人工过一遍。评估结果要记录,每次改数据或调参后重新跑,对比看有没有提升。
6. 部署与推理优化实践
6.1 量化部署与推理加速
训练完的模型要部署才有价值。7B 模型用 fp16 推理需要约 14G 显存,用 int8 量化降到 7G 左右,int4 量化能降到 4G 以内。我用的是 GPTQ 或 AWQ 量化,4-bit 量化后效果损失很小,推理速度也快。
推理框架上,vLLM 的吞吐量比 HuggingFace 原生推理高很多,适合并发场景。如果只是本地测试,transformers 加量化就够了。部署时注意设置合理的 max_new_tokens 和 temperature,医疗场景温度建议设 0.1 到 0.3,减少随机性。
6.2 上线前的安全检查清单
医疗模型上线前必须过一遍安全检查。我整理了一份清单:涉及用药剂量的回答是否都带了“遵医嘱”;急症相关的问题是否都建议立即就医;模型是否会在不确定时承认不知道;是否有输出审核层拦截高风险内容;是否有日志记录所有问答便于追溯。
这份清单不是走形式,每一条都对应真实的风险。我见过模型给用户算出具体用药剂量然后用户真去执行的案例,虽然模型加了免责声明,但风险依然存在。医疗场景下,宁可模型保守一点,也不要它“大胆”回答。
7. 个人实操体会与后续扩展方向
跑完两轮完整流程,我最大的体会是数据决定上限,流程决定下限。MedicalGPT 这套流程本身是成熟的,照着跑能出结果,但结果好不好几乎完全取决于数据质量。我第一轮用了一批没怎么清洗的数据,模型输出一堆套话;第二轮认真做了清洗和配比,效果提升非常明显。
另一个体会是不要追求一步到位。很多人想直接跑完四个阶段,结果每个阶段都做得不扎实。我的建议是先把增量预训练和监督微调做透,把评估体系建起来,再考虑要不要做对齐。医疗场景下,一个知识扎实、回答谨慎的模型,比一个对齐得很“讨喜”但会编内容的模型有价值得多。
后续扩展上,可以考虑几个方向:接入 RAG 让模型能查最新指南,弥补训练数据时效性的不足;做多模态,把影像报告和文本结合;针对特定科室做垂直微调,比如只做肿瘤或只做心血管。这些都是在现有流程基础上的自然延伸,核心骨架不用变。
最后分享一个小技巧:训练日志一定要详细记录,包括数据版本、超参、每步 loss、评估结果。我吃过亏,改了一版数据后效果变差,但没记录改了什么,只能从头排查。后来我养成了用实验管理工具记录每次实验的习惯,省了大量时间。医疗大模型是个长期工程,可复现比跑得快重要得多。