結果昨天的文章就被下架了XDD 看來是挑戰失敗?今天變回了 Day12,但我還是會繼續更新這系列文章~
前幾天一通分析了 XAI 的架構,而今天就是要來實際操作看看到底是否真的能夠透過這一系列的程式建立起一個 inference model.
首先,還記得之前透過 Cloud AI Studio 建立的 MobilenetV2 Model 嗎?今天要從零開始手把手建立起 model inference 的程式。
首先透過 CubeMX 建立一個專案,這邊選擇 U585 開發板以及使用 CMake 作為編譯工具,然後其他都可以用 default setting 就好,然後再把之前的 Model Library 放入資料夾底下,大概會長這樣:
而多出來的 AI_Inference 資料夾就是等等要進行增加 Application code 的地方。
接下來開始加入所需要的程式碼,為了方便需要,這邊直接加入少部分 X-Cube AI 寫好的程式碼,減少所需的工作成本,需要加入的程式碼檔案分別為:
但僅僅是加入上面的程式碼是不夠的,還要額外 porting 其他的程式碼,這邊我都會把它放在 app_adapter.c / app_adapter.h 之中,而再新增 My_inference_app.c / My_inference_app.h 用來放接下來的 application 程式碼。
因此最後的程式碼大概會長以下這樣:
如果是透過 cmake 來做編譯,可以直接在 source 上加入所有的 .c 程式碼,以及 include 中把包含 header 的程式碼引入,也要記得把 NetworkRuntime910_CM33_GCC.a 引入。
實際上要先實現 LC_PRINT 相關的程式碼,之後調用這個 low cost 的 Print 才會有辦法實現,相關程式因為有點冗長,這邊先不放,之後完成以後會把 source 再分享出來。
這邊直接開始加入 My_Inferece_app 相關的程式,實際上也很簡單,看著原本的原始碼依樣畫葫蘆就可以實現,但因為我這份程式只打算實現一個 model 的推力,因此省略掉不少東西:
例如 int aiInit(void)
我就省去掉原本需要透過 loop 來迭代尋找所有 model 的過程:
int aiInit(void)
{
int res = -1;
const char *nn_name;
/* Discover and initialize the network(s) ------------------------ */
LC_PRINT("Discovering the network(s)...\r\n");
// 這邊直接不迭代尋找,直接指定 index 0 作為 model
nn_name = ai_mnetwork_find(NULL, 0);
if (nn_name) {
LC_PRINT("\r\nFound network \"%s\"\r\n", nn_name);
res = aiBootstrap(&net_exec_ctx[0], nn_name);
if (res) nn_name = NULL;
}
return res;
}
而後續的幾個 function 也是如此,只要透過前面說的步驟就能夠依序建立好。
接著建立 Inference 的過程,這邊建立一個 int aiApplication(void)
的 function:
int aiApplication(void)
{
ai_buffer ai_input[AI_NETWORK_IN_NUM]; // 建立 input 的 buffer
ai_buffer ai_output[AI_NETWORK_OUT_NUM]; // 建立 output 的 buffer
LC_PRINT("\r\nRunning the network...\r\n");
if (net_exec_ctx[0].handle == AI_HANDLE_NULL) { // 確定 AI 的 handle 有正確的指派
LC_PRINT("E: network handle is NULL\r\n");
return -1;
}
ai_input[0] = net_exec_ctx[0].report.inputs[0]; // 把 input 的結構分配好
ai_input[0].data = AI_HANDLE_PTR(data_ins[0]); // 把 input data 預先設立好的空間至派給指標
ai_output[0] = net_exec_ctx[0].report.outputs[0]; // 把 output 的結構分配好
ai_output[0].data = AI_HANDLE_PTR(data_outs[0]); // 把 output data 預先設立好的空間至派給指標
int res = ai_mnetwork_run(net_exec_ctx[0].handle, ai_input, ai_output); // call runner
if (res != 1) {
aiLogErr(ai_mnetwork_get_error(net_exec_ctx[0].handle),
"ai_mnetwork_run");
return -2;
}
return 0;
}
然後最後才再 main function 把程式碼呼叫進來,應該會得到下面的結果:
到了這邊,我們至少確定了今天透過 ST 相關工具產出來的程式碼是如何使用的,也透過程式碼去呼叫了相應的 function,得到了輸出結果...但這些結果並不符合預期啊?真正想要得到的是一個可以透過輸入輸出得到結果的EdgeAI應用。
上面那個 Model 是透過 MobileNetV2 來判斷 food101 這個 dataset 的,如果我們去網路上尋找一張符合Model 輸入的圖片(2242243)想放進去這個程式裡面做辨識,就會碰到一個十分現實的問題 -- RAM 不夠了。
首先我們先看一下這個程式目前所吃掉的 RAM 大小:
可以看到幾乎已經把 RAM 吃滿了,而 FLASH(ROM)實際上也吃了快一半,這時候就會碰到第一個最容易撞到的 EdgeAI on MCU Challenge,Limited Memory。
那面對這個問題,接下來我們就會採取最簡單的暴力解,直接引入外部的 Memory 作為額為的 RAM/ROM 使用,並且再透過一些手段嘗試進行加速,不要讓外部的 Memory 拖累太多的資源。
那今天就先更新到這裡,明天開始分享要如何透過外部的 Memory 讀取 Model 資料以及存取 RAM 。