0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

结合卷积层来创建一个完整的推理函数

OpenFPGA 来源:OpenFPGA 2023-03-13 09:22 次阅读

到上一篇为止,我们已经完成了卷积层、全连接层、池化层、激活函数ReLU的所有C的编程实现。在本文中,我们将结合这些层来创建一个完整的推理函数。

模型实现

下面是在第 2 篇文章中创建的推理模型的图表。

8eefb56e-c01c-11ed-bfe3-dac502259ad0.png

首先输入一张1x28x28的图片,然后两次通过Conv2d -> ReLU -> MaxPool2d提取特征,最后转为linear,> ReLU -> Linear为10阶向量值。

用C写的时候,只需按如下依次逐层处理即可。

voidconv2d(constfloat*x,constfloat*weight,constfloat*bias,int32_twidth,int32_theight,
int32_tin_channels,int32_tout_channels,int32_tksize,float*y){
for(int32_toch=0;och< out_channels; ++och) {
     for (int32_t h = 0; h < height; ++h) {
       for (int32_t w = 0; w < width; ++w) {
         float sum = 0.f;

         for (int32_t ich = 0; ich < in_channels; ++ich) {
           for (int32_t kh = 0; kh < ksize; ++kh) {
             for (int32_t kw = 0; kw < ksize; ++kw) {
               int32_t ph = h + kh - ksize/2;
               int32_t pw = w + kw - ksize/2;

               // zero padding
               if (ph < 0 || ph >=height||pw< 0 || pw >=width){
continue;
}

int64_tpix_idx=(ich*height+ph)*width+pw;
int64_tweight_idx=((och*in_channels+ich)*ksize+kh)*ksize+kw;

sum+=x[pix_idx]*weight[weight_idx];
}
}
}

//addbias
sum+=bias[och];

y[(och*height+h)*width+w]=sum;
}
}
}
}

函数内部的缓冲区 (x1-x8) 用于连接各层之间的特征数据。

在HLS中,在哪里定义这个buffer很重要,如果像这次一样把它放在函数中,就可以指定使用FPGA中的RAM(或寄存器)。

另一方面,如果将此缓冲区作为函数的参数提供,则可以将数据连接到外部 DRAM。这个区域需要根据应用来设计,但是这次内部SRAM已经够用了,所以定义在函数内部。

如果像以前一样编写接口规范,将如下所示:

输入

x: 输入图像。shape=(1, 28, 28)

weight0:第一个卷积层的权重。shape=(4, 1, 3, 3)

bias0:第一个卷积层的偏差。shape=(4)

weight1:第二个卷积层的权重。shape=(8, 4, 3, 3)

bias1:第二个卷积层的偏差。shape=(8)

weight2:第一个全连接层的权重。shape=(32, 8 * 7 * 7)

bias2:第一个全连接层的偏差。shape=(32)

weight3:第二个全连接层的权重。shape=(10, 32)

bias3:第二个全连接层的偏差。shape=(10)

输出

y:输出向量。shape=(10)

界面设置

在目前创建的函数中,我们还没有具体定义创建电路的接口。未指定接口时,HLS 会为简单 SRAM 生成一个接口。

该接口不能用于访问DRAM等访问时间不确定的接口,不方便在真机上操作。为此,我们告诉HLS使用一种称为AMBA AXI4接口协议(以下简称AXI)的协议,该协议主要用于Xilinx FPGA上IP之间的接口。

简单介绍一下AXI,AXI是ARM公司提供的一种接口标准。

Xilinx IP主要使用以下三种协议。

AXI4:高速内存访问协议(主要用途:访问DRAM、PCIe等)

AXI4-Lite:AXI4的一个子集,一种用于低速内存访问的协议(主要用途:IP寄存器控制)

AXI4-Stream:仅用于单向数据传输的协议,无地址(主要用途:流数据处理)

这次我们将使用 AXI4 访问输入/输出数据,使用 AXI4-Lite 控制 IP。

具有接口定义的推理函数如下所示:

voidinference_top(constfloatx[kMaxSize],
constfloatweight0[kMaxSize],constfloatbias0[kMaxSize],
constfloatweight1[kMaxSize],constfloatbias1[kMaxSize],
constfloatweight2[kMaxSize],constfloatbias2[kMaxSize],
constfloatweight3[kMaxSize],constfloatbias3[kMaxSize],
floaty[kMaxSize]){
#pragmaHLSinterfacem_axiport=xoffset=slavebundle=gmem0
#pragmaHLSinterfacem_axiport=weight0offset=slavebundle=gmem1
#pragmaHLSinterfacem_axiport=weight1offset=slavebundle=gmem2
#pragmaHLSinterfacem_axiport=weight2offset=slavebundle=gmem3
#pragmaHLSinterfacem_axiport=weight3offset=slavebundle=gmem4
#pragmaHLSinterfacem_axiport=bias0offset=slavebundle=gmem5
#pragmaHLSinterfacem_axiport=bias1offset=slavebundle=gmem6
#pragmaHLSinterfacem_axiport=bias2offset=slavebundle=gmem7
#pragmaHLSinterfacem_axiport=bias3offset=slavebundle=gmem8
#pragmaHLSinterfacem_axiport=yoffset=slavebundle=gmem9
#pragmaHLSinterfaces_axiliteport=xbundle=control
#pragmaHLSinterfaces_axiliteport=weight0bundle=control
#pragmaHLSinterfaces_axiliteport=weight1bundle=control
#pragmaHLSinterfaces_axiliteport=weight2bundle=control
#pragmaHLSinterfaces_axiliteport=weight3bundle=control
#pragmaHLSinterfaces_axiliteport=bias0bundle=control
#pragmaHLSinterfaces_axiliteport=bias1bundle=control
#pragmaHLSinterfaces_axiliteport=bias2bundle=control
#pragmaHLSinterfaces_axiliteport=bias3bundle=control
#pragmaHLSinterfaces_axiliteport=ybundle=control
#pragmaHLSinterfaces_axiliteport=returnbundle=control
dnnk::inference(x,
weight0,bias0,
weight1,bias1,
weight2,bias2,
weight3,bias3,
y);
}

dnnk::inference函数就是前面提到的推理函数,这个函数将dnnk::inference“包起来”了。

和上一篇文章一样,top函数的接口是一个数组,而不是一个指针。在仿真 HLS 时,此符号对于指定仿真器保留的内存缓冲区的大小是必需的,但它并不是很重要。

第 30-50 行 #pragma HLS interfaceport=<参数名称>bundle=<要分配的接口名称> 使用语法为每个函数参数指定接口协议,使用的协议有两个,m_axi和s_axilite,其中m_/s_部分表示请求是发送还是接收(AXI术语中的master/slave),后面的部分就是前面提到的协议部分增加。

在此函数中,每个数据端口都成为 AXI4 主端口并主动从 DRAM (L30-39) 中获取数据。此时主机CPU等访问的存储器地址可以通过AXI4-Lite从端口(L40-49)进行设置。

最后,用于开始处理的控制寄存器和用于检查处理完成的状态寄存器port=return链接到 AXI4-Lite 从端口 (L50)。

综合/结果确认

界面

将这个电路作为IP输出,放到Vivado的IP Integrator中,如下图。每个端口的名称对应于上面的interface pragma bundle位置。

8f0edcc8-c01c-11ed-bfe3-dac502259ad0.png

熟悉 Vivado 开发的都知道,剩下要做的就是适当地连接端口,将能够创建能够进行推理处理的 FPGA 图像。

综合

综合时的表现如下:执行时间最短 1.775 ms,最长 7.132 ms。

8f287d68-c01c-11ed-bfe3-dac502259ad0.png

在这里,我想知道为什么输入图像大小是固定的,但执行时间不固定,这是因为第三篇文章中创建的卷积函数continue包括补零处理。

由于这个补零过程只在屏幕边缘进行,实际执行时间几乎是最大时间7.132 ms。

for(int32_tkw=0;kw< ksize; ++kw) {
     int32_t ph = h + kh - ksize/2;
     int32_t pw = w + kw - ksize/2;

     // zero padding
     if (ph < 0 || ph >=height||pw< 0 || pw >=width){
continue;
}

int64_tpix_idx=(ich*height+ph)*width+pw;
int64_tweight_idx=((och*in_channels+ich)*ksize+kh)*ksize+kw;

sum+=x[pix_idx]*weight[weight_idx];
}

在这里为了可读性,用continue中止,但是在FPGA上,与在这里中断循环的处理相比,使用已经安装的乘法加法器进行0加法运算的成本更少。

资源使用

FPGA的资源利用率如下所示:总体使用量是微不足道的,因为没有增加并行化和流水线等资源的加速。

