AoE:一種快速集成AI的終端運行環境SDK

一、背景

1.1 AoE是什麼

AoE (AI on Edge) 是一個滴滴開源的終端側AI集成運行時環境(IRE)。以 “穩定性、易用性、安全性” 爲設計原則,幫助開發者將不同框架的深度學習算法輕鬆部署到終端高效執行,Github 地址是 https://github.com/didi/aoe

爲什麼要做一個 AI 終端集成運行時框架,原因有兩個:

一是隨着人工智能技術快速發展,這兩年涌現出了許多運行在終端的推理框架,在給開發者帶來更多選擇的同時,也增加了將AI佈署到終端的成本;

二是通過推理框架直接接入AI的流程比較繁瑣,涉及到動態庫接入、資源加載、前處理、後處理、資源釋放、模型升級,以及如何保障穩定性等問題。

目前AoE SDK已經在滴滴銀行卡OCR上應用使用,想更加清晰地理解 AoE 和推理框架、宿主 App 的關係,可以通過下面的業務集成示意圖來了解它。

圖片描述

1.2 終端推理框架一覽

下面是終端運行的8種主流推理框架(排名不分先後)。

圖片描述

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

從本質上來說,無論是什麼推理框架,都必然包含下面 5 個處理過程,對這些推理過程進行抽象,是 AoE 支持各種推理框架的基礎。

目前,AoE 實現了兩種推理框架 NCNN 和 TensorFlow Lite 的支持,以這兩種推理框架爲例,說明一下 5 個推理過程在各自推理框架裏的形式。

圖片描述

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 的序列化庫的性能比較測試結果》。

三、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();
}

四、加入我們

幫助AI在終端落地,開源AoE集成運行環境是我們走出的第一步!未來,爲終端的開發者提供更多推理框架的支持,提供更多有價值的特性,是我們不懈追求的目標。如果您對這個項目感興趣,如果您在終端AI運行環境方面有想法,如果您在使用時有疑問,誠摯邀請您加入我們。

github地址:

圖片描述
您的每一個Star都是對我們最大的肯定:)

QQ交流羣:

圖片描述
您的每一個問題都會幫我們成爲更好的自己:)
QQ羣號: 815254379

同時,也歡迎您關注滴滴技術公衆號,我們會及時發佈最新的開源信息和技術乾貨!

滴滴技術公衆號:

圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章