手写体识别

手写体识别


by 唐维伟、张智欣、刘毅波&陈赏恩

 

第一部分

设计概述 /Design Introduction

 

设计目的、应用领域及适用范围

 

设计目的:

手写识别是常见的图像识别任务。计算机通过手写体图片来识别出图片中的字,与印刷字体不同的是,不同人的手写体风格迥异,大小不一, 造成了计算机对手写识别任务的一些困难。本次任务目标是在FPGA上部署手写识别网络lenet-5,以其实现更高效率更低功耗的设计。

 

学到知识点:

1.CNN的设计及部署、权重文件导出、数据集的整理

2.C代码如何复现网络、HLS优化以及相应的算法结构如何变更、如何构建更适合硬件加速的C代码

3.vivado BD设计、SDK使用以及驱动函数的结构

4.如何导出overlay在jupyter上使用、如何使用jupyter进行PL侧加速IP的调用

5.git使用、如何和组队成员互相协作

 

应用方向:

金融、交通、教育等。

 

设想应用场景:

需要用到数字识别的领域,例如银行号码登记,车牌识别,手写数字识别等等。

 

作品展示照片:

 

 

第二部分

系统组成及功能说明 /System Construction & Function Description

 

计划实现的功能

 

计划实现:

  1. 能够在pynq下,使用python读取数据集并开展数据集验证,报告最终的帧数和最终的精度。
  2. 调用摄像头实时采集手写字符,实现识别功能

 

已实现:

1、使用裸机加载SD卡的权重参数,读取数据集,并使用生成的驱动函数成功驱动PL侧CNN加速IP,并将结果进行统计。最终帧率285帧,精度99%。

2、在jupyter端,成功使用python调用overlay中的加速IP,并将一张测试图像进行处理,将地址发送写入IP寄存器,并成功启动IP得到运行结果。

 

项目计划框图

  1. 技术方向详细说明

 

模型部署:首先在pytorch框架上实现一个简单的lenet-5网络,使用minist数据集训练完成后,需要将pt模型中的各层权重参数和偏置导出,本项目中以特定格式导出到了txt中。这其中有几个注意点,原生的lenet-5网络的池化层是均值池化同时还有池化层权重参数和偏置参数,以及类似于一个另类的卷积层,我们经过考虑,将这样的池化层直接改为均值池化,去掉权重参数和偏置。Lenet-5第三层卷积是不完全卷积,即不是每个输出特征图都与前一层所有特征图相连接,原作者解释是为了保存更完善的信息,但我们将其换为了全卷积,最终训练的模型在验证集上能达到99%的准确率。

 

C语言复现:由于HLS无法对框架进行综合或者使用,需要重新用C语言进行网络的复现,包括卷积层、池化层、全连接层。这部分工作在visual stdio上进行,便于调试和验证。首先不考虑任何硬件结构的影响,以纯软件的方法进行复现,这部分工作主要是为了验证权重是否正确,同时拿0-9共10张已知的手写数字图像输入到网络中,根据输出结果进行调试。这部分工作完成后,需要将C代码进行优化,使之更符合硬件结构,主要包括循环分组以及数组合并等。这里有个注意点是不要在大幅度改动代码后进行测试,每改动一次代码,都需要重新跑一遍,看看结果对不对,这样能够保证前面的每一步都是对的,能够很快速查找到问题所在。

 

HLS优化:在C语言复现后,直接将代码复制到HLS工程中,这个时候的代码已经是可以直接加优化指令的了,本次项目主要用到的优化指令是pipeline、data partition以及unroll,这几个指令就能对网络进行大幅度优化了,由于计算保存全是浮点数类型,因此资源消耗非常大,LUT资源几乎用尽,考虑到时间很短,而且项目组成员各自都有其他事情,所以第一步是首先把整个设计的数据通路搞定,于是没有花更多时间在HLS优化上,在综合和C仿真都没有问题的情况下,导出IP。

 

Vivado工程部分:这部分相对来说比较简单,只需要把生成的IP导入,然后在block design里面连线就可以了。目前裸机部分没有使用DMA,直接通过AXI互联IP接到了PS侧DDR,PS侧只需要给IP配置数组的地址,然后启动IP就行了。

 

SDK部分:这部分是裸机在跑,没有用到操作系统,这样做有一个好处就是可以直接用HLS的测试代码,只需要进行部分更改即可,但在读取SD卡权重参数时遇到不少问题,最终是读取二进制格式权重文件,同时将部分数组扩大一些,这样才能读取到完整的数据,具体原因仍然未知。最后在裸机上面调用硬件IP得到的结果与visual stdio上结果一致。

 

 

Jupyter端部分:这部分花的时间比较多,主要是python调用overlay这个步骤,以及如何驱动IP的问题。本次设计中没有用到DMA,采用的MAXI接口直连PS侧DDR的模式,因此控制overlay中IP寄存器的读写。其中最关键的部分是如何才能把PS侧权重参数以及特征图的地址发过去,这里必须使用xlnk这个东西,在DDR开辟一段物理内存,并获取到真实物理地址,将这个地址写给IP的相应寄存器,这其中需要注意是的初始化必须使用float32,使用float则会开辟出64位的数据内存,由于部署都是在32位上进行,所以需要使用float32进行内存的开辟。还有一个注意点是使用jupyter跑系统,可以不用刷新cache,使用SDK时必须要刷新cache,保持cache一致性,这里则不用。

 

图像读写、预处理:这部分的流程全部是通过PS端进行操作和处理,通过notebook的方式运行,使用到的python库主要有cv2、Pillow、numpy、math和scipy等。图像读取分为从SD卡中读取测试图像以及从USB摄像头中读取图像,读取后的图像经过图像增强、颜色反转、裁剪等流程,转化为Lenet-5要求的输入大小。并将图像存储到本地。

 

 

第三部分

完成情况及性能参数 /Final Design & Performance Parameters

 

已实现功能:

1、在PS跑裸机的情况下,成功将验证集跑通。

验证集一共有8952张手写数字图像,如下图:

在PS侧加载完权重数据和验证集数据后,总共花了约31s,将验证集跑完,平均帧率为285帧,平均处理每幅图像用时3.4ms。

 

 

 

2、Jupyter端可以调用IP并进行处理,结果如下:

   

 

加载权重以及加载overlay总耗时:

对一幅图像进行处理最终耗时:

这里总耗时为9.4ms,计算帧率大概为106帧,比裸机跑要慢很多。

 

 

2020年9月25日 01:01
浏览量:0
点赞
首页    Catalog    2020 Summer School    手写体识别