自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

机器学习

  1. 01机器学习导论
  2. 02线性回归:从一条直线到完整建模流程
  3. 03机器学习里的线性代数:从形状、变换到最小二乘
  4. 04多元线性回归:从一条直线走向可用的预测模型
  5. 05用 Octave / MATLAB 把机器学习公式真正跑起来
  6. 06逻辑回归:从概率模型走到可执行的分类决策
  7. 07正则化:用可验证的约束换取更稳的泛化
  8. 08神经网络表示
  9. 09神经网络学习:把预测程序变成可训练系统
  10. 10机器学习系统设计:把一次模型实验变成可持续的产品闭环
  11. 11应用机器学习的建议:从错误清单到下一轮实验
  12. 12支持向量机:从最大间隔到可靠的核分类流程
  13. 13聚类:从 K-means 原理到可解释、可复现的分群流程
  14. 14降维:从 PCA 几何直觉到可靠的工程流程
  15. 15异常检测:从低密度分数到可运营的告警系统
  16. 16推荐系统:从一次曝光到一张可上线的推荐列表
  17. 17大规模机器学习:从单机瓶颈到可恢复的训练系统
  18. 18OCR 与人工数据:把模型接成一套能交付的识别系统
正在加载课程章节内容
课程编程机器学习机器学习系统设计:把一次模型实验变成可持续的产品闭环

机器学习系统设计:把一次模型实验变成可持续的产品闭环

训练出一个分数不错的模型,离“系统真的可用”还有很长一段路。模型可能学到了上线时拿不到的特征,验证集可能混进了同一位用户的重复记录,离线指标可能提高了,真实业务却没有变化。即使首发效果很好,数据分布、上游接口和用户行为也会继续改变。

所以,系统设计关心的不只是“哪种算法更强”,而是团队怎样连续做出正确决策:我们到底要改善什么;用什么可测信号判断进展;数据怎样切分才像真实上线;什么时候该改数据、特征、优化过程或模型容量;上线之后又怎样发现退化并安全回滚。

你可以把这一章看成一份机器学习项目的操作地图。地图的起点是业务决策,终点不是一次发布,而是一个能够监测、复盘和继续学习的闭环。下一章会进一步讨论误差分析、不平衡分类及精确率与召回率;本章先把这些工具放进正确的系统流程里。

本章中的“系统”包括数据采集、标签生成、特征处理、训练、验证、部署、在线服务、监控和反馈。模型只是其中一个部件。只优化模型而不检查其余环节,很容易把时间花在错误的瓶颈上。


先写决策合同,再谈模型分数

机器学习项目最容易从一句模糊的话开始:“我们想让推荐更准”“我们想自动识别高风险订单”。这样的方向可以立项,却不足以指导实验。团队需要先写清楚:模型输出会改变哪个决策,决策改变后希望哪个业务结果变好,又有哪些代价不能越界。

我习惯把这份说明叫作决策合同。它至少回答五个问题:

问题需要写清楚的内容工单分流示例
决策对象系统究竟要排序、分类还是估计数值判断新工单是否需要优先人工处理
使用时刻预测发生在什么时间点工单刚提交、客服尚未回复时
可用信息那一刻真正拿得到哪些字段用户填写的文本、产品类型、历史已结案记录
业务结果最终希望改变什么缩短真正紧急工单的首次人工响应时间
约束条件哪些代价、风险或性能不可接受普通队列不能长期拥堵,单次推理延迟有上限

“使用时刻”尤其重要。假如你要在工单刚提交时预测紧急程度,就不能使用“最终升级层级”或“客服处理时长”这样的事后字段。它们和标签高度相关,离线分数会很好看,但上线时根本不存在。

业务指标、代理指标和护栏指标

业务目标通常太慢、太远,不能直接拿来训练。比如“提高客户留存率”可能要几个月才能观察到,还会受到价格、活动和季节影响。于是我们会寻找一个较快、可归因的代理指标,例如模型对紧急工单的排序质量。

代理指标不能冒充业务目标。它只是一条可用于迭代的近路。一个完整的指标结构通常有三层:

  • 业务指标回答“项目是否产生了真实效果”,例如紧急工单的响应时间是否下降。
  • 模型指标回答“预测是否更接近我们定义的标签”,例如排序损失或某个固定决策阈值下的错误率。
  • 护栏指标回答“改善是否以不可接受的代价换来”,例如普通工单等待时间、人工队列负载、推理延迟和故障率。

如果模型指标变好而业务指标不动,可能是代理目标与真实目标关系太弱,也可能是模型输出没有真正改变产品流程。若业务指标变好但护栏恶化,系统同样不能直接全量发布。

我们可以把发布条件写成一组门槛,而不是一句“分数越高越好”:

