🧠 第一章 AI入门:认识人工智能模型训练
📄 一句话概括

量化把 32 位权重压到 4 位,体积从 32 KB 降到 4 KB,输出偏差从 0 涨到 15%。 蒸馏压的不是体积是结构:60 条样本时软标签比硬标签高 +0.0963,400 条时只剩 +0.0475。 一个省空间,一个省容量,省掉的都不是「聪明」。

📝 本节和前面几节的关系

上一节讲完训练和评估。 这一节讲两个部署阶段的技术——它们都在回答同一个问题:一个大模型,怎么塞进小机器里。

一、量化:用更少的位表示同一个数

一个权重在 fp32 里占 32 位,能表示 40 亿个不同的值。 但训练完之后,绝大多数权重都挤在 0 附近,用 32 位去记它们,大部分位是浪费的。 量化的做法是:找一个缩放系数 s,把每个权重除以 s、四舍五入成整数,存成 8 位或 4 位; 算的时候再乘回 s。信息损失全在「四舍五入」那一步。 Demo 里 128×64 的权重矩阵:fp32 是 32 KB,fp16 16 KB,int8 8 KB,int4 4 KB。体积严格按位宽线性缩。 但误差不是线性的:fp16 只偏 0.02%,int8 偏 0.83%,int4 一下跳到 15.08%。 位数每少一半,误差先小涨,过了 8 位那道坎就陡增。

二、为什么大家都停在 4 位附近

因为 4 位只剩 16 个刻度(有符号的话 -8 到 7)。 权重分布是钟形的,中间挤了一大堆数,16 个刻度分下来,每档跨的距离就很粗了。 从 8 位掉到 4 位,误差从 0.83% 跳到 15%——18 倍。而体积只省一半。 这笔买卖在 4 位以下就不划算了:再往下的 2 位、1 位,模型基本就不说人话了。 工程上还有几个补丁能让 4 位可用:分组量化(每 128 个数一组,各组一个缩放系数)、 只量化权重不量化激活、把少数「重要」权重留在高精度。 这些合起来叫「量化感知训练」或 GPTQ/AWQ 这类方法。 有一个说法要纠正: 量化不是「把模型变小变笨」,是「用更少的位重新编码」。 4 位模型在很多任务上的分数跟 fp16 只差一两个点, 比 int4 那个 15% 的输出偏差看起来好得多——因为输出偏差是逐元素的,而下游指标对小幅抖动不敏感。

三、蒸馏:让小学生学老师的「把握程度」

量化是压缩一个模型,蒸馏是训练两个模型:一个大的当老师,一个小的当学生,让学生模仿老师的输出。 关键在那句「模仿输出」——不是模仿「答案」,是模仿概率分布。 老师看到一张图,说「这是猫 0.9、这是狗 0.08、这是兔子 0.02」;硬标签只说「这是猫」。 前者的信息量大得多。 那 0.08 和 0.02 里藏着什么?藏着「猫和狗长得像、跟兔子不像」这件事。 这叫「类间距离」,是硬标签里完全没有的知识。 Demo 里两个学生模型结构完全一样(12 维输入、8 个隐单元、5 类),只换训练信号:60 条样本时, 硬标签 0.6833,软标签 0.7741,差 +0.0907。

四、但蒸馏不是无条件有效

同一张表往下看:样本加到 400 条,差值只剩 +0.0475;类间相似度低的那一列,甚至出现负值。 道理不复杂:学生自己学得动的时候,老师的软标签就是噪声。数据越多、类别越分明, 学生越不需要老师那点「类间距离」的暗示。 温度那一栏更反常识:T = 1(不升温)反而最好,T 越大越差。 因为这里的老师本身就不尖锐(最大概率只有 0.61),已经够软的东西再升温只会被抹平。 所以温度要跟老师的输出配套:真模型 top1 常常给到 0.99,那种软标签跟硬标签没区别, 才需要 T = 2 到 5 把它摊开。先看老师的概率长什么样,再定温度。 一句话总结两者的分工: 显存不够用量化,算力不够用蒸馏,两个都不够就一起用。但如果学生学不好的原因是「数据不够」, 蒸馏帮不上多少——那得回去补数据。

