原
https://zhuanlan.zhihu.com/p/76909819
https://zhuanlan.zhihu.com/p/76909819
1、背景
前段時間在知乎上溜達,看到
大神的專欄-實戰嵌入端的AI算法,進去一看,不得了,發現新大陸了,深度學習模型還能在安卓端這麼玩的嗎?
一般對我們這種初級煉丹師,要驗證算法在端上的能力以及實測效果。一般是這樣的, 如圖1(至於你們是不是這樣我是不知道),中間驗證過程又臭又長,中間有些環節還得看別人有沒有空(只怪自己水平差)。
圖1 驗證流程
那麼看了大神的專欄,發現原來可以這麼玩(嫌棄ing,說了半天還沒告訴怎麼玩)。
以mnist分類爲例,先貼下大神的部分代碼,詳細代碼看大神github。
int main(void)
{
std::string image_name = "./mnist_test.jpg";
std::string model_name = "./mnist.mnn";
int forward = MNN_FORWARD_CPU;
// int forward = MNN_FORWARD_OPENCL;
int precision = 2;
int power = 0;
int memory = 0;
int threads = 1;
int INPUT_SIZE = 28;
cv::Mat raw_image = cv::imread(image_name.c_str());
int raw_image_height = raw_image.rows;
int raw_image_width = raw_image.cols;
cv::Mat image;
……
auto net = std::shared_ptr<MNN::Interpreter>(MNN::Interpreter::createFromBuffer(modelBuffer, bufferSize));
……
auto session = net->createSession(config);
……
……
……
// run network
net->runSession(session);
……
……
// post processing steps
auto scores_dataPtr = tensor_scores_host.host<float>();
// softmax
float exp_sum = 0.0f;
for (int i = 0; i < 10; ++i)
{
float val = scores_dataPtr[i];
exp_sum += val;
}
// get result idx
int idx = 0;
float max_prob = -10.0f;
for (int i = 0; i < 10; ++i)
{
float val = scores_dataPtr[i];
float prob = val / exp_sum;
if (prob > max_prob)
{
max_prob = prob;
idx = i;
}
}
printf("the result is %d\n", idx);
return 0;
}
看下來直接在android端就能用C++完成模型效果以及性能測試了,感覺就不用去求爺爺求爸爸了,開森啊(這裏面的MNN是阿里的端上inference框架,感興趣可以去GitHub上看看,MNN可以參考MNN文檔,當然還有nihui大佬的NCNN ):
2、怎麼玩
開森歸開森,但是怎麼玩呢?把大神的專欄翻了一遍,沒看對應教程,大佬可能覺得這個so easy,大家都會,哭!!!
怎麼辦?看看大神代碼好像有mk文件,查查mk文件幹啥的呢?發現是安卓上類似makefile文件,那就查查如何用mk編譯可執行文件吧,通過問問度娘以及谷歌爸爸,發現是可以實現的,那麼大體方向是沒錯。
那麼怎麼編譯?怎麼把對應的文件傳到android上?怎麼在android上執行呢?(怒問自己,垃圾,你咋這麼多不會呢!!!)
這個時候果斷去抱安卓同學的大腿了!
大腿開始教我怎麼玩啦!!!
2.1 ndk編譯
這裏先git clone 一下大神的github代碼,前面opencv動態庫,MNN動態庫,mk文件大神都給你們弄好了,下下來改改或者直接用就行了。
再下載android的NDK(我是mac電腦,如果你們是其它系統,行不行我不知道,我只管挖不管埋)。
可以發現ndk下面有個ndk-build文件(安卓大腿說用着玩意編譯)。
# 先設置環境
PATH=$PATH:<ndk-build所在的文件目錄>
# 這裏我們就以大神裏面的mnist爲例
cd MNN-APPLICATIONS/applications/mnist/jni
ndk-build
# 這裏可以發現直接在mnist創建了libs目錄
# 運行結果
[arm64-v8a] Install : libMNN.so => libs/arm64-v8a/libMNN.so
[arm64-v8a] Install : libMNN_CL.so => libs/arm64-v8a/libMNN_CL.so
[arm64-v8a] Install : libMNN_Vulkan.so => libs/arm64-v8a/libMNN_Vulkan.so
[arm64-v8a] Install : onnx_mnist => libs/arm64-v8a/onnx_mnist
到這裏基本編譯結束了,那麼怎麼把它導到android端呢?
2.2 文件傳輸
下面這個得用adb工具,至於這玩意怎麼安裝,是什麼玩意,自己百度。
搞了個rk3399,連上電腦。
# 先來條命令
adb devices
#運行結果如下,可以看到我下面有個設備了,設備號CBI9SLBNWK
List of devices attached
CBI9SLBNWK device
# 切換一下root權限
adb root
# 運行結果
”adbd is already running as root“
這樣子我們待會進入adb shell就有root權限了,就可以爲所欲爲了。
正式push數據上去:
# 傳輸編譯好的文件,我這邊比較懶把整個目錄都push上去
adb push MNN-APPLICATIONS /data
2.3 運行可執行文件
# 下面進入adb shell環境
adb shell
# 下面就跟我們在linux的操作差不多了
# 先查看我們上傳的文件在不
ls /data
# 發現文件是在的
cd /data
cd /data/temp/MNN-APPLICATIONS/applications/mnist/onnx/libs/arm64-v8a
# 可以發現有個編譯好的可執行文件 onnx_mnist
# 把 onnx_minist拷出來,拷到有圖片的目錄,這裏可以不拷貝,只是跟cpp代碼中模型文件跟測試文件相對路徑保持一致就行
cp onnx_mnist ../../jni
cd ../../jni
# 看下大神的onnx_mnist.cpp文件,發現在該目錄下需要mnist_test.jpg以及mnist.mnn兩個文件
# 把該目錄test.jpg文件名改成對應名字
mv test.jpg mnist_test.jpg
# 把graph中,MNN轉換的mnist.mnn拷出來
cp ./graphs/minist.mnn ./
激動人心的時候到啦,終於可以運行了!(集齊七顆龍珠了,可以召喚神龍了)
./onnx_mnist
# 我去,出錯啦
CANNOT LINK EXECUTABLE "./onnx_mnist": library "libMNN.so" not found
難道是編譯目錄不對?折騰了半天,把動態庫移來移去,移到當前木來,看了mk文件,發現在對應的位置也有對應的動態庫。
OpenCV_BASE = ../../../../libraries/opencv-4.1
MNN_BASE = ../../../../libraries/mnn
搞了半天沒辦法了,加上
大神微信,求大神指點。
大神直接貼了一行代碼:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<對應動態庫路徑>
我去,原來沒有指定對應動態庫路徑,,補上:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../libs/arm64-v8a
運行結果:
Invalide device for support vulkan
Invalide device for support vulkan
the result is 5
感動,終於有運行結果了!