ΔMbusiness≥δ,Mguardrail(k)∈Ak,Lp95≤Lmax\Delta M_{business} \geq \delta, \qquad M_{guardrail}^{(k)} \in \mathcal{A}_k, \qquad L_{p95} \leq L_{max}ΔMbusiness​≥δ,Mguardrail(k)​∈Ak​,Lp95​≤Lmax​

其中,δ\deltaδ 是业务上值得发布的最小改进,Ak\mathcal{A}_kAk​ 是第 kkk 个护栏指标的可接受范围,Lp95L_{p95}Lp95​ 是推理延迟的第 95 百分位。具体数值应在看实验结果之前约定,否则团队很容易在结果出来后临时修改标准。

业务决策合同连接主体、时点、动作、成本、验收指标与发布条件

决策对象、使用时点、动作、代价与验收口径,要在看实验结果前写清。

1
团队要在用户提交工单的瞬间预测是否需要优先处理。下面哪项最适合作为决策合同的一部分?

第一版先跑通最小闭环

有了决策合同,下一步通常不是寻找最复杂的模型,而是做一个能够从原始数据走到真实决策的最小版本。第一版的价值是让我们尽早暴露接口、标签、切分和服务约束,而不是证明某个算法多先进。

基线不止一种

一个有用的项目至少应保留三类基线:

  1. 业务现状基线:当前人工规则或旧系统表现如何。若现有流程已经足够好,新模型需要超过的不是随机猜测,而是这个真实对手。
  2. 朴素统计基线:例如总是预测多数类、使用历史均值,或按最近一次状态做判断。它用来发现数据或评估代码是否明显有错。
  3. 简单模型基线:使用少量可靠特征和容易解释的模型,形成第一条端到端机器学习流水线。

如果一个复杂模型只比简单模型高出很小的离线分数,却让延迟、维护成本和排错难度明显上升,这个提升未必值得。反过来,如果朴素基线已经击败团队准备上线的模型,就应该先检查问题定义和数据,而不是继续调参。

最小闭环应该包含什么

“最小”不等于只写一个训练脚本。它至少要覆盖:

  • 从真实来源读取一份带版本的数据快照;
  • 按上线条件构造标签和特征;
  • 使用固定规则切分训练、验证和测试数据;
  • 训练一个基线模型并保存预处理步骤;
  • 在验证集与关键数据切片上生成报告;
  • 用与线上一致的输入结构完成一次推理;
  • 记录模型版本、数据版本、代码版本和运行配置。

可以先不自动重训,也可以先不追求高吞吐,但不能跳过“训练产物是否真的能被服务读取”这一步。许多项目在 Notebook 里取得不错分数,部署时才发现特征顺序、缺失值规则或类别字典完全不同。

先让一个简单规则接收与未来服务相同的请求结构,并产生可被下游消费的输出。这样能验证模型之外的接口和决策链。

再接入最小训练数据,训练简单模型,并把所有预处理与模型一起保存。此时重点是同一条样本在训练端和服务端得到相同特征。

用一批固定样本做离线回放,比较规则基线、简单模型和线上旧系统。差异较大时先检查数据路径,不急着解释算法。

最后用影子流量或不影响用户的旁路调用观察延迟、缺失字段和输出分布,为后续在线实验准备证据。

从数据版本、简单基线、训练验证到实验记录的最小机器学习闭环

能端到端复现的简单基线,是后续复杂方案最可靠的参照系。

“先做基线”不是让团队长期停留在粗糙方案,而是建立一把可信的尺子。没有这把尺子,复杂模型的每次提升都可能来自切分变化、数据泄漏或评估代码差异。

2
第一版机器学习系统只要能在 Notebook 中训练并得到验证分数,就已经形成了端到端基线。

数据切分要模拟上线,而不是凑比例

“随机分成 70%、15%、15%”只是某些独立同分布数据的起点,不是通用答案。切分真正要模拟的问题是:模型上线后会面对什么样的新样本?新时间段、新用户、新设备,还是同一对象的后续记录?

随机切分何时才合理

如果每一行样本近似独立,并且未来流量与当前数据来自稳定的同一分布,随机切分通常可用。分类任务还常用分层抽样,让各子集的大类比例相近。

但现实数据经常存在依赖关系:

  • 同一位用户有多条会话;
  • 同一台设备产生多次检测;
  • 一篇文档被切成多个片段;
  • 同一商品有不同角度的图片;
  • 同一事件在连续几天留下多条记录。

若把这些相关记录随机散到训练集和验证集,模型可能只是识别出了“同一个对象”,而不是学会泛化到新对象。此时应该按用户、设备、文档或事件分组,保证一个组只落在一个子集中。

集合关系可以写得很明确:

