太早变聪明的导入器:当代数优化抢在量化信息前面

代数变换时机、量化元数据匹配和常量处理为何互相制约。

Posted by Bruce Lee on 2026-03-03

系列目录 · 数值与量化 · Read in English

“常量除以张量,直接转成带系数的倒数,不是更高效吗?”导入器很热心,模型刚进门就替它做了一次代数整理。到了量化信息核对阶段,另一个模块却在找:“原来的除法去哪儿了?”

这类问题不是优化公式一定错误,而是发生时机可能破坏了其他阶段仍然需要的对应关系。

一、导入、标注和优化,不是可以任意交换的三件事

设模型含有 c/x。在合适的数值域与异常语义下,可以写成 c*reciprocal(x),也可以把 c作为倒数操作的系数属性。

历史导入代码曾对“左边是单元素权重、右边不是”走特殊路径,直接生成倒数形式;一处修复删掉了这条特殊处理,让这类输入先按普通除法表示。

差异证明转换时机被推迟或至少不再在该入口执行。它没有单独提供完整失败用例,因此不能把某种具体量化配置错配当成已复现事实。基于阶段关系,一个合理推断是:过早改变节点种类、输入个数或常量所在位置,会增加量化标注匹配的难度。这是解释设计取舍的假设,仍需沿上下游代码与测试验证。

导入阶段可以先忠实表示模型语义;待身份映射、类型和必要注释稳定后,再由专门优化处理等价变换。这样做不排斥优化,而是给优化一个信息更完整的时间点。

二、数学等价之外,还有“元信息等价”

量化信息可能通过节点名、张量名、输入位置或类型关系绑定。把二元除法变成一元倒数加属性,即便实数函数相同,元信息的承载位置也改变了。

一份完整重写契约至少说明:

1
2
3
4
5
旧结果对应哪个新结果?
旧输入的量化域对应哪个新输入?
常量从张量变成属性后,其尺度是否仍需要保留?
节点来源如何追踪?
异常输入和舍入行为是否保持?

如果这些问题没有答案,早期“简化”可能让后续阶段只能靠特殊条件猜测。特殊条件积累后,导入器、量化器和 lowering互相补丁,最终没有一个阶段能独立说明完整语义。

当 x接近零时,倒数近似、除法实现与量化饱和也可能产生不同边界行为;这属于通用数值风险,不能用实数代数直接证明逐位等价。适用域和异常语义应显式列出。

三、常量矩阵转置:数据换了座位,类型不能留在原地

另一处历史修改与常量左矩阵乘有关。数学上:

1
A * B = transpose(transpose(B) * transpose(A))

在某些目标只方便处理特定常量位置时,这是一条常见的变换思路。若 A是常量,可以在编译时转置其数据,避免运行时做同样工作。

问题是创建一个新的“浮点常量”或只取原常量的存储类型,可能丢失量化元素类型和附带编码。历史修复改为保留原张量元素类型与编码来构造转置后的形状,并更新对应权重数据;量化导入端此前一些特殊处理同时被删除。

差异能够支持“减少对量化导入的特殊处理,并在常量变形处保留类型信息”的观察。它不能自动证明所有共享常量使用场景都安全,也不能证明每一种逐轴量化编码都已正确重映射。

四、保留编码,不等于编码仍然指向正确的轴

假设教学常量 A形状为 [M,K],量化参数沿 M轴逐行定义。转置后 Aᵀ形状为 [K,M],同一组参数现在应对应列轴。

若编码包含量化轴编号,简单复制编码可能保住了数据,却没改轴身份;若编码只存通道参数而另有约定,也必须核对约定在哪更新。

共享使用也是如此。如果一个常量被两个算子使用,原地修改 shape与数据,会同时影响两个使用者。合法重写必须满足单一使用、所有使用者同步变换,或者为当前路径复制常量等条件之一。究竟采用哪一种,要读完整匹配前提;不应只凭几行更新代码就判断安全或不安全。

“保留身份”解决的是元信息不丢的问题,“管理共享”解决的是改动影响范围的问题。两者都重要,不能互相替代。