五、动手 Demo:量化省体积、蒸馏省结构,但蒸馏只在学生学不动时才有用

上半场做量化:把 128×64 的权重矩阵分别压到 fp16、int8、int4,算体积、权重相对误差、 以及同一批输入下输出的相对偏差。 下半场做蒸馏:造一个 5 类任务,让同结构的小模型分别用硬标签和软标签训练,扫样本量和类间相似度, 看差距怎么变。

W = rg.normal(0, 0.35, (128, 64)).astype(np.float32)
x = rg.normal(0, 1, (256, 128)).astype(np.float32)
ref = x @ W
for bits in (32, 16, 8, 4):
    if bits >= 16:
        Wq = W.astype(np.float16).astype(np.float32) if bits == 16 else W
    else:
        q = 2 ** (bits - 1) - 1
        sc = float(np.abs(W).max()) / q
        Wq = (np.round(W / sc).clip(-q - 1, q) * sc)
    err = np.linalg.norm(Wq - W) / np.linalg.norm(W)
    out = np.linalg.norm(x @ Wq - ref) / np.linalg.norm(ref)
    print(bits, W.size * bits / 8 / 1024, err, out)

实跑输出:

--------------------------------------------------------------------
量化省体积、蒸馏省结构——但蒸馏只在学生自己学不动时才有用
--------------------------------------------------------------------
  精度   位宽   权重体积(KB)   权重相对误差   输出相对偏差
  -------------------------------------------------------
  fp32     32           32.0        0.0000%        0.0000%
  fp16     16           16.0        0.0206%        0.0207%
  int8      8            8.0        0.8339%        0.8341%
  int4      4            4.0       15.0847%       15.0150%

  位宽减一半,体积就减一半,这部分是线性的。误差不是:位数每少一半,误差先小涨,跨过 8 位这道坎之后陡增。**这就是大家停在 4 位、再往下模型就开始胡说的原因——不是不想压,是压完不会说话了。**

  蒸馏是另一条路:不动位宽,动结构——让一个小模型去学大模型的输出分布。
  类间相似度   训练样本   硬标签学生   软标签学生      差值
  --------------------------------------------------------
  1.0                60       0.6833       0.7741   +0.0907
  1.0               100       0.7380       0.8120   +0.0740
  1.0               200       0.8275       0.8575   +0.0300
  1.0               400       0.8300       0.8650   +0.0350
  1.6                60       0.6833       0.7852   +0.1019
  1.6               100       0.7380       0.8160   +0.0780
  1.6               200       0.8275       0.8575   +0.0300
  1.6               400       0.8300       0.8900   +0.0600

  60 条样本时差值          +0.0963
  400 条样本时差值         +0.0475
  同一张表,样本从 60 加到 400,差值从 +0.0963 掉到 +0.0475——**蒸馏的收益不是常数,它随样本量衰减。**
  道理不复杂:硬标签只说「答案是第 2 个」;软标签还说「第 3 个也挺像、第 1 个不像」。**后者把类与类之间差多远告诉了学生**,这是硬标签里根本没有的信息。样本少时,这点信息顶得上几十条数据;样本一多,学生自己就看出来了,老师的软标签开始变成噪声。
  类间相似度那两列同理:题目越糊(各类方向越像),软标签越值钱。

  温度   学生准确率   第一条样本的最大概率
  ---------------------------------------
  T=1        0.8180                   0.61
  T=2        0.8160                   0.41
  T=4        0.7960                   0.30
  三个温度差得不多(0.7960 到 0.8180),但**方向是 T 越大越差**——这跟「蒸馏必须用高温」的常见说法反着。
  原因在这里:**这里的老师本来就不尖锐**,不升温时最大概率只有 0.61。已经够软的东西再升温,只会被抹平成「都差不多」,信息就丢了。
  所以温度不是「越大越好」也不是「越小越好」,它要跟老师输出的尖锐程度配套:真模型 top1 常常给到 0.99,那种软标签跟硬标签没区别,才需要 T=2 到 5 把它摊开。**先看老师的概率长什么样,再定温度。**

  诚实交代:这一跑是 12 维、5 类的合成数据,学生只有 8 个隐单元,真实的大模型蒸馏还要处理 logits 对齐、层映射这些工程细节。但**结论的方向是可搬的**——想靠蒸馏救一个小模型,先看它的病是不是「数据不够」,如果不是,蒸馏帮不上多少。