Gtrain∩Gval=∅,Gtrain∩Gtest=∅,Gval∩Gtest=∅G_{train}\cap G_{val}=\varnothing, \qquad G_{train}\cap G_{test}=\varnothing, \qquad G_{val}\cap G_{test}=\varnothingGtrain​∩Gval​=∅,Gtrain​∩Gtest​=∅,Gval​∩Gtest​=∅

其中 GGG 不是样本行号,而是需要隔离的实体标识。

时间数据必须尊重因果顺序

若线上任务总是“用过去预测未来”,评估也应该如此。可以使用较早数据训练、较晚数据验证、最新时间窗测试:

ttrain<tval<ttestt_{train}<t_{val}<t_{test}ttrain​<tval​<ttest​

这比随机打乱更接近真实部署,也会暴露季节变化、产品版本变化和概念漂移。时间边界附近还可能需要留出间隔。例如标签要在工单创建 7 天后才确定,就不应让训练区间末尾那些尚未成熟的样本混入评估。

泄漏常常发生在预处理里

即使样本行切对了,仍可能在下面这些步骤泄漏:

  • 用全体数据计算均值和标准差;
  • 在切分前用全部标签做特征选择;
  • 先对全体数据做目标编码,再分训练与验证;
  • 使用预测时刻之后才生成的字段;
  • 训练集和评估集存在重复或近重复样本。

正确顺序是先确定切分,再只在训练部分拟合预处理器,最后把同一个已拟合变换应用到验证和测试部分。用流水线封装预处理与模型,能减少人工漏掉这条边界的机会。

python
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
 
model = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
    ("classifier", LogisticRegression(max_iter=2000)),
])
 
model.fit(X_train, y_train)  # 只把训练数据交给 fit
val_score = model.score(X_val, y_val)

随机切分、按实体分组与按时间切分的边界及常见数据泄漏通道

切分边界必须匹配上线场景,可学习的预处理只能在训练侧拟合。

3
一个医疗数据集包含每位患者多次复诊记录,目标是预测模型对新患者的表现。下面哪些做法合理?

模型选择和最终验收必须分开

验证集会参与开发。我们根据它选择特征、阈值、正则化强度、网络结构和训练轮数;看得越多,它就越像训练数据的一部分。因此,验证分数不是最终泛化能力的无偏证明。

测试集的职责不同。它应在候选方案和发布规则已经确定后,用于一次独立验收。可以把三个集合的权限理解成:

数据可以做什么不应该做什么
训练集拟合模型参数与预处理参数代替独立评估
验证集选择模型、超参数、阈值和特征方案当作最终成绩反复对外报告
测试集对冻结方案做最终估计根据结果继续调参再重复报分

“只用一次”真正指什么

它不一定意味着文件只能被程序读取一次,而是测试结果不能再次反馈进这一轮方案选择。若团队看完测试结果后又修改了模型,那份测试集已经进入决策回路。新的最终估计需要一份尚未参与开发的数据,或等待下一时间窗形成新的测试集。

同样,模型选择不能只保留分数最高的一次偶然运行。应使用固定切分或合适的交叉验证,报告均值与波动,并比较训练成本、延迟和关键切片。对于存在用户组或时间顺序的数据,交叉验证器也必须遵守同样的组边界或时间边界。

假设候选集合为 H\mathcal{H}H,我们在验证数据上选择方案:

h∗=arg⁡min⁡h∈HEval(h)h^*=\arg\min_{h\in\mathcal{H}} E_{val}(h)h∗=argh∈Hmin​Eval​(h)

选择完成后冻结 h∗h^*h∗,才计算:

Etest(h∗)E_{test}(h^*)Etest​(h∗)

这里的 hhh 不只是算法名称,还包括特征版本、预处理参数、超参数、阈值和后处理规则。任何一项变化,都意味着候选方案发生变化。

如果测试分数“不够好”就回去继续调,测试集已经变成了验证集。最危险的地方不是团队主动作弊,而是每次只做一个看似合理的小修改,最后却忘了整套方案已经针对测试样本优化过。

4
团队在测试集上比较了 30 组超参数,选择分数最高的一组并把该分数当作最终成绩。最准确的判断是什么?

偏差与方差是行动诊断,不是模型标签

当结果不理想时,团队常说“这是过拟合”或“模型偏差太大”。这句话只有能指导下一步实验时才有用。系统设计里,我们比较的是三件事:当前任务可达到的参考误差、训练误差,以及验证误差。

记参考误差为 ErefE_{ref}Eref​。它可以来自可靠人工标注者、成熟旧系统,或业务能够接受的目标线;它不必被误称为绝对不可约误差。训练误差与参考误差的差距近似反映模型是否连训练数据里的规律都没学好:

偏差线索=Etrain−Eref\text{偏差线索}=E_{train}-E_{ref}偏差线索=Etrain​−Eref​

验证误差与训练误差的差距反映模型从训练样本泛化到未见样本时损失了多少:

方差线索=Eval−Etrain\text{方差线索}=E_{val}-E_{train}方差线索=Eval​−Etrain​

“线索”二字很重要。若训练和验证来自不同分布,第二个差距里还混有分布偏移;若标签本身不一致,第一个差距可能来自标注噪声,而不是模型容量。

四种常见局面

观察结果更可能的瓶颈优先验证的方向
训练误差高,验证误差与它接近高偏差或标签/特征信息不足更有信息的特征、更合适的模型、减弱过强约束
训练误差低,验证误差明显更高高方差或切分泄漏反向暴露更多独立数据、合理正则化、降低容量、检查重复样本
两者都低,但线上差分布偏移、代理目标错位或训练—服务偏差检查数据时点、线上特征与业务链路
两者都高且差距也大偏差和方差并存,或数据质量较差先找可验证的最大瓶颈,不要用单一标签概括

例如,训练误差 4%、验证误差 12%,不能直接断言“多收数据一定有效”。先确认 4% 是否真的低:若可靠人工参考误差是 0.5%,模型仍有明显偏差;再检查验证数据是否来自更晚时间窗,否则 8 个百分点里可能有分布变化。

训练误差与验证误差的组合如何映射到偏差、方差与下一步行动

先比较训练与验证误差,再决定加容量、加数据,还是回头检查流程。

偏差—方差诊断应使用与模型目标一致、且不含正则项的可比较误差。训练时的目标函数可能包含正则化惩罚,直接拿它与验证误差比较会把两个不同量放在一起。

5
某模型训练误差为 9%,验证误差为 10%,可靠人工参考误差约为 1%。下一步最合理的初步判断是什么?

学习曲线回答“更多数据值不值得”

学习曲线把训练样本量放在横轴,把训练与验证表现放在纵轴。它最有价值的问题不是“曲线漂不漂亮”,而是:增加数据后,验证表现还在稳定改善吗?训练与验证的差距是否在缩小?如果继续收集同分布数据,可能得到多大收益?

正确画法比形状解释更重要

对每一个训练规模 mkm_kmk​,我们要从训练池中取一个子集,重新拟合整条训练流水线,再在同一套验证规则上计算分数:

Dtrain(1)⊂Dtrain(2)⊂⋯⊂Dtrain(K)\mathcal{D}_{train}^{(1)} \subset \mathcal{D}_{train}^{(2)} \subset\cdots\subset \mathcal{D}_{train}^{(K)}Dtrain(1)​⊂Dtrain(2)​⊂⋯⊂Dtrain(K)​

嵌套子集能减少“不同规模其实抽到了不同难度样本”的干扰。分类数据要保证小子集里仍有足够的各类样本;用户或时间相关数据仍要使用相应的分组或时间验证方式。每个点最好重复抽样或使用交叉验证,并画出波动范围,而不是只连一条偶然曲线。

下面是一个简化示意。真实项目还应给 cv 传入符合任务结构的切分器,并记录每个点的标准差:

python
import numpy as np
from sklearn.model_selection import learning_curve
 
sizes, train_scores, val_scores = learning_curve(
    estimator=model,
    X=X_development,
    y=y_development,
    train_sizes=np.linspace(0.1, 1.0, 8),
    cv=splitter,
    scoring="neg_log_loss",
    n_jobs=-1,
)
 
train_mean = -train_scores.mean(axis=1)
val_mean = -val_scores.mean(axis=1)
val_std = val_scores.std(axis=1)

三种更稳妥的读法

  • 两条曲线在较高误差处靠拢且早早变平:继续收同类数据的边际收益可能有限,应检查特征信息、标签定义或模型容量。
  • 训练误差较低,验证误差更高,并且验证曲线仍随样本增加而改善:更多独立样本可能有价值。
  • 曲线忽上忽下或不同重复差异很大:先怀疑样本量太小、切分不稳、数据混杂或训练过程不稳定,不要急着做偏差—方差结论。

学习曲线只能外推“类似数据”的收益。如果未来数据来自新地区、新设备或新产品版本,简单扩大旧分布样本未必解决问题。此时更有价值的可能是定向补齐缺失切片。

训练样本增加时训练曲线与验证曲线的典型形态及诊断区域

学习曲线估计同分布更多数据的价值,不是对未来收益的永久保证。

6
某模型的训练样本从 1 万增加到 20 万时,训练误差从 3% 上升到 5%,验证误差从 16% 稳定下降到 8%,两者仍相差 3 个百分点,且最近几个验证点尚未变平。下一步怎样安排实验更合理?

正则化曲线和容量曲线决定往哪边调

学习曲线改变的是训练样本量;验证曲线改变的是一个超参数。两者回答的问题不同。正则化曲线可以帮助我们判断约束是否太强或太弱,容量曲线则观察模型从简单到复杂时训练与验证表现怎样变化。

