文章搬运自本人知乎

VGG16网络结构介绍


VGG在2014年由牛津大学Visual GeometryGroup提出,获得该年lmageNet竞赛中Localization Task(定位任务)第一名和 Classification Task (分类任务)第二名。与AlexNet相比,VGG使用了3个3x3卷积核来代替7x7卷积核,使用了2个3x3卷积核来代替5x5卷积核,从而在保证具有相同感知野的条件下,提升了网络的深度,在一定程度上提升了神经网络的效果。下表中,C即为VGG16的网络结构,其中,VGG16中的16是指该网络具有16个包含权重的网络层(卷积层和全连接层)。更具体地,VGG16由13个卷积层和3个全连接层构成,此外,VGG16还包含了5个2×2的最大池化层。

在原始的VGG16模型中,并未包含批归一化层(Batch Normalization,BN),这给VGG16的训练带来了难度。因此,在本文中,我们对VGG16进行了一些修改,如下所示:

  1. 在每一层卷积层后都加上批归一化层(BN层)。
  2. 将三个全连接层替换为一个全局平均池化层和一个全连接层。

修改后的网络结构可由pytorch代码描述如下:

import torch
import torch.nn as nn
# VGG16
class VGG16(nn.Module):def __init__(self):super(VGG16, self).__init__()# 特征提取层self.features = nn.Sequential(nn.Conv2d(in_channels=3,out_channels=64,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(num_features=64),nn.ReLU(),nn.Conv2d(in_channels=64,out_channels=64,kernel_size=3,stride=1,padding=1),nn.BatchNorm2d(num_features=64),nn.ReLU(),nn.MaxPool2d(kernel_size=2,stride=2),nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=128),nn.ReLU(),nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=128),nn.ReLU(),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=256),nn.ReLU(),nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=256),nn.ReLU(),nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=256),nn.ReLU(),nn.MaxPool2d(kernel_size=2,stride=2),nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.MaxPool2d(kernel_size=2, stride=2),nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(num_features=512),nn.ReLU(),nn.MaxPool2d(kernel_size=2, stride=2),)#self.pool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(in_features=512,out_features=5)def forward(self,x):x = self.features(x)x = self.pool(x)x = x.view(x.size(0),-1)result = self.fc(x)return resultif __name__=='__main__':net=VGG16()x=torch.randn(1,3,224,224)print(net(x).size())

卷积BN融合

在模型训练完毕后,我们对卷积层和BN层的权重进行了BN融合操作,其原理也十分简单。由于该操作十分常见,因此本文不再赘述,详细可见博客。

基于FPGA的加速器设计

由于VGG16的绝大部分计算量均集中在卷积层,因此,我们仅设计针对卷积的硬件加速IP核。此外,考虑到卷积层后有时会紧接着一个池化层,因此,如需要进行池化操作,则该硬件加速IP会在卷积完成后立即进行池化操作,然后再写回片外存储器。这样做的好处是省去了不必要的片外访存开销,既降低了延迟又减少了功耗开销。

加速器的设计是平凡的,主要采用的优化方法包括:

循环分片(loop tiling):对卷积层输出特征图的高、宽以及卷积的输出和输入通道进行了分块操作,分块大小分别记为 T r T_r Tr​, T c T_c Tc​, T m T_m Tm​, T n T_n Tn​,目前其值分别取为1414448

定点数量化:将32位浮点数量化为了16位的定点数,其中,小数部分占10位,整数部分占6位,最高位为符号位。在HLS代码中,该数据类型可以表示为ap_fixed<16,6,AP_RND,AP_SAT>。实验表明,16位定点数量化显著减少了加速器的硬件资源消耗(包括存储资源BRAM以及计算资源DSP),同时较好地保持了模型精度。(1个32位浮点数乘法需要消耗3个DSP48E1,而1个16位定点数乘法仅需消耗1个DSP48E1)

流水线:可以有效提升加速器的吞吐率。在HLS中可以简单的通过#pragma HLS PIPELINE II=1实现。

循环展开:所谓循环展开,体现在硬件层面,就是复制多个运算单元,并行执行循环中所需的运算。在本文中,我们在卷积层的输入和输出通道进行了展开,展开的大小等于分块的大小,即 P m = T m P_m=T_m Pm​=Tm​, P n = T n P_n=T_n Pn​=Tn​。在HLS中,循环展开可以通过#pragma HLS UNROLL实现。