五、什么时候应该创建新常量,什么时候应该更新旧常量?

创建新常量适合让不同使用者保留各自布局,也便于维持不可变 IR风格;代价是必须完整迁移量化类型、编码、来源和数据。更新旧常量减少身份变化,但更依赖使用关系和变更事务。

可以用如下决策原则:

条件 更容易证明的做法
常量有多个不同语义使用者 为变换路径创建独立常量并完整迁移元信息
所有使用者统一变换 可更新,但要维护全体使用关系
量化元信息依赖旧身份 先建立显式对应关系,再决定复制方式
编码含有轴语义 转置时同时变换编码,而非只改 shape
失败路径可能发生在更新之后 先验证再修改,或提供可回滚构造

这是设计建议,不是对历史实现未读部分的替代描述。它有助于把“我们是不是该保留这个名字”提升为可验证的身份与使用关系问题。

六、阶段顺序的测试应该故意交叉

只测试除法数值,只测试量化导入,只测试矩阵转置,都可能分别通过,而组合失败。建议构造小而有辨别力的流水线测试:

待执行场景 观察目标
常量左除与普通除法 导入后节点身份与输入关系
先量化再规范化、合法的其他阶段安排 是否遵守明确阶段契约
输入输出不同尺度 元信息不能靠相同值蒙混过关
常量左矩阵乘、非方阵 转置后的数据与 shape一致
共享常量两个使用者 不意外修改未参与重写的路径
逐轴量化常量 参数与轴同步迁移
x接近零及边界值 除法与倒数变换的有效域

性能方面,推迟代数变换可能让后续优化拥有更多信息,也可能让早期 IR稍大;是否影响编译耗时需要测量。常量转置把工作移到编译期,可能减少运行时重排,但会带来常量存储或编译内存成本。若后端本来能够通过布局解释避免重排,显式转置未必更划算。

导入器最大的价值未必是第一时间显得聪明,而是把信息完整地交给下一站。等到编译器知道每个数字代表什么、每个常量被谁使用,再聪明一点,往往更容易证明自己没有帮倒忙。

七、给每次重写发一张“元信息迁移单”

可以用一个很小的表记录变换:旧节点有几个数据输入、几个参数输入,产生几个结果;新节点分别是什么;每份量化注释和来源信息迁到哪里。若某一项写不出目标,就先保留原表示,不急于优化。这个做法尤其适合常量被吸收到属性、多个算子被融合、单个算子被拆成子图的场景。

迁移单还应该描述哪些信息可以重新推导,哪些必须保留。shape通常能由输入与操作推导,但校准所得尺度不一定能从 shape恢复;常量数据可以重新排列,但某个名称与外部量化表的对应关系可能无法靠数据内容重建。把可推导与不可恢复的信息分开,能帮助确定重写的安全时间点。

对于矩阵转置,可以逐项做一个小证明:新常量位置 (k,m)的值等于旧常量位置 (m,k);新 shape元素总数不变;量化参数对应的实数保持;右操作数和结果的转置关系使输出索引恢复。只验证元素总数相等,几乎没有证明计算正确;它只排除了一种非常粗糙的错误。

如果原常量已经有多个使用者,迁移单还应列出每个使用者是否参与变换。用名字唯一来推断使用唯一是不成立的,一个 SSA值或权重对象可以被很多节点引用。决定原地更新前,先建立使用关系证据,会比事后追查某条旁路模型为何变坏更直接。

阶段顺序也可以形成明确的前后条件。例如某个优化要求量化注释已经转成类型,就把它写成输入条件;某个后续阶段要求所有参数仍以张量形式存在,就不能在它之前把参数吸成属性。用这些条件检查流水线顺序,能把“换一下 pass顺序试试”的经验操作,变成有解释力的设计调整。

这套方法不会保证每次变换都带来性能收益,但能先保证它没有丢信息。性能评估随后再问:常量预处理增加多少编译工作,减少多少运行时操作,有没有新的数据副本。把正确性迁移和成本比较分两步完成,讨论会清楚得多。


返回系列目录 · 上一篇


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 !