我们经常会遇到一个诡异现象:在显卡上跑得好好的高精度模型,一上线就卡死在“要我立刻判断”的场景里。产品和算法来回拉锯,最后大家习惯性说“剪枝一下、蒸馏一下”,仿佛只要模型变小就自动配得上“System One”这个名字。但如果你真的从第一性原理去拆解,会发现问题远不是“变小”这么简单。System One模型的核心,不是“小而快”,而是在极短决策预算内,保留足够的直觉泛化能力,不因为压缩而丢掉边界情况里的常识判断。
我这段时间把一个内部项目Jeva重新梳理了一遍,目标就是完全从第一性原理出发,手写一套System One模型的构建链路。没有直接用现成的压缩工具链,也没有把“蒸馏”当成一个黑盒API去调用,而是把数据、模型容量、损失函数、校准流程全部拆开重新想了一遍。这篇文章就是整个过程的记录,适合那些已经训练过大模型、想快速落地在线推理场景,但同时对系统一/系统二这个认知框架有点兴趣,又不想被现成工具牵着走的同学。
1. 从卡尼曼式思维到机器学习直觉压缩的决策边界
1.1 系统一和系统二,不是一个哲学梗,是架构约束
很多工程师第一次听到“System One”这个概念,是在丹尼尔·卡尼曼的《思考,快与慢》里。系统一是快速的、直觉的、自动化的思维,系统二是慢速的、审慎的、需要逻辑推理的思维。放到机器学习场景里,大部分人会把两者理解为“小模型对大模型”或者“规则引擎对深度网络”,但实际关系比这复杂得多。
一个成熟的推理系统往往是混构的:高延迟路径负责处理长尾复杂样本,低延迟路径负责处理高频常规样本。System One模型的任务不是替代System Two,也不是简单做一个“阉割版”,而是要在高频常规样本上形成足够稳的自动判断,让System Two不被琐碎请求打满。也就是说,System One模型的性能指标不只是一个延迟数字,还包括“它能正确接住多大比例的系统二负载”。这个比例才是真正体现压缩质量的地方。
我见过不少团队在构建System One时只盯着大模型蒸馏,觉得软标签一灌,Model A就变成Model B了。结果一上线发现,常规样本确实快,但那些稍微绕一点的输入全乱了。原因在于,System One的“直觉”不是凭空产生的,它是System Two在大量深度推理中积累出的经验凝练。一旦你把推理逻辑简单砍掉,直觉也跟着崩塌。所以第一性原则对Jeva项目的要求是:先明确哪些经验该被保留,再讨论如何用更小的结构去承载。
1.2 为什么不能拿现成小模型“充当”System One
有些人为了省事,会直接拿一个开源的小模型或者GPT缩水版来当System One用。我试过,这种做法在简单分类任务上勉强能看,一旦涉及序列化决策或者多模态特征融合,问题就会冒出来。最大的问题有两个:
第一,现成小模型的训练数据分布和我们生产线上的真实分布不一致。它不是为你的业务“直觉”量身定做的,很多业务相关的隐含规则它根本不知道。这就像让一个聪明但没经过专业训练的人去快速判断专业领域的异常,他反应快,但判断经常不在点上。
第二,现成小模型的内部结构不是为“软标签补偿”设计的。System One训练过程中有一个非常关键的步骤,就是用大模型的软输出作为监督信号来教小模型,让小模型学会在模糊样本上保留概率化倾向。这要求小模型的容量配置、温度参数甚至激活函数都经过精心设计。如果你直接拿一个结构固定的模型,强行对接这个流程,就会发现损失下降很慢,甚至学到一半直接不收敛。
所以,我在Jeva项目里给自己定了一个规矩:不直接引用任何现成小模型作为System One起始点,而是从数据接口、网络宽度、学习率策略开始一步一步搭。这套流程虽然前期麻烦,但后期无论是精度补齐还是在线调试,可控性都强很多。
2. 第一性原理拆解:一个可靠直觉系统该由哪几部分组成
2.1 知识蒸馏只是借口,三要素闭环才是骨架
很多资料会把“System One模型构建”直接等同于“做蒸馏”,仿佛输入大模型logits,输出小模型就完事了。太低估这件事了。Jeva项目里我把System One模型拆成三要素:可保真压缩、可控制的损失地形、可观测的决策边界。
可保真压缩指的是结构层面的问题。学生模型要小,但不是随便小,它的每一层、每一个注意力头都要有存在的理由。可控制的损失地形则是说训练过程不能直接把大模型交叉熵复制过来,你需要调整温度和软硬标签比例,让梯度在早期不要把学生模型推到某一个过拟合角落。可观测的决策边界则是我重点补的部分。System One模型的特点是决策快,但它为什么这么决策,必须有迹可循。否则一旦线上效果出事,你连回溯的依据都没有。
这三个要素不是并列关系,而是环形约束。压缩方式影响损失地形,损失地形决定模型学到的决策边界,决策边界又反过来检验压缩方式是否正确保留了大模型的关键经验。任何一个要素缺失,System One模型都只是一个好看的技术Demo,不是可落地的生产系统。
2.2 上手前的数据准备与任务边界
我强烈建议先花几天时间把数据边界给划清楚,不然所有后续步骤都是空中楼阁。Jeva项目在这步做的第一件事,是把所有训练样本拆成三个桶:常规自动化桶、边界长尾桶、噪声样本桶。
常规自动化桶的样本特征清晰,大模型也能秒判,是我们希望System One完全接管的部分。边界长尾桶则是有挑战的样本,比如图像中目标不完整、文本语义有歧义、声音信号混杂其他噪声,这部分主要依靠软标签和大模型的教师判断来传递经验。噪声样本桶则是连大模型都给不出明确答案的样本,这类样本在System One训练中要适当丢弃或者降权,否则会把学生模型带到摇摆地带。
具体比例上,我在Jeva里用的是6:3:1。常规桶占六成,边界桶占三成,噪声桶占一成。这里有一个容易被忽略的点:边界桶里的样本不能只是直接在原始数据集里找,最好用大模型在验证集上的犹豫度去自动筛选。所谓犹豫度,就是预测最高类概率落在0.4到0.8之间的样本。这批样本才是让推理模型从“机械记忆”走向“直觉判断”的关键。
2.3 学生模型容量选择:先算账,后定网络
学生模型的网络宽度和层数不是拍脑袋定的。我会先估算部署环境的推理预算,比如限定单次推理不超过5毫秒、显存不超过200MB,然后在这个约束下去反推网络结构。Jeva的做法是先从最极端的小结构开始——一个只保留关键特征维度的浅层网络,让它在验证集上跑一遍,看精度差距。然后再逐级增加容量,直到达标。
这样做的好处是避免过度工程化。很多人一开始就采用“比大模型小一半”的架构,结果虽然速度满足要求,但网络里有一大半通道在冗余学习,既浪费算力又增加过拟合风险。从小结构往上加,你会发现每一层容量都有清晰的作用位置,而不是胡子眉毛一把抓。
容量加大的过程中,我特别留意的是学生模型的“低维表示瓶颈”。如果学生结构太窄,很多教师模型传下来的交互特征根本装不下,损失会长期横盘在一个尴尬水平。这时候不是简单加宽度就好,而是要看哪些交互特征是业务真正需要用到的,比如说文本时序依赖、图像通道关联、还是结构化数据的域间关系。明确任务类型后,再决定是加注意力头、加通道数,还是把特征拼接改成跨层连接。
3. 在Jeva中的实际构建流程:从零搭出一个可用的System One模型
3.1 离线生成软标签:这里藏着最多的坑
整个过程的起点,是拿到大模型在训练集上的软标签。很多人觉得这步简单,把数据喂进大模型,把logits吐出来存起来就行。实际上坑非常多。
第一个坑是温度选择。Softmax里的温度T决定了软标签的平滑程度。温度越高,分布越平滑,小模型学到的全局模糊倾向越强;温度越低,分布越尖,小模型容易模仿出大模型的过度自信。我在Jeva里试过T从1到8的各个取值,最终发现业务场景不同,最优T差异极大。文本分类任务T在3到4之间表现好,图像细粒度识别反而T在2附近更合适。一个比较靠谱的做法是:取一批验证集样本,分别用小T和大T蒸馏出两个学生模型,再对比最终效果,别指望一招鲜。
第二个坑是软标签不能和硬标签一刀切。最粗糙的蒸馏方式是把软标签当唯一监督信号,但这样培养出来的System One模型可能会出现“过度平滑”的问题,几乎所有样本的预测分数都蜷缩在0.5到0.7之间,根本没法做准确率阈值判断。我的解决方案是采用软硬标签加权混合:前30%的训练步数里,软标签权重占到0.7,让模型先学全局规律;后70%步数里,硬标签权重逐渐上升到0.5,让模型把决策边界磨得锐利一些。这里具体数值可以根据验证集调整,但“退火”这个思路非常值得保留。
3.2 自蒸馏与双路径监督设计
Jeva还加入了一个不算新鲜但很好用的做法:自蒸馏协同训练。简单说,学生模型不是只从大模型那里学,也会从自己不同迭代阶段的版本里学。我个人理解,这相当于给System One灌输了一个“复盘”机制——每次参数更新后,根据当前模型在缓存样本上的输出变化,反向修正学习方向。
实现上,我会每隔若干个训练步保存一个动态平均版本的参数副本,用这个平均模型给当前学生模型生成软标签,再和大模型的标签一起参与损失计算。这里有一个特别实用的技巧:平均模型的软标签权重随训练步数从0逐渐上升,避免训练初期模型还在漂移时把自己带上歧路。当训练接近尾声时,自蒸馏权重可以占到总软标签权重的一半,这会让系统模型的决策稳定性明显变好。
双路径监督的实际收益体现在样本利用率上。很多边界长尾样本大模型也犹豫,软标签给出的概率分布并不那么可信。此时自蒸馏路径会根据当前模型已经学到的一致性做二次修正,相当于在不确定的教师信号上增加了一个平滑的先验。官方一点说,就是让System One在“跟大模型学”和“跟自己已有直觉对齐”之间取得平衡。
3.3 损失函数的层次化组合
损失函数方面,我把它拆成一个分层结构,而不是单纯用KD loss。最底层是所有样本共享的KL散度,让学生的概率分布向老师的soft label靠拢。上面一层是针对边界长尾样本的对比损失,目的是拉大困难样本在不同类别上的区分度。再上面一层是最终决策目标的自定义损失,比如对于推荐排序场景,我会加入Listwise损失;对于分类场景,我会加入Focal Loss的变体,让模型更关注边界样本。
把多个损失叠加时最怕的是梯度过猛、互相打架。我的解决方式是给每个损失设置独立的权重衰减策略。前几轮训练KL散度权重最大,让模型快速建立起和教师模型的概率对齐。中期增加对比损失权重,把决策边界磨出来。最后几轮再压低KL散度,提高业务目标损失的权重,让模型聚焦到最终指标。
我举一个具体例子。假设现在做的是一个内容安全打分任务,大模型能够区分正常、可疑、违规三档内容。System One模型只需要在实时链路上对前两档做初步过滤。损失函数里的对比项就会设计成让“正常”和“可疑”两类样本的特征向量距离尽量拉大,而“可疑”和“违规”之间保持一定间隙但不强制分开。这样即使后续System Two介入,也不会因为System One过早偏置而丢失信息。
3.4 训练过程的稳定性控制:EMA和分段学习率
System One的训练相比大模型训练更容易震荡,因为学生模型容量小,对噪声的抵抗能力弱。我在这轮Jeva项目里特别强化了两件事。
第一件是EMA(指数移动平均)参数副本的使用。很多人理解EMA只是为了增强精度,实际上它对部署也有巨大帮助。System One模型线上推理时,我用的是历史平均参数的模型,而不是最后一个checkpoint。因为历史平均模型天然消除了权重更新过程中的随机抖动,决策边界更平滑。你可以把它理解成一个有二十年经验的老师傅在决策,而不是一个刚毕业的新手在做判断。
第二件是分段学习率加预热的深度组合。前500步用非常小的学习率做预热,让各模块先适应输入数据的统计特征。中间阶段逐步提升学习率到峰值,最后1000步再快速衰减到接近0。这个做法能有效避免小容量模型在训练初期就被大模型软标签的强大梯度直接“冲碎”。
4. 实战踩坑记录:这些弯路几乎每个项目都会遇到
4.1 训练震荡:当学生模型比老师还“自信”
第一次训练System One的时候,我遇到一个很诡异的现象:训练loss在稳步下降,但验证集上的AUC却突然掉头向下。查了很久才发现,那个阶段软标签温度设得太低,学生模型快速学到了老师最自信的那部分分布,开始对边界样本输出过高置信度。这就像一个人刚刚学会几个套路,就误以为自己是专家,结果在真正复杂的场景里频频翻车。
解决这个问题,我把温度从2.0提回3.0,同时把软标签权重百分比从0.5提高到0.75。改动之后AUC立刻回升。这次教训让我知道一件事:System One模型训练过程中,置信度平滑比最终精度更值得关注。你可以设定一个置信度上限,如果一个Batch里模型输出的最大概率均值超过某个阈值,那就说明温度太低或者软标签权重太小,要主动调策略。
4.2 二次校准回归:蒸馏模型在线上“飘了”
还有一个很值得说的问题:即使System One在离线验证集上表现不错,上线后依然会出现预测置信度分布偏移。这在多分类任务里特别明显。离线验证集上,模型预测正确的样本平均置信度在0.85,上线后同一批数据变成了0.78。置信度掉下去不是精度问题,而是决策阈值全失效了。
后来我在Jeva里加入了一个针对System One的二次校准步骤。做法是不对最后的softmax输出直接使用,而是接一个很小的线性校准层或者等温回归层,用离线的逻辑回归重新把预测分数映射到真实概率分布上。现实数据里如果发现置信度普遍偏移,就用一小部分最新的线上日志重新拟合校准参数。这步不耗时,但能救命。
4.3 重复数据和无意义主键引发的隐性过拟合
很多人会忽略数据处理时的隐性过拟合。Jeva有段时间在验证集上效果特别好看,但一到线上就疲软。拆分后发现,验证集里出现了不少和训练集几乎一模一样的重复样本,这些重复样本在蒸馏软标签时权重很大,等于把学生模型反复推向那几个固定答案。
解决方式是在蒸馏数据准备阶段做去重,并且针对文本、图像分别使用不同的相似度阈值。文本数据里,我用字符级和语义级双重去重;图像数据则用感知哈希去重。去重后再看真实回归指标,效果立刻可参考。
5. 推理链路与部署验证:System One模型到底能不能扛住真实流量
5.1 推理内核延迟对比:别只看模型参数量
部署前,我先在Jeva里做了详细的推理内核延迟对比。测试对象包括原始大模型、未量化的学生模型、量化后的学生模型。得出的结论是:参数量小不等于延迟低。有些模型结构虽然参数量小,但操作过于碎片化,在GPU上拉不起并行度,反而比一些结构规整的大模型还慢。
对比下来,比较理想的是把System One主干设计成规整的矩阵计算,而不是在推理路径上塞入大量条件分支。你可以在System One模型里加入少量轻量规则分支,但一定要让这些分支在推理时走同一份矩阵运算,而不是每来一个样本就动态判一次。动态分支太多了,延迟直接爆炸。
5.2 量化掉点与低精度补救
System One要真正部署,一般绕不开INT8量化。但直接量化很容易掉两个点以上,尤其是在蒸馏模型上。我在测试中发现,关键问题不是权重精度,而是激活值范围波动太大。某些特征维度的激活值分布非常细长,一旦量化为INT8,细节精度丢失严重。
补救方案是使用逐通道量化和动态校准。逐通道量化至少能保住权重层面的波动;动态校准则是让量化校准过程使用System One实际面对的数据分布,而不是随便拿一个静态验证集去估算范围。我实际跑下来,逐通道量化加动态校准能把掉点控制在0.5以内,这个指标是可以接受的。
部署形态上,我建议导出成通用的推理引擎格式,不要在Python环境里跑在线推理。Python解释器本身的调度开销就会吃掉System One的一大部分延迟优势。量化后的模型在轻量推理引擎上跑,单次推理控制在几毫秒内是完全可以做到的。
5.3 线上指标与离线指标之间的决策边界校验
最后一步是最容易被忽略的:校验离线机器学习指标与线上业务指标是否对齐。System One模型经常会出现在离线精确率上改善了3%,但线上转化率纹丝不动的情况。原因往往是离线指标和线上目标不完全匹配。
Jeva的做法是建立一套影子验证机制。新System One模型和老系统并行运行一段时间,只记录决策结果,不真正影响线上流量。然后针对预测分数在0.5到0.9区间内的样本做人工抽检,看看System One是不是真的把决策预算花在了正确样本上。经过这个步骤,你才能说自己构建的不只是一个更快的小模型,而是一个真正合格的System One系统。
写到这里,想要表达的已经都说了。构建System One模型没有万能配方,核心是回到第一性原理,尊重数据分布、尊重容量约束、尊重损失地形,把每一步都当成一个需要验证的科学问题。我个人实际操作中的体会是:如果学生模型在你最头疼的边界场景上,给出的判断和大模型有八九成一致,那这套System One就算成了;如果只是常规样本上刷得飞快,边界场景一碰就碎,那它离真正能用还差得很远。