MobileNet原理+手寫python代碼實現(xiàn)MobileNet

最近看到一個巨牛的人工智能教程,分享一下給大家杖虾。教程不僅是零基礎烂瘫,通俗易懂媒熊,而且非常風趣幽默奇适,像看小說一樣!覺得太牛了芦鳍,所以分享給大家嚷往。平時碎片時間可以當小說看,【點這里可以去膜拜一下大神的“小說”】柠衅。

MobileNet是針對移動端優(yōu)化的卷積皮仁,所以當需要壓縮模型時,可以考慮使用MobileNet替換卷積菲宴。下面我們開始學習MobileNet原理贷祈,并且先通過Tensorflow函數(shù)接口實現(xiàn)MobileNet,再手寫python代碼實現(xiàn)MobileNet喝峦。

轉載請注明出處:【huachao1001的簡書:http://www.reibang.com/p/7bef96816f7d】

1 對比普通卷積和MobileNet原理

MobileNet是用于替換普通卷積势誊,相比普通卷積,MobileNet參數(shù)更少谣蠢,計算速度更快粟耻。我們先看一下輸入為(h=12,w=12,c=4)查近,卷積為3*3,輸出為(h=12,w=12,c=2)前向計算中挤忙,普通卷積的參數(shù)量霜威、乘法計算次數(shù)。普通卷積如下圖所示:


普通卷積

從上圖可以很簡單的計算到册烈,普通卷積參數(shù)總數(shù)為72個戈泼,需要做10368次乘法計算。

相比普通卷積赏僧,MobileNet采用的方法是矮冬,將卷積分解為2個操作:depthwise和pointwise。pointwise比較容易理解次哈,就是普通的卷積核為11的卷積胎署。depthwise采用的方法不是普通卷積方式,我們知道窑滞,對于輸入通道數(shù)為4的feature map在計算卷積時琼牧,輸出的每個通道都需要對應4個33卷積核參數(shù)。這一步是最主要的耗時哀卫,為了提升計算速度巨坊,MobileNet把每個輸入feature map對應一個33卷積核,輸出通道數(shù)不變此改,即為4趾撵。而真正對通道數(shù)做改變的是在pointwise,也就是11的卷積共啃。

注意:上面面論述針對的是輸入為(h=12,w=12,c=4)占调,卷積為3*3,輸出為(h=12,w=12,c=2) 這種情況舉例說明移剪。

下面圖很清晰的理解mobilenet原理:

mobilenet

從上圖可以很簡單的計算到究珊,普通卷積參數(shù)總數(shù)為44個,需要做6336次乘法計算纵苛〗虽蹋可以看到,mobilenet的參數(shù)和乘法計算次數(shù)明顯比普通卷積要小攻人。這還僅僅是我列舉的簡單例子陈瘦,在實際網(wǎng)絡中解滓,幾十層的網(wǎng)絡很常見循帐,feature map也是遠遠大于12124给赞。根據(jù)我的經(jīng)驗,普通100M的網(wǎng)絡模型烙博,將所有卷積替換成mobilenet后瑟蜈,能降到20M以下烟逊,計算速度更是不在一個量級。

2 Tensorflow中使用MobileNet

在Tensorflow中铺根,有depthwise對應的函數(shù)接口宪躯,直接調用就可以了。由于pointwise就是普通的卷積核大小為1*1的卷積位迂,而卷積的原理访雪,我們在《Tensorflow卷積實現(xiàn)原理+手寫python代碼實現(xiàn)卷積》一文中已經(jīng)講的很清楚了。所以我們只要關注depthwise即可掂林。
在Tensorflow中臣缀,depthwise操作接口是:

tf.nn.depthwise_conv2d(
    input,
    filter,
    strides,
    padding,
    rate=None,
    name=None,
    data_format=None
)

假設我們的輸入和卷積核如下:

 #輸入,shape=[c,h,w]=[2,5,5]
input_data=[
              [[1,0,1,2,1],
               [0,2,1,0,1],
               [1,1,0,2,0],
               [2,2,1,1,0],
               [2,0,1,2,0]],

               [[2,0,2,1,1],
                [0,1,0,0,2],
                [1,0,0,2,1],
                [1,1,2,1,0],
                [1,0,1,1,1]],

            ]
