|
| 1 | +--- |
| 2 | +title: "为什么蒸馏后的扩散模型反而更好量化?" |
| 3 | +date: 2026-03-23 16:15:00 +0800 |
| 4 | +categories: ["ai-auto-note", "quantization"] |
| 5 | +tags: [diffusion-models, quantization, distillation, generative-models] |
| 6 | +math: true |
| 7 | +--- |
| 8 | + |
| 9 | +最近在量化文生图模型时,我碰到一个很反我直觉的现象:在相近的量化配置下,蒸馏模型比 `base model` 画质下降更不明显。 |
| 10 | + |
| 11 | + |
| 12 | +我原本觉得蒸馏把原本几十步的采样压缩成几步,看起来像是把同样的事情做得更激进了;如果一步里要完成更多事情,内部表示似乎应该更“sharp”,也就更容易出现 outlier,更难被低精度近似。 |
| 13 | + |
| 14 | +这个直觉实际上是把两件事混在了一起: |
| 15 | + |
| 16 | +- 采样轨迹是不是更短 |
| 17 | +- 权重和激活是不是更容易被量化近似 |
| 18 | + |
| 19 | + |
| 20 | +## 蒸馏做了什么 |
| 21 | + |
| 22 | +如果先看普通的 diffusion training,模型做的事情其实很朴素:给它在第 $t$ 个时间步上的带噪样本 $x_t$($x_0$ 是原始图像或原始 latent)和当前的 `timestep`,让它学会预测噪声 $\epsilon$、$v$,或者别的等价目标。也就是说,`base model` 学的是**单步怎么去噪**。 |
| 23 | + |
| 24 | +最常见的写法里: |
| 25 | + |
| 26 | +$$ |
| 27 | +x_t = \alpha_t x_0 + \sigma_t \epsilon, \quad \epsilon \sim \mathcal{N}(0, I) |
| 28 | +$$ |
| 29 | + |
| 30 | +这里的 $\mathcal{N}(0, I)$ 表示标准高斯噪声,$I$ 是单位矩阵。 |
| 31 | + |
| 32 | +如果采用这类参数化,通常会有 $\alpha_t^2 + \sigma_t^2 = 1$。 |
| 33 | + |
| 34 | +也就是说,$x_t$ 可以看成“原始信号 $x_0$”和“噪声 $\epsilon$”在第 $t$ 步的混合结果。 |
| 35 | + |
| 36 | +$v$ 则通常是 $x_0$ 和 $\epsilon$ 的另一种参数化。一种常见写法是: |
| 37 | + |
| 38 | +$$ |
| 39 | +v_t = \alpha_t \epsilon - \sigma_t x_0 |
| 40 | +$$ |
| 41 | + |
| 42 | +所以不管模型预测的是噪声 $\epsilon$、原图 $x_0$,还是 $v$,本质上都是在学同一件事的不同表达。 |
| 43 | + |
| 44 | +而蒸馏多出来的关键角色是 `teacher`。 |
| 45 | + |
| 46 | +训练时,`teacher` 先按原来的多步采样过程跑出结果,`student` 再去学这个结果。可以很粗糙地理解成: |
| 47 | + |
| 48 | +- `teacher` 负责告诉`student`,原来走很多小步之后,大概会走到哪里 |
| 49 | +- `student` 负责学习,能不能用更少的步数直接走到一个差不多的位置 |
| 50 | + |
| 51 | +所以从流程上看,蒸馏不是把原模型简单“加速”了一下,而是把一部分原本属于**推理阶段**的多步过程,提前搬到了**训练阶段**里。 |
| 52 | + |
| 53 | +最后训练出来的 `student`,学的也不再是“在很多 `timestep` 上都做局部修正”,而是“在少数几个采样点上,直接逼近 `teacher` 多步迭代后的效果”。 |
| 54 | + |
| 55 | +这么理解的话,蒸馏至少做了几件比较明确的事情: |
| 56 | + |
| 57 | +- 把监督信号从真实构造的 target,部分换成了 `teacher` 的输出 |
| 58 | +- 把原来很长的采样轨迹,压缩成了少数几个更关键的 sampling nodes |
| 59 | +- 让 `student` 重点优化少步数场景下的结果,而不是覆盖整条长轨迹 |
| 60 | + |
| 61 | +这样一来,后面真正需要问的问题就不是:蒸馏之后是不是一定更 sharp。 |
| 62 | + |
| 63 | +而是:当模型学的目标从“每一步都做一点局部修正”变成“用少数几步逼近原来很多步的结果”时,它的内部统计会更尖锐,还是反而更规整。 |
| 64 | + |
| 65 | +## "少步数 = 更 Sharp"? |
| 66 | + |
| 67 | +我最开始是这么想的: |
| 68 | + |
| 69 | +- `base model` 用很多步慢慢修 |
| 70 | +- 蒸馏模型用很少几步直接到位 |
| 71 | +- 那蒸馏模型每一步承担的"变化量"更大 |
| 72 | +- 所以它内部的激活和参数应该更极端,也就更难量化 |
| 73 | + |
| 74 | +这个直觉不能说完全没道理,但它其实偷换了一个概念。 |
| 75 | + |
| 76 | +这里的 "sharp" 可能在说两件完全不同的事: |
| 77 | + |
| 78 | +- **生成行为上更果断**:更少的步数去噪成近似的图像 |
| 79 | +- **数值统计上更尖锐**:更大的 dynamic range、更多 outlier、更高的量化敏感性 |
| 80 | + |
| 81 | +前者经常成立,但前者并不能直接推出后者。 |
| 82 | + |
| 83 | +少步数最多只能说明:student 学会了一条更短的去噪路径。它不一定意味着: |
| 84 | + |
| 85 | +- 权重分布更散 |
| 86 | +- activation range 更大 |
| 87 | +- 某些通道更依赖极端值 |
| 88 | +- 对低精度噪声更敏感 |
| 89 | + |
| 90 | +所以蒸馏并不意味着模型会更好量化还是更难量化。 |
| 91 | + |
| 92 | +## ai 给出的一个合理的解释 |
| 93 | + |
| 94 | +### 1. 蒸馏学到的更像是 teacher 诱导出来的一类稳定映射 |
| 95 | + |
| 96 | +蒸馏时,`student` 学的首先不是直接从数据分布里自己摸索所有去噪路径,而是在少数几个采样点上,尽量逼近 `teacher` 已经给出来的一类输出映射。 |
| 97 | + |
| 98 | +这和从头训练一个 `base model` 不太一样。`base model` 需要自己去覆盖整条采样轨迹;而蒸馏模型更多是在少数几个采样点上,尽量逼近 `teacher` 已经算出来的结果。 |
| 99 | + |
| 100 | +如果 `teacher` 给出的目标本身比较稳定,那么 `student` 学出来的表示就更有可能更规整一些。这里想表达的不是“它一定会更集中”,而是蒸馏这件事本身会把训练目标变得更受约束。 |
| 101 | + |
| 102 | +从部署视角看,这种“更受约束”的训练目标,可能会带来几件量化友好的事情: |
| 103 | + |
| 104 | +- 参数和激活分布更集中 |
| 105 | +- 对少数极端通道的依赖下降 |
| 106 | +- 对小数值扰动更不敏感 |
| 107 | +- calibration 样本更容易覆盖真实使用区间 |
| 108 | + |
| 109 | +### 2. 蒸馏把长采样链路里的“过渡态”折叠掉了 |
| 110 | + |
| 111 | +典型的 `base model` 会在 20 到 50 步里逐步修正噪声,内部会经过很多中间态。对非量化模型来说,这些中间态没问题;但对量化模型来说,它们往往意味着更复杂的统计变化: |
| 112 | + |
| 113 | +- 不同步的 activation range 差异更大 |
| 114 | +- 某些 step 对高幅值通道特别敏感 |
| 115 | +- layer 的分布会随 `timestep` 明显漂移 |
| 116 | + |
| 117 | +蒸馏模型把原本长链路中的一部分中间过程折叠掉之后,未必会让每一步都更“剧烈”,也可能只是减少了很多原本必须经过的中间态。这样一来,不同 step 之间的统计变化也更容易变小。 |
| 118 | + |
| 119 | +### 3. 量化误差在 `base model` 里更容易累计 |
| 120 | + |
| 121 | +`base model` 的优势在于可以多次微调,但代价是量化误差也会被反复注入。哪怕每一步只多出一点 bias,累积二三十步之后,最后的 latent 偏移也可能已经足够影响画质。 |
| 122 | + |
| 123 | +蒸馏模型虽然单步任务更重,但总共只走少数几步,误差注入次数本身就少得多。于是最终结果经常是: |
| 124 | + |
| 125 | +- 单步误差不一定更小 |
| 126 | +- 但总误差更不容易滚大 |
| 127 | + |
| 128 | +这里更像是在说整条采样链对量化误差更不敏感,而不是单次网络调用本身就一定更容易量化。 |
| 129 | + |
| 130 | +| 模型类型 | 步数 | 量化误差累计 | |
| 131 | +| --- | --- | --- | |
| 132 | +| `base model` | 20 到 50 步 | 每步很小的 bias,累计多次 | |
| 133 | +| 蒸馏模型 | 1 到 4 步 | 只累计少数几次 | |
| 134 | + |
| 135 | +所以在最终图像质量上,蒸馏模型往往会显得更抗量化。 |
| 136 | + |
| 137 | +### 4. 蒸馏模型通常只需要在更窄的操作域里表现稳定 |
| 138 | + |
| 139 | +很多蒸馏模型并不是在“所有可能配置”上都追求最优,而是在一个更明确的部署域里训练出来的,比如: |
| 140 | + |
| 141 | +- 固定或很少的采样步数 |
| 142 | +- 有限的 `CFG` 区间 |
| 143 | +- 特定分辨率或模型版本 |
| 144 | +- 更少的 scheduler 变化 |
| 145 | + |
| 146 | +操作域越窄,模型越不需要为很多极端 case 预留表达能力。对量化来说,这意味着: |
| 147 | + |
| 148 | +- 激活支持集更小 |
| 149 | +- 极端分布更少出现 |
| 150 | +- calibration 数据更容易真正覆盖部署分布 |
| 151 | + |
| 152 | +这也是为什么很多蒸馏模型在“目标场景里更稳”,但并不代表它们在更宽广的设置下也同样稳。 |
| 153 | + |
| 154 | + |
| 155 | +## 结尾 |
| 156 | + |
| 157 | +所以,真正需要警惕的不是“蒸馏模型是不是更 `sharp`”,而是我们是不是把两种不同的 `sharpness` 混在了一起: |
| 158 | + |
| 159 | +- 一种是生成轨迹上的“更快、更果断” |
| 160 | +- 一种是数值表示上的“更尖、更难量化” |
| 161 | + |
| 162 | +前者确实常常成立,但它并不自动推出后者。 |
| 163 | + |
| 164 | +更准确地说,“蒸馏模型量化后往往更稳”应该被理解成一个部署层面的现象:它不一定说明蒸馏模型本身天然更容易量化,却很可能说明它们在目标采样路径上更规整、更少犯错,也更不容易把量化噪声一步步放大。 |
0 commit comments