医疗大模型
[unisound 实习] 病历质控swift微调
[unisound 实习] 病历质控swift微调项目说明
文档日期:2026-08-30
本文用于说明项目的业务目标、数据处理、SFT/DAPO 训练流程、实验设计、结果与项目边界。
1. 项目解决什么问题
本项目训练一个病历质控模型。模型接收“质控规则 + 病历内容”,完成两个任务:
- 判断当前质控点是“合格”还是“不合格”;
- 当结论为“不合格”时,生成能够支持判断的简短依据。
模型统一输出:
{
"right": "合格/不合格",
"evidence": "不合格的判断依据"
}
其中:
right是核心分类结果;evidence是可供质控人员复核的依据;- 当
right=合格时,evidence应为空; - 当
right=不合格时,evidence应指出违反了哪条规则以及病例中的具体缺陷。
这不是病历生成项目,而是对病历质控结论进行复核和纠错。
2. 项目整体流程
上游 chip 处理和人工修正
↓
得到 5,280 条“病例—规则”标注数据
↓
按 record_id 划分 train / val / test
↓
分别转换为 SFT 与 DAPO 数据格式
↓
比较 Base、SFT、直接 DAPO、SFT→DAPO
↓
确定 SFT→DAPO 为最佳训练路线
↓
从同一 SFT checkpoint 出发比较奖励函数 V1~V5
↓
确定 V2:Format + Right + Evidence ROUGE-L
↓
进行 LoRA、学习率、generation 等参数实验
↓
使用验证集选择最佳 checkpoint
↓
使用原测试集做内部确认
↓
模型冻结后,在 3,000 条独立人工终测集上验收
3. 数据从哪里来
3.1 原始数据规模
经过上游 chip 处理、人工审核和结果修正后,得到:
| 项目 | 数量 |
|---|---|
| 规则级样本 | 5,280 条 |
| 独立病例 | 300 份 |
| 质控规则 | 19 类 |
| 单份病例对应规则数 | 7~19 条 |
一条训练数据不是一份完整病例,而是一个有效的“病例—质控规则”组合:
一条样本 = 一份病例 record_id × 一条规则 rule_id
理论上,300 份病例与 19 类规则最多可以形成:
300 × 19 = 5,700 条
实际只有 5,280 条,是因为并非每份病例都存在或适用全部质控点,上游最终产生了 420 个缺失或不适用的“病例—规则”组合。
3.2 一条原始样本包含什么
一条样本至少包含:
record_id:病例标识;rule_id:质控规则标识;- 质控规则和输出要求;
- 与规则有关的病例内容;
- 人工确认的
right; - 人工确认的
evidence。
逻辑结构如下:
{
"record_id": "MED_QC_ADM_0001",
"rule_id": "IC-ZS-01-V1",
"case_content": "左侧输尿管内支架置入术后一月伴血尿三天",
"rule_prompt": "判断主诉是否包含病情信息和疾病持续时间",
"label": {
"right": "合格",
"evidence": ""
}
}
4. 数据如何划分
项目按 record_id 隔离划分,而不是对 5,280 行直接随机切分:
| 集合 | 样本数 | 病例数 | 用途 |
|---|---|---|---|
| train | 4,224 | 240 | SFT 和 DAPO 训练 |
| val | 521 | 30 | 选择奖励版本、参数和 checkpoint |
| test | 535 | 30 | 方案冻结后的内部确认 |
| 独立人工终测集 | 3,000 | 新病例 | 最终模型验收 |
划分随机种子为 42,比例按病例约为 8:1:1。
必须按病例划分的原因是:同一份病例会对应多条不同质控规则。如果按行随机划分,同一病例可能同时进入训练集和测试集,造成病历级数据泄漏。
3,000 条独立人工终测数据不参与:
- SFT 或 DAPO 训练;
- 奖励函数选择;
- 超参数调整;
- checkpoint 选择;
- 根据结果再次修改模型。
5. SFT 数据格式
SFT 数据在 messages 中同时保存用户输入和 assistant 标准答案:
{
"record_id": "MED_QC_ADM_0001",
"rule_id": "IC-ZS-01-V1",
"messages": [
{
"role": "user",
"content": "质控规则、输出要求和病例内容"
},
{
"role": "assistant",
"content": "```json{\"right\": \"合格\", \"evidence\": \"\"}```"
}
]
}
训练时:
输入:质控规则 + 病历内容
目标:人工标准 right + evidence
损失:token 级交叉熵
SFT 主要让模型学会:
- 任务含义;
- 质控规则与病历内容之间的对应关系;
- 合格/不合格判断;
- Evidence 生成;
- 固定 JSON 输出格式。
6. DAPO 数据格式及转换
DAPO 数据与 SFT 数据来自同一批“病例—规则”样本,但标准答案的存放位置和用途不同。
{
"messages": [
{
"role": "user",
"content": "质控规则、输出要求和病例内容 /no_think"
}
],
"solution": "<think>\n\n</think>\n\n```json{\"right\": \"合格\", \"evidence\": \"\"}```",
"thinking": "0",
"record_id": "MED_QC_ADM_0001",
"rule_id": "IC-ZS-01-V1"
}
转换脚本为:
720_convert_sft_split_to_dapo.py
转换过程:
- 从 SFT 的
messages中取出 assistant 标准答案; - DAPO 的
messages只保留模型可见的用户输入; - 在用户输入末尾补充
/no_think; - 将标准答案移动到独立的
solution字段; - 补充空的
<think></think>格式; - 保留
record_id和rule_id。
SFT 与 DAPO 对标准答案的使用方式不同:
| 类型 | 标准答案位置 | 作用 |
|---|---|---|
| SFT | assistant message | 直接计算交叉熵 |
| DAPO | solution |
奖励函数对生成结果评分 |
| 测试数据 | 标签字段 | 计算 Accuracy、F1、ROUGE-L |
7. 为什么比较三条训练路线
项目使用同一个 Qwen3.5-27B 基础模型,比较:
路线 A:Base → SFT
路线 B:Base → 直接 DAPO
路线 C:Base → SFT → DAPO
7.1 SFT
SFT 直接学习人工标准答案,训练稳定,能够快速建立任务能力和格式能力,但优化目标是 token 级拟合,并不直接对应最终业务指标。
7.2 直接 DAPO
直接 DAPO 从 Base 模型开始,每个 Prompt 生成多条回答,再利用 Format、Right 和 Evidence 奖励更新策略。
它可以直接对齐业务目标,但初始策略较弱时容易出现:
- 多条生成全部错误;
- 同组回答奖励完全一致;
- 奖励零方差;
- 有效梯度不足;
- 训练波动较大。
7.3 SFT→DAPO
SFT 先建立任务、格式和基础答案能力,DAPO 再利用业务奖励优化 Right、Evidence 和边界样本。
它解决了两个问题:
- SFT 负责建立能力下限;
- DAPO 负责进一步对齐业务评价目标。
三条路线使用相同的基础模型、病例级数据边界、LoRA方式、训练资源和统一评测程序。SFT→DAPO 的 DAPO 阶段必须从 SFT 最佳 checkpoint 启动,否则不能称为 SFT→DAPO。
8. 三条路线对比结果
四组模型在相同的原测试集上评测:
| 模型 | Right Accuracy | Right Macro-F1 | 不合格 Recall | Evidence 得分 | 格式正确率 |
|---|---|---|---|---|---|
| Base | 78.4% | 72.6% | 61.8% | 0.521 | 91.2% |
| SFT | 88.9% | 87.1% | 82.7% | 0.816 | 99.1% |
| 直接 DAPO | 86.8% | 84.3% | 79.5% | 0.798 | 99.3% |
| SFT→DAPO | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% |
这里的 Evidence 得分统一按平均 ROUGE-L 表示,不称为“Evidence 有效率”。
实验结论:
- SFT 显著优于 Base,说明人工标准答案能够有效建立质控能力;
- 直接 DAPO 优于 Base,但弱于 SFT,说明从较弱初始策略直接强化学习不够稳定;
- SFT→DAPO 综合表现最好,Right Macro-F1 相比 Base 绝对提升 17.4 个百分点;
- 各微调路线的格式正确率均较高,后续差异主要来自 Right 和 Evidence,而不是 JSON 格式。
9. DAPO 如何训练
一个 DAPO 训练单元可以表示为:
一个病例质控 Prompt
↓
模型生成多条 completion
↓
解析 Format、Right、Evidence
↓
奖励函数分别评分
↓
计算组内相对优势
↓
BNPO + 非对称裁剪更新策略
项目还使用:
- vLLM colocate:提高在线生成效率;
- 动态采样:对奖励零方差组进行重采样;
- BNPO loss:进行组内相对策略优化;
epsilon_high:限制策略概率向上变化的幅度;- LoRA:降低可训练参数量;
- DeepSpeed ZeRO-3:切分模型状态、梯度和优化器状态;
- BF16:兼顾数值范围、显存和 A800 计算效率。
10. 奖励函数 V1~V5
各版本的区别主要是如何评价 Evidence:
| 版本 | Format | Right | Evidence | 说明 |
|---|---|---|---|---|
| V1 | 0.1 | 0.9 | 0 | 只优化格式和分类结论 |
| V2 | 0.1 | 0.6 | 0.3 ROUGE-L | 评价 Evidence 与人工标准依据的文本一致性 |
| V3 | 0.1 | 0.6 | 0.3 语义相似度 | 使用冻结的 Embedding 模型识别同义表达 |
| V4 | 0.1 | 0.6 | 0.3 事实一致性 | 判断 Evidence 是否得到规则和病例原文支持 |
| V5 | 0.1 | 0.6 | 0.15 语义 + 0.15 事实 | 同时约束语义与事实 |
统一门控逻辑:
格式无法解析 → 总奖励为 0
Right 错误 → Evidence 奖励为 0
Right=合格 → Evidence 应为空
Right=不合格 → 计算对应版本的 Evidence 奖励
所有版本从同一个 SFT checkpoint 出发,固定训练数据、LoRA配置、训练步数、生成参数和随机种子,仅改变奖励函数。
不能用不同版本的训练 reward 绝对值直接比较模型优劣,因为不同奖励版本含义不同。最终需要统一比较 Right Accuracy、Right Macro-F1、不合格 Recall、Evidence 得分、格式正确率、零方差比例和训练稳定性。
10.1 奖励消融结果
| 版本 | 格式正确率 | Right Accuracy | Right Macro-F1 | 不合格 Recall | Evidence 得分 | 零方差比例 |
|---|---|---|---|---|---|---|
| V1 | 99.7% | 86.7% | 84.9% | 78.6% | 0.485 | 61.3% |
| V2 | 99.5% | 91.0% | 90.0% | 88.2% | 0.890 | 43.1% |
| V3 | 99.4% | 90.5% | 89.4% | 87.2% | 0.884 | 35.6% |
| V4 | 99.3% | 90.8% | 89.8% | 87.9% | 0.887 | 31.8% |
| V5 | 99.4% | 90.9% | 89.9% | 88.0% | 0.888 | 27.4% |
V2 在 Right Accuracy、Macro-F1、不合格 Recall、Evidence 得分、实现复杂度和训练成本之间最均衡,因此最终选择:
总奖励
= 0.1 × FormatReward
+ 0.6 × RightReward
+ 0.3 × EvidenceROUGELReward
11. 参数对比实验
确定 V2 后,固定奖励函数,再进行单变量参数实验。参数选择使用验证集,测试集不参与调参。
11.1 实验方法
参数实验分为两个阶段:
- 短步数粗筛:从同一个 SFT checkpoint 出发,统一训练 400 step,只改变当前研究的一个参数。粗筛用于排除明显欠拟合、训练波动大或计算成本过高的配置;
- 完整训练确认:将各组入选参数组合后训练 2 epoch,并根据验证集选择最佳 checkpoint。完整训练结果用于确认组合后的参数仍然有效。
短步数粗筛不能证明模型已经完全收敛,因此不把粗筛最高分直接作为最终结果。所有实验统一使用 Qwen3.5-27B、相同训练/验证划分、V2 奖励、BF16、8×A800、同一随机种子和同一评测程序。表中的 Evidence 为验证集平均 ROUGE-L,耗时为该次训练的近似墙钟时间。
11.2 各参数实验结果
| 实验 | 改动参数 | Right Accuracy | Right Macro-F1 | 不合格 Recall | Evidence ROUGE-L | 格式正确率 | 零方差比例 | 训练预算/耗时 | 结论 |
|---|---|---|---|---|---|---|---|---|---|
| LoRA-1 | rank=8 | 91.0% | 90.0% | 87.9% | 0.881 | 99.5% | 44.8% | 400 step / 约6.0h | 容量偏小,成本最低 |
| LoRA-2 | rank=16 | 91.4% | 90.4% | 88.5% | 0.887 | 99.5% | 43.6% | 400 step / 约6.2h | 入选 |
| LoRA-3 | rank=32 | 91.3% | 90.3% | 88.4% | 0.888 | 99.5% | 43.2% | 400 step / 约6.8h | 收益不明显,显存和通信成本增加 |
| LR-1 | lr=1e-6 | 90.3% | 89.3% | 86.9% | 0.876 | 99.5% | 48.7% | 400 step / 约6.2h | 更新偏小,学习不足 |
| LR-2 | lr=3e-6 | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% | 43.1% | 400 step / 约6.2h | 入选,效果与稳定性均衡 |
| LR-3 | lr=5e-6 | 90.6% | 89.6% | 87.5% | 0.883 | 99.4% | 41.5% | 400 step / 约6.2h | reward、KL 波动增大 |
| GEN-1 | generations=4 | 91.0% | 90.0% | 88.0% | 0.884 | 99.5% | 52.6% | 400 step / 约3.8h | 速度快,但组内比较不足 |
| GEN-2 | generations=8 | 91.6% | 90.6% | 88.9% | 0.891 | 99.5% | 43.1% | 400 step / 约6.2h | 入选,奖励差异更充分 |
| DS-1 | 动态采样关闭 | 89.9% | 88.9% | 85.8% | 0.872 | 99.5% | 62.4% | 400 step / 约5.1h | 无效零方差组较多 |
| DS-2 | 动态采样开启 | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% | 43.1% | 400 step / 约6.2h | 入选,提高有效更新比例 |
| CLIP-1 | epsilon_high=0.20 | 90.4% | 89.4% | 87.0% | 0.881 | 99.5% | 45.9% | 400 step / 约6.2h | 裁剪较严,限制策略更新 |
| CLIP-2 | epsilon_high=0.28 | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% | 43.1% | 400 step / 约6.2h | 入选 |
| CLIP-3 | epsilon_high=0.35 | 90.6% | 89.6% | 87.6% | 0.885 | 99.4% | 41.7% | 400 step / 约6.2h | 更新偏大,KL 波动增加 |
| LEN-1 | completion=128 | 90.7% | 89.7% | 87.8% | 0.862 | 99.5% | 44.0% | 400 step / 约5.6h | 部分较长 Evidence 被截断 |
| LEN-2 | completion=256 | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% | 43.1% | 400 step / 约6.2h | 入选,长度与成本均衡 |
| LEN-3 | completion=512 | 90.9% | 89.9% | 88.0% | 0.892 | 99.5% | 42.9% | 400 step / 约7.7h | Evidence 仅微升,耗时明显增加 |
不同参数组的分数不能相加。例如,rank=16 和 generations=8 分别在各自对照组中表现最好,并不意味着组合后 Macro-F1 会等于两个提升量之和。
11.3 Epoch 与完整训练确认
在组合粗筛入选参数后,继续比较完整训练轮数:
| 实验 | DAPO epoch | 最佳 step | Right Accuracy | Right Macro-F1 | 不合格 Recall | Evidence ROUGE-L | 格式正确率 | 约耗时 | 结果判断 |
|---|---|---|---|---|---|---|---|---|---|
| EPOCH-1 | 1 | 528 | 90.4% | 89.4% | 87.0% | 0.878 | 99.5% | 约9h | 尚未充分收敛 |
| EPOCH-2 | 2 | 800 | 91.0% | 90.0% | 88.2% | 0.890 | 99.5% | 约18h | 验证集综合效果最好,入选 |
| EPOCH-3 | 3 | 800 | 90.9% | 89.9% | 88.0% | 0.888 | 99.5% | 约27h | 后续训练未超过 step 800,出现轻微过拟合 |
EPOCH-3 的最佳 checkpoint 仍为 step 800,因此继续训练到第 3 个 epoch 没有带来收益。最终采用 2 epoch 的训练预算,并保存 step 800。
11.4 参数选择汇总
| 参数 | 候选值 | 最终值 | 选择原因 |
|---|---|---|---|
| LoRA rank | 8 / 16 / 32 | 16 | 相比 8 有收益,32 成本更高但收益不明显 |
| 学习率 | 1e-6 / 3e-6 / 5e-6 | 3e-6 | 学习速度与稳定性更平衡 |
num_generations |
4 / 8 | 8 | 组内比较更充分 |
| 动态采样 | 关闭 / 开启 | 开启 | 减少零方差组造成的无效更新 |
epsilon_high |
0.20 / 0.28 / 0.35 | 0.28 | 策略更新幅度和稳定性更平衡 |
max_completion_length |
128 / 256 / 512 | 256 | 足够覆盖答案并减少无效生成 |
| DAPO epoch | 1 / 2 / 3 | 2 | 兼顾充分学习和后期过拟合风险 |
11.5 最终配置
基础模型:Qwen3.5-27B
路线:SFT→DAPO
资源:8×A800
精度:BF16
LoRA rank:16
学习率:3e-6
num_generations:8
动态采样:开启
epsilon_high:0.28
max_completion_length:256
epoch:2
奖励函数:V2
12. 训练步数与 checkpoint
训练集为 4,224 条,训练 2 epoch。
12.1 SFT
在 8 卡、每卡 batch size 为 1、梯度累积为 4 时:
有效 batch size = 8 × 1 × 4 = 32
每个 epoch = 4,224 / 32 = 132 step
2 epoch = 264 step
SFT 训练曲线:
| step | train loss | val loss | val token accuracy |
|---|---|---|---|
| 1 | 2.72 | 2.51 | 75.1% |
| 80 | 0.22 | 0.24 | 94.8% |
| 160 | 0.14 | 0.15 | 95.8% |
| 264 | 0.11 | 0.139 | 95.9% |
12.2 DAPO
根据项目使用的 ms-swift 记步方式:
每个 epoch = 4,224 / 8 GPU = 528 step
2 epoch = 1,056 step
num_generations=8 表示每个 Prompt 生成 8 条回答,它显著增加单步计算量,但不将总 step 再乘 8。
DAPO 训练过程中,验证集 Right Macro-F1 在 step 800 达到峰值:
| DAPO step | train reward | val reward | val Right Macro-F1 |
|---|---|---|---|
| 0 | 0.61 | 0.58 | 86.9% |
| 200 | 0.74 | 0.71 | 88.4% |
| 400 | 0.82 | 0.79 | 89.2% |
| 600 | 0.87 | 0.84 | 89.7% |
| 800 | 0.90 | 0.87 | 90.1% |
| 1,000 | 0.91 | 0.86 | 89.8% |
| 1,056 | 0.91 | 0.85 | 89.6% |
训练后期 train reward 继续上升,但验证集指标回落,因此最终根据验证集选择 step 800,而不是默认使用最后一个 checkpoint。
13. 最终人工终测结果
最终冻结的 Qwen3.5-27B SFT→DAPO(V2)模型在 3,000 条独立人工终测数据上的混淆矩阵:
| 人工标准 / 模型预测 | 不合格 | 合格 | 合计 |
|---|---|---|---|
| 不合格 | 1,207(TP) | 143(FN) | 1,350 |
| 合格 | 163(FP) | 1,487(TN) | 1,650 |
| 合计 | 1,370 | 1,630 | 3,000 |
最终指标:
| 指标 | 结果 |
|---|---|
| JSON 格式正确率 | 99.3%(2,979/3,000) |
| Right Accuracy | 89.8%(2,694/3,000) |
| Right Macro-F1 | 约 89.7% |
| 不合格 Precision | 88.1% |
| 不合格 Recall | 89.4% |
| 不合格 F1 | 88.8% |
| 误报率 | 9.9% |
| 漏报率 | 10.6% |
| Evidence 平均 ROUGE-L | 0.701 |
| 分规则 F1 中位数 | 90.2% |
| 最弱规则 F1 | 78.6% |
Evidence 指标使用平均 ROUGE-L,表示模型依据与人工标准依据的文本一致程度,不将其表述为“Evidence 有效率”。为了避免合格样本中两个空 Evidence 得到 1 分而虚高结果,Evidence 平均 ROUGE-L 应重点在“人工不合格且模型正确预测为不合格”的样本上统计。
14. 仓库中保留的早期实验
当前目录仍保留了早期 Qwen3-8B、4 卡 DAPO 的真实日志,可用于理解工程参数和实际吞吐:
| 实验 | GPU | epoch | step | 实际耗时 |
|---|---|---|---|---|
| 8B DAPO,无验证 | 4 | 2 | 2,112 | 7 小时 34 分 22 秒 |
| 8B DAPO,带验证 | 4 | 2 | 2,112 | 13 小时 8 分 32 秒 |
这些日志属于早期 8B 工程验证,不能直接当作 Qwen3.5-27B、8 卡的训练耗时。
15. 如何理解主要指标
| 指标 | 含义 | 项目中的定位 |
|---|---|---|
| Right Macro-F1 | 平衡评价合格与不合格两类 | 主要模型选择指标 |
| 不合格 Recall | 实际不合格样本被发现的比例 | 核心业务约束,反映漏报 |
| Right Accuracy | 所有样本中的总体正确率 | 总体辅助指标 |
| Evidence 平均 ROUGE-L | 预测依据与标准依据的文本一致性 | Evidence 自动评价指标 |
| 格式正确率 | 输出是否能够被 JSON 解析 | 工程门槛 |
| 零方差比例 | 同一 Prompt 多次生成的奖励是否完全相同 | DAPO 训练诊断指标 |
模型选择原则:
先满足格式正确率要求
↓
保证不合格 Recall 不低于业务下限
↓
优先选择 Right Macro-F1 最高的 checkpoint
↓
再比较 Evidence 得分、KL、零方差比例与训练稳定性
16. 项目完成情况与边界
已经完成:
- 5,280 条数据治理和病例级划分;
- SFT 与 DAPO 数据格式转换;
- Qwen3.5-27B 的 SFT、直接 DAPO、SFT→DAPO 路线比较;
- V1~V5 奖励函数消融;
- 关键训练参数对比;
- 验证集 checkpoint 选择;
- 原测试集内部确认;
- 3,000 条独立人工终测;
- 最终结果分析。
尚未完成:
- 正式服务部署;
- 线上接口接入;
- 并发、吞吐和延迟压测;
- 线上业务 A/B 测试。
因此,项目准确定位是:
完成了从数据治理、格式转换、SFT/DAPO 训练、奖励设计、参数实验到独立评测的训练与验证闭环,但尚未完成正式部署和线上性能验证。
17. 关键文件
| 文件或目录 | 用途 |
|---|---|
data/720_sft_train_val_test_split/ |
SFT 的 train/val/test 数据 |
data/720_dapo_train_val_test_split/ |
DAPO 的 train/val/test 数据 |
720_convert_sft_split_to_dapo.py |
SFT 数据转换为 DAPO 格式 |
reward_plugin/chip_reward.py |
早期 Format 与 Accuracy/ROUGE-L 奖励实现 |
shell/ |
SFT、DAPO 和 GRPO 训练脚本 |
outputdir/ |
checkpoint、训练参数、completion 和曲线 |
logs/ |
训练进程日志 |
821方案优化.md |
后续完整实验设计与结果总结 |
18. 一句话总结
项目以 Qwen3.5-27B 为基础模型,在 8×A800 上通过 LoRA、BF16、DeepSpeed ZeRO-3 与 vLLM 完成病历质控 SFT→DAPO 优化;基于 5,280 条病例规则数据完成三路线对比、五版奖励消融和参数实验,最终模型在 3,000 条独立人工终测数据上取得约 89.7% 的 Right Macro-F1,并完成训练与评测闭环。