DL代碼參考

AlexNet實現

在2012年ImageNet競賽中,來自多倫多大學的Hinton教授帶着他的學生Alex Krizhevsky驚豔了世界,他們構建的神經網絡模型以超過第二名10.9個百分點的絕對優勢一舉奪冠。 而這種新型網絡結構以第一作者Alex名字進行命名,被稱爲AlexNet。那麼爲什麼AlexNet在那個時候會有如此巨大的優勢呢?

在這裏插入圖片描述

AlexNet中包含了幾個比較新的技術點,也首次在CNN中成功應用了ReLU、Dropout和LRN等Trick。同時AlexNet也使用了GPU進行運算加速。

AlexNet將LeNet的思想發揚光大,把CNN的基本原理應用到了很深很寬的網絡中。AlexNet主要使用到的新技術點如下:

(1)成功使用ReLU作爲CNN的激活函數,並驗證其效果在較深的網絡超過了Sigmoid,成功解決了Sigmoid在網絡較深時的梯度彌散問題。雖然ReLU激活函數在很久之前就被提出了,但是直到AlexNet的出現纔將其發揚光大。

(2)訓練時使用Dropout隨機忽略一部分神經元,以避免模型過擬合。Dropout雖有單獨的論文論述,但是AlexNet將其實用化,通過實踐證實了它的效果。在AlexNet中主要是最後幾個全連接層使用了Dropout。

(3)在CNN中使用重疊的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。並且AlexNet中提出讓步長比池化核的尺寸小,這樣池化層的輸出之間會有重疊和覆蓋,提升了特徵的豐富性。

(4)提出了LRN層,對局部神經元的活動創建競爭機制,使得其中響應比較大的值變得相對更大,並抑制其他反饋較小的神經元,增強了模型的泛化能力。

(5)使用CUDA加速深度卷積網絡的訓練,利用GPU強大的並行計算能力,處理神經網絡訓練時大量的矩陣運算。AlexNet使用了兩塊GTX 580 GPU進行訓練,單個GTX 580只有3GB顯存,這限制了可訓練的網絡的最大規模。因此作者將AlexNet分佈在兩個GPU上,在每個GPU的顯存中儲存一半的神經元的參數。因爲GPU之間通信方便,可以互相訪問顯存,而不需要通過主機內存,所以同時使用多塊GPU也是非常高效的。同時,AlexNet的設計讓GPU之間的通信只在網絡的某些層進行,控制了通信的性能損耗。

(6)數據增強,隨機地從256256的原始圖像中截取224224大小的區域(以及水平翻轉的鏡像),相當於增加了2*(256-224)^2=2048倍的數據量。如果沒有數據增強,僅靠原始的數據量,參數衆多的CNN會陷入過擬閤中,使用了數據增強後可以大大減輕過擬合,提升泛化能力。進行預測時,則是取圖片的四個角加中間共5個位置,並進行左右翻轉,一共獲得10張圖片,對他們進行預測並對10次結果求均值。同時,AlexNet論文中提到了會對圖像的RGB數據進行PCA處理,並對主成分做一個標準差爲0.1的高斯擾動,增加一些噪聲,這個Trick可以讓錯誤率再下降1%。

tf實現

# -*- coding=UTF-8 -*-
import tensorflow as tf
# 輸入數據
import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
# 定義網絡超參數
learning_rate = 0.001  
training_iters = 200000  
batch_size = 64  
display_step = 20  
# 定義網絡參數
n_input = 784  # 輸入的維度
n_classes = 10 # 標籤的維度
dropout = 0.8  # Dropout 的概率
# 佔位符輸入
x = tf.placeholder(tf.types.float32, [None, n_input])
y = tf.placeholder(tf.types.float32, [None, n_classes])
keep_prob = tf.placeholder(tf.types.float32)
# 卷積操作
def conv2d(name, l_input, w, b):  
    return tf.nn.relu(tf.nn.bias_add( \
    tf.nn.conv2d(l_input, w, strides=[1, 1, 1, 1], padding='SAME'),b) \
    , name=name)
# 最大下采樣操作
def max_pool(name, l_input, k):  
    return tf.nn.max_pool(l_input, ksize=[1, k, k, 1], \
    strides=[1, k, k, 1], padding='SAME', name=name)
# 歸一化操作
def norm(name, l_input, lsize=4):  
    return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)