8f415f86-c01c-11ed-bfe3-dac502259ad0.png






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • DRAM
    +关注

    关注

    40

    文章

    2303

    浏览量

    183289
  • 寄存器
    +关注

    关注

    31

    文章

    5310

    浏览量

    119983
  • SRAM芯片
    +关注

    关注

    0

    文章

    65

    浏览量

    12048
  • HLS
    HLS
    +关注

    关注

    1

    文章

    128

    浏览量

    24026

原文标题:总结

文章出处:【微信号:Open_FPGA,微信公众号:OpenFPGA】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    函数计算序列线性卷积结果

    定义函数文件,功能是采用重叠相加法完成无限长序列与有限长序列的线性卷积,然后用该函数文件计算序列x[k]=2k+1,0≤k≤18与序列h
    发表于 11-29 13:36

    CNN之卷积

    相乘再求和)的操作就是所谓的『卷积』操作,也是卷积神经网络的名字来源。非严格意义上来讲,下图中红框框起来的部分便可以理解为滤波器,即带着
    发表于 10-17 10:15

    卷积神经网络卷积的处理过程

    。本文就以卷积神经网络为例谈谈怎么步优化卷积神经网络使用的memory。文章(卷积神经
    发表于 12-23 06:16

    【飞凌RK3568开发板试用体验】RKNN模型推理测试

    MobileNet是Google团队2017年提出深度学习模型,专注于移动端或者嵌入式设备中的轻量级CNN网络。模型推理卷积操作占用了大部分的时间,因此MobileNet V1使
    发表于 12-08 19:06

    如何在PyTorch上学习和创建网络模型呢?

    的网络模型是卷积神经网络,该网络由以下三和激活函数的组合组成。全连接
    发表于 02-21 15:22

    卷积的C++实现详细介绍

    。第一个三级循环确定输出图像上的位置,随后的三级循环对该位置执行卷积操作。零填充在第 24-26 行完成。由于实际创建零填充输入图像是低效的,所以零填充是通过在访问图像外部时不参与乘积之和
    发表于 02-24 15:41

    一层卷积能做啥?一层卷积可以做超分吗?

      Abstract  经典的图像缩放(比如bicubic)可以视作卷积+上采样滤波器
    发表于 03-06 14:05

    结合卷积与全连接创建完整推理函数

    连接的偏差。shape=(10)  输出  y:输出向量。shape=(10)  界面设置  在目前创建函数中,我们还没有具体定义创建电路的接口。未指定接口时,HLS 会为简单 S
    发表于 03-17 16:19

    C语言入门教程-创建函数

    创建函数库 上述程序中的rand和bubble_sort函数很实用,很可能在您写其他程序时也能派上用场。为了能更方便地重复使用,您可以
    发表于 07-29 11:18 3121次阅读

    种改进的残差网络结构以减少卷积参数

    网络结构,以降低残差块数量与卷积核数量减少卷积参数。同时,在原始损失函数中加入类间相似惩罚项
    发表于 03-23 14:48 8次下载
    <b class='flag-5'>一</b>种改进的残差网络结构以减少<b class='flag-5'>卷积</b><b class='flag-5'>层</b>参数

    如何去理解CNN卷积与池化计算?

    概述 深度学习中CNN网络是核心,对CNN网络来说卷积与池化的计算至关重要,不同的步长、填充方式、卷积核大小、
    的头像 发表于 04-06 15:13 2718次阅读
    如何去理解CNN<b class='flag-5'>卷积</b><b class='flag-5'>层</b>与池化<b class='flag-5'>层</b>计算?

    卷积神经网络层级结构 卷积神经网络的卷积讲解

    像分类、目标检测、人脸识别等。卷积神经网络的核心是卷积和池化,它们构成了网络的主干,实现了对图像特征的提取和抽象。
    的头像 发表于 08-21 16:49 7339次阅读

    卷积神经网络共有几层 卷积神经网络模型三

    卷积神经网络共有几层 卷积神经网络模型三  卷积神经网络 (Convolutional Neural Networks,CNNs) 是
    的头像 发表于 08-21 17:11 6848次阅读

    卷积神经网络每一层的作用

    (Input Layer) 输入卷积神经网络的第一层,负责接收输入数据。在图像识别任务中,输入通常接收
    的头像 发表于 07-02 15:28 1103次阅读

    卷积神经网络激活函数的作用

    卷积神经网络(Convolutional Neural Networks, CNNs)是深度学习中种重要的神经网络结构,广泛应用于图像识别、语音识别、自然语言处理等领域。在卷积神经网络中,激活
    的头像 发表于 07-03 09:18 724次阅读