作者:京东AI研究院 张建浩
炼丹师在转换模型的时候,经常会发现给转换前后的模型输入同样的图片,模型结果有微小的差别。其中的原因有数值算法的误差、不同 jpeg 解码库产生的结果不同等等,也有不同框架内部对某些算子的实现差异。
在给 ONNX 贡献 Resize 算子的 spec 的时候,我发现 Resize 是一个突出体现了框架实现差异的算子——多种 Resize 类型、不统一的超参数、将错就错的历史遗留 bug 和其它极易被忽略的问题集中在一起,导致几乎每个框架的 Resize 操作的结果都有差异,而 ONNX 是一个神经网络模型的中间格式,它应该尽量保留原始框架的算子的语义。经过查看相关论文和各种框架的源代码,我分析和总结了 Resize 操作众多的实现方式。最终为 ONNX 贡献了一个较为完善的、标准化的 Resize 算子的 spec,它包含多个(基本)正交的参数,TensorFlow 1.x、TensorFlow 2.x、PyTorch、OpenCV 的 resize/interpolation 方法都可以用这个算子 100% 无损的表达。本文将简单介绍各种 resize 操作的共同流程,并分析是哪些因素引起了不同框架 resize 操作的不同。
多维 tensor (例如二维图像)的 resize 操作是用多个在一维 tensor 上进行的 resize 操作组合出来的,所以我们只讨论一维 tensor 上的 resize 操作,经过分析各个框架的源代码,我发现它的流程可以总结如下:
1
先讨论w和f,w(i)是第 i 个像素点的坐标,乍一看, w(i)完全可以等于i本身,其实没有这么简单。例如一个长度为 3 的 tensor,如果第i个像素点的坐标等于i本身,那么三个像素点在tensor 中的位置就如下图中最左边的样子