ZYNQ MPSOC 搭建 LeNet-5 卷积神经网络

目录

LeNet-5介绍

PS资源搭建LeNet-5

1. LeNet-5介绍

LeNet-5 是一个非常经典和成功的卷积神经网络结构,它在手写数字识别上取得了惊人的成绩。当年美国大多数银行就是用它来识别支票上面的手写数字的。能够达到这种商用的地步,它的准确性可想而知。

C1 层是一个卷积层,卷积运算一个重要的特点就是,通过卷积运算,可以使原信号特征增强,并且降低噪音。它由 6 个特征图 Feature Map 构成。特征图中每个神经元与输入中 5*5 的邻域相连。特征图的大小为 28*28。C1 有156 个可训练参数,每个滤波器有 5*5=25 个权重参数和 1 个偏置参数,一共 6 个滤波器,共(5*5+1)*6=156 个参数,共 156*(28*28)=122,304 个连接。

S2 层是一个池化层,利用图像局部相关性的原理,对图像进行子抽样,可以减少数据处理量同时保留有用信息。有 6 个 14*14 的特征图,特征图中的每个单元与 C1 中相对应特征图的 2*2 邻域相连接。S2 层每个单元的 4 个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过 sigmoid 函数计算。S2 层每个单元的 4 个输入相加,乘以一个可训练参数,再加上一个可训练偏置。每个单元的 2*2 感受野并不重叠,因此 S2 中每个特征图的大小是 C1 中特征图大小的 1/4(行和列各 1/2)。S2 层有 12(6*(1+1)=12)个可训练参数和 5880(14*14*(2*2+1)*6=5880)个连接。

C3 层也是一个卷积层,它同样通过 5x5 的卷积核去卷积层 S2,然后得到的特征 map 就只有 10x10 个神经元,但是它有 16 种不同的卷积核,所以就存在 16 个特征 map 了。

S4 层是一个下采样层,由 16 个 5*5 大小的特征图构成。特征图中的每个单元与 C3 中相应特征图的 2*2 邻域相连接,跟 C1 和 S2 之间的连接一样。S4 层有 32 个可训练参数(每个特征图 1 个因子和一个偏置)和 2000 个连接。

C5 层是一个卷积层,有 120 个特征图。每个单元与 S4 层的全部 16 个单元的 5*5 邻域相连。由于 S4 层特征图的大小也为 5*5(同滤波器一样),故 C5 特征图的大小为 1*1:这构成了 S4 和 C5 之间的全连接。C5 层有 48120个可训练连接。

F6 层有 84 个单元,与 C5 层全相连。有 10164 个可训练参数。F6 层计算输入向量和权重向量之间的点积,再加上一个偏置,将其传递给 sigmoid 函数产生一个输出。

2. PS 资源搭建 LeNet-5

Block Design:

CAM0 部分的内部框图如下所示:

PS 部分 main 函数代码如下所示:

int main()
2 {
3 u8 number;
4 Xil_DCacheDisable();
5 wfram1_cap_done = 0;
6
7 VIDEOOUT_DBUF[0] = (u8*)BUF1_ADDR;
8 VIDEOOUT_DBUF[1] = (u8*)BUF2_ADDR;
9 VIDEOOUT_DBUF[2] = (u8*)BUF3_ADDR;
10
11 memset(VIDEOOUT_DBUF[0], 0x00, IMG_SIZE);
12 memset(VIDEOOUT_DBUF[1], 0x00, IMG_SIZE);
13 memset(VIDEOOUT_DBUF[2], 0x00, IMG_SIZE);
14
15 Xil_DCacheFlushRange((INTPTR)VIDEOOUT_DBUF[0], IMG_SIZE);
16 Xil_DCacheFlushRange((INTPTR)VIDEOOUT_DBUF[1], IMG_SIZE);
17 Xil_DCacheFlushRange((INTPTR)VIDEOOUT_DBUF[2], IMG_SIZE);
18
19 XGpio_Initialize(&rstn_5640, XPAR_GPIO_RSTN_DEVICE_ID);
20 XGpio_SetDataDirection(&rstn_5640, 1, 0x0);
21 XGpio_DiscreteWrite(&rstn_5640, 1, 0x0);
22
23 sccb_gpio_init(&sscb_cam0,XPAR_CAM0_GPIO_SCCB_DEVICE_ID);
24
25 ov5640_init(sscb_cam0,1280,720,0x46,0x01);
27 init_intr_sys();
28
29 XGpio_DiscreteWrite(&rstn_5640, 1, 0x1);
30
31 while(1)
32 {
33 if(wfram1_cap_done == 1)//wait uifdmadbuf write channel intrrupt
34 {
Xil_DCacheEnable();
36 image_process((xrgb *)VIDEOOUT_DBUF[fdma_buf], 720, 1280);
37 Xil_DCacheFlushRange((INTPTR) VIDEOOUT_DBUF[fdma_buf], (INTPTR)(IMG_SIZE)) ;
38 //Xil_DCacheDisable();
39 Dpdma_FrameBuf_init(&RunCfg,VIDEOOUT_DBUF[fdma_buf]);
40 Xil_DCacheDisable();
41 number = lenet_process();
42 xil_printf("The number is: %d\r\n", number);
43 wfram1_cap_done =0;//clear for next
44 }
45 }
46
47 return XST_SUCCESS;
48}

