开源背后 | 面对端侧推理引擎的挑战,阿里工程师如何应对?

阿里妹导读:MNN(Mobile Neural Network)已于今年5月7日在 Github 上正式开源。淘宝无线开发专家——陈以鎏(离青)在 GMTC 全球大前端技术大会为你们分享了 MNN 开发、开源中的思考与总结,经过淘宝在移动 AI 上的实践经验,你将会了解移动 AI 的发展情况和应用场景,以及经过端侧推理引擎了解移动/ IoT 深度优化策略。

开源与背景

人工智能从 2006 年开始,迎来了第三次浪潮。随着 AlphaGo 在 2016 年、 2017 年前后打败李世石和柯洁,人工智能完全进入了公众的视野。人工智能大热的背后,是大数据的积累,是深度学习的发展,也是设备算力的提高。与此同时,深度学习的框架也在不断演进 —— 从 Torch、Caffe 到 TensorFlow、PyTorch ,再到更面向移动端的 CoreML、NNAPI、NCNN、MACE 等。淘宝的深度学习推理引擎 MNN 也于 2019 年 5 月宣布开源。前端

MNN 是一个轻量级的深度学习端侧推理引擎,核心解决深度神经网络模型在端侧推理运行问题,涵盖深度神经网络模型的优化、转换和推理。目前,MNN已经在手淘、手猫、优酷、聚划算、UC、飞猪、千牛等 20 多个 App 中使用,覆盖直播、短视频、搜索推荐、商品图像搜索、互动营销、权益发放、安全风控等场景,天天稳定运行上亿次。此外,菜鸟自提柜等 IoT 设备中也有应用。在 2018 年双十一购物节中,MNN 在天猫晚会笑脸红包、扫一扫、明星猜拳大战等场景中使用。算法

开源地址:https://developer.aliyun.com/...后端

MNN 项目从 2017 年开始启动,在经历一年多的开发迭代并经过了淘宝双十一的考验后,于 2018 年末启动开源计划,在历时小半年的开源改造后,今年 5 月份正式在 Github 开源。缓存

开源首先仍是由于经历过双十一以后,咱们以为本身作好了准备,开源有助于咱们鞭策本身,把 MNN 作的更好;另外一方面,业界的开源方案,不管是 TensorFlow Lite 、 NCNN 仍是 Mace ,都给了咱们很好的输入和借鉴,咱们也但愿借着开源,将咱们的思考和创新回馈社区。安全

下文将主要围绕着 MNN ,来介绍淘宝在移动 AI 上的一些实践经验。网络

挑战与应对

端侧推理引擎面临的挑战中,碎片化是最为显著的,这种碎片化是多层次、多维度的 。框架

  • 训练框架上, Caffe 、 TensorFlow 、 PyTorch 、 MXNet 在训练模型时都很经常使用;
  • 计算设备上, CPU 、 GPU 已经是主流, NPU 、 TPU 渐渐成为标配, DSP 、 FPGA 在 IoT上也很常见;
  • 算子层面上,众多参数会造成不一样的组合,从而对应出不一样的优化方式,轻量化和通用化须要取舍;

一款优秀的端侧推理引擎,就须要在这样碎片化的环境下,利用设备有限的资源,尽量发挥出设备的性能。为此,也须要在转换、调度、执行上加入相应的优化策略。下文,会就其中的部分展开说明。ide

转换工具

模型优化函数

在模型优化中,MNN 引入了前端的概念来统一训练框架。不一样的前端负责加载不一样训练框架的模型,统一转换为 MNN 的模型格式。对于最经常使用的训练框架 TensorFlow 和 Caffe ,咱们提供了独立的前端;其余训练框架,好比 MXNet ,则须要先将模型转换为 ONNX ,再经过 ONNX 前端加载。这里,因为 TensorFlow 的算子颗粒度相比 Caffe 和 ONNX 要更小,咱们引入了图优化的模块来对齐算子之间的颗粒度。模型转换以后,会通过优化器优化,包含算子融合、算子替换、布局调整等等。以后,能够选择对浮点模型执行量化压缩。目前模型压缩的模块尚未开源,咱们会在完善以后,将相关代码开源。这些步骤都完成以后,会使用 flatbuffer 来保存部署模型。工具

图优化

这里以 RNN-GRU cell 为例,说明一下图优化。

左图是 RNN-GRU cell 在 TensorBoard 中的可视化描述。它足足包含了 3584 个节点,而每个节点都表明了必定的数据读写或运算,累积起来的总量很是大。然而,全部这些节点能够打包使用一个大颗粒的算子来替代。这不只大幅下降了部署模型的大小,还能够在大颗粒算子的实现中聚合大量的计算,避免没必要要的数据读写。