以带 L2L_2L2​ 正则化的模型为例,训练目标为:

J(θ;λ)=Jdata(θ)+λ∥θ∥22J(\theta;\lambda) = J_{data}(\theta) + \lambda\lVert\theta\rVert_2^2J(θ;λ)=Jdata​(θ)+λ∥θ∥22​

λ\lambdaλ 很大时,参数受到强约束,模型可能连训练数据都拟合不好;λ\lambdaλ 很小时,模型更自由,训练误差通常更低,但验证误差可能因为过拟合上升。我们用对数尺度尝试一系列 λ\lambdaλ,每个值都只在训练数据上拟合,并在验证规则下比较。

这里有一个常被忽略的细节:画曲线时,纵轴应该使用同一个纯预测误差 JdataJ_{data}Jdata​ 或同一评分指标。不能把“含正则项的训练目标”与“不含正则项的验证误差”直接比较。

容量不是只有层数

模型容量可能由很多设置共同决定:

  • 多项式次数、树深和叶节点最小样本数;
  • 神经网络层数、宽度与训练轮数;
  • 特征数量、交叉项和词表规模;
  • 正则化强度、早停条件与数据增强;
  • 决策阈值之后的规则复杂度。

因此,一次曲线最好只改变一个主变量,其余训练预算、切分与预处理保持一致。若同时把模型加深、学习率调小、训练时间翻倍,我们即使看到提升,也很难知道原因。

曲线用于缩小搜索空间,不替代最终验证

验证曲线可能呈现一个宽阔平台。此时不必机械选择分数最高的单点;可以偏向平台内更简单、更快、更稳定的设置。只有当差异明显超过重复实验的波动,并且在重要切片上方向一致,才值得把它当成可靠提升。

7
关于正则化曲线和容量曲线,下面哪些说法正确?

改进顺序从证据最强的瓶颈开始

模型效果不好时,可选动作很多:清洗数据、重做标签、增加特征、换优化器、调正则化、加大模型。真正困难的不是列出清单,而是决定哪一步最可能改变当前瓶颈。

我建议按下面的顺序做一次“故障树”检查。它不是永远固定的排名,而是一条能减少无效计算的默认路径。

先确认测量和数据没有坏

先抽样核对原始记录、标签与时间点。检查重复样本、缺失值、单位变化、类别字典和 join 后的行数。若标签规则本身前后不一致,模型容量再大也只会更精细地拟合混乱。

还要检查评估实现:指标方向是否写反,样本权重是否正确,阈值是否和生产一致,预处理是否只在训练数据上拟合。一个简单基线突然异常强或异常弱,往往是这一步的报警器。

再看信息是否足够

若训练误差相对参考线仍高,观察现有特征是否真的包含完成任务所需的信息。预测快递延误却没有发货地、天气或承运商信息,调学习率很难补上这些缺口。此时可以增加在预测时刻可得的特征,或重新定义更可预测的目标。

然后检查优化是否把已有容量用出来

损失是否稳定下降?不同随机种子是否经常失败?梯度、数值范围和训练轮数是否合理?在一小批数据上,模型能否刻意过拟合到很低训练误差?如果连很小的数据都拟合不了,先修训练实现或优化设置。

最后才是容量与规模

证据显示高偏差时,再提高模型容量、减弱过强正则化或加入更有效的特征;证据显示高方差时,再考虑更多独立数据、增强、正则化或更简单模型。对于推理成本敏感的系统,还要把每一点离线提升换算成延迟和资源代价。

证据优先实验不宜先做
标签抽查大量不一致修订标注规范并重标关键样本盲目扩大神经网络
小数据也无法拟合检查实现、优化与特征尺度先收百万新样本
训练好、验证差且曲线仍改善增加独立数据或正则化继续增加无依据的特征交叉
离线好、线上差查分布与训练—服务偏差只在旧验证集上调参

从数据正确性、切分、基线和错误分析到模型复杂度的证据优先阶梯

证据越直接、验证成本越低的瓶颈,越应该优先处理。

8
一个图像分类模型连 50 张训练图片都无法拟合,训练损失还经常出现 NaN。此时最合适的第一步是什么?

分布偏移和训练—服务偏差要分开查

一个模型可能在测试集上表现稳定,上线后却迅速退化。常见原因有两类:世界真的变了,或者训练端与服务端对同一世界做了不同处理。前者是分布偏移,后者是训练—服务偏差。

分布偏移:数据生成过程发生变化

训练数据来自分布 Ptrain(X,Y)P_{train}(X,Y)Ptrain​(X,Y),线上请求来自 Pserve(X,Y)P_{serve}(X,Y)Pserve​(X,Y)。只要下面任一部分改变,既有评估就可能失真:

Ptrain(X,Y)≠Pserve(X,Y)P_{train}(X,Y) \neq P_{serve}(X,Y)Ptrain​(X,Y)=Pserve​(X,Y)
  • 输入分布 P(X)P(X)P(X) 变了,例如新地区用户、传感器升级或季节变化;
  • 标签关系 P(Y∣X)P(Y\mid X)P(Y∣X) 变了,例如欺诈策略改变、市场规则调整;
  • 类别先验 P(Y)P(Y)P(Y) 变了,例如活动期间高风险订单比例上升。

输入分布变化容易监控,但它不自动等于模型质量下降。有些无关特征变化很大,预测仍然正常;有些关键特征只轻微变化,却可能影响决策。因此,漂移告警要结合特征用途、模型输出、业务切片和延迟到达的真实标签一起判断。

训练—服务偏差:同一字段被算成两种含义

这类偏差更像工程 bug:

  • 训练按自然日聚合 7 天行为,线上按最近 168 小时聚合;
  • 训练缺失值填 0,线上填历史均值;
  • 类别编码字典版本不同;
  • 训练读取修订后的数据库,线上只能看到请求当时的值;
  • 训练批处理使用一种语言实现,线上服务用另一套重写逻辑。

最有效的检查是保留服务时的原始请求与特征快照,再让训练特征代码离线回放同一批样本,逐字段比较。能共享变换代码时尽量共享;不能共享时,就建立契约测试和容差范围。

python
def compare_feature_vectors(offline, online, atol=1e-8):
    problems = {}
    for name in offline:
        if name not in online:
            problems[name] = "线上缺失"
        elif abs(float(offline[name]) - float(online[name])) > atol:
            problems[name] = (offline[name], online[name])
    return problems

数据分布偏移与训练服务偏差的两条不同诊断链

分布偏移是世界变了;训练—服务偏差是两条数据流水线没有对齐。

“线上流量和训练数据不同”不是一个足够具体的结论。先区分是世界发生变化,还是两条流水线计算不一致;前者可能需要新数据和重训,后者首先需要修复数据契约。

9
只要监测到某个输入特征的分布变化,就可以断定模型线上准确率已经下降。

离线胜出只是线上实验的入场券

离线评估回答“在历史样本和既定标签上,候选模型是否更好”。线上实验回答“模型进入真实决策后,用户行为和业务结果是否改善”。两者之间隔着界面、规则、队列容量、延迟、用户适应和反馈回路。

例如,一个排序模型提高了离线相关性,却可能把内容集中到少数热门项目,短期点击上升,长期多样性下降。一个风险模型提高了召回,却可能把人工审核队列挤满,导致真正高风险样本处理更慢。

在线实验先确定随机化单位

若同一用户会连续使用产品,就通常按用户随机,而不是按单次请求随机。否则同一个人会在两个策略间来回切换,体验互相污染。群组、门店或地区存在强相互影响时,随机化单位还可能需要提升到群组层面。

实验前应固定:

  • 主业务指标与最小有意义改进;
  • 护栏指标和停止条件;
  • 随机化单位、流量比例和实验时长;
  • 纳入与排除规则;
  • 新旧版本、特征与配置的唯一标识;
  • 出现故障时的回滚路径。

不要在实验过程中频繁查看结果,一看到显著就提前停止;这会增加偶然胜出的概率。若业务需要连续监测,应事先采用合适的序贯分析方法,而不是临时改变结束规则。

渐进发布把模型风险变成可控风险

常见路径是离线回放、影子模式、小流量金丝雀、受控实验、逐步扩量。影子模式只计算新模型输出,不改变用户决策,适合发现延迟、缺失字段和输出异常;金丝雀让很小比例流量真正受影响,用于验证系统稳定;受控实验再评估因果业务效果。

先确认候选模型超过离线门槛,并在时间、用户群和关键业务切片上没有明显倒退。

用影子流量检查线上输入覆盖、特征一致性、延迟与资源消耗,不让模型输出改变真实流程。

通过小流量金丝雀验证服务稳定和回滚机制,护栏异常时自动退回旧版本。

再运行有预注册指标与随机化单位的线上实验,根据业务指标和护栏共同决定是否扩量。

10
一个推荐模型离线分数更高,准备进入线上验证。下面哪些做法合理?

可复现实验让每次提升都能追溯

机器学习实验同时依赖代码、数据、配置和随机过程。只保存一个模型文件,无法回答“这个模型用哪天的数据、哪套标签、哪个特征字典训练出来”。一旦线上退化,团队就很难定位是模型变化、数据变化还是服务变化。

一次实验至少要绑定四类版本

类别最低记录内容
代码提交哈希、训练入口、依赖环境
数据原始快照或查询版本、切分规则、标签截止时间
配置特征列表、模型超参数、随机种子、指标与阈值
产物预处理器、模型权重、评估报告、服务接口模式

随机种子很有用,但它不等于完整复现。并行计算、硬件内核和依赖版本仍可能带来差异。更稳妥的做法是把结果看成一个带容差的分布:关键实验运行多个种子,报告均值、波动和失败次数。

数据切分也需要稳定身份

如果每次运行都重新随机切分,两个实验的差异可能来自样本换了。可以用稳定实体标识做确定性哈希,让同一个用户在多次实验中落入同一子集:

python
import hashlib
 
def stable_bucket(entity_id: str, buckets: int = 100) -> int:
    digest = hashlib.sha256(entity_id.encode("utf-8")).hexdigest()
    return int(digest[:16], 16) % buckets
 
bucket = stable_bucket(user_id)
split = "train" if bucket < 70 else "val" if bucket < 85 else "test"

若目标是预测未来,哈希切分不能替代时间边界;若同一实体的时间行为需要分别出现在训练和后续评估中,哈希键还要根据真实上线问题谨慎设计。稳定性是手段,模拟部署才是目的。

实验台账要记录失败

一条有用记录不只写最终分数,还写实验假设和唯一改动。例如:“假设缺失值本身有业务含义;新增缺失指示特征,其余保持不变。”若结果没有提升,这条记录仍能阻止团队几周后重复同一实验。

每次改动尽量小。若一次提交同时更换标签、模型结构和切分,哪怕分数提高,也无法判断哪部分有效。需要批量搜索超参数时,至少保持数据、特征与评估协议固定,并保存完整搜索空间,而不是只记录冠军。

11
为了让同一用户在多次实验中稳定落入同一个数据子集,可以对稳定的用户标识做确定性 ____,再按结果分桶。

监控要覆盖数据、模型、服务和业务

上线不是项目结束,而是从受控实验进入开放环境。监控的目标也不只是画仪表盘,而是让团队在异常发生时知道:哪里变了、影响多大、该自动回滚还是人工调查。

四层信号各自回答不同问题

  1. 数据层:输入是否缺失、越界、模式改变,类别字典是否出现新值,关键切片占比是否变化。
  2. 模型层:预测分布、置信度、阈值通过率和切片表现是否异常;真实标签到达后,质量是否下降。
  3. 服务层:吞吐、错误率、超时、资源使用和延迟百分位是否越界。
  4. 业务层:模型实际影响的业务指标与护栏是否沿预期方向变化。

只监控平均值通常不够。整体平均延迟稳定,某个地区的超时率可能已经激增;总体预测通过率不变,新用户切片可能发生反向变化。切片选择应来自决策风险和数据生成过程,而不是上线后随意翻找几十个维度。

没有即时标签时怎样监控

很多标签会延迟几天甚至几个月。此时可以分两阶段:先使用输入模式、预测分布、业务代理和人工抽检做早期告警;等成熟标签到达后,再回填真正的模型质量,并按模型版本与预测时间归档。

模型年龄也值得监控。若预定的重训任务停止,服务仍可能正常返回预测,但模型已经长时间没有看到新数据。数据管道新鲜度、最近成功训练时间和最近成功部署时间应分别记录。

告警必须对应行动

每个告警都应绑定负责人、阈值依据和处理手册。比如:

  • 线上特征缺失率超过上限,切换到保守默认策略;
  • 推理错误率持续升高,自动回滚上一稳定模型;
  • 某关键切片质量下降,暂停扩量并抽样复核;
  • 输入漂移但质量尚无标签,增加人工审查和观测窗口;
  • 训练任务逾期,冻结自动发布,排查数据新鲜度。

数据、模型、服务与业务四层监控连接告警、复盘和反馈闭环

监控必须把四层信号连成可定位、可处置、可复盘的行动闭环。

好的监控不是“什么都测”,而是每条信号都能追到一个系统假设,并能触发明确动作。没有负责人、阈值依据和处理路径的告警,久而久之只会变成被忽略的噪声。

12
一个贷款风险模型的真实违约标签要 90 天后才能确认。上线后的前 90 天,哪些监控仍然有价值?

反馈闭环既能改进模型,也可能放大偏差

模型输出会改变用户看到什么、工作人员处理什么,进而改变下一批训练数据。推荐系统把某些内容排在前面,它们自然获得更多点击;审核模型把高分样本交给人工,低分样本可能永远没有可靠标签。这就是反馈闭环。

先区分“被观察到”与“真实发生”

如果只有被审核的订单才有欺诈标签,那么训练数据描述的是“模型和审核策略共同选择后观察到的结果”,并不等于所有订单的真实分布。直接用这些数据重训,系统可能越来越确信自己原先关注的区域,而忽略从未被检查的区域。

降低风险的方法包括:

  • 保留一小部分随机探索流量,获得更接近总体的标签;
  • 记录每条样本当时由哪个模型、阈值和策略选中;
  • 区分用户主动反馈、人工复核和系统自动生成的标签;
  • 训练前检查新数据是否被当前模型选择机制严重过滤;
  • 对高风险决策设置人工复核、申诉和退出路径。

