RISC-V MCU中文社区

【分享】 从零开始利用NMSIS库搭建神经网络(一)

发表于 开源蜂鸟E203 2023-05-18 20:15:11
0
1471
1

报名编号:CICC2469

团队名称:AM

学校名称:广东工业大学

队伍成员:林贤、刘合明、谢泽铭

指导老师:郑欣、高怀恩

环境:Vivado2021.1、NucleiStudio_IDE_202102-win64

内容:从零开始利用NMSIS库搭建神经网络,这一节主讲基本的NMSIS库卷积函数的解读。

一、自测检查
利用NMSIS库之前,先利用简单的加减乘除测试下仿真输出情况是否正确,要先确保自己的整个仿真过程正确无误,才能去debug自己设计的NN是否正确。测试的仿真代码可以先定好,自己想要实现的功能的位宽,这里测试的是32bit和64bit的例子:

#include 
#include "hbird_sdk_soc.h"

int main(void)
{
    // define 16bit input
    int16_t input_data_A;
    int16_t input_data_B;

    // 32bit input
    int32_t input_data_C;
    int32_t input_data_D;

    // 32bit
    int32_t output_data_sum32;        // +

    // 64bit
    int64_t output_data_sum64;        // +

    // initial

    // printf function
    printf("%d\n", output_data_sum32);
    return 0;
}

这里只展示了代码的思路,具体操作很简单,按照自己的想法去设计测试代码,然后进行build,生成.Verilog代码文件之后,用该文件来进行仿真即可。

二、代码解读与实践
按照官方给出的NMSIS库,我们找到对应的example示例来学习,路径为:NMSIS/NN/Examples/cifar10,里面有几个最为主要的文件,分别是:示例的权重文件riscv_nnexamples_cifar10_weights.h、示例的输入图片数据文件:riscv_nnexamples_cifar10_inputs.h、示例的参数配置文件:riscv_nnexamples_cifar10_parameter.h、示例的顶层c文件:riscv_nnexamples_cifar10.c,首先从最简单的riscv_convolve_HWC_q7_basic()函数看起,进入这个函数的定义文件riscv_convolve_HWC_q7_basic.c文件里面,可以看到如下定义:

riscv_status riscv_convolve_HWC_q7_basic(const q7_t *Im_in,
                                     const uint16_t dim_im_in,
                                     const uint16_t ch_im_in,
                                     const q7_t *wt,
                                     const uint16_t ch_im_out,
                                     const uint16_t dim_kernel,
                                     const uint16_t padding,
                                     const uint16_t stride,
                                     const q7_t *bias,
                                     const uint16_t bias_shift,
                                     const uint16_t out_shift,
                                     q7_t *Im_out,
                                     const uint16_t dim_im_out,
                                     q15_t *bufferA,
                                     q7_t *bufferB)

这里分别是:输入图片数据Im_in、输入图片尺寸dim_im_in、输入通道数ch_im_in、权重文件wt、输出通道数ch_im_out、卷积核尺寸dim_kernel、填充padding、步长stride、偏置bias、偏置移位bias_shift、输出移位out_shift、输出图片Im_out、输出图片尺寸dim_im_out、以及两个buffer。

自己设计的简单卷积操作:6X6输入图片,卷积核是3X3尺寸,输出为4X4尺寸:

#include 
#include "hbird_sdk_soc.h"

// data
#define CONV1_WT {...}
#define IMG_DATA {...}
#define CONV1_BIAS {0}

// define
#define CONV1_IM_DIM 6                // image dimension
#define CONV1_IM_CH 1                // image channel
#define CONV1_KER_DIM 3                // kernel dimension
#define CONV1_PADDING 0                // padding
#define CONV1_STRIDE 1                // stride
#define CONV1_OUT_CH 1                // output channel
#define CONV1_OUT_DIM 4                // output dimension
#define CONV1_BIAS_LSHIFT 0            // bias left shift
#define CONV1_OUT_RSHIFT 0            // output right shift

static int8_t conv1_wt[CONV1_IM_CH * CONV1_KER_DIM * CONV1_KER_DIM * CONV1_OUT_CH] = CONV1_WT;
static int8_t conv1_bias[CONV1_OUT_CH] = CONV1_BIAS;
int8_t scratch_buffer[4 * 4 * 1];                    // output dim
int8_t scratch_buffer2[6 * 6 * 1] = IMG_DATA;        // input dim
int16_t col_buffer[2 * 4 * 4 * 6 * 2];

int main(void)
{
    int8_t *img_buffer1 = scratch_buffer;        // output
    int8_t *img_buffer2 = scratch_buffer2;        // input

    riscv_convolve_HWC_q7_basic(img_buffer2, CONV1_IM_DIM, CONV1_IM_CH, conv1_wt, CONV1_OUT_CH, CONV1_KER_DIM, CONV1_PADDING,CONV1_STRIDE, conv1_bias, CONV1_BIAS_LSHIFT, CONV1_OUT_RSHIFT, img_buffer1, CONV1_OUT_DIM, (int16_t *) col_buffer, NULL);
    // note: img_buffer1 is output data
    return 0;
}

这里数据自己定义即可,至此,迈出了搭建NN的第一步,实现卷积操作,其他类似的卷积函数其实都是同一个道理,不多讲,最后再自己打印出数据,拿去仿真即可验证结果。

喜欢1
用户评论
小小白吖

小小白吖 实名认证

懒的都不写签名

积分
问答
粉丝
关注
  • RV-STAR 开发板
  • RISC-V处理器设计系列课程
  • 培养RISC-V大学土壤 共建RISC-V教育生态
RV-STAR 开发板