三个版本的校验和完全不同,但 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 秒实验
- 把 v2 的改动元素从 300 个改成 3000 个,看回归偏离会不会也跟着涨 10 倍。
- 把门禁阈值从 5% 改成 1%,看 v2 还能不能通过。
- 想一想:为什么回归用的输入必须固定、必须进版本库?
常见误区
- 「校验和不同就是不同的模型」——哈希判同一性,判不了差异大小。
- 「余弦高就说明变化小」——v3 余弦 0.986 但每个参数都改了。
- 「新版本比旧版本好」——好坏只在具体数据上成立,升版本不等于升级。
自测三问(点开对答案)
1. 模型管理要回答的四个问题是什么? —— 线上是哪个版本、它用什么数据训的、比上一版好在哪、出问题能不能快速回滚。 2. 为什么模型版本号要带数据版本? —— 因为数据换了模型就等于换了,光看模型版本分不清两次训练差在哪。 3. 回归门禁的输入集为什么必须固定? —— 换一批输入,历史结果就没法横向比较,门禁失去意义。模型管住了,但它的原料——数据集——更容易出问题。下一节讲数据管理。
- ☐ (本节完,下一节 10.1.2 流程2:数据集的管理和维护)