乒乓操作:乒乓操作是一个常常应用于数据流控制的设计思想,典型的乒乓操作如下图所示,其处理流程为:输入数据流通过“输入数据选择单元”将数据流等时分配到两个“数据缓冲模块”, 数据缓冲模块可以为任何存储模块,比较常用的存储单元为双口RAM (DPRAM)、单口RAM (SPRAM)、FIFO等。 在第2个缓冲周期, 通过“输入数据选择单元”的切换,将输入的数据流缓存到“数据缓冲模块2”,同时将“数据缓冲模块1”缓存的第1个周期数据通过“输出数据选择单元”的选择,送到“数据流运算处理模块”进行运算处理。事实上,乒乓操作是一种粗粒度的流水线技术,它以硬件资源(或面积)为代价,提升了系统整体的吞吐率。

PS部分设计

HLS代码编写完毕后,可以通过C仿真、C/RTL协同仿真验证其正确性,然后通过C综合将它转化为RTL代码,最后导出为Vivado可用的IP核。在Vivado中,我们将该IP核与ZYNQ IP核通过AXI总线连接在一起,然后综合、实现、生成比特流文件。

上述步骤完毕后,我们便可以在Vitis中编写C/C++代码,通过调用挂载在ZYNQ上的加速器,实现对VGG16网络的加速。如下代码展示了如何在PS上调用FPGA部分的加速器。

void conv_and_pool_init(){XStd_conv_Initialize(&hls_inst, 0);
}void conv_and_pool_pl(short* in,short* weight,short* bias,short* out,int ch_in,int ch_out,int h,int w,int pool){//Xil_DCacheFlushRange((u32)in,ch_in*h*w*sizeof(short));XStd_conv_Set_in1_V(&hls_inst, (u32)in);XStd_conv_Set_in2_V(&hls_inst, (u32)in);XStd_conv_Set_in3_V(&hls_inst, (u32)in);XStd_conv_Set_in4_V(&hls_inst, (u32)in);//XStd_conv_Set_w1_V(&hls_inst, (u32)weight);XStd_conv_Set_w2_V(&hls_inst, (u32)weight);XStd_conv_Set_w3_V(&hls_inst, (u32)weight);XStd_conv_Set_w4_V(&hls_inst, (u32)weight);//XStd_conv_Set_bias_V(&hls_inst, (u32)bias);XStd_conv_Set_out1_V(&hls_inst, (u32)out);XStd_conv_Set_out2_V(&hls_inst, (u32)out);XStd_conv_Set_out3_V(&hls_inst, (u32)out);XStd_conv_Set_out4_V(&hls_inst, (u32)out);//XStd_conv_Set_ch_in(&hls_inst, (u32)ch_in);XStd_conv_Set_ch_out(&hls_inst, (u32)ch_out);XStd_conv_Set_fm_size(&hls_inst, (u32)h);XStd_conv_Set_pool(&hls_inst, (u32)pool);XStd_conv_Start(&hls_inst);while(XStd_conv_IsDone(&hls_inst)==0);//if(pool==1)//    Xil_DCacheInvalidateRange((u32)((unsigned int)out&0xffffffe0), 32*((ch_out*h*w/4*sizeof(short))/32+2));//else//    Xil_DCacheInvalidateRange((u32)((unsigned int)out&0xffffffe0), 32*((ch_out*h*w*sizeof(short))/32+2));
}

其中,conv_and_pool_init函数用于初始化加速器,而conv_and_pool_pl函数用于调用加速器进行计算,其参数的含义解释如下:

in: 输入特征图在DDR中的的起始地址。

weight: 权重在DDR中的起始地址。

bias: 偏置在DDR中的起始地址。

out: 输出特征图在DDR中的起始地址。

ch_in: 卷积的输入通道数。

ch_out: 卷积的输出通道数。

h: 输入特征图的高度。

w: 输入特征图的宽度。

pool: 是否进行池化操作,为1表示需要进行池化操作,为0则表示无需进行池化操作。

上述两个函数中用到的驱动函数可在头文件xstd_conv.h中查询(如下图),这些驱动函数是由HLS工具自动生成的,可以大大简化程序员调用加速器的难度。

结果

实验的硬件平台为zynq7020开发板(xc7z020clg400-2),所用的vivado版本为2019.2。硬件加速器的资源、功耗情况(时钟频率130MHz)如下图所示。

由图可知,加速器的片上功耗为2.939W,共消耗了35874个LUTs,40766个FF以及220个DSP。可见,加速器的规模主要受限于DSP的数目(zynq7020的DSP总数仅为220个)。

推理测试


共测试了100张图片,精度为0.95,总共耗时约74000000us,故单张图片的推理延迟为0.74s,考虑到VGG16的计算量为15.5GMACs(15.5G乘累加操作),如果将一次乘累加操作算作2次运算,则VGG16的GOPs为31GOPs,因此,加速器的吞吐率为31/0.74=41.9GOP/s。