# 定義整個網絡
def alex_net(_X, _weights, _biases, _dropout):  
    _X = tf.reshape(_X, shape=[-1, 28, 28, 1]) # 向量轉爲矩陣
    # 卷積層
    conv1 = conv2d('conv1', _X, _weights['wc1'], _biases['bc1'])
    # 下采樣層
    pool1 = max_pool('pool1', conv1, k=2)
    # 歸一化層
    norm1 = norm('norm1', pool1, lsize=4)
    # Dropout
    norm1 = tf.nn.dropout(norm1, _dropout)
   
    # 卷積
    conv2 = conv2d('conv2', norm1, _weights['wc2'], _biases['bc2'])
    # 下采樣
    pool2 = max_pool('pool2', conv2, k=2)
    # 歸一化
    norm2 = norm('norm2', pool2, lsize=4)
    # Dropout
    norm2 = tf.nn.dropout(norm2, _dropout)
   
    # 卷積
    conv3 = conv2d('conv3', norm2, _weights['wc3'], _biases['bc3'])
    # 下采樣
    pool3 = max_pool('pool3', conv3, k=2)
    # 歸一化
    norm3 = norm('norm3', pool3, lsize=4)
    # Dropout
    norm3 = tf.nn.dropout(norm3, _dropout)
   
    # 全連接層,先把特徵圖轉爲向量
    dense1 = tf.reshape(norm3, [-1, _weights['wd1'].get_shape().as_list()[0]])
    dense1 = tf.nn.relu(tf.matmul(dense1, _weights['wd1']) + _biases['bd1'], name='fc1')
    # 全連接層
    dense2 = tf.nn.relu(tf.matmul(dense1, _weights['wd2']) + _biases['bd2'], name='fc2')
    # Relu activation
    # 網絡輸出層
    out = tf.matmul(dense2, _weights['out']) + _biases['out']
    return out
   
# 存儲所有的網絡參數
weights = {
    'wc1': tf.Variable(tf.random_normal([3, 3, 1, 64])),
    'wc2': tf.Variable(tf.random_normal([3, 3, 64, 128])),
    'wc3': tf.Variable(tf.random_normal([3, 3, 128, 256])),
    'wd1': tf.Variable(tf.random_normal([4*4*256, 1024])),
    'wd2': tf.Variable(tf.random_normal([1024, 1024])),
    'out': tf.Variable(tf.random_normal([1024, 10]))
}
biases = {
    'bc1': tf.Variable(tf.random_normal([64])),
    'bc2': tf.Variable(tf.random_normal([128])),
    'bc3': tf.Variable(tf.random_normal([256])),
    'bd1': tf.Variable(tf.random_normal([1024])),
    'bd2': tf.Variable(tf.random_normal([1024])),
    'out': tf.Variable(tf.random_normal([n_classes]))
}
# 構建模型
pred = alex_net(x, weights, biases, keep_prob)
# 定義損失函數和學習步驟
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# 測試網絡
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 初始化所有的共享變量
init = tf.initialize_all_variables()
# 開啓一個訓練
with tf.Session() as sess:
    sess.run(init)
    step = 1  
    # Keep training until reach max iterations
    while step * batch_size < training_iters:
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        # 獲取批數據
        sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, keep_prob: dropout})
        if step % display_step == 0:
            # 計算精度
            acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            # 計算損失值
            loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc)
        step += 1  
    print "Optimization Finished!"  
    # 計算測試精度
    print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images[:256], y: mnist.test.labels[:256], keep_prob: 1.})
 
# 以上代碼忽略了部分卷積層,全連接層使用了特定的權重。

keras實現

輸入圖片大小爲 227*227*3
1. 卷積池化層
  卷積:96個卷積核,11*11*3的卷積核規模 步長爲4 卷積後的特徵矩陣規模爲55*55*96 不填充
  池化:最大池化層,3 * 3池化規模 2步長 池化後的特徵矩陣規模爲27*27*96
2. 卷積池化層
  卷積:256個卷積核,5 * 5 * 96的卷積核規模 步長爲1 填充爲2 卷積後的特徵矩陣規模爲256*27*27
  池化:最大池化層,3 * 3池化規模 2步長 池化後的特徵矩陣規模爲13*13*256
3. 卷積層
  卷積:384個卷積核,3*3*256的卷積核規模 步長爲1 填充爲1 卷積後的特徵矩陣規模爲13*13*384
4. 卷積層
  卷積:384個卷積核,3*3*384的卷積核規模 步長爲1 填充爲1 卷積後的特徵矩陣規模爲13*13*384
5. 卷積池化層
  卷積:256個卷積核,3*3*384的卷積核規模 步長爲1 填充爲2 卷積後的特徵矩陣規模爲256*13*13
  池化:最大池化層,3 * 3池化規模 2步長 池化後的特徵矩陣規模爲6*6*256
