← 返回项目

医疗大模型

[unisound 实习] 病历质控swift微调

[unisound 实习] 病历质控swift微调项目说明

文档日期:2026-08-30
本文用于说明项目的业务目标、数据处理、SFT/DAPO 训练流程、实验设计、结果与项目边界。

1. 项目解决什么问题

本项目训练一个病历质控模型。模型接收“质控规则 + 病历内容”,完成两个任务:

  1. 判断当前质控点是“合格”还是“不合格”;
  2. 当结论为“不合格”时,生成能够支持判断的简短依据。

模型统一输出:

{
  "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

转换过程:

  1. 从 SFT 的 messages 中取出 assistant 标准答案;
  2. DAPO 的 messages 只保留模型可见的用户输入;
  3. 在用户输入末尾补充 /no_think;
  4. 将标准答案移动到独立的 solution 字段;
  5. 补充空的 <think></think> 格式;
  6. 保留 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 有效率”。

实验结论:

  1. SFT 显著优于 Base,说明人工标准答案能够有效建立质控能力;
  2. 直接 DAPO 优于 Base,但弱于 SFT,说明从较弱初始策略直接强化学习不够稳定;
  3. SFT→DAPO 综合表现最好,Right Macro-F1 相比 Base 绝对提升 17.4 个百分点;
  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 实验方法

参数实验分为两个阶段:

  1. 短步数粗筛:从同一个 SFT checkpoint 出发,统一训练 400 step,只改变当前研究的一个参数。粗筛用于排除明显欠拟合、训练波动大或计算成本过高的配置;
  2. 完整训练确认:将各组入选参数组合后训练 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,并完成训练与评测闭环。