tensorflow用dropout解決over fitting


在機器學習中可能會存在過擬合的問題,表現為在訓練集上表現很好,但在測試集中表現不如訓練集中的那么好。

技術分享圖片

圖中黑色曲線是正常模型,綠色曲線就是overfitting模型。盡管綠色曲線很精確的區分了所有的訓練數據,但是並沒有描述數據的整體特征,對新測試數據的適應性較差。

一般用於解決過擬合的方法有增加權重的懲罰機制,比如L2正規化,但在本處我們使用tensorflow提供的dropout方法,在訓練的時候, 我們隨機忽略掉一些神經元和神經聯結 , 是這個神經網絡變得”不完整”. 用一個不完整的神經網絡訓練一次.
到第二次再隨機忽略另一些, 變成另一個不完整的神經網絡. 有了這些隨機 drop 掉的規則, 我們可以想象其實每次訓練的時候, 我們都讓每一次預測結果都不會依賴於其中某部分特定的神經元. 像l1, l2正規化一樣, 過度依賴的 W , 也就是訓練參數的數值會很大, l1, l2會懲罰這些大的 參數. Dropout 的做法是從根本上讓神經網絡沒機會過度依賴.

本次我們使用之前sklearn中手寫數字作為例子來進行。

加載數據

from sklearn.datasets import load_digits from sklearn.preprocessing import LabelBinarizer digits = load_digits() X = digits.data y = digits.target # 把數值轉換成one hot格式,例如:數字4就會被轉換成:[0 0 0 0 1 0 0 0 0 0] y = LabelBinarizer().fit_transform(y) # 拆分數據集,以總量的30%作為測試集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

添加層

添加層函數如下:

import tensorflow as tf def add_layer(inputs, in_size, out_size, activation_function=None): """  添加層  :param inputs: 輸入數據  :param in_size: 輸入數據的列數  :param out_size: 輸出數據的列數  :param activation_function: 激勵函數  :return:  """ # 定義權重,初始時使用隨機變量,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反復計算就一直是固定在0中,導致可能下降不到其它位置去。 Weights = tf.Variable(tf.random_normal([in_size, out_size])) # 偏置shape為1行out_size列 biases = tf.Variable(tf.zeros([1, out_size]) + 0.1) # 建立神經網絡線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這里的權重就是每個神經元傳遞某信號的強弱系數,偏置值是指這個神經元的原先所擁有的電位高低值 Wx_plus_b = tf.matmul(inputs, Weights) + biases if activation_function is None: # 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去 outputs = Wx_plus_b else: # 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制 outputs = activation_function(Wx_plus_b) return outputs

定義placehoder和創建實際的網絡結構

# 定義placeholder # 輸入的手寫數字大小為8*8單位的數據 xs = tf.placeholder(tf.float32, [None, 8*8]) # 輸出值為one hot結構的數據 ys = tf.placeholder(tf.float32, [None, 10]) # 添加層 # 第一層輸入為8*8單位的手寫輸入數字圖像,輸出設定為100個神經元的層(為了能夠看出是overfitting的問題),激活函數一般用tanh比較好 l1 = add_layer(xs, 8*8, 100, activation_function=tf.nn.tanh) # 輸出層因為最終是一個one hot的結構,因此輸出的大小為10,激活函數用softmax prediction = add_layer(l1, 100, 10, activation_function=tf.nn.softmax)

定義損失函數

# 定義損失函數 cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1)) # 在tensorboard中記錄損失函數值 tf.summary.scalar(‘loss‘, cross_entropy) # 用梯度下降優化器進行訓練 train_step = tf.train.GradientDescentOptimizer(0.6).minimize(cross_entropy)

記錄損失函數並運行

sess = tf.Session() merged = tf.summary.merge_all() # 分別記錄訓練集的loss和測試集的loss值,目的是為了能夠對比訓練集和測試集中得擬合情況 train_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/train", sess.graph) test_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/test", sess.graph) init = tf.global_variables_initializer() sess.run(init) for i in range(500): sess.run(train_step, feed_dict={xs:X_train, ys:y_train}) if i % 50 == 0: # 分別用訓練集和測試集數據獲得損失函數值 train_result = sess.run(merged, feed_dict={xs:X_train, ys: y_train}) train_writer.add_summary(train_result, i) test_result = sess.run(merged, feed_dict={xs:X_test, ys: y_test}) test_writer.add_summary(test_result, i)

