关于自动融合模块精度一致性的说明【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾Ascend芯片的轻量级、解耦式组件集合旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion结论自动融合模块无法保证与 Eager 模式下手写算子的输出在二进制上完全一致但通过保守的精度策略可保证精度不差于 Eager 模式。本文说明其原理与适用边界并给出希望继续使用自动融合时的处理路径。一、精度差异的根本来源1.1 代码生成路径不同Eager 模式每个算子由人工预先编写、编译为固定的二进制 kernel。自动融合通过 codegen 技术在运行时JIT生成算子代码。即使不做任何融合同一语义的算子自动融合生成的代码与手写 kernel 也不是同一份源码。源码不同是一切数值差异的起点。1.2 源码差异如何演变为数值差异同一算子的两份不同源码至少会在以下四个层面引入可观察的末位差异升精度策略不同为保持 fp16 下的数值稳定性算子内部常把中间计算提升到 fp32。是否升、在哪一步升、何时降回手写实现与 codegen 实现的选择不一定一致。指令选择不同同一语义可以用不同指令实现。例如Tensor * Scalar先 broadcast scalar再走标准乘法指令直接使用muls类的标量乘指令。两者数学上等价但在硬件上走的是不同的乘法单元舍入行为不完全一致。切块策略与 Reduction 累加顺序不同浮点加法不满足结合律(ab)c与a(bc)的结果在最低位通常不同。reduce、matmul、layernorm等计算的归约顺序由切块策略和并行度决定而切块策略本身在自动融合与手写算子之间从硬件资源约束上不可能一致。展开来说在相同大小切块下一个融合 kernel 内部要承担多个算子的中间计算单步 UB 占用会高于单算子 kernel。而手写单算子为了降低搬运开销往往会把切块尽量做大用满 ub——这种切块尺寸在融合场景下可能放不下。算法实现不同同一数学函数可以用不同算法逼近。例如rsqrt、div常采用多轮迭代逼近实现迭代轮次、初值选取不同末位误差就不同。1.3 小结源码不同 → 在升精度、指令、累加序、算法等任一维度产生差异 → 最终输出在二进制位上不一致。这是原理层面的限制不是可通过工程对齐消除的 bug。二、可以保证二进制一致的场景精度差异来源于计算。对纯搬运类算子不做任何数学运算只重排数据broadcastconcat/splittranspose/reshape/slice这类算子理论上可以做到二进制一致。但这类场景的融合收益通常很小。自动融合的收益主要来自多个计算算子融合消除中间结果的读写多个计算 单个搬运的融合计算与访存 overlap。纯搬运融合在真实业务中占比低因此能保证一致的场景与值得融合的场景交集很窄。三、自动融合提供的保证虽然与手写算子之间不保证二进制一致自动融合在精度与可复现性两个方向上提供以下保证3.1 融合区域内部强制 fp32 累积自动融合在生成的融合 kernel 内部默认将所有中间计算统一提升到 fp32只在融合块的输入/输出边界保留原 dtype。以损失部分计算性能的代价换取更高的中间精度。3.2 精度不差于 Eager 模式Eager 模式下每个算子独立执行算子与算子的中间结果必须按 tensor 原 dtype如 fp16/bf16落盘再由下一个算子读入。这意味着Eager 模式在每个算子边界都发生一次 fp16/bf16 截断自动融合把一串算子合并为一个 kernel只在融合块边界截断块内全程 fp32。因此自动融合的累积舍入误差上界 ≤ Eager 模式下对应算子链的累积误差。即精度不低于 Eager 模式。3.3 自身的确定性可复现性本节讨论的是自动融合自身的输出确定性不涉及与 Eager 模式的对比。与 Eager 的关系参见 §1、§2、§3.2。自动融合的确定性可以分两层来看编译确定性相同的编译期输入 → 相同的 kernel 二进制。编译期输入包括模型结构、shape、dtype、自动融合的版本与编译选项。执行确定性相同的 kernel 二进制 相同的运行期输入 → 相同的输出。运行期输入包括张量数值、芯片型号等。两层叠加即可得到端到端的保证编译期输入与运行期输入都不变时多次运行的输出在二进制上完全一致。也就是说§1.2 列举的差异来源升精度策略、指令选择、切块/累加顺序、算法实现在自动融合自身的两次编译运行之间是完全一致的——它们只在自动融合 vs 手写算子之间产生分歧。自动融合本身不引入额外的不确定性。四、PyTorch Inductor 的相关说明PyTorch Inductor 社区不保证精度的二进制一致其逻辑与自动融合一致。摘录原文如下4.1 PyTorch 文档《Numerical accuracy》涉及浮点结合律与 bit-exact原文PyTorch is not guaranteed to produce bitwise identical results for floating point computations that are mathematically identical.floating point addition and multiplication are not associative, so the order of the operations affects the results.来源https://docs.pytorch.org/docs/stable/notes/numerical_accuracy.html4.2 Edward Yang《Ways to use torch.compile》2024作者为 PyTorch 核心开发者文章在 Improve training efficiency on a small-medium scale 一节中说明torch.compile与 eager 的数值关系Unfortunately, the compiler does not guarantee exact bitwise equivalence with eager code; we reserve the right to do things like select different matrix multiply algorithms with different numerics or eliminate unnecessary downcast/upcasts when fusing half precision compute together.来源https://blog.ezyang.com/2024/11/ways-to-use-torch-compile/4.3 PyTorch 文档《torch.compile Troubleshooting》Accuracy Debugging 一节中关于下游编译器数值表现的说明the reason we need this is downstream compilers will codegen code whether its Triton code or the C backend, the numerics from those downstream compilers can be different in subtle ways yet have dramatic impact on your training stability.来源https://docs.pytorch.org/docs/stable/torch.compiler_troubleshooting_old.html五、对照表维度Eager 手写算子自动融合源代码人工固定JIT 生成二进制一致性基准不保证计算类/ 可一致纯搬运类中间计算精度受限于算子间 dtype常为 fp16融合块内部 fp32累积误差上界基准≤ Eager 基准性能基准显著优于基准六、使用建议希望继续使用自动融合时的处理路径若用户关注的是数值正确性模型精度、收敛性、下游业务指标自动融合在精度上无劣势、在性能上有显著收益可直接启用无需额外处理。若用户有与 Eager 严格对齐的诉求又希望继续使用自动融合的性能收益是十分有挑战的参考以下策略组合使用。6.1 PyTorch-Inductor 场景状态和使用建议与 Eager 模式严格对齐涉及 Inductor 融合前的图处理与融合阶段两部分。Inductor 对多数相关流程未提供原生关闭开关需通过 patch 或修改源码实现。融合前处理Inductor 在自动融合前会执行以下可能引入数值差异的流程Inplace 算子函数化例如relu_→relu函数化后的 Kernel 实现与 Eager 可能存在差异。Decompose将复杂算子分解为基础算子以扩大融合范围与单算子实现相比算法有差异。FX 图 Pass主要为 pattern 替换类融合 Pass涉及 Kernel 替换。前反向切图基于自动重计算的切图策略重计算的融合 Kernel 与 Eager 模式可能存在差异。建议措施Inplace 算子函数化为融合前提、非功能项无法关闭。实践中建议仅实现 Inplace 版本通过自动函数化减少 Kernel 差异带来的数值差异。DecomposeInductor 原生不提供关闭选项需通过 patch 或修改源码实现。FX 图 PassInductor 不提供全部关闭的选项且每个版本生效的 Pass 可能不同需根据执行时实际情况依次关闭。前反向切图Inductor 使用最大流最小割算法切图会引入重计算可通过配置custom_partitioner_fn替换为无重计算的切图策略。自动融合Inductor 融合阶段因重新生成执行 Kernel 引入数值差异涉及的融合类型如下融合类型影响二进制精度数值差异来源用户可配置Reduction 类是累加顺序不同需 patch 或修改源码Pointwise 类是类型提升规则、指令映射、迭代算法差异需 patch 或修改源码MM/FA 模板类是算法实现差异需 patch 或修改源码使用须知如追求与 Eager 严格对齐需关闭上述所有融合类型。Inductor 未提供相应配置项需通过 patch 或修改源码实现。建议显式开启TORCHINDUCTOR_EMULATE_PRECISION_CASTS避免 Lowering 过程中的 Cast 消除优化引入数值误差。6.2 TensorFlow 场景状态和使用建议通用优化TensorFlow 场景默认使用 GE 作为图编译器后端。GE 自带的融合 pass 与硬件无关优化 pass例如常量折叠、等价公式变换都会导致 kernel 源代码变化使二进制精度一致无法保障。建议措施将图编译优化等级设为 O0仅保留功能类优化。O0 等级下仍会执行功能相关与静态 shape 相关优化。可打开 DUMP 图开关观察 GE 中实际生效的 pass对照 1.2 节判断对精度的影响若有影响可通过手工调整脚本规避相应优化难度较高。自动融合自动融合目前支持以下类别算子的融合默认策略与可控性如下融合类型默认状态影响二进制精度用户可配置elemwise含 broadcast开启是否reduce关闭是是concat关闭否是slice关闭否是gather关闭否是transpose关闭否是默认关闭的融合可通过环境变量显式开启以 concat 为例export AUTOFUSE_FLAGS--enable_autofusetrue;--autofuse_enable_passconcat使用须知elemwise 融合暂不支持关闭。该类融合既是二进制精度差异的主要来源也是融合收益的主要来源关闭后整体收益将显著降低。如确有关闭需求可提交 issue 反馈。默认关闭的融合属于实验特性开启后可能出现功能异常或性能劣化建议在目标网络上实测后再决定是否保留。【免费下载链接】graph-autofusionGraph-autofusion 是一个面向昇腾Ascend芯片的轻量级、解耦式组件集合旨在通过自动融合技术加速模型执行。 目前已开源 SuperKernel 组件未来将持续开放更多自动融合相关模块。项目地址: https://gitcode.com/cann/graph-autofusion创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考