上篇筆記咱們利用MNIST數據集訓練了一個手寫數字識別的模型,可是準確率很是的低,維持在91%左右,咱們能夠嘗試着將準確率提升到96%以上,在實驗以前咱們須要先了解一些基本的概念,本篇文章可能會有些枯燥,由於大多都是理論知識。git
想了解什麼是激活函數,就要先了解神經網絡的基本模型,下圖所示爲一單一人工神經網絡的基本模型圖: 算法
神經網絡中的每一個神經元節點接受上一層神經元的輸出值做爲本神經元的輸入值,並將輸入值傳遞給下一層,輸入層神經元節點會將輸入屬性值直接傳遞給下一層(隱藏層或輸出層)。在多層神經網絡中,上層節點的輸出和下層節點的輸入之間具備一個函數關係,這個函數稱爲激活函數(又稱激勵函數)。數據庫
若是咱們不運用激活函數的話,則輸出信號將僅僅是一個簡單的線性函數。線性函數一個一級多項式。現現在,線性方程是很容易解決的,可是它們的複雜性有限,而且從數據中學習複雜函數映射的能力更小。一個沒有激活函數的神經網絡將只不過是一個線性迴歸模型(Linear regression Model)罷了,它功率有限,而且大多數狀況下執行得並很差。咱們但願咱們的神經網絡不只僅能夠學習和計算線性函數,並且還要比這複雜得多。一樣是由於沒有激活函數,咱們的神經網絡將沒法學習和模擬其餘複雜類型的數據,例如圖像、視頻、音頻、語音等。這就是爲何咱們要使用人工神經網絡技術,諸如深度學習(Deep learning),來理解一些複雜的事情,一些相互之間具備不少隱藏層的非線性問題,而這也能夠幫助咱們瞭解複雜的數據。bash
非線性函數是那些一級以上的函數,並且當繪製非線性函數時它們具備曲率。如今咱們須要一個能夠學習和表示幾乎任何東西的神經網絡模型,以及能夠將輸入映射到輸出的任意複雜函數。神經網絡被認爲是通用函數近似器(Universal Function Approximators)。這意味着他們能夠計算和學習任何函數。幾乎咱們能夠想到的任何過程均可以表示爲神經網絡中的函數計算。網絡
而這一切都歸結於這一點,咱們須要應用激活函數f(x),以便使網絡更增強大,增長它的能力,使它能夠學習複雜的事物,複雜的表單數據,以及表示輸入輸出之間非線性的複雜的任意函數映射。所以,使用非線性激活函數,咱們便可以從輸入輸出之間生成非線性映射。機器學習
激活函數的另外一個重要特徵是:它應該是能夠區分的。咱們須要這樣作,以便在網絡中向後推動以計算相對於權重的偏差(丟失)梯度時執行反向優化策略,而後相應地使用梯度降低或任何其餘優化技術優化權重以減小偏差。函數
二次代價函數的公式以下:post
其中,C表示代價,x表示樣本,y表示實際值,a表示輸出值,n表示樣本的總數。爲簡單起見,以一個樣本爲例進行說明,此時二次代價函數爲:性能
其中,是激活函數學習
加入咱們使用梯度降低法來調整權值參數的大小,權值w和偏置b的梯度推導以下:
其中,z表示神經元的輸入,表示激活函數。從以上公式能夠看出,w和b的梯度跟激活函數的梯度成正比,激活函數的梯度越大,和的大小調整得越快,訓練收斂得就越快。而神經網絡經常使用的激活函數爲sigmoid函數,該函數的曲線以下所示:
因此在這種狀況下,權值和偏置的變化就會出現以下異常:
假設咱們目標是收斂到 1。A 點爲 0.82 離目標比較遠,梯度比較大,權值調整比較大。B 點爲 0.98 離目標比較近,梯度比較小,權值調整比較小。調整方案合理。 假如咱們目標是收斂到 0. A 點爲 0.82 離目標比較近,梯度比較大,權值調整比較大。B 點爲 0.98 離目標比較遠,梯度比較小,權值調整比較小。調整方案不合理。
那麼可能有人就會說,若是咱們想要解決上述問題,選擇一個梯度不變化或變化不明顯的激活函數不就解決問題了嗎?圖樣圖森破,那樣雖然簡單粗暴地解決了這個問題,但可能會引發其餘更多更麻煩的問題。並且,相似sigmoid這樣的函數(好比tanh函數)有不少優勢,很是適合用來作激活函數,具體請自行google之。
在這裏咱們不改變激活函數,選擇將代價函數改成交叉熵代價函數。
先放公式:$$C=-\frac{1}{n}\sum_{x}^{ }[ylna+(1-y)ln(1-a)]$$ 其中,C表示代價,x表示樣本,y表示實際值,a表示輸出值,n表示樣本的總數。那麼,從新計算參數w的梯度:
其中:$${\sigma }'(z)=\sigma (z)(1-\sigma (z))$$ 所以,w的梯度公式中原來的被消掉了;另外,該梯度公式中的表示輸出值與實際值之間的偏差。因此,當偏差越大,梯度就越大,參數w調整得越快,訓練速度也就越快。同理可得,b的梯度爲:
實際狀況證實,交叉熵代價函數帶來的訓練效果每每比二次代價函數要好。
tf.nn.sigmoid_cross_entropy_with_logits()來表示跟sigmoid搭配使用的交叉熵。
tf.nn.softmax_cross_entropy_with_logits()來表示跟softmax搭配使用的交叉熵。
複製代碼
以上一篇文章手寫數字識別的模型爲例子,在這給出採用交叉熵函數的模型的代碼:
import datetime
# 4.1 交叉熵代價函數
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
start = datetime.datetime.now()
# 載入數據
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
# 每一個批次的大小
batch_size = 50
# 計算一共有多少個批次
n_batch = mnist.train.num_examples // batch_size
# 定義兩個placeholder
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
# 建立一個簡單的神經網絡
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
prediction = tf.nn.softmax(tf.matmul(x, W)+b)
# 二次代價函數
# loss = tf.reduce_mean(tf.square(y-prediction))
# 交叉熵代價函數
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
labels=y, logits=prediction))
# 使用梯度降低法
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# 初始化變量
init = tf.global_variables_initializer()
# 結果存放在一個布爾型列表中
# argmax返回一維張量中最大的值所在的位置
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(prediction, 1))
# 求準確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(init)
for epoch in range(30):
for batch in range(n_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step, feed_dict={x: batch_xs, y: batch_ys})
acc = sess.run(accuracy, feed_dict={
x: mnist.test.images, y: mnist.test.labels})
print("Iter "+str(epoch)+",Testing Accuracy "+str(acc))
end = datetime.datetime.now()
print((end-start).seconds)
複製代碼
在這裏咱們將二次代價函數更改成了交叉熵代價函數:
# 二次代價函數
# loss = tf.reduce_mean(tf.square(y-prediction))
# 交叉熵代價函數
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
labels=y, logits=prediction))
複製代碼
接下來咱們來對比下訓練的結果:
由上圖可知,使用二次代價函數訓練第10次的精確度爲0.9063,而使用交叉熵代價函數訓練到第2次的精確度就已經超過0.9了,結果顯而易見。
擬合分爲三種:1.欠擬合(underfitting);2. 正確擬合(just right);3. 過擬合(overfitting);以下圖所示:
其中每一個x
表示的是樣本,每條曲線表明的是模型。 下圖是分類問題中的擬合狀況,和上述狀況相似。
在這裏介紹過擬合,下面是wikipedia對於overfitting的解釋。 在統計學和機器學習中,overfitting通常在描述統計學模型隨機偏差或噪音時用到。它一般發生在模型過於複雜的狀況下,如參數過多等。overfitting會使得模型的預測性能變弱,而且增長數據的波動性。
發生overfitting是由於評判訓練模型的標準不適用於做爲評判該模型好壞的標準,模型一般會加強模型在訓練模型的預測性能。可是模型的性能並非由模型在訓練集的表現好壞而決定,它是由模型在未知數據集上的表現肯定的。當模型開始「memorize」訓練數據而不是從訓練數據中「learning」時,overfitting就出現了。好比,若是模型的parameters大於或等於觀測值的個數,這種模型會顯得過於簡單,雖然模型在訓練時的效果能夠表現的很完美,基本上記住了數據的所有特色,但這種模型在未知數據的表現能力會大減折扣,由於簡單的模型泛化能力一般都是很弱的。
上面這個圖,是經過線性函數和多項式函數來擬合這些數據點,顯然多項式函數擬合效果很完美,包含了全部的點,而線性函數丟失了大部分點。但實際上,線性函數有一個很好的泛化能力,若是用這些點來作一個迴歸線,多項式函數過擬合的狀況更糟糕。
過擬合不只和參數的個數以及數據有關,也和數據形狀模型結構的一致性有關。
爲了不過擬合,有必要使用一些額外的技術(如交叉驗證、正則化、early stopping、貝斯信息量準則、赤池信息量準則或model comparison),以指出什麼時候會有更多訓練而沒有致使更好的通常化。
Overfitting的概念在機器學習中很重要。一般一個學習算法是藉由訓練樣原本訓練的,在訓練時會伴隨着訓練偏差。當把該模型用到未知數據的測試時,就會相應的帶來一個validation error。下面經過訓練偏差和驗證偏差來詳細分析一下overfitting。以下圖:
在上圖總,藍色表示訓練偏差training error,紅色表示validation error。當訓練偏差達到中間的那條垂直線的點時,模型應該是最優的,若是繼續減小模型的訓練偏差,這時就會發生過擬合。
其實你能夠這樣來理解overfitting:數據集中信息分爲兩部分,一部分是和預測將來數據有關的數據,另外一部分是無關的,二者地位是平等的。用來做爲預測的評判標準越不精確,代表噪聲數據就越多,須要忽略掉的數據也就越多,而關鍵就是究竟那一部分應該忽略掉。因此咱們把一個學習算法對噪聲的削減能力就叫作它的魯棒性。咱們須要的就是魯棒性很強的學習算法
舉一個簡單的例子,一個零售購物的數據庫包括購買項、購買人、日期、和購買時間。根據這個數據能夠很容易的創建一個模型,而且在訓練集上的擬合效果也會很好,經過使用日期、購買時間來預測其它屬性列的值,可是這個模型對於新數據的泛化能力很弱,由於這些過去的數據不會再次發生。
這裏推薦閱讀機器學習中用來防止過擬合的方法有哪些?,說的比較詳細。
這樣就讓神經網絡變得冗長和重複,由於它沒法再依賴具體的神經元或鏈接來提取具體的特徵。等完成模型訓練後,全部的神經元和鏈接會被保存下來。試驗顯示這種方法能起到和神經網絡集成方法同樣的效果,能夠幫助模型泛化,這樣就能減小過擬合的問題。
咱們來用代碼體驗下dropout:
import datetime
# 4.2 Dropout
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
start = datetime.datetime.now()
# 載入數據
mnist = input_data.read_data_sets("MNIST_data", one_hot=True)
# 每一個批次的大小
batch_size = 50
# 計算一共有多少個批次
n_batch = mnist.train.num_examples // batch_size
# 定義兩個placeholder
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
keep_prob = tf.placeholder(tf.float32)
# 建立一個神經網絡
W1 = tf.Variable(tf.truncated_normal([784, 2000], stddev=0.1))
b1 = tf.Variable(tf.zeros([2000])+0.1)
L1 = tf.nn.tanh(tf.matmul(x, W1)+b1)
L1_drop = tf.nn.dropout(L1, keep_prob)
W2 = tf.Variable(tf.truncated_normal([2000, 2000], stddev=0.1))
b2 = tf.Variable(tf.zeros([2000])+0.1)
L2 = tf.nn.tanh(tf.matmul(L1_drop, W2)+b2)
L2_drop = tf.nn.dropout(L2, keep_prob)
W3 = tf.Variable(tf.truncated_normal([2000, 1000], stddev=0.1))
b3 = tf.Variable(tf.zeros([1000])+0.1)
L3 = tf.nn.tanh(tf.matmul(L2_drop, W3)+b3)
L3_drop = tf.nn.dropout(L3, keep_prob)
W4 = tf.Variable(tf.truncated_normal([1000, 10], stddev=0.1))
b4 = tf.Variable(tf.zeros([10])+0.1)
prediction = tf.nn.softmax(tf.matmul(L3_drop, W4)+b4)
# 二次代價函數
# loss = tf.reduce_mean(tf.square(y-prediction))
# 交叉熵代價函數
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(
labels=y, logits=prediction))
# 使用梯度降低法
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
# 初始化變量
init = tf.global_variables_initializer()
# 結果存放在一個布爾型列表中
# argmax返回一維張量中最大的值所在的位置
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(prediction, 1))
# 求準確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(init)
for epoch in range(20):
for batch in range(n_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step, feed_dict={
x: batch_xs, y: batch_ys, keep_prob: 1.0})
test_acc = sess.run(accuracy, feed_dict={
x: mnist.test.images, y: mnist.test.labels, keep_prob: 1.0})
train_acc = sess.run(accuracy, feed_dict={
x: mnist.train.images, y: mnist.train.labels, keep_prob: 1.0})
print("Iter "+str(epoch)+",Testing Accuracy " +
str(test_acc)+",Train Accuracy"+str(train_acc))
end = datetime.datetime.now()
print((end-start).seconds)
複製代碼
相較於以前的代碼咱們更改了如下一些地方:
W1 = tf.Variable(tf.truncated_normal([784, 2000], stddev=0.1))
b1 = tf.Variable(tf.zeros([2000])+0.1)
L1 = tf.nn.tanh(tf.matmul(x, W1)+b1)
L1_drop = tf.nn.dropout(L1, keep_prob)
W2 = tf.Variable(tf.truncated_normal([2000, 2000], stddev=0.1))
b2 = tf.Variable(tf.zeros([2000])+0.1)
L2 = tf.nn.tanh(tf.matmul(L1_drop, W2)+b2)
L2_drop = tf.nn.dropout(L2, keep_prob)
W3 = tf.Variable(tf.truncated_normal([2000, 1000], stddev=0.1))
b3 = tf.Variable(tf.zeros([1000])+0.1)
L3 = tf.nn.tanh(tf.matmul(L2_drop, W3)+b3)
L3_drop = tf.nn.dropout(L3, keep_prob)
W4 = tf.Variable(tf.truncated_normal([1000, 10], stddev=0.1))
b4 = tf.Variable(tf.zeros([10])+0.1)
prediction = tf.nn.softmax(tf.matmul(L3_drop, W4)+b4)
複製代碼
我額外的爲神經網絡添加了兩個隱藏層,爲了方便體現出差別,我將每一個隱藏層的神經元數量設置的比較多。 而後在訓練過程當中,
for epoch in range(10):
for batch in range(n_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step, feed_dict={
x: batch_xs, y: batch_ys, keep_prob: 1.0})
test_acc = sess.run(accuracy, feed_dict={
x: mnist.test.images, y: mnist.test.labels, keep_prob: 1.0})
train_acc = sess.run(accuracy, feed_dict={
x: mnist.train.images, y: mnist.train.labels, keep_prob: 1.0})
print("Iter "+str(epoch)+",Testing Accuracy " +
str(test_acc)+",Train Accuracy"+str(train_acc))
複製代碼
其中keep_prob
表示啓用神經元佔神經元總數的百分比(1.0表示所有使用),train_acc
表示用訓練樣原本測試訓練出來的模型的精確度,test_acc
表示用測試樣原本測試訓練出來的模型的精確度,用這兩個數據來反映出擬合程度。訓練結果以下圖所示:
在本文中爲了提升精確度,引入了代價函數這個概念,爲了更好的理解代價函數所以提早介紹了什麼是激活函數以及爲何須要激活函數。在只用一層神經網絡的時候經過更改代價函數,咱們可使精確度達到93%左右(訓練次數較多時),但這仍是不夠,因此咱們嘗試多添加幾層神經元,可是這時候就會出現「過擬合」這個新的問題了,一般有三種方式解決過擬合的問題。以後的文章將會在此基礎上介紹如下優化器,而且告知你們如何使用谷歌免費的GPU服務加速深度學習的模型訓練。