基于神经网络的手写数字识别及其ZYNQ实现

        基于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, 3020, 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的网络训练结果

在重新训练之后,网络相应的权重和偏置文件也会更新:

图 24 网络重新训练后,权重和偏置文件会更新

 4 FPGA实现 

拷贝neuralNetwork-master\Tut-8目录中的“mnistZyNet.py”文件至当前文件夹(DaLei_FPGA):

图 25 拷贝mnistZyNet.py

使用Notepad++查看mnistZyNet.py文件:

图 26 ZyNet模型

       上图红色方框中描述的网络结构与图 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”,如下图所示:

图 35 运行仿真

仿真运行后在控制台会提示警告信息,无法读取名为“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)。

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值