完整代碼

from sklearn.datasets import load_digits from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split digits = load_digits() X = digits.data y = digits.target # 把數值轉換成one hot格式,例如:數字4就會被轉換成:[0 0 0 0 1 0 0 0 0 0] y = LabelBinarizer().fit_transform(y) # 拆分數據集,以總量的30%作為測試集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) import tensorflow as tf def add_layer(inputs, in_size, out_size, activation_function=None): """  添加層  :param inputs: 輸入數據  :param in_size: 輸入數據的列數  :param out_size: 輸出數據的列數  :param activation_function: 激勵函數  :return:  """ # 定義權重,初始時使用隨機變量,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反復計算就一直是固定在0中,導致可能下降不到其它位置去。 Weights = tf.Variable(tf.random_normal([in_size, out_size])) # 偏置shape為1行out_size列 biases = tf.Variable(tf.zeros([1, out_size]) + 0.1) # 建立神經網絡線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這里的權重就是每個神經元傳遞某信號的強弱系數,偏置值是指這個神經元的原先所擁有的電位高低值 Wx_plus_b = tf.matmul(inputs, Weights) + biases if activation_function is None: # 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去 outputs = Wx_plus_b else: # 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制 outputs = activation_function(Wx_plus_b) return outputs # 定義placeholder # 輸入的手寫數字大小為8*8單位的數據 xs = tf.placeholder(tf.float32, [None, 8*8]) # 輸出值為one hot結構的數據 ys = tf.placeholder(tf.float32, [None, 10]) # 添加層 # 第一層輸入為8*8單位的手寫輸入數字圖像,輸出設定為100個神經元的層(為了能夠看出是overfitting的問題),激活函數一般用tanh比較好 l1 = add_layer(xs, 8*8, 100, activation_function=tf.nn.tanh) # 輸出層因為最終是一個one hot的結構,因此輸出的大小為10,激活函數用softmax prediction = add_layer(l1, 100, 10, activation_function=tf.nn.softmax) # 定義損失函數 cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1)) # 在tensorboard中記錄損失函數值 tf.summary.scalar(‘loss‘, cross_entropy) # 用梯度下降優化器進行訓練 train_step = tf.train.GradientDescentOptimizer(0.6).minimize(cross_entropy) sess = tf.Session() merged = tf.summary.merge_all() # 分別記錄訓練集的loss和測試集的loss值,目的是為了能夠對比訓練集和測試集中得擬合情況 train_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/train", sess.graph) test_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/test", sess.graph) init = tf.global_variables_initializer() sess.run(init) for i in range(500): sess.run(train_step, feed_dict={xs:X_train, ys:y_train}) if i % 50 == 0: # 分別用訓練集和測試集數據獲得損失函數值 train_result = sess.run(merged, feed_dict={xs:X_train, ys: y_train}) train_writer.add_summary(train_result, i) test_result = sess.run(merged, feed_dict={xs:X_test, ys: y_test}) test_writer.add_summary(test_result, i)

輸出結果

當我們運行了上面的代碼后,會在D:/todel/data/tensorflow/目錄下生成tensorboard收集的日志文件,我們可以在那個目錄下輸入:

最終在tensorboard中顯示的圖形為:

我們發現,訓練集(藍色的那條曲線)損失值要比測試集(黃色的那條曲線)小,這樣就存在過擬合的情況。

消除過擬合

為了消除過擬合,我們采用dropout方式來進行。
首先設置一個保留概率的placeholder,這樣在運行時可以通過參數來進行設置

# 設置保留概率,即我們要保留的結果所占比例,它作為一個placeholder,在run時傳入, 當keep_prob=1的時候,相當於100%保留,也就是dropout沒有起作用。 keep_prob = tf.placeholder(tf.float32)

然后在add_layer函數中調用dropout功能:

 # 調用dropout功能 Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob)