6. 全連接層
  4096個神經元,6*6*256的卷積核規模 步長爲1 不填充 卷積後的特徵矩陣規模爲1*1*4096
7. 全連接層
  卷積:4096個神經元,1*1*4096的卷積核規模 步長爲1 不填充 卷積後的特徵矩陣規模爲1*1*4096
8. 全連接層
  1000個神經元,與fc7層全連接,經過softmax輸出1000個值
from keras.models import Sequential
from keras.layers import Dense, Flatten, Dropout,Activation
from keras.layers.convolutional import Conv2D, MaxPooling2D

# AlexNet
model = Sequential()
# 第一層
model.add(Conv2D(filters=96, kernel_size=(11, 11),
                 strides=(4, 4), padding='valid',
                 input_shape=(227, 227, 3),
                 activation='relu'))

model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2),
                       padding='valid'))
# 第二層
model.add(Conv2D(filters=256, kernel_size=(5, 5),
                 strides=(1, 1), padding='same',
                 activation='relu'))

model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2),
                       padding='valid'))
# 第三層
model.add(Conv2D(filters=384, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
# 第四層
model.add(Conv2D(filters=384, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
# 第五層
model.add(Conv2D(filters=256, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2), padding='valid'))
# 第六段
model.add(Flatten())
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
# 第七層
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
# 第八層
model.add(Dense(1000, activation='relu'))
model.add(Dropout(0.5))

# Output Layer
model.add(Dense(2))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

VGG16-Keras實現

VGG16有13個卷積層,3個全連接層,5個池化層,由於卷積層和全連接層具有權重係數,
因此也被稱爲權重層,一共16個權重層,因而得名

VGG16的突出特點是簡單,體現在:

  1. 卷積層均採用相同的卷積核參數
    卷積層均表示爲conv3-XXX,其中conv3說明該卷積層採用的卷積核的尺寸是3,
    即寬(width)和高(height)均爲3,3*3是很小的卷積核尺寸,結合其它參數
    (步幅stride=1,填充方式padding=same),這樣就能夠使得每一個卷積層(張量)與前一層
    (張量)保持相同的寬和高。XXX代表卷積層的通道數。

  2. 池化層均採用相同的池化核參數
    池化層的參數均爲2×

  3. 模型是由若干卷積層和池化層堆疊(stack)的方式構成,
    比較容易形成較深的網絡結構(在2014年,16層已經被認爲很深了)

VGG16輸入224*224*3的圖片
VGG16的卷積和池化共分爲51. 兩個卷積一個池化
  卷積:64個卷積核 3*3的大小 填充方式爲 padding=same 步長爲1
  池化:2*2的規模 步長爲2
2. 兩個卷積一個池化
  卷積:128個卷積核 3*3的大小 填充方式爲 padding=same 步長爲1
  池化:2*2的規模 步長爲2
3. 三個卷積一個池化
  卷積:256個卷積核 3*3的大小 填充方式爲 padding=same 步長爲1
  池化:2*2的規模 步長爲2
4. 三個卷積一個池化
  卷積:512個卷積核 3*3的大小 填充方式爲 padding=same 步長爲1
  池化:2*2的規模 步長爲2
5. 三個卷積一個池化
  卷積:512個卷積核 3*3的大小 填充方式爲 padding=same 步長爲1
  池化:2*2的規模 步長爲2
全連接層
1. 4096個神經元
1. 4096個神經元
1. 1000個神經元 激活函數爲softmax
from keras import Sequential
from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, Dropout
from keras.layers import Input
from keras.optimizers import SGD

model = Sequential()
 
# BLOCK 1
model.add(Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block1_conv1', input_shape = (224, 224, 3)))
model.add(Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block1_conv2'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block1_pool'))
 
# BLOCK2
model.add(Conv2D(filters = 128, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block2_conv1'))
model.add(Conv2D(filters = 128, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block2_conv2'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block2_pool'))
 
# BLOCK3
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv1'))
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv2'))
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block3_pool'))
 
# BLOCK4
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv1'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv2'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block4_pool'))
 
# BLOCK5
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv1'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv2'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block5_pool'))
 
model.add(Flatten())
model.add(Dense(4096, activation = 'relu', name = 'fc1'))
model.add(Dropout(0.5))
model.add(Dense(4096, activation = 'relu', name = 'fc2'))
model.add(Dropout(0.5))
model.add(Dense(1000, activation = 'softmax', name = 'prediction'))

Keras還自帶了一個VGG16的模型

import keras
model = keras.applications.vgg16.VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章