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開源交流羣