最后在訓練時設置保留的概率,但在獲得損失值時用全部的數據來進行獲取:

for i in range(500): sess.run(train_step, feed_dict={xs:X_train, ys:y_train, keep_prob: 0.7}) if i % 50 == 0: # 分別用訓練集和測試集數據獲得損失函數值 train_result = sess.run(merged, feed_dict={xs:X_train, ys: y_train, keep_prob:1}) train_writer.add_summary(train_result, i) test_result = sess.run(merged, feed_dict={xs:X_test, ys: y_test, keep_prob:1}) test_writer.add_summary(test_result, i)

這樣全部代碼為:

from sklearn.datasets import load_digits from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split digits = load_digits() X = digits.data y = digits.target # 把數值轉換成one hot格式,例如:數字4就會被轉換成:[0 0 0 0 1 0 0 0 0 0] y = LabelBinarizer().fit_transform(y) # 拆分數據集,以總量的30%作為測試集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) import tensorflow as tf def add_layer(inputs, in_size, out_size, activation_function=None): """  添加層  :param inputs: 輸入數據  :param in_size: 輸入數據的列數  :param out_size: 輸出數據的列數  :param activation_function: 激勵函數  :return:  """ # 定義權重,初始時使用隨機變量,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反復計算就一直是固定在0中,導致可能下降不到其它位置去。 Weights = tf.Variable(tf.random_normal([in_size, out_size])) # 偏置shape為1行out_size列 biases = tf.Variable(tf.zeros([1, out_size]) + 0.1) # 建立神經網絡線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這里的權重就是每個神經元傳遞某信號的強弱系數,偏置值是指這個神經元的原先所擁有的電位高低值 Wx_plus_b = tf.matmul(inputs, Weights) + biases # 調用dropout功能 Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob) if activation_function is None: # 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去 outputs = Wx_plus_b else: # 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制 outputs = activation_function(Wx_plus_b) return outputs # 定義placeholder # 輸入的手寫數字大小為8*8單位的數據 xs = tf.placeholder(tf.float32, [None, 8*8]) # 輸出值為one hot結構的數據 ys = tf.placeholder(tf.float32, [None, 10]) # 設置保留概率,即我們要保留的結果所占比例,它作為一個placeholder,在run時傳入, 當keep_prob=1的時候,相當於100%保留,也就是dropout沒有起作用。 keep_prob = tf.placeholder(tf.float32) # 添加層 # 第一層輸入為8*8單位的手寫輸入數字圖像,輸出設定為100個神經元的層(為了能夠看出是overfitting的問題),激活函數一般用tanh比較好 l1 = add_layer(xs, 8*8, 100, activation_function=tf.nn.tanh) # 輸出層因為最終是一個one hot的結構,因此輸出的大小為10,激活函數用softmax prediction = add_layer(l1, 100, 10, activation_function=tf.nn.softmax) # 定義損失函數 cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1)) # 在tensorboard中記錄損失函數值 tf.summary.scalar(‘loss‘, cross_entropy) # 用梯度下降優化器進行訓練 train_step = tf.train.GradientDescentOptimizer(0.6).minimize(cross_entropy) sess = tf.Session() merged = tf.summary.merge_all() # 分別記錄訓練集的loss和測試集的loss值,目的是為了能夠對比訓練集和測試集中得擬合情況 train_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/train", sess.graph) test_writer = tf.summary.FileWriter("D:/todel/data/tensorflow/test", sess.graph) init = tf.global_variables_initializer() sess.run(init) for i in range(500): sess.run(train_step, feed_dict={xs:X_train, ys:y_train, keep_prob: 0.7}) if i % 50 == 0: # 分別用訓練集和測試集數據獲得損失函數值 train_result = sess.run(merged, feed_dict={xs:X_train, ys: y_train, keep_prob:1}) train_writer.add_summary(train_result, i) test_result = sess.run(merged, feed_dict={xs:X_test, ys: y_test, keep_prob:1}) test_writer.add_summary(test_result, i)

運行后輸出tensorboard圖形為(記得把之前的文件或目錄進行刪除並運行tensorboard進行顯示圖形):

這樣訓練集和測試集的損失值就比較接近了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM