今天要科普的东西是专业做 AI 评测的同学才会做的事情,而且在专业评测领域里,做的人也比较少。 一般会出现在大厂以及专业做 AI 业务的团队中。毕竟训练一个模型,还想训练的好,不是一件很容易的事。 并且今天要说的这个模型,也只有在数据量大,对评测效率和 token 成本都有要求的前提下才会有的方案,说白了就是让自动化评测运行的更快,且更节省 token 的方案。 如果是面对的是小规模的数据集且迭代速度很慢的情况,那就没什么必要了。
本次只是科普,不会涉及到具体操作细节。只是给感兴趣的同学一个参考方向。毕竟这个类型的工作,还是偏小众了。是较为资深的从业人员会研究的东西。要求对算法原理有一定的理解。
这个词我查资料说是借自卡尼曼《思考,快与慢》的双系统理论:
System One 决策模型:完全放弃文本生成,只做 “给定状态 + 若干预设问题,返回带概率的类型化答案”。这是一类新的模型品类,并非只是 “某个比较快的 LLM”。说白了就是它不会像我们一般见到的大模型一样有漫长的思考过程,并把思考过程输出给用户,也生成大段的答案。 它只是在快速的告诉你它的决策结果。 比如:
说白了 System One 就是用于进行快速决策的小模型(一般都很小),就像人类的基于直觉的判断一样,简单,快速。不用像大模型那样有漫长的思考过程。用于一些简单的决策场景。
看过我教程的同学都知道 “裁判模型这个东西”,在评测任务中,由于 LLM 的答案的不确定性(一题多解,语言的博大精深,同样正确的答案,可以无数种不同的表达方式),所以在评测工作的早期,大部分评测任务是人工来完成的。 但这样带来的问题是每一次评测工作都要投入大量的人力和时间,这对于一个希望能快速迭代的团队来说是无法接受的。所以没过多久人们就开始引入一个 “裁判模型” 来代替人类来进行这样的评测工作 -- 通过引入一个大模型来判断被测模型产出的答案,和预先标注的 “标准答案” 是否是等价的。这就是我们常说的 “裁判模型” 与自动化评测。
目前引入裁判模型的自动化评测体系已经受到业界广泛的认可,基本上大家都是这么做的。 但引入裁判模型也确实带来了一定的问题:
System One 就是解决上面说的第二个问题的。而我一开始才说在迭代慢或者数据规模小的团队,是没有必要用这套方案的。数据小,迭代慢,那这个成本问题,也就是不是问题了。
自动化评测其实就是一个典型的决策场景,判断 “被测模型答案” 和 “标准答案” 是否一致,我们想要的结果无非就是:是或否。大模型给出的大段推理过程和答案其实是没必要的,所以倒不如交给 System One 来解决这个问题。
但 System One 有个问题,就是效果可能不及大模型,System one 模型不管是 jev 还是最近刚出的 laya,都是体积十分小的模型,它的效果几乎注定了没有大模型强。所以想要引入 System One 来解决问题,必然就要进行一些微调。
这里我只介绍大概原理,大家可以去网上下载 laya 的原始模型和一些训练脚本。毕竟我这的项目都比较敏感。
这里我选取 Laya 这个 System One 模型来进行举例,它的模型结构如下:
输入序列: [CLS] bool question: 指令 [SEP] [MASK] 选项false [MASK] 选项true [SEP] state [SEP]
│
▼
┌─────────────────────────────────┐
│ 编码器 mmBERT-base (~307M) │ ◀── 冻结,一个参数都不动
└─────────────────────────────────┘
│ last_hidden_state [T,768]
▼
┌─────────────────────────────────┐
│ 决策头 (~15M),全部可训练: │
│ type_emb Embedding(3,768) │ ◀── 训练
│ head 2层TransformerEncoder│ ◀── 训练
│ scorer LN→Linear→GELU→Linear│ ◀── 训练
└─────────────────────────────────┘
│ 取两个 [MASK] 位置的隐藏态 → 2 个 logit
▼
softmax(logits / T) → P(正确), P(错误)
这个涉及到了模型的原理,看过我很早之前写的深度学习的原理的同学应该知道,参数就是模型中的规则权重,你可以认为模型本质上就是一个参数的数据库,模型所有的推理都是基于这些参数而来。
所谓过拟合,就是模型过于 “适应” 用户的某种场景,而对于其他场景的表现就很差。 理论上模型的泛化能力要求是很高的,它要能在用户的大部分场景下都能良好的工作。而如果模型过于对用户的某个场景 “定制化”,就会在其他场景的表现很差。这就是 “过拟合”。
就是为了防止过拟合, 我们要微调模型,学名是进行迁移学习,就是使用新的数据再进行一次增量训练。 为了防止过拟合的出现,我们需要冻结一部分参数,避免它过于偏向 “训练数据”。
而我们从 Laya 的模型结构上看,它分为编码器(一个单独的 bert 模型,用于把输入数据,按 token 生成 768 维的词向量,通常也叫 embedding,这是 Laya 预先训练好的)和决策头(用于决策),最后再由一个 softmax(激活函数,用于多分类,把决策结果转化为从 0~1 的概率值)组成。
而我们要的就是不懂编码器的参数, 只去微调决策头。
我这里选择的方法是:
也就是我们把模型拆开了, 训练过程完全不用跑 编码器的前向/反向计算,使用 CPU 就能训完,缓存可复用,调超参不用重复编码。决策头也不是随机初始化,而是从官方的模型文件中把权重文件进行复制:官方 model.safetensors 里 head.* / type_emb.* / scorer.* 前缀的键就是官方已训好的决策头,HeadOnly.load_pretrained() 把它们抽出来作为起点。小学习率(1e-4)在此基础上精调。
原始数据 (Excel: 问题/预期答案/实际答案)
│
▼ data/clean_labels.py 规则清洗出干净标签
data/clean_labels.parquet (1064 行: 正确844/错误220)
│
▼ data/finetune_head.py --rebuild_cache 冻结编码器跑一遍,缓存特征
data/_feat_cache.pt (每条样本 [T,768] hidden states)
│
▼ 同一脚本继续 训练决策头 + 温度校准
weights/laya-finetuned-grading/
├── head.pt (57M, 决策头权重)
└── finetune_meta.json (温度 T、验证集指标)
│
▼ data/eval_finetune.py 生成对比报表
data/laya_finetune_report.xlsx
用到的参数:
| 参数 | 默认 | 说明 |
|---|---|---|
--epochs |
6 | 训练轮数(6~8 足够,多了也会过拟合) |
--lr |
1e-4 | 学习率(别调太大) |
--bs |
16 | batch size(梯度下降训练,每 batch size 走一次梯度计算) |
--val_frac |
0.2 | 验证集比例(seed=42 固定划分,可复现) |
| 现象 | 调整 |
|---|---|
| 验证集指标不再升甚至下降 | epochs 调小;脚本已按 acc+F1 保留最优轮 |
| 召回"错误"类太低(漏判错误答案) | 加大类别权重中少数类一侧;检查标签口径是否过松 |
| 概率太极端(都接近 0/1)或太绵(都在 0.5 附近) | 属校准问题,确认使用了 finetune_meta.json 里的温度 T |
| 训练集很好、验证集差 | 过拟合:减 epochs、增 weight_decay、补数据 |
| 某类样本反复判错 | 看报表"判错样本"sheet,八成是标签口径问题而非模型问题 |
| 数据换了/措辞改了 | 必须 --rebuild_cache
|
样本量建议:几百~几千条时冻结编码器方案最优;上万条且冻结头效果到瓶颈时,才考虑解冻编码器末端几层(需要自行改造脚本,把编码器放回计算图,CPU 不再可行)。
写这篇科普文章的目的其实有两个:
PS:对于上面一些术语,比如过拟合参数/超参数/学习率/epochs/batch size 这都是训练的时候的知识点,感兴趣的同学或者想自己试试的同学,建议先看看我以前关于这部分的文章。当然大家问问 AI 也行,毕竟现在这些公共知识的获取靠 AI 也是可以的了。 不想自己实践的同学就纯当了解下,看看与自己不同的工种在做什么事。
最后再宣传下自己的星球:
