iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0
佛心分享-SideProject30

從零開始打造超邊緣AI應用系列 第 11

Day 11 - 分析 X-Cube-AI 程式碼 (3)

  • 分享至 

  • xImage
  •  

推論區程式碼分析

今天是分析 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;
}

可以先看到上述程式的註解,基本上這邊進行了幾個步驟:

  1. 宣告 ai_input 和 ai_output 的 buffer
    如果去看 ai_buff 的 structure 可以看到他在 data 的部分是使用 void pointer,因此後續還要再分配實際的記憶體空間。
    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;
    
  2. 接著就是給予實際的 ai_buffer 空間作為實際的 input/output 存放。
  3. 給 input 付值,這邊是給隨機數
  4. 輸入 input/output 以後進行推論

而這邊的推論函數 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 了。


上一篇
Day 10 - 分析 X-Cube-AI 程式碼 (2)
系列文
從零開始打造超邊緣AI應用11
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言