done -> _out_39a.txt

量化那张表:体积 32 → 16 → 8 → 4 KB,严格按位宽折半。 误差 0.0000% → 0.0206% → 0.8339% → 15.0847%。8 位是那条拐点,过了它误差开始按倍涨。 蒸馏那张表的差值从 +0.1019 掉到 +0.0600(400 条样本那一档), 整体均值从 60 条的 +0.0963 降到 400 条的 +0.0475。两个数都在说同一件事:样本越多,蒸馏越没用。 表格里有个细节:同一个样本量下,类间相似度 1.6 那两行差值更大。 题目越糊,学生越分不清,老师那点「谁跟谁像」的暗示就越值钱。 最后一句是诚实交代:这一跑只有 12 维、5 类、8 个隐单元,真实的大模型蒸馏还要处理 logits 对齐、 层映射。能搬走的是「先判断学生的病在哪」这个思路,不是这几个数。

30 秒实验

  1. 把 int4 那档的分组从「整个矩阵一个缩放系数」改成「每 8 个数一个」, 误差会明显下降——这就是分组量化在做的事。
  2. 把学生的隐单元从 8 个改成 3 个,看蒸馏的收益会不会变大。学生越笨,老师越有用; 但学生太笨,学不动,收益又会掉下来。
  3. 把温度从 1 扫到 8,画出准确率的曲线。 你会看到它先平后降——这条曲线就是「温度」这个超参存在的全部理由。

常见误区

  1. 「量化就是用精度换速度,掉点是必然的」。 掉点确实有,但方向不一定是「掉」——某些任务上 4 位模型比 fp16 还稳,因为量化顺带做了正则化。 必须实测,不能推论。
  2. 「蒸馏就是把大模型的知识搬到小模型」。搬过去的是「输出的分布形状」,不是知识本身。 数据里没有的东西,老师也不会,学生更不会有。
  3. 「温度越高,软标签信息越多」。温度太高会把分布抹成均匀分布,信息反而归零。 温度要匹配老师输出的尖锐程度。
自测三问(点开对答案) 1. 为什么 int8 到 int4 的误差是「跳」上去的,而不是「涨」上去的? —— 因为 int4 只有 16 个刻度,权重分布是钟形的,中间密集区域每个刻度要覆盖很宽的值域。 从 8 位的 256 个刻度掉到 16 个,是断崖式变粗。 2. 蒸馏里学生的损失函数和普通训练有什么不同? —— 普通训练比的是「预测分布 vs 硬标签(one-hot)」,蒸馏比的是「学生分布 vs 老师的软分布」。 后者多了「别的类有多像」这层信息。 3. 一个 4 位量化模型和一个蒸馏出来的小模型,什么时候选哪个? —— 瓶颈在显存就选量化(同样结构,体积小一半到四分之一); 瓶颈在计算量就选蒸馏(结构本身就小,每步算得少)。两者可以叠加,代价是精度损失也叠加。

第一章的「基础入门」这五篇讲完了:成本、历史、原理、训练、压缩。 接下来进 1.2 节,把这些原理装进具体的大模型里看。

相关笔记