一个函数名,装不下它的数值契约:非线性与归一化的边界

定义域、近似、精度与归约轴,比函数名字更重要。

Posted by Bruce Lee on 2026-09-20

系列目录 · 工程与交付 · Read in English

“函数名字没写错,输入形状也对,为什么结果差这么多?”

同事把错误样本缩成两个负数。原先看起来只是一点误差的问题,忽然变成了正负号相反。排查到最后,双方实现的竟然不是同一个函数。

非线性运算经常以一个短名字出现在图里:Pow、Sigmoid、Softmax、Norm。真正决定答案的,却是这个名字背后的定义域、公式、轴、常数、近似方式和中间精度。

普通幂与带符号幂,差的不是精度

普通平方满足 (-3)^2=9。一种带符号幂可以定义成:

1
signed_power(x,p) = sign(x) * |x|^p

当 x=-3、p=2 时,它得到 -9。两者都可能在某些文档里被简写成“power”,但数学含义完全不同。

更一般的实数幂还要处理负底数与非整数指数、零的特殊情况以及指数是否为编译期常量。后端看到一个名字相近的硬件功能,不应该直接认为可以替换。

历史规范材料中出现了需要进一步澄清的数学命名与候选映射。这适合写成一个设计问题:在选择硬件路径之前,是否已有一份不依赖实现的函数契约?它不构成任何具体实现存在错误的证明。

Hypot:输出不大,中间量也可能爆掉

计算两个数的平方和再开方,可以直写:

1
sqrt(x*x + y*y)

但当 x、y 很大时,平方可能先溢出;当它们非常小时,平方也可能先下溢。最终结果在可表示范围内,并不保证这条计算路径的中间值安全。

对有限实数,一种教学推导是令 m=max(|x|,|y|),当 m≠0 时写成:

1
m * sqrt((x/m)^2 + (y/m)^2)

这能解释缩放思路,却不是一个自动覆盖无穷、NaN、舍入和所有定点边界的完整生产实现。特别是在整数或量化表示中,除法和缩放还会引入自己的误差与成本。

类似地,输入和输出都是低位宽整数,并不能证明内部累加也只需同样位宽。数值范围分析应沿计算路径进行,而不是从接口类型倒推。

一张 LUT,首先要知道自己近似了哪段实数

对 Sigmoid、GELU 或其他平滑函数,查表和分段近似可以降低计算成本,但它们需要回答几个独立问题:

  • 输入整数如何还原成实数?
  • 表覆盖哪个区间,区间外如何处理?
  • 采样点如何分布,插值采用什么精度?
  • 输出怎样重新量化,在哪一步舍入或饱和?

假设输入采用 x=s(q-z)。改变 s 后,同一个整数 q 对应的实数位置会改变。如果表的索引仍然按旧标尺解释,错误不一定表现为随机噪声,可能表现为整个函数在横轴上被拉伸。

GELU 还存在不同近似表达;ReLU 与有限上下界的 Clip 也不能仅因都做截断就视为相同函数。近似方法应出现在契约或可追踪的配置中,否则数值回归只能看到“变了”,却很难判断变化是否被允许。

周期函数还有一个容易忽略的相位

某些激活包含类似 sin²(αx) 的周期项。此时 α 决定输入的相位尺度,也就是振荡频率;若完整公式还带有 1/α 等系数,才需要另外分析它对幅度的影响。

如果定点内核把实数角度映射到某个整数周期,量化比例、周期归一化和取模范围必须一致。把输入 scale 只应用到线性部分,而没有进入周期项,会产生位置相关的系统误差。

这类问题很适合使用结构化测试:在几个周期内取等距点,包含零点、峰值附近和周期连接处。平均误差可以作为一个指标,但还应观察相位是否偏移、对称性是否保持、连接处是否出现不连续。

Softmax 稳定化,改变的是计算路径

Softmax 在某个轴上计算:

1
p_i = exp(x_i) / sum_j exp(x_j)

对精确实数运算,给同一归约组的全部输入减去同一个常数,不改变结果。常用选择是该组最大值,这样最大的指数输入变为零,有助于控制指数溢出。

“同一组”四个字很重要。若错误归约轴改变了求和分组,或者同一目标组内各元素减去不同的常数,就会改变语义。各组共用一个全局最大值仍可保持精确算术中的结果,但更大的取值跨度可能加剧有限精度下的下溢与分辨率损失。若指数、求和和除法分别量化,中间舍入还会影响概率和及小概率分辨率。

需要验证的性质包括同组平移不变性、元素非负、概率和接近 1,以及一个值明显较大时输出的合理变化。有限精度实现只能在给定误差范围内满足部分性质,应把容差与输入范围写清楚。

“Norm” 是在哪些元素之间求统计量

BatchNorm 在推理时通常可以使用固定统计量,形成通道仿射变换。LayerNorm 则通常对当前输入的一组元素计算动态统计量。RMS 类归一化又省略了均值中心化,采用均方根相关的分母。

它们可能共享乘法、加法、归约和倒平方根的实现组件,却不能因为名字都带 Norm 就互相替代。

以中心化方差为例:

1
2
mean = average(x)
variance = average((x - mean)^2)

average(x*x)-mean*mean 在精确算术中等价的表达,在有限精度中可能发生不同程度的消减误差。若输入具有很大的共同偏置和很小的波动,后者尤其值得仔细评估。

此外还要明确 epsilon 放在哪里、归约哪些轴、是否有 gamma/beta、采用哪种方差约定。一个自定义的多轴 RMS 形式,不应在文档里不加说明地等同于某个框架的标准末轴算子。

位移也需要数学定义

右移一个负整数,到底是在传播符号位,还是补零?希望实现“除以二的幂”时,是向负无穷取整、向零截断,还是采用舍入规则?不同语言表达式和硬件指令的行为不能靠相似名称对齐。

在量化计算里,移位经常与乘数配合实现重定标。单独看移位一位的差异很小,放在多层网络里却可能形成持续偏差。因此测试应包含正负数、恰好在中点两侧的值和饱和边界,而不只是随机的正整数。

规范中尚未明确的位移行为应保持为待验证问题。填上一个“看起来合理”的答案,只会把不确定性传给下一位实现者。

写一份让实现无法误解的契约

一个可用的非线性或归一化说明,至少应该容纳以下内容:

契约部分 应给出的信息
数学定义 公式、参数、轴、定义域和特殊值
数值表示 输入输出量化、内部范围与舍入位置
实现限制 支持的形状、参数是否必须静态、拒绝条件
近似保证 适用区间、误差指标和验证方法
证据状态 设计说明、已有实现、已有测试分别覆盖什么

历史材料的价值,既在于记录已知答案,也在于保留了尚不能从代码和测试确认的细节。公开文章应把这些问题解释清楚,而不是替未知的硬件字段编出一个完整故事。

讨论中,大家最后给那个函数补的第一项不是更密的查找表,而是一句数学定义。因为在双方连“正确答案是什么”都没有对齐之前,提高近似精度只会更准确地算出另一个函数。


返回系列目录 · 上一篇


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 !