TT Lab
开始
学习 学习路径 课程

AI瘦身失败事件

图优化改变了什么 — 数一数节点

在 TT Lab 中继续学习

一句话总结

onnxruntime 在打开会话时会 重写 计算图。哪些内容被改到什么程度,由优化级别决定,其结果可以导出成文件,数一数节点 就能确认。

为什么需要它

在比较量化前后速度的实验中,数字怎么都对不上。同一个模型、同一台机器,每次测量结果都不一样,有时量化后的反而更慢。

原因常常出在优化级别上。如果一次测量用的是默认值(全部开启),另一次因为某种原因关闭了优化,那这个比较测的就不是量化,而是优化。更常见的事故是把优化后的文件保存下来去部署——这个文件离开生成它的环境之后,可能就打不开了。

四个级别分别做什么

Graph optimizations 文档把级别分成四个。

这些说明是文档上的话,实际能做到哪一步,由模型和机器决定。所以与其读文档,导出来数一数 更快也更准确。

导出并数节点的方法

给 SessionOptions.optimized_model_filepath 指定一个路径,打开会话时重写后的计算图就会保存到该路径。用 onnx.load 打开这个文件,数一数节点,发生了什么就一目了然。

원본                 Add(상수,상수) MatMul Add Relu Identity Mul   노드 6개
ORT_DISABLE_ALL      Add MatMul Add Relu Identity Mul             노드 6개
ORT_ENABLE_BASIC     Gemm Relu Mul                                노드 3개
ORT_ENABLE_EXTENDED  FusedGemm Mul                                노드 2개

这里三件事同时可见。只对常量做加法的节点消失了,结果变成 initializer 留了下来。Identity 可有可无,所以被删掉了。MatMul 后面的 Add 被合并成一个 Gemm,到下一个级别又把 Relu 也吞进去,变成 FusedGemm。

节点减少了,并不意味着答案会变。把同一个输入放进四个级别,比较结果就能确认。只是 float32 的有效位数只有 7 位,融合后的内核如果改变了乘法的顺序,最后一位可能会有抖动。所以要问的不是“是否完全相同”,而是 先定好容差,看是否在容差之内。

在现场相遇的样子

第一,部署优化后的文件。导出的文件小、节点少,让人觉得“发这个就行了”。但这个文件里有 com.microsoft 之类的非标准域节点。只认识标准算子的其他运行时打不开它。ORT 自己在保存时也会发出警告,提醒只能在相同环境中使用。

第二,onnx.checker 让这个文件通过了。因为检查器把不认识的域当作某人的扩展而放行。所以用“检查器通过”作为可移植的依据,抓不到这个事故。依据应该是 节点的域列表。

第三,没有对齐比较的基线。要测量量化的效果,两次测量必须处于同一优化级别。把基线定在 ORT_DISABLE_ALL,就不会抖动。而真正要上线的数字,要用与线上相同的级别另外测量。

第四,把节点数当成性能。节点减少不一定会变快。像这台 Mac 这样夹着模拟层,时间最多会抖动到两倍。所以这个实验不测时间——只判定 结构发生了怎样的变化 和 答案是否相同。时间要在与线上相同的机器上另外测量。

实际工作中真正重要的事

下一项实验要做什么

把常量折叠、可有可无的节点、会被融合的三个节点汇集到一个计算图中亲手搭建,再一步步扩展工具 optlevel.py。把四个级别全跑一遍,数出节点并排摆放,写下每个级别消失了什么、新增了什么;用评分器定的随机种子,把同一个输入放进四个级别,测量答案是否相同。最后根据导出文件的域列表,判定能否移植。评分器每次都会用不同的形状、权重和随机种子构建自己的计算图,真正运行你的工具并核对答案。