使用 NNOM ,我們可以先用 Python 訓練好模型,然後移植到C環境進行預測!

準備

  1. 把這個 repository clone 下來: https://github.com/majianjia/nnom

  2. 若使用 VSCode 開發,安裝這個套件:

    https://marketplace.visualstudio.com/items?itemName=danielpinto8zz6.c-cpp-project-generator

建立專案

因為要進行動態連結,所以必須將所有檔案包裝於一個專案中,並用 makefile 編譯和執行。

  1. 進入 vscode ,用剛剛的套件產生專案 template 。
  2. Makefile 中,把 SRC 後面的 src 刪掉,這樣程式進入點才會在作業目錄。

Untitled

  1. 移植 nnom 標頭檔和 source 檔:

    cp -r path/to/nnom/inc/ path/to/workpath/include/
    cp -r path/to/nnom/port/ path/to/workpath/include/
    cp -r path/to/nnom/src/* path/to/workpath/src/
    
  2. 作業目錄建立 main.c 開始寫程式!

    實作

  3. 執行時可呼叫 ./output/main ,繞過 vscode 的除錯。

實作

使用 nnom/examples/mnist-simple 來練習

  1. 用 mnist_simple.py 訓練模型 (直接執行即可,確認腳本路徑 ../../scripts)

    在程式碼裡面可以看到連接 NNOM 的 API,把模型轉換成 .h 檔

    # select a few image and write them to image.h
    image_to_cfile(x_test*127, y_test_num, 10, file='image.h')
    
    # save weight
    generate_model(model, np.vstack((x_train, x_test)), name="weights.h")
    
  2. 把生成的 weights.h, images.h 還有 mcu/main.c 丟到 C project 的作業目錄當中

    此時,你的目錄應該會長這樣

    Untitled

  3. 或是,使用調整過的 main.c

    hello_nnom.zip

    /*
     * Copyright (c) 2018-2020, Jianjia Ma
     *
     * SPDX-License-Identifier: Apache-2.0
     *
     * Change Logs:
     * Date           Author       Notes
     * 2019-03-29     Jianjia Ma   first implementation
     */
     
    #include <stdio.h>
    
    #include "nnom.h"
    #include "image.h"
    #include "weights.h"
    
    nnom_model_t *model;
    
    int main(void){
    
    	model = nnom_model_create();
    	model_run(model);
    	mnist(2, (char*[]){"mnist", "0"});
    }
    
    const char codeLib[] = "@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\\\|()1{}[]?-_+~<>i!lI;:,\\"^`'.   ";
    void print_img(int8_t * buf)
    {
        for(int y = 0; y < 28; y++) 
    	{
            for (int x = 0; x < 28; x++) 
    		{
                int index =  69 / 127.0 * (127 - buf[y*28+x]); 
    			if(index > 69) index =69;
    			if(index < 0) index = 0;
                printf("%c",codeLib[index]);
    			printf("%c",codeLib[index]);
            }
            printf("\\n");
        }
    }
    
    // Do simple test using image in "image.h" with model created previously. 
    void mnist(int argc, char** argv){
    
    	uint32_t tick, time;
    	uint32_t predic_label;
    	float prob;
    	int32_t index = atoi(argv[1]);
    	
    	if(index >= TOTAL_IMAGE || argc != 2)
    	{
    		printf("Please input image number within %d\\n", TOTAL_IMAGE-1);
    		return;
    	}
    	
    	printf("\\nprediction start.. \\n");
    	
    	// copy data and do prediction
    	memcpy(nnom_input_data, (int8_t*)&img[index][0], 784);
    	nnom_predict(model, &predic_label, &prob);
    	
    	//print original image to console
    	print_img((int8_t*)&img[index][0]);
    	
    	printf("Time: %d tick\\n", time);
    	printf("Truth label: %d\\n", label[index]);
    	printf("Predicted label: %d\\n", predic_label);
    	printf("Probability: %d%%\\n", (int)(prob*100));
    }
    
    void nn_stat(){
    	model_stat(model);
    	printf("Total Memory cost (Network and NNoM): %d\\n", nnom_mem_stat());
    }