右图展现的是一个实际业务模型在图优化先后的性能对比。在华为 P10 、红米 3x 、小米 6 上都有 1 倍左右的性能提高。而若是是双向 GRU ,效果还会更明显。

算子融合

再以 Convolution、Batchnorm、Scale、ReLU 为例说明优化器中的算子融合。

首先融合 Convolution 和 Batchnorm,Convolution 的 weight 等于 weight 乘 alpha ,而 bias 等于 bias 乘 alpha 再加 beta ;然后融合 Convolution 和 Scale ,融合过程和 Batchnorm 相似;最后融合 Convolution 和 ReLU ,在输出结果前,计算激活函数 ReLU 便可。

这样,四个算子就能够合并成一个算子。融合的过程避免了三次 tensor 读写、两次 tensor 乘加。优化效果:MobileNet V1 在小米 5 和华为 P10 上有 20 ~ 40% 的性能提高,效果仍是比较明显的。

智能调度

总体设计

在调度上, MNN 将每一类计算设备抽象为一个后端,将算子在特定后端上的实现抽象为执行器。后端负责特定设备上的资源分配和计算调度,执行器负责具体的实现。后端和算子的添加都经过注册表来实现,这是一个双层注册表结构,拓展起来就相对灵活。

调度时,能够为子图选择相应的后端,再由后端建立出相应的执行器,组成管线;也能够为子图选择后端组,实现混合调度。好比,在 GPU 上不宜实现排序算子时,能够回退到 CPU 来执行。

目前, MNN 在 CPU 上实现了 76 个算子, Metal 上有 55 个, OpenGL 覆盖了基础的 CNN 网络, OpenCL 和 Vulkan 分别有 29 和 31 个。

缓存管理

在建立完执行器以后,子图和管线已经就绪。下来,须要计算出全部 tensor 的形状,在相应的后端上完成内存的分配。然后,在准备执行器时,再为全部的执行器预先在后端上申请好必要的 buffer。运行结束后,返回 tensor 便可。

因为推理所需的全部内存在准备期就已经申请完毕,在后续推理时,若是输入的形状不变,就能够复用 tensor 和 buffer,从而避免频繁地申请、释放内存;只有输入形状改变的时候,才须要从形状计算开始,调整一次内存分配。同时,因为使用后端统一管理缓存,后端内的执行器之间,缓存就能够充分复用的,这就大大减小了内存的需求量。此外,MNN 分配内存时,默认按照32位对齐,内存对齐有利于数据读写。

执行优化

数据布局与滑窗卷积

数据布局对性能影响巨大。

先来看一看在 NCHW 的布局下,怎么利用 SIMD 加速 3x3 的 depth-wise 卷积。

首先,读取数据时,须要一次性读取四个 float 做为第一行的数据,后两行的读取也是类似的;此时,读取出的三行数据已经足够计算两列输出,即,能够复用部分数据;然后,为了提升数据复用,会再读取出第四行数据,一次计算两行两列,即,能够引入循环展开;然而,残留的 5~25 和 21~25 亮度眼边界没法利用 SIMD 计算,只能逐一循环读写完成计算;按照这样的方式,就能够相应完成后几个通道的计算。

可是, NCHW 布局下,没法充分利用 SIMD 进行加速,同时,实现优化分支越多,占用包大小也就越多。

再来看一看 NC/4HW4 布局下,利用 SIMD 加速的状况又是怎样的。

这里的 "C/4" 指的是按照 4 个通道对齐的方式重排数据。重排全部输入和权重数据后,每次 SIMD 读写都自然是 4 个通道的输入数据和 4 个通道的权重数据。这样,不论 kernel、stride、dilation 怎么变化,咱们均可以简单地使用 for 循环和 SIMD 的一套通用优化完成卷积计算。既不会有边缘数据没法加速的问题,也不会对包大小形成影响。

Winograd

对于对于 KxK 卷积,可使用 Winograd 算法进一步加速。MNN 中支持 2x2 到 7x7 的 Winograd 实现。Winograd 计算时,须要把输出拆分红 NxN 的小块,把输入拆分红 (N+K-1)x(N+K-1) 的小块。这样,问题就能够简化为两个小矩阵的卷积。

