推荐一个开源项目给大家,快速接入 AI 能力

推荐一个开源项目给大家,快速接入 AI 能力_第1张图片

本次给大家推荐一个开源项目,它的名字叫AoE,可以在终端(iOS + Android)和 Linux 平台上快速部署 AI 运行环境,这是它的项目地址:

https://github.com/didi/AoE

欢迎大家star、fork,下面给大家介绍下它的背景和工作原理。

一、背景

1.1 AoE是什么

AoE (AI on Edge) 是一个终端侧AI集成运行时环境(IRE)。以 “稳定性、易用性、安全性” 为设计原则,帮助开发者将不同框架的深度学习算法轻松部署到终端高效执行,Github 地址是 https://github.com/didi/aoe

为什么要做一个 AI 终端集成运行时框架:

一、随着人工智能技术快速发展,这两年涌现出了许多运行在终端的推理框架,在给开发者带来更多选择的同时,也增加了将AI布署到终端的成本;

二、通过推理框架直接接入AI的流程比较繁琐,涉及到动态库接入、资源加载、前处理、后处理、资源释放、模型升级,以及如何保障稳定性等问题。

以下是 AoE 的业务集成示意图:

推荐一个开源项目给大家,快速接入 AI 能力_第2张图片

1.2 终端推理框架一览

下面是终端运行的 8 种主流推理框架(排名不分先后)。

推荐一个开源项目给大家,快速接入 AI 能力_第3张图片

1.3  AoE 如何支持各种推理框架

从本质上来说,无论是什么推理框架,都必然包含下面 5 个处理过程,对这些推理过程进行抽象,是 AoE 支持各种推理框架的基础。

目前,AoE 实现了两种推理框架 NCNN 和 TensorFlow Lite 的支持,以这两种推理框架为例,说明一下 5 个推理过程在各自推理框架里的形式。

推荐一个开源项目给大家,快速接入 AI 能力_第4张图片

1.4 AoE 支持哪些平台

目前,AoE 已经开源的运行时环境 SDK 包括 Android 和 iOS 平台,Linux 平台运行时环境 SDK 预计在9月底提供。

二、工作原理

2.1 抽象推理框架的处理过程

前面我们了解到,不同推理框架包含着共性的过程,它们分别是初化、前处理、执行推理、后处理、释放资源。对 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);	
}

2.2 稳定性保障

众所周知,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 的序列化库的性能比较测试结果》。

推荐一个开源项目给大家,快速接入 AI 能力_第5张图片

三、MNIST集成示例

3.1 对TensorFlowLiteInterpreter的继承

当我们要接入一个新的模型时,首先要确定的是这个模型运行在哪一个推理框架上,然后继承这个推理框架的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;	
    }	
}

3.2 运行时环境配置

接入MNIST的第二个步骤是配置推理框架类型和模型相关参数,代码如下:

mClient = new AoeClient(requireContext(), "mnist",	
        new AoeClient.Options()	
                .setInterpreter(MnistInterpreter.class)/*	
                .useRemoteService(false)*/,	
        "mnist");

3.3 推理执行

以下是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();	
}	


推荐阅读普通人如何提高格局谈谈我最近的一些思考谈谈程序员个人品牌的塑造

编程·思维·职场

640?wx_fmt=jpeg

你可能感兴趣的:(推荐一个开源项目给大家,快速接入 AI 能力)