给卷积神经网络动动刀:加法网络探究

2020 年 7 月 26 日 AI科技评论


卷积神经网络(CNN)在计算机视觉任务中有着广泛的应用,然而它的运算量非常巨大,这使得我们很难将CNN直接运用到计算资源受限的移动设备上。为了减少CNN的计算代价,许多模型压缩和加速的方法被提出。
其中AdderNet就是一种从新角度对模型进行加速的方法,以往的模型加速方法通过减少CNN的参数,AdderNet通过重新定义卷积计算,将卷积中的乘法替换为了加法。我们知道,乘法的计算代价要远远大于加法,AdderNet通过这种方式减少了计算量。


图1 加法和乘法计算量对比
CNN卷积计算:  

 

AdderNet计算:



1


代码解读

AdderNet的训练代码已经在github上开源(https://github.com/huawei-noah/AdderNet),接下来我们对代码进行分析和解读。
AdderNet的训练代码主要分为几个文件:
  • adder.py

  • main.py

  • resnet20.py

  • resnet50.py

  • test.py

 

其中adder.py定义了AdderNet的基础算子,main.py是训练AdderNet的文件,test.py是测试文件,resnet20.py和resnet50.py定义了网络结构。
由于训练和测试的代码以及网络结构的代码和正常的卷积神经网络一样,这里我们不对它们做解析,我们主要解读定义adder算子的adder.py文件。  
adder.py中共含有两个类和一个函数,两个类分别是adder2d和adder,一个函数为adder2d_function。我们首先来看adder2d这个类。


class adder2d(nn.Module):
   def __init__(self, input_channels, output_channels, kernel_size, stride=1, padding=0, bias=False):
       super(adder2d, self).__init__()
       self.stride = stride
       self.padding = padding
       self.input_channel=output_channel
       self.kernel_size=kernel_size
       self.adder=torch.nn.Parameter(nn.init.normal_(torch.randn(output_channel,input_channel,kernel_size,kernel_size)))
       
self.bias=bias
       
if bias:
             self.b = torch.nn.Parameter(nn.init.uniform_(torch.zeros(output_channel)))

       def forward(self, x):
       output = adder2d_function(x,self.adder, self.stride, self.padding)

       
if self.bias:
           output += self.b.unsqueeze(0).unsqueeze(2).unsqueeze(3)
       
return output
     
 


可以看到,adder2d这个类定义了adder算子,是继承于nn.module的,所以在网络定义时可以直接使用adder2d来定义一个adder层。例如resnet20.py中就如下定义一个3*3 kernel大小的adder层:


def conv3x3(in_planes, out_planes, stride=1):  
       " 3x3 convolution with padding "
       return adder.adder2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False)
        


可以看到adder2d的使用方式和nn.Conv2d基本完全一样。
接下来我们进一步解读adder2d包含的属性,和卷积算子相同,adder算子包括几个属性:stride,padding,input_channel,output_channel和kernel_size,给定这几个属性后,adder2d就会根据这些属性定义adder filter和bias

self.adder= torch.nn.Parameter(nn.init.normal_(torch.randn(output_channel,input_channel,kernel_size,kernel_size)))  
self.b = torch.nn.Parameter(nn.init.uniform_(torch.zeros(output_channel)))     
        


最后对前向传播使用函数adder2d_function来得到结果


output= adder2d_function(x,self.adder, self.stride, self.padding)

 
所以接下来我们进一步分析adder2d_function这个函数是如何进行adder算子的运算的:


 def adder2d_function(X, W, stride=1, padding=0):
     n_filters, d_filter, h_filter, w_filter = W.size()
     n_x, d_x, h_x, w_x = X.size()  
     h_out = (h_x - h_filter + 2 * padding) / stride + 1       
     w_out = (w_x - w_filter + 2 * padding) / stride + 1        h_out, w_out = int(h_out), int(w_out)
     X_col = torch.nn.functional.unfold(X.view(1, -1, h_x, w_x), h_filter, dilation=1, padding=padding, stride=stride).view(n_x, -1, h_out*w_out)           
X_col = X_col.permute(1,2,0).contiguous().view(X_col.size(1),-1)       
     W_col = W.view(n_filters, -1)       
     out = adder.apply(W_col,X_col) 
     out = out.view(n_filters, h_out, w_out, n_x)
     out = out.permute(3, 0, 1, 2).contiguous()

     
     return out:
     


可以看到,adder2d_function将输入X和卷积核W先进行了一系列变换,变换为W_col和X_col两个矩阵后再进行计算,这和卷积的计算十分类似,在卷积中,我们通常将输入图片通过im2col变换变为矩阵,将卷积核reshape成矩阵,将卷积计算转换为矩阵乘法运算进行。这里adder的计算也是同样的:


X_col = torch.nn.functional.unfold(X.view(1, -1, h_x, w_x), h_filter, dilation=1, padding=padding, stride=stride).view(n_x, -1, h_out*w_out)               

X_col = X_col.permute(1,2,0).contiguous().view(X_col.size(1),-1)     

上面两行代码就是将输入的X进行im2col变成二维矩阵


W_col = W.view(n_filters, -1)

同样的W也reshape成二维矩阵。
接下来如果我们要进行卷积,就将这两个矩阵进行矩阵乘法的运算。然而我们现在进行的是adder运算,相当于将卷积中的乘法改为加法,所以需要重新定义这个矩阵运算:


out = adder.apply(W_col,X_col)

可以看到adder.apply就是重新定义的对应加法神经网络的矩阵运算。


out = out.view(n_filters, h_out, w_out, n_x)
out =
out.permute(3, 0, 1, 2).contiguous()

最后得到的output矩阵同样通过reshape变回4维。

接下来我们仔细分析这个adder运算是如何实现的。


class adder(Function):
   @staticmethod
   def forward(ctx, W_col, X_col):        
       ctx.save_for_backward(W_col,X_col) 
       output = -(W_col.unsqueeze(2)-X_col.unsqueeze(0)).abs().sum(1)
       return output
       
       @staticmethod
def backward(ctx,grad_output):       
       W_col,X_col = ctx.saved_tensors
grad_W_col = ((X_col.unsqueeze(0)-W_col.unsqueeze(2))*grad_output.unsqueeze(1)).sum(2)       
       grad_W_col = grad_W_col/grad_W_col.norm(p=2).clamp(min=1e-12)*math.sqrt(W_col.size(1)*W_col.size(0))/5    
grad_X_col = (-(X_col.unsqueeze(0)-W_col.unsqueeze(2)).clamp(-1,1)*grad_output.unsqueeze(1)).sum(0)      
       
       
       return grad_W_col, grad_X_col
       

 
这个adder运算分为两部分:前向传播和反向传播。
我们先来看前向传播的部分,只用了很简单的一句代码来实现:


output = -(W_col.unsqueeze(2)-X_col.unsqueeze(0)).abs().sum(1)

 
实际上这个代码就是将矩阵乘法中的乘法运算用减法和绝对值来代替,我们回顾矩阵乘法,其实就是将两个矩阵的中间维度进行对应点的相乘后再相加,假设是m*n的矩阵A和n*k的矩阵B相乘,可以将A在第三个维度复制k份,将B在第零个维度复制m份,得到m*n*k大小的矩阵A和B,将这两个矩阵每个点相乘,最后再第二个维度求和,就得到了m*k的矩阵,也就是矩阵乘法的输出结果,这其实就是上面代码的实现过程,将W在第三个维度扩充,将X在第一个维度扩充,然后相减取绝对值,在第二个维度求和,就得到了adder的矩阵运算结果。  
我们知道,在pytorch如果你定义好前向传播,pytorch是会对它进行自动求导的,然而在AdderNet里,反向传播的梯度和真实梯度不一样,所以我们要自己定义这个反向传播的梯度。
AdderNet中真实梯度为:

梯度被修改为:

 

所以,和上面前向传播类似的矩阵计算方法,可以用以下代码计算反向传播的值,再乘上链式法则中输出的梯度,就得到了W和X的梯度。


grad_W_col= ((X_col.unsqueeze(0)-W_col.unsqueeze(2))*grad_output.unsqueeze(1)).sum(2)

grad_X_col = (-(X_col.unsqueeze(0)-W_col.unsqueeze(2)).clamp(-1,1)*grad_output.unsqueeze(1)).sum(0)

 
最后再加上论文中提到的adaptive learning rate:
 
代码可以表示为:


grad_W_col=grad_W_col/grad_W_col.norm(p=2).clamp(min=1e-12)*math.sqrt(W_col.size(1)*W_col.size(0))/5


以上就是对AdderNet开源代码的完整解读。

2


结果


我们最后来看看AdderNet的实验结果。


  可以发现,AdderNet在CIFAR-10和ImageNet数据集上都取得了和CNN相似准确率的结果,并且基本不需要任何乘法,使用github开源的代码就可以复现以上的结果。
当然,目前AdderNet的训练还是十分慢的,作者说这是因为AdderNet没有cuda实现加速,主要的运行速度在于adder这个矩阵计算函数。我们在这里提供一个简单的思路来实现cuda加速,我们先参考矩阵乘法的cuda实现https://github.com/NVIDIA/cuda-samples/blob/master/Samples/matrixMul/matrixMul.cu,将矩阵乘法中的乘改为减法和绝对值就可以了,最后,我们可以通过pytorch自带的cuda extension来编译cuda代码(https://pytorch.org/tutorials/advanced/cpp_extension.html),就可以完成AdderNet的cuda加速了。


该论文已被CVPR 2020接收。

论文一作:

陈汉亭,北京大学智能科学系硕博连读三年级在读,同济大学学士,师从北京大学许超教授,在华为诺亚方舟实验室实习。研究兴趣主要包括计算机视觉、机器学习和深度学习。在 ICCV,AAAI,CVPR 等会议发表论文数篇,目前主要研究方向为神经网络模型小型化。


论文二作:

王云鹤,在华为诺亚方舟实验室从事边缘计算领域的算法开发和工程落地,研究领域包含深度神经网络的模型裁剪、量化、蒸馏和自动搜索等。王云鹤博士毕业于北京大学,在相关领域发表学术论文40余篇,包含NeurIPS、ICML、CVPR、ICCV、TPAMI、AAAI、IJCAI等。


  • 论文地址:https://arxiv.org/pdf/1912.13200.pdf

  • Github 代码地址:https://github.com/huawei-noah/AdderNet


招 聘

AI 科技评论希望能够招聘 科技编辑/记者
办公地点:北京/深圳
职务:以跟踪学术热点、人物专访为主
工作内容:
1、关注学术领域热点事件,并及时跟踪报道;
2、采访人工智能领域学者或研发人员;
3、参加各种人工智能学术会议,并做会议内容报道。
要求:
1、热爱人工智能学术研究内容,擅长与学者或企业工程人员打交道;
2、有一定的理工科背景,对人工智能技术有所了解者更佳;
3、英语能力强(工作内容涉及大量英文资料);
4、学习能力强,对人工智能前沿技术有一定的了解,并能够逐渐形成自己的观点。

感兴趣者,可将简历发送到邮箱:jiangbaoshang@yanxishe.com



击"阅读原文",直达“CVPR 交流小组”了解更多会议信息。


登录查看更多
1

相关内容

专知会员服务
73+阅读 · 2020年5月21日
专知会员服务
31+阅读 · 2020年5月20日
【CVPR 2020-商汤】8比特数值也能训练卷积神经网络模型
专知会员服务
25+阅读 · 2020年5月7日
3D目标检测进展综述
专知会员服务
191+阅读 · 2020年4月24日
神经网络的拓扑结构,TOPOLOGY OF DEEP NEURAL NETWORKS
专知会员服务
32+阅读 · 2020年4月15日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
【ICLR-2020】网络反卷积,NETWORK DECONVOLUTION
专知会员服务
38+阅读 · 2020年2月21日
小于1MB的行人检测网络
极市平台
8+阅读 · 2019年9月23日
深度卷积神经网络中的降采样
极市平台
12+阅读 · 2019年5月24日
卷积神经网络四种卷积类型
炼数成金订阅号
18+阅读 · 2019年4月16日
CVPR 2019 | 用异构卷积训练深度CNN:提升效率而不损准确度
【CNN】一文读懂卷积神经网络CNN
产业智能官
18+阅读 · 2018年1月2日
深度学习中的「卷积层」如何深入理解?
深度学习世界
6+阅读 · 2017年11月30日
[学习] 这些深度学习网络训练技巧,你了解吗?
菜鸟的机器学习
7+阅读 · 2017年7月29日
CNN之卷积层
机器学习算法与Python学习
8+阅读 · 2017年7月2日
Learning to Weight for Text Classification
Arxiv
8+阅读 · 2019年3月28日
Arxiv
19+阅读 · 2018年6月27日
Arxiv
3+阅读 · 2017年10月1日
VIP会员
相关VIP内容
专知会员服务
73+阅读 · 2020年5月21日
专知会员服务
31+阅读 · 2020年5月20日
【CVPR 2020-商汤】8比特数值也能训练卷积神经网络模型
专知会员服务
25+阅读 · 2020年5月7日
3D目标检测进展综述
专知会员服务
191+阅读 · 2020年4月24日
神经网络的拓扑结构,TOPOLOGY OF DEEP NEURAL NETWORKS
专知会员服务
32+阅读 · 2020年4月15日
【CVPR2020】图神经网络中的几何原理连接
专知会员服务
56+阅读 · 2020年4月8日
【ICLR-2020】网络反卷积,NETWORK DECONVOLUTION
专知会员服务
38+阅读 · 2020年2月21日
相关资讯
小于1MB的行人检测网络
极市平台
8+阅读 · 2019年9月23日
深度卷积神经网络中的降采样
极市平台
12+阅读 · 2019年5月24日
卷积神经网络四种卷积类型
炼数成金订阅号
18+阅读 · 2019年4月16日
CVPR 2019 | 用异构卷积训练深度CNN:提升效率而不损准确度
【CNN】一文读懂卷积神经网络CNN
产业智能官
18+阅读 · 2018年1月2日
深度学习中的「卷积层」如何深入理解?
深度学习世界
6+阅读 · 2017年11月30日
[学习] 这些深度学习网络训练技巧,你了解吗?
菜鸟的机器学习
7+阅读 · 2017年7月29日
CNN之卷积层
机器学习算法与Python学习
8+阅读 · 2017年7月2日
Top
微信扫码咨询专知VIP会员