Lenet 是一系列网络的合称,包括 Lenet1 - Lenet5,由 Yann LeCun 等人在 1990 年《Handwritten Digit Recognition with a Back-Propagation Network》中提出,是卷积神经网络的 HelloWorld。
神经网络部分我是看这位up主学习的。
Lenet是一个 7 层的神经网络,包含 2 个卷积层,2 个池化层,3 个全连接层。其中所有卷积层的所有卷积核都为 5x5,步长 strid=1,池化方法都为全局 pooling,激活函数为 Sigmoid。
使用pytorch实现如下:
对上述网络模型进行训练后,将模型参数进行保存,方便我们之后对参数的读取。
读取模型参数代码如下:
打印出来后,我们可以看到主要的几个参数模块分为:
conv1.weightconv1.biasconv2.weightconv2.biasfc1.weightfc1.biasfc2.weightfc2.biasfc3.weightfc3.bias之后我们便只需要一个一个打印并保存为单独的dat文件即可。
二、参数格式打开我们的conv1_weight.dat文件可以看到,数据被以数个"[ ]"所分隔,如下:
其中最里层的"[ ]"是5x5卷积核参数,"[[ ]]"是3个通道(第一层输入则表示RGB3个通道),"[[[ ]]]"是16个卷积核。
但这种格式不利于我们在HLS中调取,因此需要对数据格式进行单独的调整,在HLS设计中卷积层的权重是由3维数组组成的,因此我们也要将dat数据也改成3维数组可读的形式。首先,我们需要知道3维数组在文件的形式,如下:
所以dat格式如下:
除了第一层卷积与第二层卷积是上述格式外,其余的数据均为如下:
将其保存在同一目录下:由于第一层全连接层参数过多,我将其分成了上下两部分,分开调用
三、HLS设计1)、conv函数设计输入类型的是HLS中的半浮点型half(16位,能较为节省空间,需要更小的话可以使用8位定点数)。
input : 输入的图像数据(conv1是32x32,conv2是14x14)
weight : 输入权重
in_row : 输入大小(32或14)
out_row : 输出大小 (28或10)
core : 卷积核大小 (5)
3)、pool函数设计这个函数是采用最大值池化
input : 输入数据(28或10)
output : 输出数据 (14或5)
in_row : 输入大小 (28或10)
out_row : 输出大小 (14或5)
4)、激活函数设计sigmoid激活函数
a : 需要激活的数据
size : 输入大小 (28x28等)
5)、端口设计通过axi 总线调用模块,并借助bram来传递数据。
6)、仿真测试7)、HLS设计报告四、Block Design设计需要用到的:SD卡、uart、HLS生成的IP核、两个bram模块以及bram controller(分别控制输入与输出)
如图:
Data address editor :
五、SDK设计(没有读卡器 T_T)所以就模拟一下读卡的场景:将图像数据存入SD卡里成为txt文件,再从SD卡中读取并传到bram0里作为输入数据传输到lenet IP核中,再从bram1中读取结果并打印出来。
其中SD卡的操作部分就不赘述了,不熟悉的可以去看看正点原子的教程,里面讲得很详细。
将数组传入bram0:
初始化lenet IP核以及读取bram1数据并打印:
最终结果如图:
结果与仿真测试一致。
参考文章:
如何从零开始将神经网络移植到FPGA(ZYNQ7020)加速_Jarvis码员的博客-CSDN博客_zynq 神经网络