再套用 Winograd 的公式,将矩阵间的卷积运算转换为矩阵点乘运算。在这个过程当中,除了矩阵点乘外,还引入三个矩阵转换,分别是输入矩阵 d 、权重矩阵 g 和结果矩阵 Y’ 的转换。其中,权重转换时, G 矩阵能够利用中国剩余数定理计算, GgGT 就能够在准备执行器时提早计算;输入转换和输出转换时用到的 A 和 B 矩阵须要根据 N 和 K 计算,咱们在代码中内置了几种优化后的组合,因此实际计算时,这两个转换并不须要通过复杂的矩阵乘法。

这样,原来矩阵卷积所须要的 9x4 次乘法计算就能够用矩阵点乘的 4x4 次乘法计算代替。只考虑乘法耗时的话,加速了 2.25 倍。示例中, K=3,N=2 ,但实际使用时,能够选择更大的 N 值,获取高的加速倍数,但也要相应消耗更多的内存。

Strassen

MNN 多是端侧推理引擎中,第一个应用 Strassen 算法优化矩阵乘法的。

Strassen 在计算矩阵乘法时,首先须要将矩阵平均拆分红四个小矩阵。这里使用 a11 ~ a2二、b11 ~ b2二、c11 ~ c22 表明四个小矩阵,计算过程一共须要8次小矩阵乘法运算。

这里能够引入中间小矩阵, s1 ~ s四、t1 ~ t四、m1 ~ m七、u1 ~ u7 。其中,只有 m1 ~ m7 包含小矩阵乘法,一共 7 次小矩阵乘法运算。而其余的,只包含小矩阵的加减法。也就是说,经过 4 + 4 + 7 次小矩阵加减法,替代了一次小矩阵乘法。

与原来的矩阵乘法相比, Strassen 的时间复杂度从 n 的 3 次方,下降到 n 的 2.81 次方。在矩阵较大时,矩阵乘法远远慢于矩阵加减法,收益就更明显。

在 MNN 中,咱们会递归使用 Strassen 。也就是说,递归拆分矩阵。在矩阵足够大时,继续拆分;在矩阵不够大时,使用普通的矩阵算法。这里使用减免的矩阵乘法开销做为收益,使用小矩阵 s 、小矩阵 t 、小矩阵 u 矩阵的加减法开销之和做为代价,收益大于代价时,就能够考虑使用 Strassen 算法。

链路优化

链路优化能够举一个 19 年春节淘宝扫年货的例子。在得到手机相机输入后,每一帧的图像首先须要通过一次预处理,将图片缩放到年货检测模型的输入大小上,然而再通过推理,断定图像有没有年货,若是有,就发放相关权益。这个过程当中,图片预处理的耗时也不容忽视。下降这个耗时,就能够帮助咱们提高帧率,从而改进用户体验。为此,咱们引入了一个轻量级的 2D 图片处理库,能够高效地完成色值变化、色彩空间的转换或者仿射变换等。这样, MNN 的用户就再也不须要为图片处理引入 libyuv 或者 opencv 了。

性能比较

通过种种优化后,这是咱们在性能上交出的答卷。

MobileNet V2 ,在 OPPO r17 和 iPhone 7Plus 上作了一系列的性能对比。

如图, MNN 的性能在 CPU 和 GPU 上都有必定的优点。

小结

总的来讲, MNN 吸纳了前人的经验,也结合本身对端侧推理引擎的认知,作了许多创新。综合考虑性能、模型和后端的拓展性、缓存、 CPU 和 GPU 的算子实现,以及 CV 库等方面的表现,在端侧推理引擎中, MNN 是值得移动 AI 用户尝试的选择。

后续规划

在后续计划上,转换部分,咱们计划追加更多算子和更多图优化匹配模板,也计划将模型量化工具开源;调度部分,咱们计划分步实现端侧训练和边缘学习,计算设备自动选择也在筹划中;执行部分,仍是会持续优化现有各端算子的实现,也计划优化量化卷积、矩阵乘算法,计划在 CV 库上直接支持 GPU ,咱们也考虑将现有 NC/4HW4 实现的算法,整理为独立的高性能计算库,算法自动选择一样在筹划中;其余部分,咱们会持续建设项目的可用性,持续加入更多的文档和示例。

淘宝基础平台部-端智能团队欢迎推理引擎研发工程师、AR技术研发工程师、高性能计算研发工程师的加入。对新技术感兴趣,善于创新突破,渴望用新技术给用户带来创新体验的同窗请联系咱们,简历投递至chengfei.lcf@alibaba-inc.com。



本文做者:陈以鎏(离青)

原文连接

本文来自云栖社区合做伙伴“阿里技术”,如需转载请联系原做者。

相关文章
相关标签/搜索