系列目录 · 数值与量化 · Read in English
“为了防止除零,我们加了 epsilon。”大家听得很安心。另一个人看着量化常量问:“它量化以后是多少?”房间安静了一下。
epsilon 在浮点公式里很小,是一种保护;到了固定刻度的整数域里,很小也可能意味着不可表示。标准化的难点,就藏在这些看起来最温柔的常数里。
一、归一化先统一成“每组 M 个数”
对某组输入 x,使用总体方差:
1 | mu = sum(x_i)/M |
InstanceNorm按样本、通道分别统计空间元素;GroupNorm把一组通道及其空间元素一起统计。对每个样本分别处理(或批次大小为 1)时,若使用通道优先输入,可将工作形状整理为 [G,(C/G)*H,W],沿后两轴归约。InstanceNorm可视作组数等于通道数的特例。
历史实现采用共同处理逻辑,先检查静态形状、组数整除、正且有限的 epsilon及量化类型,再构造均值、方差统计、加 epsilon、倒平方根、中心化和乘法,最后处理可选仿射参数。支持范围在当时还明确限制了某些批次和秩条件。公开表述应承认这些边界,而不是把一个有限接入写成通用全覆盖。
二、为什么每个中间量都要单独分配尺度
输入、均值、中心化值、方差、倒标准差和归一化结果,不在同一个数值范围。
中心化值 x-mu可能比单个输入相对零点的幅度更大;方差使用平方单位;倒标准差在方差接近零时很大;最终归一化结果又可能回到相对可控的范围。给它们全部复用输入尺度,相当于让长度、面积和倒长度共用一把刻度尺。
历史实现为这些中间量建立不同量化类型,部分使用更宽存储。这里最值得读的不是某个具体位宽,而是类型规划背后的职责:每个中间类型必须解释自己的范围来自哪里、精度损失在哪发生、是否可能饱和。
一个实用的检查顺序是:先确定数学范围,再确定存储范围,再选尺度,最后推导计算系数。反过来先挑一个“看起来够用”的 scale,很容易在后面靠特例修补。
三、方差上界从哪里来?
若每个输入都在闭区间 [L,U],令区间宽度 D=U-L。总体方差满足:
1 | variance <= D^2 / 4 |
可以这样理解:把数值推向两个端点最容易制造分散,而两端各占一半时达到这个上界。形式上,从 (x-L)(U-x)>=0取期望,可得方差不超过 (U-mu)(mu-L),后者最大为 D²/4。
历史实现用输入存储范围乘输入尺度构造跨度,再用这一上界估计方差范围。该界不依赖校准样本,比较保守;但它假设输入实数确实处于所声明的表示范围,且统计量是相应的总体方差。若硬件统计模式返回别的定义,编译器和执行单元仍需另行核对。
保守范围保护溢出,也会牺牲小方差分辨率。若把整个大范围压进有限个整数刻度,接近零的差别可能被压平。这就是稳定性与精度的第一场拔河。
四、让 epsilon 至少占一个整数刻度,会改变什么?
设方差尺度为 s_v,整数 epsilon为 e:
1 | e = max(1, round(epsilon / s_v)) |
这样可以避免量化后 epsilon变成零,但真正参与计算的是 epsilon_effective。若 s_v远大于原 epsilon,两者可能明显不同。
教学例子:希望 epsilon为 0.000002,而方差刻度是 0.00008。最近舍入会给出 0;强制至少为 1后,实际使用 0.00008。保护仍在,但小方差区域的缩放行为改变了。
这不是“加一就修好了”的故事。正确做法是把有效 epsilon纳入误差分析和测试报告,尤其检查几乎常量的输入。对于严格要求框架数值一致的场景,可能需要更宽方差类型、不同尺度、分段表示或保留更高精度统计。
历史实现确实兼顾方差范围与 epsilon表示,并由有效 epsilon推导倒标准差上界。因此能写的是“显式处理不可表示问题”;是否满足某个模型的误差预算,仍需要数值实验。
五、倒标准差和最终结果的范围可以推导
由于方差非负,倒标准差满足:
1 | 1/sqrt(variance + epsilon_effective) |
这个界有助于给倒平方根输出分配尺度。若某条近似实现可能产生负方差或非预期舍入,就需要在更早阶段解释,而不是假装公式自动保证实际整数值非负。
对 M个数、总体方差且非退化的精确情况,标准化后单个元素绝对值不超过 sqrt(M-1)。推导思路是:中心化值总和为零;若某个值为 a,其余 M-1个值总和为 -a,平方和至少为 a²/(M-1)。因此总体方差至少为 a²/(M-1),得到该上界。加正 epsilon只会增大分母。
M=1时,精确中心化结果为零,需单独处理或给保守范围,不能机械使用一个零 scale。历史中也对小归约长度做了保守处理。
但要划清边界:这些是实数公式的界。均值被量化后,中心化和为零的性质可能不再精确;倒平方根近似也会引入误差。所以数学上界是类型规划的起点,整数实现仍应留出分析或验证空间。
六、仿射参数可选,并不意味着末端类型可选
标准化常接 gamma*y+beta。四种组合都需要处理:两者都有、只有 gamma、只有 beta、都没有。
当两者都没有时,归一化内部类型可能与原输出不同,仍然需要重量化。只有 gamma时,乘法可直接产出目标类型;若后面还有 beta,保留更宽中间范围可能避免过早截断。历史测试分别覆盖这些结构分支,并检查无仿射时的重量化存在。
参数读取也应验证长度与有限性。组参数展开成通道参数时,必须明确“一个组的参数重复给哪些通道”。直接将原始字节按浮点解释,与按其实际存储格式读取,是两个完全不同的操作。历史 canonicalize修正了常量读取与参数长度检查,这类细节足以让一个数学等价重写在工程里失效。
七、回归测试要向“几乎没变化”进攻
| 数据或属性 | 待执行检查 | 容易揭示的问题 |
|---|---|---|
| 完全常量输入 | 输出中心化部分接近零 | epsilon、均值量化、零点 |
| 极小方差 | 对照有效 epsilon | 稳定项量化后过大 |
| 单个离群值 | 范围与饱和 | 归一化上界及中间位宽 |
| 不同组数 | 统计集合正确 | shape整理与归约轴 |
| 可选仿射四组合 | 最终类型和数值 | 末端重量化缺失 |
| 非法参数 | 非有限常量、不可整除组数 | 尽早诊断 |
| M=1与大 M | 特例和累加范围 | 零尺度、统计溢出 |
性能上,均值与方差可能需要多次读输入,中心化和乘法还会产生全尺寸中间量;倒平方根则通常只作用于每组统计量。优先优化哪个阶段,应看各阶段的数据量,而不是看公式里哪个函数最吓人。融合统计、复用读取、分块处理都是可评估方向。
标准化最值得警惕的并不是大数,而是那些以为自己小到不会惹事的数。让 epsilon的实际表示、每个中间量的单位和范围都出现在设计说明里,调试就从猜测回到了可核对的算术。
八、方差相同,计算它的路线也可能不同
常见方差表达有两种:先减均值再平方求平均,或者用平方的平均减去均值的平方。在精确实数中它们等价;有限精度下,当输入整体很大、波动很小时,后一种形式会用两个接近的大数相减,可能损失有效精度。前一种则需要中心化数据和额外访问,资源代价可能不同。
历史实现使用带统计种类的归约表达方差,因此不能仅从上层节点名推断执行单元采用哪一种具体算法。公开讨论可以指出这项契约需要核对,但不应擅自宣布内部硬件用了某种稳定算法。IR表达“方差”与实现“怎样计算方差”,是两个层次。
一个有辨别力的教学测试是给所有输入加同一个常量偏移。理想标准化在不考虑仿射和 epsilon数值误差时,对整体平移具有相应不变性:中心化结果和方差保持不变。但量化输入范围可能因此变化,所以要在同一可表示范围内设计测试,并明确是否同时改变量化尺度。否则测试失败可能只是输入重新量化导致,不能直接归罪于方差算法。
再把所有偏差乘一个正系数,若 epsilon相对方差可以忽略,归一化结果应近似不变;当 epsilon不可忽略时,这种缩放不变性本来就不严格成立。这个对比恰好可以观察有效 epsilon是否过大:小方差输入被显著压低,而大方差输入影响较小。仍需通过参考计算区分预期效应与实现错误。
性能评估也应分开统计张量和特征张量。每组均值、方差、倒标准差的数量与组数相关,中心化和最终输出的数量与总元素数相关。把倒平方根实现优化一倍,未必改善由全尺寸中间读写主导的总时间;反过来,在组非常多、每组很短时,统计与函数近似开销又可能占较大比例。
因此标准化优化的第一张图最好画数据规模与生存期,第二张图再画算子依赖。先找到反复被读写的大张量,再决定融合、缓冲区复用或更精细尺度是否值得。
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !