基于MNIST数据集的手写数字识别是神经网络(Neural Network)的经典应用。
本文将讨论一种名为“ZYNET”的全连接神经网络框架,它可以自动生成针对FPGA的硬件实现架构。我们以手写数字识别为例,在ZYNQ平台上对该架构进行验证。本章包括以下几个部分:
1环境配置
2下载工程文件
3训练神经网络
4 FPGA实现
5 FPGA仿真
6 ZYNQ开发板验证
1环境配置安装Anaconda
Anaconda是一个功能强大的Python开发环境,主要用于科学计算、数据分析和机器学习等领域。它包含了各种用于数学计算、数据绘图的扩展包,并提供了交互式的开发工具(Jupyter Notebook)。
我们需要下载并安装Anaconda,通过它我们可以非常方便地安装各种深度学习框架,如知名的TensorFlow、PyTorch,以及本文中所使用的ZYNET。
首先进入Anaconda官网(https://www.anaconda.com/download),找到对应操作系统的Anaconda安装包进行下载。
图 1 点击跳过注册,进入下载界面
图 2 下载Windows版本
图 3 下载完成后双击安装包进行安装
Anaconda安装过程如下图所示:
图 4 Anaconda安装完成
安装ZYNET
在系统的“开始”菜单中找到“Anaconda3 (64-bit)”并展开,在“Anaconda Prompt”上右击,选择“以管理员身份运行”。
图 5 以管理员身份运行Anaconda Promp
在弹出的命令窗口中,输入指令“python --version”,查看当前Python的版本:
图 6 在命令窗输入指令查看python版本
接下来输入指令“pip install zynet”,安装“基于ZYNQ的神经网络库”,此过程需要保持联网:
图 7 安装zynet
2 下载工程文件
在作者vipinkmenon的GitHub主页下载配套的工程文件,网址vipinkmenon (Vipin K) · GitHub,工程名为neuralNetwork:
图 8 vipinkmenon的GitHub主页
图 9 下载neuralNetwork工程文件
将下载的工程文件压缩包解压出来,路径中不要包含中文或者特殊字符:
图 10 解压工程文件
在解压后的工程目录中,打开并查看“Tut-6”文件夹中的内容:
图 11 解压后的工程目录
将“Tut-6”文件夹中的全部内容拷贝到一个新建的文件夹中,文件夹的名称由英文字母和下划线组成,如“DaLei_FPGA”:
图 12 拷贝Tut-6文件夹中的内容
3 训练神经网络
使用Notepad++打开上图中名为“trainNN.py”的文件:
图 13 trainNN.py中的网络结构
在代码的第6行,修改网络的结构为“net = network2.Network([784, 30, 20, 10])”,然后保存,如下图所示:
图14 修改后的网络结构
在Anaconda Prompt窗口通过cd指令将目录切换至刚刚新建的文件夹,我这里的目录为“F:\task\ZYNQ_NN\DaLei_FPGA”:
图 15 切换Anaconda Prompt窗口的工作目录
输入指令“python trainNN.py”,对修改后的网络进行训练:
图 16 训练修改后的神经网络
网络的训练将迭代30次,该迭代次数是由图 14中trainNN.py文件的第9行——“net.SGD(training_data, 30, 10, 0.1”中的第一个数字所指定的。
在前面几次迭代过程中,神经网络的准确率将不断提高,如下图所示:
图 17 神经网络的训练过程
神经网络训练结束之后,可以看到最终的准确率,我这里是96.47%:
图 18 训练过程迭代30次结束
在训练结束后,网络的“权重”和“偏置”数据会输出到名为“WeigntsAndBiases.txt”文件中:
图 19 网络的权重和偏置信息
需要注意的是,在上面的训练过程中,神经网络所采用的激活函数为ReLu。接下来,我们将使用Sigmod作为激活函数,重新进行网络的训练。
使用Notepad++打开上图中名为“network2.py”的文件,代码的第326行用于指定网络所使用的激活函数,而第331行是它的反函数。我们需要注释掉代码的329行和334行,同时取消对328行和333行的注释,即使用Sigmoid代替Relu作为激活函数。
图 20 修改前,使用ReLu激活函数
按照下图箭头所指示的位置进行修改,设置激活函数的输出为Sigmoid,并保存:
图 21 修改后,使用Sigmoid激活函数
接下来,在Anaconda Prompt窗口再次输入指令“python trainNN.py”,重新对网络进行训练:
图 22 重新进行训练
激活函数选择Sigmoid后重新训练,迭代30次之后网络的准确度为96.75%
图 23 激活函数设置为Sigmoid的网络训练结果
在重新训练之后,网络相应的权重和偏置文件也会更新:
4 FPGA实现
拷贝neuralNetwork-master\Tut-8目录中的“mnistZyNet.py”文件至当前文件夹(DaLei_FPGA):
图 25 拷贝mnistZyNet.py
使用Notepad++查看mnistZyNet.py文件:
上图红色方框中描述的网络结构与图 14 修改后的网络结构相同。需要注意的是,在硬件实现的过程中,网络的最后一层没有使用常见的“softmax”,而是替换成了“hardmax”,如代码中的第11行所示。该模块通过比较各个分类结果的评分大小,输出评分最高的类别,从而避免在FPGA中实现softmax的复杂运算。
在代码第20行,红色箭头所指示的dataWidth用于指定输入数据的位宽,由整数部分和小数部分组成,其中整数部分的位宽由inputIntSize所指定(包含符号位)。
在Anaconda Prompt窗口输入指令“python mnistZyNet.py”,执行该文件:
图 27 执行mnistZyNet,生成FPGA实现代码
以上指令会生成神经网络所对应的FPGA实现代码,同时解析图 24中的权重和偏置文件WeigntsAndBiases.txt,生成后缀为.mif的ROM初始化文件。
在解析网络权重的过程中,窗口中会打印出权重数据整数部分所对应的bit位宽,即上图中第一个红色箭头所指的数字4。该数字被用来设置图 26中第20行的变量weightIntSize。
上面的指令同样会创建一个Vivado工程,在工程中完成整个神经网络的硬件实现架构。该过程需要在系统变量中添加Vivado的路径,否则会打印信息:‘Vivado’不是内部或外部命令, 也不是可运行的程序。
在系统变量中添加Vivado的过程如下图所示:
图 28 在系统变量中添加Vivado
环境变量添加完成后,重启Anaconda Prompt,并将目录切换至“DaLei_FPGA”,最后重新执行指令“python mnistZyNet.py”。
接下来Vivado会启动,并创建相应的工程,在该工程中完成神经网络的RTL实现、封装IP核并创建Block Design,如下图所示:
图 29 创建Vivado工程
工程创建完毕后Vivado会自动退出,然后在当前文件夹中会新增一个名为“myProject1”的工程文件夹。同时还新增了一个名为“src”的文件夹,其中包含了神经网络的HDL代码、mif文件以及tb文件
图 30 自动创建的Vivado工程
在名为“myProject1”的文件夹中双击打开后缀为.xpr的Vivado工程:
图 31 打开Vivado工程
在打开的Vivado工程中可以看到顶层模块是一个名为“zyNet”的神经网络,下面例化了三个名为Layer_1、Layer_2、Layer_3的全连接网络层,以及一个名为maxFinder的“hardmax”层:
图 32 创建的Vivado工程
在上面的Vivado工程中还包含了一个名为“myBlock2”的设计,双击打开后如下图所示:
图 33 基于ZYNQ的Block Design
图中是一个基于ZYNQ平台的Block Design,除了前面所实现的zyNet神经网络,还添加了一个AXI DMA。ZYNQ的PS端可以通过DMA输出大小为28*28的MNIST手写数据集,zyNet神经网络在识别结束后输出中断信号,通知PS端从AXI-Lite接口读取手写数字的识别结果。
ZYNET最终实现的神经网络模块如下图所示:
图 34 最终实现的神经网络模块
在zyNet模块中,AXI-Lite接口(s_axi)除了用来读取最终的识别结果,还可以用来动态配置神经网络的权重和偏置。这样就可以在网络重新训练之后,通过PS端非常方便地更新网络的权重和偏置,而不需要重新编译硬件(生成bitstream)。前提是在mnistZyNet.py文件的第14行中(图 26),需要将pretrained='Yes'修改为pretrained='No',否则权重信息将以mif文件的形式存储在PL中的ROM里,无法通过PS进行动态配置。
5 FPGA仿真
在Vivado左侧的导航栏里点击“Run Simulation”,然后选择“Run Behavioral Simulation”,如下图所示:
仿真运行后在控制台会提示警告信息,无法读取名为“test_data_0000.txt”的文件:
图 36 仿真过程中的警告信息
上面的警告信息是由于仿真过程需要将MNIST数据集中的测试数据作为输入,因此需要生成测试数据。首先在Vivado中结束当前的仿真过程,然后拷贝neuralNetwork-master\Tut-8目录中的“genTestData.py”文件至当前文件夹(DaLei_FPGA):
图 37 拷贝genTestData.py
由于DaLei_FPGA文件夹中已经存在相同名称的文件,因此在弹出的对话框中选择“替换目标中的文件”,最后在Notepad++中打开genTestData.py:
图 38 用于生成测试数据的脚本genTestData.py
代码的第3行指定了输出测试数据的路径,即Vivado工程的仿真目录。红色箭头所标注的位置指示出测试数据的bit位宽,包含整数部分和小数部分,其中整数部分的位宽由IntSize指定(包含符号位)。测试数据通过解压名为“mnist.pkl.gz”的压缩包获得,即MNIST数据集。
在Anaconda Prompt窗口中输入指令“python genTestData.py”,等待指令运行结束后,会在指定的路径中生成一万个测试数据,编号从“test_data_0000.txt”一直到“test_data_9999.txt”。
图 39 生成仿真过程使用的测试数据
按照图 35中的操作过程重新运行仿真,在SIMULATION界面中,点击工具栏中的“Run All”按钮,如下图所示:
图 40 Vivado仿真界面选择Run All
仿真过程会对MNIST数据集中的100个测试用例进行识别,并统计FPGA神经网络的识别准确率,每个测试用例的识别结果和期望结果均打印在控制台中:
图 41 仿真过程中打印的识别结果和准确率
上图中红色箭头指示出FPGA神经网络在仿真过程中的识别准确率为99%,这是针对前一百个测试用例的统计结果。如果需要修改仿真过程中测试用例的数目,可以结束仿真过程,然后在Simulation Sources中双击打开top_sim.v,在代码的第24行修改名为MaxTestSamples的变量:
图 42 指定仿真过程中测试数据的数目
6 ZYNQ开发板验证
在“征服者”ZYNQ开发板上搭建测试环境:
图 43 在征服者ZYNQ开发板上验证
使用OV5640摄像头采集图像,对手写数字进行识别,并将识别的结果打印在HDMI显示器的左上角。验证结果如下图所示:
图 44 ZYNET手写数字识别结果
最后给出整个设计完整的Block Design架构图:
图 45 在ZYNQ上实现手写数字识别Block Design设计
该设计的视频讲解会在b站【第二期——ZYNQ图像处理】课程中更新,大家有兴趣可以关注磊哥的b站账号(大磊FPGA)。
标签:仿真,FPGA,py,Vivado,神经网络,ZYNQ,Anaconda,手写 From: https://blog.csdn.net/sunshine_atk/article/details/140923471