#卷積核泻帮,shape=[in_c,k,k]=[2,3,3]
weights_data=[ 
               [[ 1, 0, 1],
                [-1, 1, 0],
                [ 0,-1, 0]],
               [[-1, 0, 1],
                [ 0, 0, 1],
                [ 1, 1, 1]] 
             ]

下面我們貼上完整調用depthwise的代碼:

import tensorflow as tf
def get_shape(tensor):
    [s1,s2,s3]= tensor.get_shape() 
    s1=int(s1)
    s2=int(s2)
    s3=int(s3)
    return s1,s2,s3
def chw2hwc(chw_tensor): 
    [c,h,w]=get_shape(chw_tensor) 
    cols=[]

    for i in range(c):
        #每個通道里面的二維數(shù)組轉為[w*h,1]即1列 
        line = tf.reshape(chw_tensor[i],[h*w,1])
        cols.append(line)

    #橫向連接精置,即將所有豎直數(shù)組橫向排列連接
    input = tf.concat(cols,1)#[w*h,c]
    #[w*h,c]-->[h,w,c]
    input = tf.reshape(input,[h,w,c])
    return input

def hwc2chw(hwc_tensor):
    [h,w,c]=get_shape(hwc_tensor) 
    cs=[] 
    for i in range(c): 
        #[h,w]-->[1,h,w] 
        channel=tf.expand_dims(hwc_tensor[:,:,i],0)
        cs.append(channel)
    #[1,h,w]...[1,h,w]---->[c,h,w]
    input = tf.concat(cs,0)#[c,h,w]
    return input
def tf_depthwise(input,weights ):
    depthwise=tf.nn.depthwise_conv2d( input, weights, [1, 1, 1, 1], padding='SAME' ) 
    return depthwise
def main(): 
    const_input = tf.constant(input_data , tf.float32)
    const_weights = tf.constant(weights_data , tf.float32 ) 
    input = tf.Variable(const_input,name="input")
    #[2,5,5]------>[5,5,2]
    input=chw2hwc(input)
    #[5,5,2]------>[1,5,5,2]
    input=tf.expand_dims(input,0) 
    weights = tf.Variable(const_weights,name="weights")
    #[2,3,3]-->[3,3,2]
    weights=chw2hwc(weights)
    #[3,3,2]-->[3,3,2,1]
    weights=tf.expand_dims(weights,3) 
    print(weights.get_shape().as_list())

    #[b,h,w,c]
    conv=tf_depthwise(input,weights )
    rs=hwc2chw(conv[0]) 

    init=tf.global_variables_initializer()
    sess=tf.Session()
    sess.run(init)
    conv_val = sess.run(rs)

    print(conv_val) 


if __name__=='__main__':
    main()

打印結果如下:

[[[ 1. -3.  0.  1. -2.]
  [-1.  3.  1. -1.  3.]
  [ 1. -1.  0.  3. -2.]
  [ 1.  1.  1. -2.  1.]
  [ 4.  1.  4.  2. -1.]]

 [[ 1.  3.  2.  3.  2.]
  [ 2.  1.  3.  4.  2.]
  [ 3.  4.  5.  6.  1.]
  [ 2.  3.  5.  4.  0.]
  [ 1.  2.  1. -1. -1.]]]

我們通過一個動畫演示計算過程:
[圖片上傳失敗...(image-f2e073-1530334796526)]

3 手寫python代碼實現(xiàn)depthwise

import numpy as np
input_data=[
              [[1,0,1,2,1],
               [0,2,1,0,1],
               [1,1,0,2,0],
               [2,2,1,1,0],
               [2,0,1,2,0]],

               [[2,0,2,1,1],
                [0,1,0,0,2],
                [1,0,0,2,1],
                [1,1,2,1,0],
                [1,0,1,1,1]] 
            ]
weights_data=[ 
               [[ 1, 0, 1],
                [-1, 1, 0],
                [ 0,-1, 0]],
               [[-1, 0, 1],
                [ 0, 0, 1],
                [ 1, 1, 1]] 

           ]

#fm:[h,w]
#kernel:[k,k]
#return rs:[h,w] 
def compute_conv(fm,kernel):
    [h,w]=fm.shape
    [k,_]=kernel.shape 
    r=int(k/2)
    #定義邊界填充0后的map
    padding_fm=np.zeros([h+2,w+2],np.float32)
    #保存計算結果
    rs=np.zeros([h,w],np.float32)
    #將輸入在指定該區(qū)域賦值,即除了4個邊界后锣杂,剩下的區(qū)域
    padding_fm[1:h+1,1:w+1]=fm 
    #對每個點為中心的區(qū)域遍歷
    for i in range(1,h+1):
        for j in range(1,w+1): 
            #取出當前點為中心的k*k區(qū)域
            roi=padding_fm[i-r:i+r+1,j-r:j+r+1]
            #計算當前點的卷積,對k*k個點點乘后求和
            rs[i-1][j-1]=np.sum(roi*kernel)

    return rs

def my_depthwise(chw_input,chw_weights):
    [c,_,_]=chw_input.shape
    [_,k,_]=chw_weights.shape
    #outputs=np.zeros([h,w],np.float32)
    outputs=[] #注意跟conv的區(qū)別
    #對每個feature map遍歷脂倦,從而對每個feature map進行卷積
    for i in range(c):
        #feature map==>[h,w]
        f_map=chw_input[i]
        #kernel ==>[k,k]
        w=chw_weights[i]
         
        rs =compute_conv(f_map,w)
        #outputs=outputs+rs   
        outputs.append(rs) #注意跟conv的區(qū)別
    return np.array( outputs)

def main():  

    #shape=[c,h,w]
    input = np.asarray(input_data,np.float32)
    #shape=[in_c,k,k]
    weights =  np.asarray(weights_data,np.float32) 
    rs=my_depthwise(input,weights) 
    print(rs) 


if __name__=='__main__':
    main() 

同樣,注釋寫的很清楚元莫,不再解釋代碼赖阻。運行結果如下:

[[[ 1. -3.  0.  1. -2.]
  [-1.  3.  1. -1.  3.]
  [ 1. -1.  0.  3. -2.]
  [ 1.  1.  1. -2.  1.]
  [ 4.  1.  4.  2. -1.]]

 [[ 1.  3.  2.  3.  2.]
  [ 2.  1.  3.  4.  2.]
  [ 3.  4.  5.  6.  1.]
  [ 2.  3.  5.  4.  0.]
  [ 1.  2.  1. -1. -1.]]]

可以看到,跟tensorflow的結果是一模一樣踱蠢。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末火欧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茎截,更是在濱河造成了極大的恐慌苇侵,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稼虎,死亡現(xiàn)場離奇詭異衅檀,居然都是意外死亡,警方通過查閱死者的電腦和手機霎俩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沉眶,“玉大人打却,你說我怎么就攤上這事』丫螅” “怎么了柳击?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長片习。 經(jīng)常有香客問我捌肴,道長蹬叭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任状知,我火速辦了婚禮秽五,結果婚禮上,老公的妹妹穿的比我還像新娘饥悴。我一直安慰自己坦喘,他們只是感情好,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布西设。 她就那樣靜靜地躺著瓣铣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贷揽。 梳的紋絲不亂的頭發(fā)上棠笑,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音禽绪,去河邊找鬼腐晾。 笑死,一個胖子當著我的面吹牛丐一,可吹牛的內容都是我干的藻糖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼库车,長吁一口氣:“原來是場噩夢啊……” “哼巨柒!你這毒婦竟也來了?” 一聲冷哼從身側響起柠衍,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤洋满,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后珍坊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牺勾,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年阵漏,在試婚紗的時候發(fā)現(xiàn)自己被綠了驻民。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡履怯,死狀恐怖回还,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情叹洲,我是刑警寧澤柠硕,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站运提,受9級特大地震影響蝗柔,放射性物質發(fā)生泄漏闻葵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一癣丧、第九天 我趴在偏房一處隱蔽的房頂上張望槽畔。 院中可真熱鬧,春花似錦坎缭、人聲如沸竟痰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坏快。三九已至,卻和暖如春憎夷,著一層夾襖步出監(jiān)牢的瞬間莽鸿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工拾给, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留祥得,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓蒋得,卻偏偏與公主長得像级及,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子额衙,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356