(CNN计算量的计算方法可以参见博客)

附:整个工程有偿出售(包括Python代码,HLS代码以及Vitis代码),若有意向可私聊。

基于FPGA的VGG16卷积神经网络加速器相关推荐

  1. 基于FPGA的CNN卷积神经网络加速器

    目录 1.本文背景 2.高级设计 2.1数学概述: 3.硬件设计 3.1 输入图像 3.2 VGA/摄像头 3.3卷积第一层 3.4 池化层 3.4 卷积第二层 3.5部分和 3.6第一个全连接层 3 ...

  2. 基于FPGA的一维卷积神经网络CNN的实现(二)资源分配

    资源分配 环境:Vivado2019.2. Part:xcku040-ffva1156-2-i,内嵌DSP个数 1920个,BRAM 600个也就是21.1Mb. 说明:通过识别加高斯白噪声的正弦波. ...

  3. 基于FPGA的一维卷积神经网络CNN的实现(一)框架

    理论建立与效果展示 环境:Vivado2019.2. Part:xcku040-ffva1156-2-i,内嵌DSP个数 1920个,BRAM 600个也就是21.1Mb. 说明:通过识别加高斯白噪声 ...

  4. 基于FPGA的一维卷积神经网络CNN的实现(三)训练网络搭建及参数导出(附代码)

    训练网络搭建 环境:Pytorch,Pycham,Matlab. 说明:该网络反向传播是通过软件方式生成,FPGA内部不进行反向传播计算. 该节通过Python获取训练数据集,并通过Pytorch框架 ...

  5. 基于FPGA的深度卷积神经网络的加速器设计

    英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...

  6. 优化基于FPGA的深度卷积神经网络的加速器设计

    英文论文链接:http://cadlab.cs.ucla.edu/~cong/slides/fpga2015_chen.pdf 翻译:卜居 转载请注明出处:http://blog.csdn.net/k ...

  7. 基于ZYNQ平台的卷积神经网络加速器设计及其应用研究

    摘 要 近些年来,深度学习作为机器学习的一种新的形式,它使计算机能够从经验 中学习并根据概念层次来理解世界.作为一种崭新的人工神经网络方法,卷积神 经网络(CNN)使神经元之间可以权值共享来减少样本的 ...

  8. 【FPGA教程案例54】深度学习案例1——基于FPGA的CNN卷积神经网络之理论分析和FPGA模块划分

    FPGA教程目录 MATLAB教程目录 -------------------------------------------------------------------------------- ...

  9. 【FPGA教程案例59】深度学习案例6——基于FPGA的CNN卷积神经网络之整体实现

    FPGA教程目录 MATLAB教程目录 -------------------------------------------------------------------------------- ...

最新文章

  1. mina处理断包和粘包
  2. java excel导入前台_java上传excel表格并读取数据返回到前台
  3. 算法:三角形最小路径和
  4. VIJOS国庆节模拟赛之繁星春水
  5. C#链接数据库增删改查的例子
  6. Python3 动态导入模块的两种方式
  7. 关于js复制文本信息(按钮级别)
  8. 1 分钟 Serverless 部署掌上游戏机,“一行命令”找回小时候的乐趣!
  9. Oracle设置权限和还原数据库
  10. git生成秘钥配置SSH公钥的简单方法
  11. 学习:重写hashCode()方法的必要性
  12. AI又输了!中国传奇Dota2冠军联手,OpenAI快速进化然并卵
  13. 六、传递、返回复杂类型的对象
  14. Delphi 小知识汇总
  15. Android开源项目:微信打飞机游戏源代码
  16. C语言学生信息管理系统详细设计
  17. 【自用】C语言15个简单程序
  18. Java方法重写和重载的区别
  19. 【网络安全】OSSIM平台网络日志关联分析实战
  20. PE,成长,利率:对估值的再思考——最聪明的投资回收期

热门文章

  1. Linux 为何对病毒免疫?
  2. 3dmax基本建模 - 做一个简单四合院
  3. 记——实现第一个pyecharts可视化_北京地图的艰辛经历
  4. 群集共享卷建文件服务器,定义群集共享卷
  5. 史上最污技术解读,让你秒懂IT术语(转载)
  6. C++中字符减去‘0‘的作用
  7. 1秒钟带你读懂WAF的技术导向和天御的防御机制
  8. 不需要再手工指定JVM启动参数-XX:+UseCompressedOops
  9. 应用程序服务器GigaSpaces XAP 6.6 发布
  10. 判断圆形三角形面积大小