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 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 {
/**
* 資料預處理,将輸入資料轉換成模型輸入資料
*
* @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 元件,代碼如下:
<service
android:name=".AoeProcessService"
android:exported="false"
android:process=":aoeProcessor" />
第二個部分,異常重新綁定獨立程序,在推理時,如果發現 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 {
@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位址:
https://github.com/didi/AoE您的每一個Star都是對我們最大的肯定:)
您的每一個問題都會幫我們成為更好的自己:)