給卷積神經網絡動動刀:加法網絡探究

2021-01-12 騰訊網

卷積神經網絡(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

點擊播放 GIF 0.0M

擊"閱讀原文",直達「CVPR 交流小組」了解更多會議信息。

相關焦點

  • 卷積神經網絡(CNN)新手指南
    然而最經典的,或者說最流行的神經網絡使用範例是將其用於圖像處理領域。提到圖像處理,本文主要介紹的是如何使用卷積神經網絡來進行圖像分類。以類似的方式計算機能夠進行圖像分類任務,通過尋找低層次的特徵如邊緣和曲線,然後運用一系列的卷積層建立一個更抽象的概念。這是卷積神經網絡應用的一個總體概述,接下來我們來探討下細節。生物聯繫首先要稍微普及下一點背景知識,當你第一次聽到卷積神經網絡這個詞時,你也許會想這是是不與神經科學或者生物學有關?恭喜你,猜對了一部分。卷
  • 卷積神經網絡理解(一):濾波器的意義
    卷積神經網絡的發展二. 卷積神經網絡的重要性三. 卷積神經網絡與圖像識別四.濾波器一.卷積神經網絡的發展卷積神經網絡受到視覺細胞研究的啟發,1962年,Hubel和Wiesel發現初級視覺皮層中的神經元會響應視覺環境中特定的簡單特徵(比如有向的邊等)。
  • 基於卷積神經網絡的目標檢測算法簡介
    什麼是卷積神經網絡?卷積神經網絡是一個層次模型,主要包括輸入層,卷積層,池化層、全連接層以及輸出層。卷積神經網絡是專門針對圖像而設計,主要特點在於卷積層的特徵是由前一層的局部特徵通過卷積共享的權重得到。
  • YJango的卷積神經網絡——介紹
    PS:YJango是我的網名,意思是我寫的教程,並不是一種網絡結構。。關於卷積神經網絡的講解,網上有很多精彩文章,且恐怕難以找到比斯坦福的CS231n還要全面的教程。 所以這裡對卷積神經網絡的講解主要是以不同的思考側重展開,通過對卷積神經網絡的分析,進一步理解神經網絡變體中「因素共享」這一概念。注意:該文會跟其他的現有文章有很大的不同。
  • 「範例卷積神經網絡」和信息最大化
    這一方法也能被理解為「卷積神經網絡範例」。本文內容摘要:本文對「範例卷積神經網絡」的訓練方法僅作了簡單簡單的概述,所以如果想要獲得更多、更真實的信息,請閱讀論文原文。本文簡要介紹了「變分信息最大化」,並將其運用到了「範例卷積神經網絡」的案例中。
  • 卷積神經網絡的卷積到底是什麼
    打開APP 卷積神經網絡的卷積到底是什麼 人工智慧遇見磐創 發表於 2020-05-05 08:40:00 卷積神經網絡是一種特殊的神經網絡結構,是自動駕駛汽車、人臉識別系統等計算機視覺應用的基礎,其中基本的矩陣乘法運算被卷積運算取代。
  • 深度學習入門:淺析卷積神經網絡
    至今已有數種深度學習方法,如卷積神經網絡(CNN)、自編碼神經網絡(包括Auto encoder和Sparse Coding)和深度置信網絡(DBN),並在各個領域中取得了極好的效果。卷積神經網絡概述在了解卷積神經網絡之前,我們先簡單了解最基本的神經網絡。
  • 斯坦福ICLR 2018錄用論文:高效稀疏Winograd卷積神經網絡| ICLR 2018
    引言卷積神經網絡在許多機器學習應用中體現出巨大優勢。其計算性能和功耗主要由卷積過程中乘法操作的數量決定。但卷積神經網絡的巨大計算量限制了其在行動裝置上的應用。在稀疏卷積神經網絡上使用 Winograd 卷積算法會反而使計算量增大。針對上述問題,本文提出兩點改進。
  • 淺析人工智慧的卷積神經網絡與圖像處理
    淺析人工智慧的卷積神經網絡與圖像處理 demi 發表於 2019-11-02 11:23:43 在人工智慧深度學習技術中,有一個很重要的概念就是卷積神經網絡 CNN(Convolutional Neural
  • 解析卷積神經網絡的應用
    解析卷積神經網絡的應用 貿澤電子 發表於 2019-08-21 15:29:46 目前,人們對性能強大且結構複雜的計算機已是司空見慣。
  • 梯度上升可視化卷積神經網絡
    為什麼我的CNN網絡模型訓練出來的東西總是過度擬合?已經改了很多次參數都不行,到底是樣本有問題還是網絡模型定義有問題?問題在哪來?CNN網絡模型中的每一層學習的是些什麼特徵?為什麼有的人說第一層卷積核提取的是邊緣信息特徵,有的人卻說第一層卷積核提取的是顏色特徵?到底是兩者都有還是什麼回事?CNN網絡可不可以減掉幾層然後保持相同的精度和損失率呢?
  • 卷積神經網絡超詳細總結
    什麼是卷積神經網絡:卷積神經網絡是一種帶有卷積結構的深度神經網絡,卷積結構可以減少深層網絡佔用的內存量,其三個關鍵的操作,其一是局部感受野,其二是權值共享,其三是pooling層,有效的減少了網絡的參數個數,緩解了模型的過擬合問題。
  • 揭秘卷積神經網絡熱力圖:類激活映射
    本文將介紹在卷積神經網絡(CNN)中,針對圖像的某些部位可視化從而做出判斷的不同技術。類激活映射(CAM)是一種生成熱力圖的技術,用於突出圖像的類的特定區域。熱力圖效用下面是一個典型的熱力圖:視覺化神經網絡正在查看的位置大有用處,因為它幫助我們理解神經網絡是否在查看圖像的適當部位,或者神經網絡是否在進行誤導。下列是一些神經網絡在做分類決判斷時可能進行誤導和錯誤查看位置的例子:· 卷積神經網絡將一張圖片分類為「火車」,而實際上它是在尋找「火車軌道」(這意味著它會錯誤地將一張火車軌道圖片單獨分類為「火車」)。
  • 反思卷積神經網絡:圖像角度泛化上的困難重重
    毫無疑問,卷積神經網絡在圖像處理及周邊領域的開發進程和研究突破中發揮著重要作用。 但是,這不代表卷積神經網絡表現完美。 研究人員經常會遇到一個問題:他們往往由於過於追求理論和完美數據集而深陷其中。然而事與願違,要求數據準確度到小數點後幾位其實往往與現實情況中圖像處理的實際用處恰恰相反。
  • 卷積神經網絡(CNN)介紹與實踐
    - 來源:http://cs231n.github.io/classification/為了「教會」一種算法如何識別圖像中的對象,我們使用特定類型的人工神經網絡:卷積神經網絡(CNN)。他們的名字源於網絡中最重要的一個操作:卷積。卷積神經網絡受到大腦的啟發。
  • 一文讀懂卷積神經網絡工作原理
    卷積神經網絡(Convolutional Neural Network, CNN)是人工神經網絡的一種,是當下語音分析和圖像識別領域的研究熱點。這篇文章用最簡明的語言和最直觀的圖像,帶你入門CNN。準備好了嗎?
  • 應用豐富的「卷積神經網絡」技術,怎樣實現了圖像識別?
    解決這一問題的方法之一是利用神經網絡。理論上,我們可以利用卷積神經網絡來分析圖像;但實際上從計算的角度來看,這樣做的成本非常高。舉例來說,即使是一個處理一張很小的圖像(假設是30*30像素)的卷積神經網絡,仍需要五十萬的參數和900個輸入。
  • 9大主題卷積神經網絡(CNN)的PyTorch實現
    上文聚焦於源碼和論文,對於各種卷積神經網絡模型的實現,本文將介紹它們的 PyTorch 實現,非常有用!這份資源已經開源在了 GitHub 上,連結如下:https://github.com/shanglianlm0525/PyTorch-Networks先來個總結介紹,該系列的卷積神經網絡實現包含了 9 大主題,目錄如下:1. 典型網絡2. 輕量級網絡3. 目標檢測網絡4.
  • 圖像識別技術——卷積神經網絡CNN詳解
    前言卷積神經網絡不同於神經網絡,在圖片處理這方面有更好的表現。本文主要對神經網絡和卷積神經網絡做了簡要的對比,著重介紹了卷積神經網絡的層次基礎,同時也簡要介紹了卷積神經網絡參數更新方式和其優缺點,並結合具體實例對卷積神經網絡的工作方式做了介紹。
  • 卷積神經網絡算法結構分析及其工作效率的影響因素
    2012年Krizhevsky等提出的卷積神經網絡卷積神經網絡算法結構分析。就目前算法的發展狀況而言,卷及神經網絡作為當前在圖像識別領域的主流算法,被諸多工作團隊所廣泛接受,而對於圖像識別的研究重點,也從尋找更為優秀的算法,轉移到了對卷積神經網絡算法本身的優化上,並且在應用領域,也在近年取得了長足進展。