自动重训不等于自动发布

自动化流水线可以定期收集数据、生成特征、训练并验证候选模型,但发布仍应受质量门槛、护栏、切片检查和回滚能力约束。尤其当标签来自模型影响后的环境时,新模型“超过旧模型”可能只是更适应旧模型制造的数据。

一个稳妥的闭环是:线上记录完整上下文,标签成熟后进入受控数据版本;训练任务产生候选模型;候选模型经过离线门槛、偏差检查和线上渐进实验;发布后继续按版本监控。任何阶段失败,都保留上一稳定版本,而不是把“最新”当作“最好”。

删除也属于系统设计

特征、模型和告警会积累维护成本。一个只带来极小提升、却依赖不稳定上游服务的特征,可能不值得保留。定期做消融实验,删除无贡献特征,能减少故障面和训练—服务偏差。系统设计的成熟标志之一,是团队敢于移除复杂度。

13
只要新训练数据量持续增加,并且自动重训后的离线分数高于旧模型,就可以自动全量发布。

用一个工单分流系统走完整条设计链

我们把前面的判断放进一个具体案例。某售后团队每天收到大量工单,希望尽快把真正紧急的工单送给人工处理,但人工队列容量有限。团队准备训练一个模型,为新工单输出紧急分数。

从决策而不是标签名开始

系统在工单提交后 30 秒内打分,决定是否进入优先队列。业务主指标是“最终确认紧急的工单,其首次人工响应时间的第 90 百分位”;护栏是普通工单等待时间、优先队列负载、推理延迟和服务错误率。

标签不能简单写成“进入过优先队列”,因为过去的人工规则本身决定了谁被优先处理。团队结合后续升级记录、退款与安全事件,并抽样人工复核,形成一个版本化标签规范。对于尚未经过足够观察期的工单,不提前当作负例。

基线与数据边界

业务基线是现有关键词规则;统计基线是按历史紧急率给所有样本相同分数;机器学习基线使用文本长度、产品类型、用户已结案记录和少量文本特征训练逻辑回归。

同一用户可能反复提交相似工单,因此验证时按用户分组。测试集使用更晚的完整时间窗,模拟未来流量。所有文本词表、缺失值规则和数值缩放只在训练部分拟合。工单最终处理时长、升级结果等事后字段明确禁止进入特征。

实验诊断与优先级

第一轮结果显示:模型训练误差与验证误差都明显高于参考线,增加训练样本后两条学习曲线很快靠拢。团队没有立刻收更多同类数据,而是抽查标签与输入,发现某类产品的故障代码只存在于结构化附件,没有进入当前特征。

加入预测时刻可读的故障代码后,训练与验证都改善。随后验证差距变大,学习曲线仍随新增用户下降,团队再定向补充新产品用户,并加入适度正则化。每次实验只改变一个主因素,记录数据、代码、配置和产物版本。

从离线到线上

候选模型先在历史请求上回放,再以影子模式接收真实流量。影子阶段发现线上文本清洗器把全角标点处理成了不同 token,团队修复并加入训练—服务一致性测试。

接下来,模型以小流量金丝雀运行,输出异常时可以退回关键词规则。服务稳定后,团队按用户随机开展线上实验。是否发布不看离线分数,而看紧急工单响应时间是否改善,同时普通队列等待时间和队列负载是否守住护栏。

上线后的闭环

监控分为四层:数据层检查字段缺失、新产品代码和文本长度;模型层检查分数分布、优先队列通过率与产品切片;服务层检查延迟和错误率;业务层在标签成熟后回填真实紧急工单响应时间。

因为只有部分工单会被人工深度处理,团队保留少量随机抽检,避免标签完全由当前模型的选择决定。自动重训只产生候选版本,候选仍需通过固定测试窗、关键切片和渐进发布。若上游故障代码接口不可用,系统降级到不依赖该字段的稳定模型。

工单分流系统从问题定义、数据切分、训练验收到上线监控的端到端链路

一次可靠发布来自完整系统链,而不是某次离线实验的单一高分。

这个案例的重点不在逻辑回归本身。真正让系统可靠的是每个决策都有证据:目标能测,输入符合预测时点,切分模拟上线,实验可以复现,线上风险能回滚,反馈数据不会在没有审查的情况下自动变成“真相”。模型以后可以更换,这条闭环仍然成立。

14
某工单模型离线验证很好,但影子流量中有 18% 的请求缺少训练时很重要的“历史退款次数”特征;线上用 0 填充,训练时用中位数填充。下面哪些后续动作合理?
上一章神经网络学习:把预测程序变成可训练系统下一章应用机器学习的建议:从错误清单到下一轮实验