AoE (AI on Edge) 是一个滴滴开源的终端侧AI集成运行时环境(IRE)。以 “稳定性、易用性、安全性” 为设计原则,帮助开发者将不一样框架的深度学习算法轻松部署到终端高效执行,Github 地址是 https://github.com/didi/aoe。android
为何要作一个 AI 终端集成运行时框架,缘由有两个:git
一是随着人工智能技术快速发展,这两年涌现出了许多运行在终端的推理框架,在给开发者带来更多选择的同时,也增长了将AI布署到终端的成本;github
二是经过推理框架直接接入AI的流程比较繁琐,涉及到动态库接入、资源加载、前处理、后处理、资源释放、模型升级,以及如何保障稳定性等问题。算法
目前AoE SDK已经在滴滴银行卡OCR上应用使用,想更加清晰地理解 AoE 和推理框架、宿主 App 的关系,能够经过下面的业务集成示意图来了解它。安全
下面是终端运行的8种主流推理框架(排名不分前后)。微信
序号 | 名称 | 开发者 | 开源时间 | 描述 |
---|---|---|---|---|
1 | TensorFlow Lite | 2017 | TensorFlow Lite使用Android Neural Networks API,默认调用CPU,目前最新的版本已经支持GPU。 | |
2 | Core ML | Apple | 2017 | Core ML是2017年Apple公司在WWDC上与iOS11同时发布的移动端机器学习框架,底层使用Accelerate和Metal分别调用CPU和GPU。Core ML须要将你训练好的模型转化为Core ML model |
3 | Caffe2 | 2017 | Caffe2是facebook在2017年发布的一个跨平台的框架,不只仅支持Windows,Linux,Macos三大桌面系统,也支持移动端iOS,Android,能够说是集训练和推理于一身。 | |
4 | NCNN | 腾讯 | 2017 | NCNN是2017年腾讯优图实验室开源的移动端框架,使用C++ 实现,支持Android和iOS两大平台。 |
5 | Paddle-Mobile | 百度 | 2017 | Paddle-Mobile是2017年百度PaddlePaddle组织下的移动端深度学习开源框架,当时叫作mobile-deep-learning(MDL)。支持安卓和iOS平台,CPU和GPU使用,提供量化工具。 |
6 | QNNPACK | 2018 | QNNPACK是Facebook在2018年发布的int8量化低精度高性能开源框架,全称Quantized Neural Network PACKage,用于手机端神经网络计算的加速,已经被整合到PyTorch 1.0中,在Caffe2里就能直接使用。 | |
7 | MACE | 小米 | 2018 | MACE是2018年小米在开源中国开源世界高峰论坛中宣布开源的移动端框架,以OpenCL和汇编做为底层算子,提供了异构加速能够方便在不一样的硬件上运行模型,同时支持各类框架的模型转换。 |
8 | MNN | 阿里巴巴 | 2019 | MNN是2019年阿里开源的移动端框架,不依赖第三方计算库,使用汇编实现核心运算,支持Tensorflow、Caffe、ONNX等主流模型文件格式,支持CNN、RNN、GAN等经常使用网络。 |
从本质上来讲,不管是什么推理框架,都必然包含下面 5 个处理过程,对这些推理过程进行抽象,是 AoE 支持各类推理框架的基础。网络
目前,AoE 实现了两种推理框架 NCNN 和 TensorFlow Lite 的支持,以这两种推理框架为例,说明一下 5 个推理过程在各自推理框架里的形式。app
推理框架 | 初使化 | 前处理 | 执行推理 | 后处理 | 释放资源 |
---|---|---|---|---|---|
NCNN | int load_param(const unsigned char mem);int load_model(const unsigned char mem); | ... | int input(const char blob_name, const Mat& in);int extract(const char blob_name, Mat& feat); | ... | void release(); |
TF Lite | public Interpreter(@NonNull ByteBuffer byteBuffer, Interpreter.Options options) | ... | public void run(Object input, Object output) | ... | public void close(); |
目前,AoE 已经开源的运行时环境 SDK 包括 Android 和 iOS 平台,此外 Linux 平台运行时环境 SDK 已和你们正式见面。框架
前面已经介绍了,不一样推理框架包含着共性的过程,它们分别是初使化、前处理、执行推理、后处理、释放资源。对 AoE 集成运行环境来讲,最基本的即是抽象推理操做,经过 依赖倒置 的设计,使得业务只依赖AoE的上层抽象,而不用关心具体推理框架的接入实现。这种设计带来的最大的好处是开发者随时能够添加新的推理框架,而不用修改框架实现,作到了业务开发和 AoE SDK 开发彻底解耦。机器学习
在 AoE SDK 中这一个抽象是 InterpreterComponent(用来处理模型的初使化、执行推理和释放资源)和 Convertor(用来处理模型输入的前处理和模型输出的后处理),InterpreterComponent 具体实现以下:
/** * 模型翻译组件 */ interface InterpreterComponent<TInput, TOutput> extends Component { /** * 初始化,推理框架加载模型资源 * * @param context 上下文,用与服务绑定 * @param modelOptions 模型配置列表 * @return 推理框架加载 */ boolean init(@NonNull Context context, @NonNull List<AoeModelOption> modelOptions); /** * 执行推理操做 * * @param input 业务输入数据 * @return 业务输出数据 */ @Nullable TOutput run(@NonNull TInput input); /** * 释放资源 */ void release(); /** * 模型是否正确加载完成 * * @return true,模型正确加载 */ boolean isReady(); }
Convertor的具体实现以下:
interface Convertor<TInput, TOutput, TModelInput, TModelOutput> { /** * 数据预处理,将输入数据转换成模型输入数据 * * @param input 业务输入数据 * @return 模型输入数据 */ @Nullable TModelInput preProcess(@NonNull TInput input); /** * 数据后处理,将模型输出数据转换成业务输出数据 * * @param modelOutput 模型输出数据 * @return */ @Nullable TOutput postProcess(@Nullable TModelOutput modelOutput); }
众所周知,Android平台开发的一个重要的问题是机型适配,尤为是包含大量Native操做的场景,机型适配的问题尤为重要,一旦应用在某款机型上面崩溃,形成的体验损害是巨大的。有数据代表,由于性能问题,移动App天天流失的活跃用户占比5%,这些流失的用户,6 成的用户选择了沉默,再也不使用应用,3 成用户改投竞品,剩下的用户会直接卸载应用。所以,对于一个用户群庞大的移动应用来讲,保证任什么时候候App主流程的可用性是一件最基本、最重要的事。结合 AI 推理过程来看,不可避免地,会有大量的操做发生在 Native 过程当中,不只仅是推理操做,还有一些前处理和资源回收的操做也比较容易出现兼容问题。为此,AoE 运行时环境 SDK 为 Android 平台上开发了独立进程的机制,让 Native 操做运行在独立进程中,同时保证了推理的稳定性(偶然性的崩溃不会影响后续的推理操做)和主进程的稳定性(主进程任什么时候候不会崩溃)。
具体实现过程主要有三个部分:注册独立进程、异常从新绑定进程以及跨进程通讯优化。
第一个部分,注册独立进程,在 Manifest 中增长一个 RemoteService 组件,代码以下:
<application> <service android:name=".AoeProcessService" android:exported="false" android:process=":aoeProcessor" /> </application>
第二个部分,异常从新绑定独立进程,在推理时,若是发现 RemoteService 终止了,执行 “bindService()” 方法,从新启动 RemoteService。
@Override public Object run(@NonNull Object input) { if (isServiceRunning()) { ...(代码省略)//执行推理 } else { bindService();//重启独立进程 } return null; }
第三个部分,跨进程通讯优化,由于独立进程,必然涉及到跨进程通讯,在跨进程通讯里最大的问题是耗时损失,这里,有两个因素形成了耗时损失:
相比较使用binder机制的传输耗时,序列化/反序列化占了整个通讯耗时的90%以上。因而可知,对序列化/反序列化的优化是跨进程通讯优化的重点。
对比了当下主流的序列化/反序列化工具,最终AoE集成运行环境使用了kryo库进行序列化/反序列。 如下是对比结果,数据参考oschina的文章《各类 Java 的序列化库的性能比较测试结果》。
当咱们要接入一个新的模型时,首先要肯定的是这个模型运行在哪个推理框架上,而后继承这个推理框架的InterpreterComponent实现,完成具体的业务流程。MNIST是运行在TF Lite框架上的模型,所以,咱们实现AoE的TF Lite的Interpreter抽象类,将输入数据转成模型的输入,再从模型的输出读取业务须要的数据。初使化、推理执行和资源回收沿用TensorFlowLiteInterpreter的默认实现。
public class MnistInterpreter extends TensorFlowLiteInterpreter<float[], Integer, float[], float[][]> { @Nullable @Override public float[] preProcess(@NonNull float[] input) { return input; } @Nullable @Override public Integer postProcess(@Nullable float[][] modelOutput) { if (modelOutput != null && modelOutput.length == 1) { for (int i = 0; i < modelOutput[0].length; i++) { if (Float.compare(modelOutput[0][i], 1f) == 0) { return i; } } } return null; } }
接入MNIST的第二个步骤是配置推理框架类型和模型相关参数,代码以下:
mClient = new AoeClient(requireContext(), "mnist", new AoeClient.Options() .setInterpreter(MnistInterpreter.class)/* .useRemoteService(false)*/, "mnist");
如下是MINST初使化推理框架、推理执行和资源回收的实现:
//初使化推理框架 int resultCode = mClient.init(); //推理执行 Object result = mClient.process(mSketchModel.getPixelData()); if (result instanceof Integer) { int num = (int) result; Log.d(TAG, "num: " + num); mResultTextView.setText((num == -1) ? "Not recognized." : String.valueOf(num)); } //资源回收 if (mClient != null) { mClient.release(); }
帮助AI在终端落地,开源AoE集成运行环境是咱们走出的第一步!将来,为终端的开发者提供更多推理框架的支持,提供更多有价值的特性,是咱们不懈追求的目标。若是您对这个项目感兴趣,若是您在终端AI运行环境方面有想法,若是您在使用时有疑问,诚挚邀请您加入咱们。
github地址 https://github.com/didi/AoE
微信添加小助手加入AOE开源交流群