🧠 第十章 管理和部署
📄 一句话概括

三个版本的校验和完全不同,但 v2 和 v1 的余弦是 0.99993。 v2 只改 3.7% 的元素、回归最大偏离 1.52%,通过; v3 改动 100%、偏离 20.62%,200 个输入全部超标被拦下。

📝 本节和前面几节的关系

第九章讲怎么评估、怎么优化。 从这一章开始换角度:模型训出来之后,怎么管、怎么部署、怎么一直盯住它。

一、模型也是文件,而且是最难管的文件

代码有 git,改了什么一目了然。模型是二进制大文件,几百 MB 到几十 GB,没法看 diff、没法合并。 偏偏它的迭代速度比代码快:调一次学习率跑一轮就是一个新版本,一天下来几十个文件, 文件名还都叫 model_final.pth。 所以「模型管理」要回答四个问题:哪个版本在线上、这个版本是谁在什么数据上训的、 它比上一版好在哪、出问题能不能一分钟回滚。 四个问题答不上来任何一个,就不算管住了。这跟模型多先进无关。

二、版本号怎么定

代码界习惯语义化版本 主.次.修:主版本改接口、次版本加功能、修版本修 bug。 模型可以借用,但含义要重新定义。 我的建议是模型版本只表示「换了个东西」,不表示「更好」。 因为模型的好坏只在具体数据上成立,升版本不等于升级。 更实用的命名是三段式:模型名_日期_数据版本,比如 lora_style_20261024_v3。 日期解决「哪个新」,数据版本解决「用哪批料训的」。 再加上一个内容哈希(文件前 12 位就够了)钉住具体文件。 哈希不是给人看的,是给机器判断「这两个文件是不是同一个」用的。

三、光有校验和不够:差多少才是关键

Demo 里造了三个版本。三个的校验和完全不同——按哈希判断,它们是三个互不相干的模型。 但 v2 和 v1 的余弦是 0.99993,改动只涉及 3.7% 的元素,L2 相对差 0.0117。 这就是一次小步微调,人眼看输出根本分不出来。 v3 是另一个故事:所有元素都被改过,L2 差 0.1685,余弦掉到 0.98615。 余弦看着还挺高,其实已经是一次大步重训了。 所以版本记录里至少要存三个量:L2 相对差(幅度)、改动元素占比(范围)、余弦(方向)。 只看一个会误判——v3 的余弦 0.986 看着很安全,实际上已经全变了。

四、上线前的那道门禁

有了版本记录还不够,得有东西拦住坏版本。 这个东西叫回归门禁:固定一批输入,跑新旧两版,比输出差多少。 Demo 用 200 个输入、阈值 5%:v2 的最大偏离 1.52%,通过; v3 的最大偏离 20.62%,200 个输入全部超标,直接拦下。 注意 v3 是「全部超标」而不是「部分超标」。 小幅改动只影响少数输入,整体重训会影响几乎所有输入——这个分布形态本身就是诊断信息。 阈值定多少没有标准答案,取决于业务能容忍多大扰动。 分类任务可以看预测类别变化率,生成任务只能看输出距离。 定不出来的,说明还没想清楚「模型输出给谁用」。 这批输入要固定下来、存进版本库。每次换一批,历史结果就没法比了。

五、动手 Demo:版本比对:校验和全不同,但余弦 0.99993

造一个 64 × 128 的权重矩阵当初始版本,再派生两个:v2 只随机改 300 个元素(小步微调), v3 全量加噪(大步重训); 对三个版本分别算校验和、与 v1 的 L2 相对差、改动元素占比、余弦,再用 200 个固定输入跑回归比对。

idx = rg.choice(d * k, 300, replace=False)
v2.flat[idx] += rg.normal(0, 0.02, 300)      # 只改 300 个元素
v3 = v1 + rg.normal(0, 0.05, (k, d))          # 全量加噪

dev = np.linalg.norm(yv - y1, axis=1) / np.linalg.norm(y1, axis=1)
nbad = int((dev > 0.05).sum())                # 超过 5% 就算超标

实跑输出:

--------------------------------------------------------------------
版本管理:三个版本的校验和、差异比例与回归通过率
--------------------------------------------------------------------
  版本          校验和前 12 位   与 v1 的 L2   改动占比      余弦   回归最大偏离
  -----------------------------------------------------------------------------
  v1 初始         4e24d0e9b9bb        0.0000       0.0%   1.00000          0.00%
  v2 小步微调     eba318fa7ed0        0.0117       3.7%   0.99993          1.52%
  v3 大步重训     129796955bce        0.1685     100.0%   0.98615         20.62%

  回归判据:200 个输入里,任何一个的输出相对偏离超过 5% 就算不通过。
  v1 初始      最大偏离 0.00%,超标 0 个,通过
  v2 小步微调  最大偏离 1.52%,超标 0 个,通过
  v3 大步重训  最大偏离 20.62%,超标 200 个,拦截

  三个版本的校验和完全不同,但 v2 和 v1 的余弦是 0.99993——只看校验和会以为这是三个不同的模型。
  所以版本管理不能只存校验和,还要存「和上一版差多少」。差多少这件事,L2 相对值和改动占比从两个角度描述:一个看幅度,一个看范围。

校验和那列三个都不一样。这说明哈希只能判断「是不是同一个文件」,判断不了「变了多少」。 v2 三个量分别是 0.0117、3.7%、0.99993——幅度小、范围小、方向几乎没偏。 三个数一起看,才知道这是一次微调。 v3 是 0.1685、100%、0.98615。 余弦 0.986 单看很容易被当成「几乎一样」,但改动占比 100% 说明每个参数都动了,只是方向整体还接近。 回归门禁拦下了 v3,放过了 v2。 这个结果符合预期:微调该过,重训该拦——重训出来的东西必须重新评估,不能直接顶上线。 这个 Demo 用的是随机矩阵,不是真模型。 但 L2、余弦、改动占比、回归偏离这四个量的关系是纯线性的,换成真权重规律一样。

30 秒实验

  1. 把 v2 的改动元素从 300 个改成 3000 个,看回归偏离会不会也跟着涨 10 倍。
  2. 把门禁阈值从 5% 改成 1%,看 v2 还能不能通过。
  3. 想一想:为什么回归用的输入必须固定、必须进版本库?

常见误区

  1. 「校验和不同就是不同的模型」——哈希判同一性,判不了差异大小。
  2. 「余弦高就说明变化小」——v3 余弦 0.986 但每个参数都改了。
  3. 「新版本比旧版本好」——好坏只在具体数据上成立,升版本不等于升级。
自测三问(点开对答案) 1. 模型管理要回答的四个问题是什么? —— 线上是哪个版本、它用什么数据训的、比上一版好在哪、出问题能不能快速回滚。 2. 为什么模型版本号要带数据版本? —— 因为数据换了模型就等于换了,光看模型版本分不清两次训练差在哪。 3. 回归门禁的输入集为什么必须固定? —— 换一批输入,历史结果就没法横向比较,门禁失去意义。

模型管住了,但它的原料——数据集——更容易出问题。下一节讲数据管理。

相关笔记