1. 导读
作为 DAU 过亿的国民出行服务平台,高德地图每天为用户提供海量的检索、定位和导航服务,实现这些服务须要有精准的路线信息,比方电子眼地位、路况信息、交通标识地位信息等。读者是否会好奇,高德是如何感知到事实世界的路线信息,并提供这些数据给用户呢?
事实上,咱们有很多的办法将事实世界的路线因素采集回收,并更新到高德地图 App 上。其中一种十分重要的办法是利用计算机视觉的伎俩,将视觉算法部署到客户端,通过对图片的检测辨认,疾速将路线的信息回收。
为了低成本,高实时性地实现路线因素回收,咱们借助 MNN 引擎(一个轻量级的深度神经网络推理引擎),将卷积神经网络模型部署到客户端,在客户端进行端侧的模型推理,从而实现在 计算能力低,内存小的客户端 进行路线因素采集的工作。
传统的 CNN(卷积神经网络)计算量十分大,且业务场景须要部署多个模型。如何在低性能的设施上部署多个模型,并在不影响实时性的状况下保障利用的 ” 小而优 ” 是一个十分大的挑战。本文将分享利用 MNN 引擎在低性能设施上部署深度学习利用的实战经验。
2. 部署
2.1 背景介绍
如 Figure2.1.1 所示,业务背景是将路线因素辨认相干的 CNN 模型部署到客户端,在端侧进行模型推理,并提取路线因素的地位和矢量等信息。
因为该业务场景的须要,目前端上需同时部署 10+ 甚至更多的模型,以满足更多的不同路线因素的信息提取须要,对于低性能设施来说是十分大的挑战。
Figure 2.1.1 高德数据采集
为了达到利用的 ” 小而优 ”,MNN 引擎部署模型的过程中遇到了很多问题和挑战。上面就这些问题和挑战分享一些教训和解决办法。
2.2 MNN 部署
2.2.1 内存占用
利用运行内存对于开发者来说是始终绕不开的话题,而模型推理产生的内存在利用运行内存中占有很大的比例。因而,为了使模型推理内存尽可能小,在模型部署的过程中,作为开发者必须分明模型运行产生内存的次要起源。依据咱们的部署教训,部署单模型的过程中,内存次要来源于以下四个方面:
Figure 2.2.1 单模型部署内存占用
ModelBuffer: 模型反序列化的 buffer,次要存储模型文件中的参数和模型信息,其大小和模型文件大小靠近。
FeatureMaps: Featuremaps 的内存,次要存储模型推理过程中,每一层的输出和输入。
ModelParams: 模型参数的内存,次要存储模型推理所需的 Weights, Bias, Op 等内存。其中 Weights 占用了该局部的大部分内存。
Heap/Stack: 利用运行中产生的堆栈内存。
2.2.2 内存优化
通晓模型运行内存占用后,就能不便了解模型运行时的内存变动。通过多个模型部署实际, 为了升高部署模型的内存峰值,咱们采取的措施如下:
- 模型反序列化 (createFromFile) 并创立内存 (createSession) 后,将模型 Buffer 开释(releaseModel), 防止内存累加。
- 解决模型输出,图像内存和 inputTensor 可内存复用。
- 模型后处理,模型输入 Tensor 和输入数据的内存复用。
Figure 2.2.2.1 MNN 模型部署内存复用计划
通过内存复用,以部署 1 个 2.4M 的视觉模型为例,模型运行时从加载到开释,两头各阶段所占用内存变动能够用以下曲线示意:
Figure 2.2.2.2 单模型利用内存曲线(Android memoryinfo 统计)
- 模型运行前,模型占用内存为0M。
- 在模型加载 (createFromFile) 和创立内存 (createSession) 后,内存升到5.24M, 来源于模型反序列化和 Featuremaps 内存创立。
- 调用 releaseModel 内存升高至3.09M,起因是开释了模型反序列化后的 buffer。
- InputTensor 和图像内存复用,利用内存减少到4.25M, 起因是创立了存储模型输出的 Tensor 内存。
- RunSession(),利用内存减少到5.76M,起因是减少了 RunSession 过程中的堆栈内存。
- 在模型开释后,利用复原到了模型加载前的内存值。
通过屡次模型部署的实际,上面总结了部署单模型到端的内存峰值预估公式:
MemoryPeak: 单模型运行时内存峰值。
StaticMemory: 动态内存,包含模型 Weights, Bias, Op 所占内存。
DynamicMemory: 动态内存,包含 Feature-maps 所占内存。
ModelSize: 模型文件大小。模型反序列化所占内存。
MemoryHS: 运行时堆栈内存(教训取值 0.5M-2M 之间)。
2.2.3 模型推理原理
本章节分享模型推理原理,以便于开发者遇到相干问题时,疾速定位和解决问题。
模型推理前模型的调度: MNN 引擎推理放弃了高度的灵便度。即能够指定模型不同的运行门路,也能够对不同的运行门路指定不同的后端,以进步异构零碎的并行性。此过程次要是调度或者工作散发的过程。
对于分支网络,能够指定以后运行分支,也能够调度分支执行不同后端,进步模型部署的性能。图 Figure2.2.3.1 所示为一个多分支模型, 两个分支别离输入检测后果和宰割后果。
Figure 2.2.3.1 多分支网络
部署时可做如下优化 :
- 指定模型运行的 Path。当仅需检测后果时,只跑检测分支,无需跑完两个分支, 减小模型推理工夫。
- 检测和宰割指定用不同的后端。比方检测指定 CPU, 宰割指定 OpenGL,进步模型并行性。
模型推理前的预处理 : 本阶段会依据上一步的模型调度信息进行预处理,实质是利用模型信息和用户输出配置信息,进行 Session(持有模型推理数据) 的创立。
Figure 2.2.3.2 依据 Schedule 创立 Session
本阶段依据模型反序列化的模型信息和用户调度配置信息来进行运算调度。用于创立运行的 Piplines 和对应的计算后端。如 Figure2.2.3.3 所示。
Figure 2.2.3.3 Session 创立
模型的推理: 模型推理实质是依据上一步创立的 Session,顺次执行算子的过程。运算会依据预处理指定的模型门路和指定后端进行模型每层的运算。值得一提的是,算子在指定的后端不反对时,会默认复原到备用后端执行计算。
Figure 2.2.3.4 模型推理计算图
2.2.4 模型部署工夫
本局部统计了单模型部署过程各阶段耗时,不便开发者理解各阶段的耗时,以便更好的设计代码架构。(不同设施有性能差别,耗时数据仅供参考)
Figure 2.2.4.1 模型推理计算图
模型反序列化和 Session 创立绝对耗时较长,进行多张图的推理时,尽量执行一次。
2.2.5 模型误差剖析
模型部署时,开发者难免会遇到部署端和 X86 端 (Pytorch, Caffe, Tensorflow) 训练模型输入后果有偏差的状况。上面分享误差起因, 定位思路以及解决办法。
模型 Inference 示意图如 Figure 2.2.5.1 所示:
Figure 2.2.5.1 模型 Inference 示意图
模型误差的确定: 查看是否有模型误差最直观的办法是,固定部署端模型和 X86 端模型的输出值,别离推理,比照部署端模型和 X86 端模型输入值,可确认是否有误差。
模型误差的定位: 当确定有模型误差时,先排除因模型输出误差导致的模型输入误差。因为 X86 端和局部 Arm 设施浮点的示意精度不统一,输出误差在某些模型中会被累积,最终造成较大的输入误差。用什么办法来排除是输出误差导致的问题呢?咱们提供一种办法是将模型输出设置为 0.46875(起因是该值在 X86 设施和局部 Arm 设施示意统一,实质是 1 通过移位取得的浮点数在两种端上示意均统一)。而后察看输入是否统一即可。
模型误差的定位思路 : 在排除模型输出误差导致模型输入误差(即模型输出统一时,模型输入不统一) 的状况下,很可能是模型某些算子导致的误差了。如何定位模型哪个 OP 导致的误差呢?通过下述的步骤能够定位模型外部引起误差的起因:
1)通过 runSessionWithCallBack 来回调模型每个 OP 的两头计算结果。目标是定位模型从哪个 Op 开始呈现误差。
2)定位到该层之后,即可定位到产生误差的算子。
3)定位到算子后,通过指定的后端信息即可定位到对应的算子执行代码。
4)定位到对应的执行代码后,调试定位产生误差的代码行,从而定位到产生模型误差的根本原因。
3. 总结
MNN 引擎是一个十分好的端侧推理引擎,作为开发者来说,模型的端上部署和性能优化在关注业务逻辑优化的同时,也需关注对引擎计算过程,框架设计和模型减速的思维,反过来能够更好的优化业务代码,做出真正 ” 小而优 ” 的利用。
4. 将来布局
随着设施性能的广泛晋升,后续的业务会搭载到性能更高的设施,咱们会利用更丰盛的计算后端做模型的减速,比方 OpenCL, OpenGL 等, 从而减速模型的推理。
将来设施会搭载更多的模型到客户端,用于实现更多品类路线因素信息的回收,咱们也会利用 MNN 引擎,探索更高效,更高实时性的代码部署框架,以更好的服务于地图采集业务。
咱们是 高德地图数据研发团队,团队中有大量 HC,欢送对 Java 后端、平台架构、算法端上工程化(C++)、前端开发感兴趣的小伙伴退出,请发送您的简历到 gdtech@alibaba-inc.com,邮件题目格局: 姓名 - 技术方向 - 来自高德技术。咱们求贤若渴,期待您的退出。