今天是分析 X-Cube-AI 程式碼的第三天,也準備進入推論區域的分析。
首先一樣先看入口函數,在 main.c 底下的 MX_X_CUBE_AI_Process
就是開始進行應用的區域,而接著馬上就又進入了 aiSystemPerformanceProcess
進行 Performance 的應用,這個 function 看似很長,但實際上真正進行 AI 推理的就是 r = aiTestPerformance(idx);
這段,於是又在進入這個函式準備分析:
很不幸的,這個函式也是長得可怕,因此也只能先簡略一些最相關的內容:
/* -----------------------------------------------------------------------------
* Specific APP/test functions
* -----------------------------------------------------------------------------
*/
static int aiTestPerformance(int idx)
{
// 省略...
// 這邊先宣告 ai_input 和 ai_output 作為輸入和輸出的 buffer
ai_buffer ai_input[AI_MNETWORK_IN_NUM];
ai_buffer ai_output[AI_MNETWORK_OUT_NUM];
// 再省略...
// 這邊對 input/output ptr 進行給予實際上的 buffer 空間
/* Fill/set the input tensor descriptors */
for (int i = 0; i < net_exec_ctx[idx].report.n_inputs; i++) {
ai_input[i] = net_exec_ctx[idx].report.inputs[i];
if (net_exec_ctx[idx].report.inputs[i].data)
ai_input[i].data = AI_HANDLE_PTR(net_exec_ctx[idx].report.inputs[i].data);
else
ai_input[i].data = AI_HANDLE_PTR(data_ins[i]);
}
/* Fill/set the output tensor descriptors */
for (int i = 0; i < net_exec_ctx[idx].report.n_outputs; i++) {
ai_output[i] = net_exec_ctx[idx].report.outputs[i];
if (net_exec_ctx[idx].report.outputs[i].data)
ai_output[i].data = AI_HANDLE_PTR(net_exec_ctx[idx].report.outputs[i].data);
else
ai_output[i].data = AI_HANDLE_PTR(data_outs[i]);
}
// 省略 again...
// 這邊會把各個 input 根據不同的輸入 type 給予不同的 random 值
for (iter = 0; iter < niter; iter++) {
/* Fill input tensors with random data */
for (int i = 0; i < net_exec_ctx[idx].report.n_inputs; i++) {
const ai_buffer_format fmt = AI_BUFFER_FORMAT(&ai_input[i]);
ai_i8 *in_data = (ai_i8 *)ai_input[i].data;
for (ai_size j = 0; j < AI_BUFFER_SIZE(&ai_input[i]); ++j) {
/* uniform distribution between -1.0 and 1.0 */
const float v = 2.0f * (ai_float) rand() / (ai_float) RAND_MAX - 1.0f;
if (AI_BUFFER_FMT_GET_TYPE(fmt) == AI_BUFFER_FMT_TYPE_FLOAT) {
*(ai_float *)(in_data + j * 4) = v;
}
else {
if (AI_BUFFER_FMT_GET_BITS(fmt) >= 8) {
in_data[j] = (ai_i8)(v * 127);
if (AI_BUFFER_FMT_GET_TYPE(fmt) == AI_BUFFER_FMT_TYPE_BOOL) {
in_data[j] = (in_data[j] > 0)?(ai_i8)1:(ai_i8)0;
}
}
}
}
}
// 省略 ...
// 這邊開始進行推理
batch = ai_mnetwork_run(net_exec_ctx[idx].handle, ai_input, ai_output);
if (batch != 1) {
而這邊的推論最後也是透過 function pointer 導向 ```ai_network_run``
aiLogErr(ai_mnetwork_get_error(net_exec_ctx[idx].handle),
"ai_mnetwork_run");
break;
}
// 省略 ...
return 0;
}
可以先看到上述程式的註解,基本上這邊進行了幾個步驟:
typedef struct ai_buffer_ {
ai_buffer_format format; /*!< buffer format */
ai_handle data; /*!< pointer to buffer data */
ai_buffer_meta_info* meta_info; /*!< pointer to buffer metadata info */
/* New 7.1 fields */
ai_flags flags; /*!< shape optional flags */
ai_size size; /*!< number of elements of the buffer (including optional padding) */
ai_buffer_shape shape; /*!< n-dimensional shape info */
} ai_buffer;
而這邊的推論函數 ai_mnetwork_run
最後也是透過 function pointer 導向 ai_network_run
。
AI_API_ENTRY
ai_i32 ai_mnetwork_run(ai_handle network, const ai_buffer* input,
ai_buffer* output)
{
struct network_instance* inn;
inn = ai_mnetwork_handle((struct network_instance *)network);
if (inn)
return inn->entry->ai_run(inn->handle, input, output);
else
return 0;
}
因此推理的過程比起前面建立稍微簡單一點,只需把 input/output 的空間/值都先準備好以後丟進去 run 函數即可。
於是到此已經把建立->推理的程式都分析完了,明天就可以開始上手測試到底是不是能夠透過這樣呼叫完成一個建立在 MCU 上的 NN 推理。
那今天就到這邊,明天就可以來 EdgeAI from scratch 了。