经验驱动。同一份数据、同一个 bert-base-chinese、同样二分类情感任务,我用三种方式各跑了一遍:冻结 BERT 骨干只训分类头、LoRA、全量微调。结果差距大到不需要统计学显著性检验:冻结版 86.25%,LoRA 93.92%,差 7.67 个点。本文数字全部来自真实的 trainer_state.json。

一、实验设置

项目值
基座bert-base-chinese
任务二分类情感分类(left0ver/sentiment-classification)
框架🤗 transformers Trainer
精度fp32
max_length256
batch size32
学习率2e-5
epoch5(冻结)/ 4(LoRA,早停生效)

三个脚本的差别只有”哪些参数 requires_grad“这一处:

脚本做法
work13sol.pyfor p in model.bert.parameters(): p.requires_grad = False
work13sol2.pyget_peft_model + LoraConfig(r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["query","value"])
work13sol3.py什么都不冻,全参训练

二、结果:冻结和 LoRA 不是”差不多”,是差一个档次

BERT 情感分类微调:冻结骨干 vs LoRA

方式eval_accuracyeval_loss
冻结 BERT 骨干0.8375 → 0.86250.3873 → 0.3424
LoRA (r=8)0.9150 → 0.93920.2217 → 0.1841
差距+7.67 个点低 46%

而且不是”最后追上来的”——LoRA 从第 1 个 epoch 就领先(0.9150 vs 0.8375),冻结版到第 5 个 epoch 也没摸到 LoRA 的起点。

为什么差距这么大

冻结骨干之所以差,是因为它只训练 pooler + 分类头。按 bert-base-chinese 的结构算一下:

  • 可训练:pooler(768×768+768 ≈ 59.06 万)+ 分类头(768×2+2 ≈ 1.5 千)≈ 59.2 万参数
  • 占总量(约 1.0227 亿)的 0.58%

而 BERT 的 12 层 encoder 全被冻住了。预训练表示和下游任务分布不匹配时,仅靠一个线性层是补不回来的——尤其是情感分类这种需要细腻语义判断的任务。

LoRA 只在 query 和 value 上注入低秩适配器(r=8),按结构算约 29.5 万参数(0.29%)——比冻结版训练的参数还少一半,但效果好 7.67 个点。这说明问题不在”训练参数多少”,而在”训练的位置对不对“:LoRA 动的是注意力内部,冻结版动的是最后一层之后。

三、全量微调那一路,我没跑完

诚实说明:work13sol3.py(全量微调)写好了,但 results_full/ 目录没有留在素材里,所以我拿不到它的真实曲线。

按经验和文献,全量微调在这个量级的数据上大概率能到 0.94~0.95,和 LoRA 接近甚至略高,但代价是:

  • 要存完整优化器状态(Adam 两个动量 + 参数本身),显存约是 LoRA 的十几倍
  • 每个任务都要保存一份完整模型副本,LoRA 只要存几十 MB 的适配器
  • 更容易过拟合(数据量小的时候)

所以这篇的结论是”冻结 vs LoRA”的硬对比,全量微调只做定性讨论,不编数字。

四、几个能直接抄走的判断

  1. 永远别默认冻结骨干。除非你的下游任务和预训练分布极度接近(比如同领域的二分类且数据量极大),否则冻结等于自断一臂。
  2. LoRA 的性价比在这里是压倒性的:参数少一半、效果好 7.67 个点、还能顺带解决多任务部署(一个基座 + N 个适配器)。
  3. target_modules 选 query+value 就够。原论文和社区实践都指出,只适配 q/v 在多数任务上已经接近全模块适配的效果,参数量还能再降。
  4. 看 eval_loss 比看 accuracy 更灵敏。冻结版 accuracy 从 0.8492 到 0.8592 看着在涨,但 epoch 4 出现过回落(0.8558)——这种波动在 loss 曲线上看得更清楚。

五、和上一篇的关系

这篇和《Qwen3-0.6B 医疗 SFT 实战》是两个不同层面的实验:那篇讲训练轮数和过拟合(3 epoch 就够,多了会反弹),这篇讲微调方式的选择(冻结/LoRA/全量)。两个结论可以叠加使用:LoRA + 早停,通常是小数据集上的最优组合。


关于本文数据:来自 Week13/results/(冻结)和 Week13/results_lora/(LoRA)下真实 checkpoint 的 trainer_state.json,共 5+4 个评估点。参数量为按 BERT 标准结构的计算值,非实测打印;训练脚本为 work13sol.py / work13sol2.py / work13sol3.py。