基于FPGA的卷积神经网络实现(六)数据量化(2)

目录:

  1. 简介
  2. 框架
  3. 资源分配(1)
  4. 资源分配(2)
  5. 数据量化(1)
  6. 数据量化(2)
  7. 数据读写
  8. 卷积模块
  9. 池化、全连接与输出

我发现点下面的链接会跳到一个不知道是谁的CSDN下面需要付费下载,这个很迷惑,麻烦自行复制下面的链接。
Github:https://github.com/MasLiang/CNN-On-FPGA
那个不知道是谁的链接:https://download.csdn.net/download/weixin_42138780/18551586
没有下载不让举报,有办法的朋友麻烦举报一下

上一节我们介绍了如何对一个离线模型进行简单的线性量化,这一节我们来说一下如何在Pytorch上面训练一个量化模型。这一节可能是彻底脱离了FPGA实现,想要安心做FPGA实现的可以跳过这一节。
首先在Pytorch实现有两种情况,一种是训练的时候就是用int类型,另一种是训练的参数进行量化,但是使用float来表示。另外就是在实现过程中,现在的研究大家使用了各种各样的trick来减小量化带来的误差,从而进一步减小bit位宽,这不是我们研究的重点,真要研究起来这个领域也是非常的深奥,这里我们仅围绕如何在一个模型的训练过程里面加入量化来展开,就是用第二种方式来做例子。
首先我们先来分析一个Pytorch的卷积类。

class Conv2d(_ConvNd):
    def __init__(
        self,
        in_channels: int,
        out_channels: int,
        kernel_size: _size_2_t,
        stride: _size_2_t = 1,
        padding: _size_2_t = 0,
        dilation: _size_2_t = 1,
        groups: int = 1,
        bias: bool = True,
        padding_mode: str = 'zeros'  # TODO: refine this type
    ):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(Conv2d, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding, dilation,
            False, _pair(0), groups, bias, padding_mode)

    def _conv_forward(self, input, weight):
        if self.padding_mode != 'zeros':
            return F.conv2d(F.pad(input, self._reversed_padding_repeated_twice, mode=self.padding_mode),
                            weight, self.bias, self.stride,
                            _pair(0), self.dilation, self.groups)
        return F.conv2d(input, weight, self.bias, self.stride,
                        self.padding, self.dilation, self.groups)

    def forward(self, input: Tensor) -> Tensor:
        return self._conv_forward(input, self.weight)

分析一下这个类首先是__ init__方法,这一方法的作用顾名思义,就是初始化各个参数,比如输入通道数量,输出通道数量等(实际上这是一个python的特性,创建该类对象的时候会使用此初始化函数)。之后的_conv_forward方法是一个功能方法,也就是描述了这个模块的功能,实际上是调用function.py文件中的conv2d方法。最后forward用于该层全向传播的。
也就是说我们想要自己构造一个可用的,那么也需要三部分函数,一个初始化方法,一个(或者一些)功能方法,一个前向传播方法。至少从这个类看起来是这样的。
实际上这是Pytorch的一个特性,在前向传播的时候会调用所有网络中的层的forward方法来进行,因此我们在构建自己的类的时候只需要也同样的使用forward方法,在网络前向传播的时候就会自动调用。那么反向传播呢?事实上,如果我们在自己创建的类继承nn.conv2d,那么就可以继承其反向传播,来更新weight和bias。因此我们自己创建的类里面需要有这样几个要素:

  1. 功能方法,在这个方法里面实现量化的过程
  2. forward方法,在这个方法里面实现前向传播过程
  3. weight和bias变量
  4. 继承nn.conv2d

下面来看这段代码(要注意的是这并不是一段可执行的代码,因为疫情原因我的程序在学校的电脑上没法取出来,所以只是写了一个模板,后面会对他进行更新):

class DCconv2d(nn.Conv2d):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True):
        super(DCconv2d, self).__init__(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias)
        self.lamda = 2**st
        
    def quan(self,x):
    	max_data = max(x.abs())
    	scale = max_quan/max_data
        x_q = round(self.x*scale)
        return x_q
        
    def forward(self, x):
        return  F.conv2d(self.quan(x), self.quan(self.weight), self.quan(self.bias), self.stride, self.padding, self.dilation, self.groups)

这样一来,我们在自己创建的类当中定义了quan这一功能方法,并在forward当中调用了F.conv2d方法。而后我们在构建网络的时候使用新的类来取代nn.conv2d就好了。
在计算卷积的时候使用的是量化后的参数和数据,而在更新的时候更新的是self.weight和self.bias,对于我们这个类来说,是更新了没有量化的参数,另一种方式是使用量化后的参数对self.weight和self.bias进行覆盖,这样一来更新的也是量化后的参数,两种方式的优劣还是各位自行比较,因为有许多优化的方法在里面,通过对quan这一功能方法进行修改,来得到更优的量化效果。当然这种很简单没有优化的方法已经比仅仅对训练好的模型进行量化并部署效果要好很多了。
还是老话,这个系列的文章主要在于介绍整个流程,适用于入门新手,对于深入的研究,每个点都是一个很深奥的领域,我的研究点在于模型压缩,对于量化的方法研究的不是非常的深入,有兴趣的同学可以自行深入研究。

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值