函数image_process(xrgb *src_data, u32 rows, u32 cols) 进行了图像的缩放工作,这个函数有 3 个输入参数,其中第一个参数是一个结构体类型的指针,指向的是原图像第一个像素数据在内存中的地址,通过判断像素点的位置,如果像素点的位置是在水平方向为 584 到 695,垂直方向为304 到 415 内,那么则每隔 4 个像素提取出一个像素,这样就相当于将待处理的图像缩小了四倍,变成了一个 28*28的图像,然后保存到 img_data 这个数组中。它的原函数代码如下所示:

1 void image_process(xrgb *src_data, u32 rows, u32 cols)
2 {
3 u32 index = 0;
4 u32 count = 0;
5
6 for(u32 y = 0; y < rows ; y++)
7 {
8 for(u32 x = 0; x < cols ; x++)
9 {
10 if(x>=584 && x<=695 && y>=304 && y<=415)
{
12 if(x%4 == 0 && y%4 == 0)
13 {
14 img_data[count] = src_data[index].r;
15 count++;
16 }
17 }
18 index++;
19 }
}
21}

下面我们就将 img_data 输入给 Lenet 卷积神经网络做处理,首先是卷积层计算:

1 for(int n=0; n<30; n++)
2 {
3 for(int row=0; row<=23; row++)
4 {
5 for(int col=0; col<=23; col++)
6 {
7 conv_temp = 0;
8 for(int x=0; x<5; x++)
9 {
10 for(int y=0; y<5; y++)
11 {
12 conv_temp += img_data[row*28+col+x*28+y] * cnn_param_w[x*5+y+n*25];
13 }
14 }
15 conv_temp += cnn_param_b[n];
16
17 // 激活函数
18 if(conv_temp > 0)
19 conv_rlst[row*24+col+n*24*24] = conv_temp;
20 else
21 conv_rlst[row*24+col+n*24*24] = 0;
22 }
23 }
24}

卷积层的计算结果保存到了 conv_temp 这个变量中,代码的第 18 行到第 21 行进行的是 ReLU 激活函数,通过激活函数增加了卷积神经网络的非线性处理能力。cnn_param_w 表示的是卷积神经网络的权重参数,cnn_param_b表示的是卷积神经网络的偏置参数。

卷积层的输出连接到池化层的输入,代码如下所示:

1 for(int n=0; n<30; n++)
2 {
3 for(int row=0; row<24; row=row+2)
4 {
5 for(int col=0; col<24; col=col+2)
6 {
7 pool_temp = 0;
8 for(int x=0; x<2; x++)
9 {
10 for(int y=0; y<2; y++)
11 {
12 if(pool_temp <= conv_rlst[row*24+col+x*24+y+n*576])
13 pool_temp = conv_rlst[row*24+col+x*24+y+n*576];
14 }
15 }
16 pool_rslt[(row/2)*12+col/2+n*144] = pool_temp;
17 }
18 }
19}

代码的第 12 行和第 13 行通过最大值池化实现了图像的降采样工作。将程序下载到板子上,可以看到串口打印了识别到的数字。

需要工程文件与指导的同仁,请私聊。

本文来源:RudyLews

最新文章

最新文章