Fine-tuning

启动模型训练(下)

场景二:医疗问答数据集微调 上一节课我们完成了自我认知微调,接下来换一个更实际的场景:基于医疗问答数据集对 Qwen3 0.6B 进行 LoRA 微调。 执行命令:

TL;DR

场景二:医疗问答数据集微调 上一节课我们完成了自我认知微调,接下来换一个更实际的场景:基于医疗问答数据集对 Qwen3 0.6B 进行 LoRA 微调。 执行命令:

场景二:医疗问答数据集微调

上一节课我们完成了自我认知微调,接下来换一个更实际的场景:基于医疗问答数据集对 Qwen3-0.6B 进行 LoRA 微调。 执行命令:

swift sft \

和自我认知训练有什么区别?

指定训练集和验证集

自我认知训练使用 Swift 内置数据集,可以通过 --split_dataset_ratio 自动划分。医疗数据集是自定义 JSONL 文件,需要显式指定:

-
-

Epoch 更少

自我认知数据集只有一百多条样本,需要较大 Epoch(20)。医疗数据集有 2000+ 条,设置 3~5 即可获得较好效果。

--num_train_epochs 3~5

LoRA 容量更大

这里将:

--lora_rank 16
--lora_alpha 32

相比自我认知训练的 Rank=8,模型能够学习更多领域知识,但同时显存占用也会略有增加。

学习率略微降低

--learning_rate 1e-5

医疗数据集规模远大于自我认知数据集,且需要学习较复杂的医学问答模式和推理过程。为了避免模型对领域数据过拟合,同时尽可能保留基模型原有能力,这里将学习率降低至 1e-5。虽然训练速度会略慢一些,但通常能够获得更稳定的训练效果和更好的泛化能力。

运行模型

训练完成后,可以直接加载 LoRA 权重进行测试:

swift infer \

可以看到,模型已经能够按照医生问诊的方式进行回答。 抛开正确性先不说,从表达方式来看,模型已经学到了医疗数据集中的表达方式,这说明本次 SFT 的训练结果是有效的。

导出模型

训练完成后建议将 LoRA 合并到基模型:

swift export \

导出完成后直接推理:

swift infer \

或者:

swift infer \

重点关注哪些指标?

其实不用关注所有的训练日志,重点关注三个指标: - loss(训练集损失) - eval_loss(验证集损失) - eval_token_acc(验证集 Token 准确率) 理想情况: loss 持续下降,eval_loss 持续下降,eval_token_acc 持续上升。 如果 loss 持续下降但 eval_loss 开始上升,说明模型开始过拟合。此时可以:减少 Epoch、提前停止训练、增加训练数据、降低学习率。 训练命令已配置了 --load_best_model_at_end true 和 --metric_for_best_model eval_loss,Swift 会自动选择 eval_loss 最低的 checkpoint 作为最终模型,即使出现过拟合也不用手动干预。

能不能合并多个数据集训练?

在生产环境中,模型训练肯定会准备大量垂域数据 + 通用数据 + 自我认知数据的混合数据集。数据集的分布、比例都需要反复调整,调一次其实根本不够,要反复试错,甚至训练很多次,准备很多种不同的数据组合。 我在这边做了一个实验:在 0.6B 小模型上,先训练自我认知,再训练医疗数据,看看能不能两件事一起搞定。 首先基于 0.6B 小模型结合自我认知数据集训练并导出模型 Qwen3-0.6B-self-condition,然后想当然地在这个模型上继续 LoRA 微调,加入医疗数据。结果如下: 整体过程就是:

Qwen3-0.6B → 自我认知训练 → Qwen3-0.6B-self-condition → 继续医疗训练

结果发现:医疗问题回答还行,但自我认知完全错了。 原因:LoRA 微调改变的是模型的矩阵参数分布。第二次 LoRA 训练时,大量医疗数据会覆盖掉自我认知相关的特征。

正确的做法是什么?

核心思路是:重新构建一个混合数据集,把自我认知和医疗数据融合在一起训练。 数据质量当然很重要,但说实话要做到严格把控非常耗时耗力。我这边采用了一个简单策略:直接融合两类数据,不刻意调整比例,先跑起来看效果。 基于之前的自我认知模型,用融合后的数据集开启训练:

from
import
import
import

## =========================
## 配置
## =========================
DATA_PATH
os

random

SELF_DATASET
MEDICAL_DATASET

SELF_RATIO
VAL_RATIO


## =========================
## 身份设定
## =========================
NAME
AUTHOR

SYSTEM_PROMPT


## =========================
## 工具函数
## =========================
def


def
    text

        text


## =========================
## medical
## =========================
def
    q
    think
    answer


        a

        a


## =========================
## self cognition
## =========================
def
    q
    a


## =========================
## load datasets
## =========================
print
self_ds
self_list
print

print
med_ds
med_list
print


## =========================
## build
## =========================
self_clean
for
    r

        self_clean

med_clean
for
    r

        med_clean

print
print

medical_target
self_target

self_sample
med_sample

merged
random

print
print


## =========================
## train / val split(同分布)
## =========================
split_idx

train_data
val_data


## =========================
## save
## =========================
def


            json
            f


print
save

print
save

print

print
print

然后在 self-condition 的基础上,用融合数据集开启训练:

swift sft \

训练完成后,推理时需要注意一个关键点:

swift infer \

注意这里的 --system 参数,为什么要加它? 训练数据中每条样本都包含 system prompt,训练时 System Prompt 同样参与 Loss 计算。因此推理时继续提供相同的 System Prompt 可以强化身份认知、提高回答稳定性、减少随机身份漂移。

总结

到这里,我们已经完整走通了大模型微调的全流程:

环境准备
↓
数据集构建
↓
训练集/验证集划分
↓
LoRA 微调
↓
训练监控
↓
模型推理验证

关于训练集和验证集:训练集参与前向传播、Loss 计算、反向传播和参数更新。验证集只做前向传播和 Loss 计算,不更新参数,用于检查模型是否真的学到了知识。 关于训练效果: 模型最终效果受很多因素共同影响:基础模型、数据质量、数据分布、学习率、Batch Size、LoRA 参数、训练轮数等。即使你知道所有参数的含义,也不一定能一次训练出理想效果。这也是为什么业内把模型训练叫做”炼丹”。 关于多数据集混合训练:先训练 A 再训练 B 会导致 A 的能力被覆盖。正确做法是把所有数据混合在一起训练。数据之间的比例需要反复调整,没有万能公式。 关于生产环境: 真实的垂域大模型上线远不止 SFT 这一步。还需要数据清洗、数据评测、人类反馈对齐(RLHF)、后训练优化、安全评估等多个环节。这些是算法工程师的专业领域。 作为大模型应用开发工程师,我们需要理解的是:数据怎么准备、模型怎么训练、Loss 怎么看、为什么会过拟合。

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。