文章目录

  • FPGA实现mnist手写数字识别
  • ① 环境配置
  • ② 数据集及代码下载
  • ③ 代码操作
  • (1)训练模型
  • (2)权重输出
  • (3)关于灰度转换


FPGA实现mnist手写数字识别

① 环境配置

使用的环境:tf1.12,具体配置见here

FPGA CNN 数字识别 fpga手写数字识别_fpga开发


首先打开环境tf1.12,,再安装以下的包:

  • opencv
    这里下载“linux-64/opencv3-3.1.0-py36_0.tar.bz2”,通过共享文件夹copy到download文件夹中,在文件夹下打开终端,输入以下命令进行安装:
conda install opencv3-3.1.0-py36_0.tar.bz2
  • matplotlib(时刻注意是py36)
conda install matplotlib

FPGA CNN 数字识别 fpga手写数字识别_python_02

FPGA CNN 数字识别 fpga手写数字识别_python_03

  • Pillow(貌似不用了,上面已经安装过了)
conda install Pillow
  • Pandas
conda install pandas

FPGA CNN 数字识别 fpga手写数字识别_权重_04

② 数据集及代码下载

在download文件夹下打开终端,用下列命令下载原代码

git clone https://github.com/suisuisi/FPGAandCNN.git

FPGA CNN 数字识别 fpga手写数字识别_权重_05

包括以下python脚本:

  • 常用图像处理函数定义以及工程所需要的基本函数:a00_common_functions.py,a02_generate_random_non_number.py
  • 网络模型构建:a01_model_low_weights_digit_detector.py
  • 网络训练与测试:r01_train_neural_net_and_prepare_initial_weights.py
  • 权重浮点转定点缩放系数计算,并压缩权重:r02_rescale_weights_to_use_fixed_point_representation.py
  • 定点权重位宽选择:r03_find_optimal_bit_for_weights.py
  • 生成对输入图像进行灰度转换的verilog代码:r04_verilog_generator_grayscale_file.py
  • 生成定点权重数据:r05_generator_database.py

③ 代码操作

下载的是一个完整的工程文件夹,包括已经训练好的模型和已经转换过的权重信息,下面是尝试自己用脚本运行,得到结果。

(1)训练模型

在documents文件夹中,新建一个keras_mnist文件夹,将dataset和7个脚本复制到该空文件夹中,再建立一个空文件夹weights。

FPGA CNN 数字识别 fpga手写数字识别_cnn_06

修改python3 r01_train_neural_net_and_prepare_initial_weights.py代码,将训练次数设置为50次(201行),每次训练200个batch_size(193行),学习率为0.001(182行):

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_07

输入以下命令运行该脚本(在tf1.12环境下):

python3 r01_train_neural_net_and_prepare_initial_weights.py

这个脚本的逻辑是,首先会展示一下model:

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_08

然后会判断数据集是否存在:

FPGA CNN 数字识别 fpga手写数字识别_cnn_09

然后判断模型是否存在,不存在则重新开始训练:

FPGA CNN 数字识别 fpga手写数字识别_python_10

然后就开始训练了…

FPGA CNN 数字识别 fpga手写数字识别_权重_11

训练结束后,使用mnist测试集进行测试,精确度达97.66;使用用户测试集对已训练好的网络进行测试,精确度可以达到80.39%;

这里先节约时间看看流程,所以设置epoch为5,可以看到精确度只达到了0.81,但是看到验证集的测试出错了:

FPGA CNN 数字识别 fpga手写数字识别_权重_12

根据报错,应该是函数调用的错误,在文件中找到该函数的定义与调用:

FPGA CNN 数字识别 fpga手写数字识别_python_13

FPGA CNN 数字识别 fpga手写数字识别_权重_14

可见是函数调用时参数给多了一个,去掉最后一个参数’valid’即可。

FPGA CNN 数字识别 fpga手写数字识别_python_15

修改该错误后重新运行脚本,就不报错了。可见在mnist测试集的精确度达到了0.94。

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_16


使用用户自己的测试集mnist_keras/dataset/test进行测试,精确度只有0.64:

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_17

在keras_mnist/weights文件夹下可以看到存储的权重信息,以及可视化训练精确度曲线图,橙色为训练曲线,蓝色为测试曲线:

FPGA CNN 数字识别 fpga手写数字识别_cnn_18

FPGA CNN 数字识别 fpga手写数字识别_权重_19

这个精确度是不太好的,并且如果模型训练不好,之后权重分布很奇怪,导致权重位宽确定的时候会需要更多的bit数,需要的时间也很长,所以这里还是跑50个epoch吧!(耗时约40min)

可见,此时mnist测试集准确率达到0.97,用户测试集准确率达到0.87。

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_20

FPGA CNN 数字识别 fpga手写数字识别_python_21

(2)权重输出

权重输出,即得到一个database.v文件,包含这么几个步骤:权重转换、权重位宽确定、写verilog文件。

首先,要运行r02_rescale_weights_to_use_fixed_point_representation.py脚本,进行权重转换。

什么是权重转换(转载):

  在网络训练完成后,需要对网络训练后所得参数进行处理,浮点转定点与数据量化。在此,我们通过寻找合适的缩放系数进行定点转换,具体原理如下。
  以神经网络的第一个卷积层为例子,在第一个卷积层输入处,输入是二维矩阵(原始图片)28*28,像素值的范围为[0:1]。

  对于3 * 3卷积,第二层中某个像素(i,j)的值可以如下计算:

FPGA CNN 数字识别 fpga手写数字识别_FPGA CNN 数字识别_22

  由于使用软件已经训练出网络结构中的浮点型权重w (i,j),因此可以计算第二层输入的潜在最小mn和最大mx。设M = max(|mn|,|mx|),这样,我们将w(i,j)除以M的值,我们可以保证对于任意输入数据,第二层的输入都不会不超过1。我们称M为该层的缩放系数。对于第二层,我们使用相同的原理,即该层输出的值(第三层的输入)属于区间[-1;1] 。
  对于所有权重使用上述方法减小,最后一个神经元输出的最大位置是不会发生改变的,也就是说,转换后的权重信息等效于从浮点型权重所提供的信息。在经过缩减系数缩减后,可以确切的知道每个计算阶段输出值的大小范围,使用浮点定点转换算法,可以进一步实现浮点定点转换:FPGA CNN 数字识别 fpga手写数字识别_权重_23,表示浮点数,FPGA CNN 数字识别 fpga手写数字识别_FPGA CNN 数字识别_24表示定点数。

  也就是说:我们必须将转定点后的结果除以2N或者是将其移位N个位置才能得到实数值。
  如果我们对所有可能的输入图像进行排序并关注潜在的最小值和最大值,我们可以获得非常大的缩减系数,这样会在层与层之间降低计算精度。这样将需要很大的位宽和中间结果来表示定点结果。为了避免这种情况,我们只使用训练集的全部(或部分)来查找每层中最可能的最大值和最小值,然后进一步进行浮点定点转换。

因此,输入以下命令运行转换的脚本:

python3 r02_rescale_weights_to_use_fixed_point_representation.py

可见,他会首先搜索每层的最大值和最小值:

FPGA CNN 数字识别 fpga手写数字识别_python_25

然后会根据找到的整体的最大最小值进行浮点转换,最后在keras_mnist/weights文件夹下将生成对应的权重文件keras_model_low_weights_digit_detector_rescaled.h5文件。

FPGA CNN 数字识别 fpga手写数字识别_fpga开发_26

FPGA CNN 数字识别 fpga手写数字识别_FPGA CNN 数字识别_27

然后运行脚本r02_rescale_weights_to_use_fixed_point_representation.py:

在运行前排除一个坑,将1600行左右的创建文件夹的代码移动到main函数的最前面,然后修改os.mkdir()为os.makedirs()【因为创建的是多级目录】,如下图所示。(以免都跑了半天了才发现文件夹创建不成功…血的教训)

FPGA CNN 数字识别 fpga手写数字识别_权重_28

然后输入以下命令运行最后一个脚本:

python3 r05_verilog_generator_neural_net_structure.py

观察一下这个文件,首先会确定最佳的bit_size,这一步是调用的r03_find_optimal_bit_for_weights.py文件的函数,然后会找到之前保存的权重转换过的模型,然后使用大量的file.write()进行写verilog文件。

FPGA CNN 数字识别 fpga手写数字识别_权重_29

FPGA CNN 数字识别 fpga手写数字识别_python_30

FPGA CNN 数字识别 fpga手写数字识别_python_31

关于权重位宽的确定(转载)

  对于具有有限宽度的权重和中间结果的定点计算,不可避免地会出现舍入误差,从一层到另一层累积,并且可能导致“不准确”的预测。为了验证预测的“准确性”,我们通过使用浮点型权重的神经网络对测试图像进行预测,以及使用定点型权重的神经网络对测试图像进行预测,将预测结果进行比较。
  衡量的标准为:不匹配次数 / 总测试次数 * 100%。
  因此,我们为找出合适的定点权重位宽,在脚本中将会从8bit量化位宽开始进行测试。测 试会计算该宽度的定点权重预测精度与浮点权重预测精度的误差。如果误差不为0,则增加位宽宽度,并继续执行测试,直到误差为0。

可以看到,会先按照8bit进行测试,error为7.84%,继续按照9bit测试:

FPGA CNN 数字识别 fpga手写数字识别_python_32

9bit时,结果error为0,因此确定了我们权重位宽为9+1bit:

FPGA CNN 数字识别 fpga手写数字识别_权重_33

最后读取模型,生成以下files:

FPGA CNN 数字识别 fpga手写数字识别_权重_34


FPGA CNN 数字识别 fpga手写数字识别_FPGA CNN 数字识别_35

(3)关于灰度转换

运行r04_verilog_generator_grayscale_file.py这个文件生成一个能加下“灰度转换”的verilog文件(其中会调用确定最佳位宽的函数,即在模型训练好后,要根据最终的模型确定灰度转换的代码)。

首先同样排掉这个坑,在文件main函数中添加创建文件夹的代码,或者自己先创建也行…

FPGA CNN 数字识别 fpga手写数字识别_python_36

运行下列命令,可以生成灰度转换的verilog文件:

python3 r04_verilog_generator_grayscale_file.py

生成成功:

FPGA CNN 数字识别 fpga手写数字识别_cnn_37


FPGA CNN 数字识别 fpga手写数字识别_python_38