深度學習之 alexnet詳解3

一、綜述:

本篇將介紹AlexNet網絡的基本結構。AlexNet網絡是12年Alex Krizhevsky提出的深度網絡模型,故網絡用他的名字命名。該網絡一共有8個訓練參數的網絡(不包括池化層和LRN層),前5層爲卷積層,後3層爲全連接層。最後一個全連接層爲1000類輸出的Softmax分類層。LRN層出現在第1個及第2個卷積層後,最大池化層出現在LRN層及最後一個卷積層。每一層都有ReLu激活函數。

圖片來源:《ImageNet Classification with Deep ConvolutionalNeural Networks》

論文作者訓練網絡時,用了兩塊GPU顯卡,故網絡結構有兩條路徑組成,最後一層合二爲一。

AlexNet網絡參數(此表展示了一個GPU網絡參數)

二、網絡結構(AlexNet_model.py):

1.定義print_activations函數,用來展示卷積層和池化層的輸出tensor尺寸。這個函數的入口接受一個Tensor的作爲輸入,並且顯示結構尺寸:

def print_activations(t):
    print(t.op.name, ' ', t.get_shape().as_list())

2.第一個卷積層conv1。TensorFlow的name_scope函數可以區分不同層的組件。用法是

with tf.name_scope('conv1') as scope:能夠將scope中生成的變量自動命名爲conv1/xxxx (後同)。使用tf.truncated_normal截斷的正態分佈函數,標準差爲0.1,初始化卷積核參數kernel;[11, 11, 3, 64]:卷積核的尺寸爲11x11,3個顏色通道,64個卷積核;使用tf.nn.conv2d對輸入的images進行卷積操作,[1, 4, 4, 1]:卷積的核移動步長爲4(橫豎),padding模式選取“SAME”,即全畫幅掃描(最終不改變圖片尺寸);用tf.constant將卷積層的bias全部初始化爲常數0,trainable=True設置bias可以被訓練改變;使用tf.nn.bias_add把conv和biases加起來,用tf.nn.relu做非線性操作;最後使用預先定義的print_activations函數把這一層的conv1的tensor打印出來,並將這層的可訓練參數kernel和biases添加到parameters列表中;使用tf.nn.lrn對輸出的conv1參數進行LRN處理,depth_radius爲4,bias爲1,alpha爲0.001/9,beta爲0.75(基本是論文中的推薦值),LRN層的作用是對局部神經活動創建競爭機制,使得其中響應比較大的值變得相對更大,並抑制其他反饋較小的神經元,增強模型泛化能力(但是目前的神經網絡已經不再使用LRN層操作了,主要是效果不太明顯,反而會一直前饋和反饋的速度(整體速度下降1/3));使用tf.nn.max_pool對前面得到的LRN值做最大池化操作,[1, 3, 3, 1]池化尺寸爲3x3,[1, 2, 2, 1]橫豎移動步長爲2(這種池化尺寸大於移動步長,會有特徵得重複,可以增加特徵的豐富性),padding模式爲“VALID”(不填充,取樣不能超過邊框);最後將輸出結果結構參數pool1打印出來:

# conv1
    with tf.name_scope('conv1') as scope:
        kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 64], dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        conv = tf.nn.conv2d(images, kernel, [1, 4, 4, 1], padding='SAME')
        biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32),
                             trainable=True, name='biases')
        bias = tf.nn.bias_add(conv, biases)
        conv1 = tf.nn.relu(bias, name=scope)
        print_activations(conv1)
        parameters += [kernel, biases]
# pool1
    lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='lrn1')
    pool1 = tf.nn.max_pool(lrn1,
                           ksize=[1, 3, 3, 1],
                           strides=[1, 2, 2, 1],
                           padding='VALID',
                           name='pool1')
    print_activations(pool1)

3.第二個卷積層。和第一個卷積層基本一樣,區別在於[5, 5, 64, 192]:卷積核爲5x5,輸入通道數爲第一個卷積層的卷積核數量64,卷積核數量擴充爲192,卷積的步長爲1(全圖像素掃描):

# conv2
    with tf.name_scope('conv2') as scope:
        kernel = tf.Variable(tf.truncated_normal([5, 5, 64, 192], dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.Variable(tf.constant(0.0, shape=[192], dtype=tf.float32),
                             trainable=True, name='biases')
        bias = tf.nn.bias_add(conv, biases)
        conv2 = tf.nn.relu(bias, name=scope)
        parameters += [kernel, biases]
    print_activations(conv2)

  # pool2
    lrn2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='lrn2')
    pool2 = tf.nn.max_pool(lrn2,
                           ksize=[1, 3, 3, 1],
                           strides=[1, 2, 2, 1],
                           padding='VALID',
                           name='pool2')
    print_activations(pool2)

4.第三個卷積層。基本和一、二層卷積類似。區別在[3, 3, 192, 384]:卷積核尺寸爲3x3,輸入通道數爲第二層卷積核數量192,卷積核數量繼續擴充到384,卷積步長爲1,padding爲‘SAME’即:全畫像素掃描,不改變圖像尺寸:

# conv3
    with tf.name_scope('conv3') as scope:
        kernel = tf.Variable(tf.truncated_normal([3, 3, 192, 384],
                                                 dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32),
                             trainable=True, name='biases')
        bias = tf.nn.bias_add(conv, biases)
        conv3 = tf.nn.relu(bias, name=scope)
        parameters += [kernel, biases]
        print_activations(conv3)

5.第四個卷積層。和之前的卷積層類似,區別是[3, 3, 384, 256]:卷積核爲3x3,輸入通道數爲384,輸出通道數爲256:

 # conv4
    with tf.name_scope('conv4') as scope:
        kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256],
                                                 dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        conv = tf.nn.conv2d(conv3, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),
                             trainable=True, name='biases')
        bias = tf.nn.bias_add(conv, biases)
        conv4 = tf.nn.relu(bias, name=scope)
        parameters += [kernel, biases]
        print_activations(conv4)

6.第五個卷積層。[3, 3, 256, 256]:卷積核爲3x3,輸入通道數爲256,輸出通道數爲256:

 # conv5
    with tf.name_scope('conv5') as scope:
        kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 256],
                                                 dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        conv = tf.nn.conv2d(conv4, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),name='biases')
        bias = tf.nn.bias_add(conv, biases)
        conv5 = tf.nn.relu(bias, name=scope)
        parameters += [kernel, biases]
        print_activations(conv5)

  # pool5
    pool5 = tf.nn.max_pool(conv5,
                           ksize=[1, 3, 3, 1],
                           strides=[1, 2, 2, 1],
                           padding='VALID',
                           name='pool5')
    print_activations(pool5)

7.第一個全連接層。把第五個卷積層輸出結果pool5用tf.reshape函數扁平化成一維向量;用get_shape函數獲取數據扁平化後的數據長度;使用tf.truncated_normal初始化weight,正態分佈標準差定爲0.1,隱藏層節點爲4096,bias初始化爲0;使用tf.nn.relu函數進行非線性化操作;將weights和bias添加到parameters參數列表中;打印fc1的結構參數:

 # fc1
    with tf.name_scope('fc1') as scope:
        reshape = tf.reshape(pool5, [batch_size,-1])
        dim = reshape.get_shape()[-1].value
        weights = tf.Variable(tf.truncated_normal([dim,4096],dtype=tf.float32,
                                                 stddev=1e-1), name='weights')
        bias = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),name='biases')
        fc1 = tf.nn.relu(tf.matmul(reshape,weights) + bias,name=scope)
        parameters += [weights, bias]
        print_activations(fc1)

8.第二個全連接層。和第一個全連接層基本類似,唯一區別是[4096,4096]:輸入節點爲4096:

  # fc2
    with tf.name_scope('fc2') as scope:
        weights = tf.Variable(tf.truncated_normal([4096,4096],dtype=tf.float32,stddev=1e-1)
                              ,name='weights')
        bias = tf.Variable(tf.constant(0.0,shape=[4096], dtype=tf.float32),name='biases')
        fc2 = tf.nn.relu(tf.matmul(fc1,weights) + bias,name=scope)
        parameters += [weights, bias]
        print_activations(fc2)

9.第三個全連接層。和前兩個全連接層基本類似,唯一區別是[4096,1000]:輸出爲1000個分類:

 # fc3
    with tf.name_scope('fc3') as scope:
        weights = tf.Variable(tf.truncated_normal([4096,1000], dtype=tf.float32, stddev=1e-1)
                              , name='weights')
        bias = tf.Variable(tf.constant(0.0, shape=[1000], dtype=tf.float32),name='biases')
        logits = tf.nn.relu(tf.matmul(fc2,weights) + bias,name=scope)

        parameters += [weights, bias]
        print_activations(logits)

一直嘗試着在IMageNet數據集上覆現AlexNet等一些列工作,但硬件設備不允許。故,退而求其次:學習AlexNet的思想,在Cifar-10數據集上,展現經典網絡的威力。

AlexNet網絡的幾點總結:

1.網絡的整體思路架構是:卷積層+池化層+非線性變化層,然後到全連接層將輸入歸結到不同 類別的概率值;

2.LRN層的效果在之後的網絡中表現不佳,故漸漸淡出了人們的視野;

AlexNet思想實現:

1.數據集:CIFAR-10 and CIFAR-100 datasets

2.參考代碼:python+tensorflow1.13

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章