【QNN】——Binarized Neural Networks论文

转载自:https://blog.csdn.net/liujianlin01/article/details/80917646
使用micronet
论文:https://arxiv.org/pdf/1602.02830.pdf
亮点:NIPS2016

[NIPS ‘16]论文地址:https://arxiv.org/pdf/1602.02830.pdf
代码地址:https://github.com/MatthieuCourbariaux/BinaryNet
这篇文章提出了一个新的网络:二值化网络(BNN),在运行时使用二值权重和激活。在训练时,二值权重和激活用于计算参数梯度。

摘要
我们介绍了一种训练二值化神经网络(BNNs)的方法——在运行时具有二值权值和激活的神经网络。在训练时,使用二值权值和激活值来计算参数梯度。在前向传递过程中,bnn大大减少了内存大小和访问次数,并将大部分算术操作替换为位操作,预计将大幅提高能效。为了验证bnn的有效性,我们在Torch7和Theano框架上进行了两组实验。在这两方面,BNNs都在MNIST、CIFAR-10和SVHN数据集上取得了几乎最先进的结果。最后但并非最不重要的是,我们编写了一个二进制矩阵乘法GPU内核,它可以比使用未优化的GPU内核运行MNIST BNN的速度快7倍,而不会损失任何分类精度。培训和运行BNNs的代码可以在线获得。

1. 二值化准则

文章给出了2种二值化函数。 第一种是决策式二值化:
这里写图片描述

第二种是随机二值化:
在这里插入图片描述
在这里插入图片描述

看公式第二种随机二值化更合理,但是每次生成随机数的时候非常耗时,所以文章中采用的第一种方法。

2. 梯度计算

如果采用上面的第一种二值化函数求导的话,那么求导后的值都是0。所以文章采用了一种方法,将sign(x)进行宽松。这样,函数就变成可以求导的了。
在这里插入图片描述
2.1 code

  • 梯度反向训练的时候,权重和激活值采用了不同的反向策略。权重采用了"直通估计器",激活值采用了Htanh
class BinaryActivation(Function):
    @staticmethod
    def forward(self, input):
        self.save_for_backward(input)
        output = torch.sign(input)
        # ******************** A —— 1、0 *********************
        #output = torch.clamp(output, min=0)
        return output

    @staticmethod
    def backward(self, grad_output):
        input, = self.saved_tensors
        # *******************ste*********************
        grad_input = grad_output.clone()
        # ****************saturate_ste***************
        grad_input[input.ge(1.0)] = 0
        grad_input[input.le(-1.0)] = 0
        '''
        #******************soft_ste*****************
        size = input.size()
        zeros = torch.zeros(size).cuda()
        grad = torch.max(zeros, 1 - torch.abs(input))
        #print(grad)
        grad_input = grad_output * grad
        '''
        return grad_input

# weight
class BinaryWeight(Function):
    @staticmethod
    def forward(self, input):
        output = torch.sign(input)
        return output

    @staticmethod
    def backward(self, grad_output):
        # *******************ste*********************
        grad_input = grad_output.clone()
        return grad_input

3. pytorch中网络定义

可以看到网络的第一层conv是没有二值化的,是在第一层relu的时候才对激活值进行输出二值化。最后一个模块的Conv+bn+relu是没有做量化的。
if layer_counter[0] > 1 and layer_counter[0] < layer_num: # 第一层和最后一层不进行二值化

OrderedDict([('model', Sequential(
  (0): ConvBNReLU(
    (conv): Conv2d(3, 256, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
    (bn): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (1): ConvBNReLU(
    (conv): QuantConv2d(
      256, 256, kernel_size=(1, 1), stride=(1, 1), groups=2
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (2): ConvBNReLU(
    (conv): QuantConv2d(
      256, 256, kernel_size=(1, 1), stride=(1, 1), groups=2
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (4): ConvBNReLU(
    (conv): QuantConv2d(
      256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=16
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (5): ConvBNReLU(
    (conv): QuantConv2d(
      512, 512, kernel_size=(1, 1), stride=(1, 1), groups=4
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (6): ConvBNReLU(
    (conv): QuantConv2d(
      512, 512, kernel_size=(1, 1), stride=(1, 1), groups=4
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (8): ConvBNReLU(
    (conv): QuantConv2d(
      512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (9): ConvBNReLU(
    (conv): QuantConv2d(
      1024, 1024, kernel_size=(1, 1), stride=(1, 1), groups=8
      (weight_quantizer): WeightQuantizer()
    )
    (bn): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ActivationQuantizer(
      (relu): ReLU(inplace=True)
    )
  )
  (10): ConvBNReLU(
    (conv): Conv2d(1024, 10, kernel_size=(1, 1), stride=(1, 1))
    (bn): BatchNorm2d(10, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (relu): ReLU(inplace=True)
  )
  (11): AvgPool2d(kernel_size=8, stride=1, padding=0)
))])

总结

  • 训练后模型的参数还是浮点数,只是在前向计算的时候,多了一步将conv2d的weight,relu的激活值二值化为{-1,1}的操作,可能计算更快。
  • 这样的操作需要推理框架,实际硬件的支持才能加速,不然利用现有的框架与硬件推理速度应该是差不多的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值