1. 程式人生 > >深度學習正則化Tricks——dropout

深度學習正則化Tricks——dropout

drop Out——神經網路中的模型平均

寫在前面

帶我入機器學習的偉大導師Andrew Ng曾經做過一個非常好的比喻。他把深度學習比作火箭,對於一個火箭來說,最重要的一部分必然是引擎,而在這個領域,目前來看引擎的核心是神經網路。但是要讓一個火箭執行,除了的有引擎還要有燃料。在深度學習領域,資料則充當了燃料的角色。

這裡寫圖片描述

對於深度網路來說,模型的容量都很高(因為其具有很多層,每層具有很有神經元),一般來說都會大於實際問題需要的表示容量。在這樣的情況下,如果訓練資料不夠,很容易導致模型簡單的記住有限的訓練資料,導致過擬合。因此,深度學習中許多Trick都是用於正則化,減小模型的泛化誤差。這裡主要記錄下自己關於dropout的學習理解,有不正確的地方希望各位提出來。

什麼是dropout

簡單來說,dropout就是在神經網路訓練過程中,每一個神經元(不包括輸出層神經元)以一定的暫時概率被丟棄。這裡丟棄是暫時性的,並不是說拋棄了,很有可能這一次被丟棄的神經元會出現在下一次訓練的網路中。由於丟棄神經元的隨機性,在每一次訓練過程中,神經網路的結構可能都不同。訓練結束以後,保留所有的節點用於測試。

這裡寫圖片描述

為什麼dropout可以提高泛化能力

  • 斯坦福CS231n課上面給出了一種形象解釋:因為每次在訓練過程中都會損失一些神經元,網路表達能力減小,為了保證訓練結果的正確性,未被丟失的神經元不得不承擔起更大的“責任”,因此在測試的時候會有更好的結果。

  • 一些神經元在訓練過程中被丟失,這樣會促使神經網路依賴更多的特徵。

  • 訓練過程中會隨機丟失一些神經元,不同的過程丟失神經元不同,行程不同結構的網路。最後測試的時候,保留所有的神經元,相當於對前面所有的網路結構做了一個模型平均。
    這裡解釋一下為什麼模型平均能夠提高模型的泛化能力。
    模型平均奏效的原因是不同的模型通常不會在測試集上面產生完全相同的錯誤。
    考慮k個模型的例子。假設每個模型在每個例子上面的誤差是ϵi , 這個誤差服從零均值方差為 E[ϵ2i]=v 且協方差是E[ϵiϵj]=c 的多維正態分佈。通過所有整合模型的平均預測的誤差所得的誤差是1kiϵi。 整合預測器平方誤差的期望是:

E[(1kiϵi)2]=1k2E[i(ϵ2i+ijϵiϵj)]=1kv+k1kc

在誤差完全相關,即c = v的情況下,均方誤差為v,模型平均沒有任何幫助,但是在誤差完全不相關的,即 c= 0的情況下,均方誤差個為1kv,這意味著模型平均的均方誤差會隨著模型數量增多而線性地減小。

在tensorflow中如何使用dropout

用MNIST為例,用CNN做分類器,其中使用了dropout, 直接上程式碼

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

sess = tf.InteractiveSession()  #建立一個互動式的session

# 權重初始化方法
def weight_variable(shape):
    init = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(init)

# 偏置初始化方法
def bias_variable(shape):
    init = tf.constant(0.1, shape=shape)
    return tf.Variable(init)

# 構造卷積層
def conv2d(x, W):
    return tf.nn.conv2d(x, W,strides=[1, 1, 1, 1], padding='SAME')

# 構造pooling層
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

# 建立佔位符
x = tf.placeholder(tf.float32, [None, 784]) # 用於把資料喂入網路中
y_ = tf.placeholder(tf.float32, [None, 10]) # 用於把標籤喂入網路中
keep_prob = tf.placeholder(tf.float32) # dropout的佔位符,用於輸入我們設定的dropout

x_image = tf.reshape(x, [-1, 28, 28, 1])

# 第一層,包括卷積、pooling
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = conv2d(x_image, W_conv1)
h_pool1 = max_pool_2x2(h_conv1)

# 第二層,包括卷積、pooling
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = conv2d(h_pool1, W_conv2)
h_pool2 = max_pool_2x2(h_conv2)

# 全連線層1
W_fc1 = weight_variable([7*7*64, 1000])
b_fc1 = bias_variable([1000])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# dropout,以keep_prob的概率隨機丟棄一些神經元
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 全連線層2
W_fc2 = weight_variable([1000, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2))

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv), axis=1))
correct_prediction = tf.equal(tf.argmax(y_, 1), tf.argmax(y_conv, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

train = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)

tf.global_variables_initializer().run()
for i in range(1000):
    batch = mnist.train.next_batch(128)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob:1.0})
        print("step: %d, accuracy %g" %(i, train_accuracy))
    train.run(feed_dict={x: batch[0], y_: batch[1], keep_prob:0.75})

print("test accuracy %g" %accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob:1.0}))

drop out一些小擴充套件

權重比例推斷規則:設定drop_out = p,訓練結束之後把權重乘以p,然後像平常一樣使用模型。這個結論詳細介紹在benjio的deep learning第七章中有更加詳細的介紹。這裡舉一個例子幫助理解:
這裡寫圖片描述
在上述例子中,drop_out = 0.5,根據模型平均的想法,最終使用的時候需要對最終的權重做一個乘以0.5的操作。

寫在後面
Benjor在介紹完dropout後寫到,另一種深度學習演算法——batch normalization有時會使Dropout變得沒有必要,博主在一個專案中確實嘗試了一下,batch normalization 真神器,BN之後,dropout、weight_decay、learning_rate變得不關鍵了。