TensorFlow學習筆記(4)——深層神經網路
1 深度學習與深層神經網路
維基百科對深度學習的精確定義為“一類通過多層非線性變換對高複雜性資料建模演算法的合集”。可見深度學習的兩個重要特性:多層和非線性。
1 非線性模型的侷限性
一個線性模型中通過輸入得到輸出的函式被稱為一個線性變換,線性模型的最大特點是任意線性模型的組合仍是線性模型。而前向傳播演算法完全符合其定義。因此只通過線性變換,任意層的全連線神經網路和單層神經網路的表達能力沒有任何區別,且都是線性模型,而線性模型能解決的問題太有限,只能解決線性可分問題。
2 啟用函式實現去線性化
如果將每個神經元(節點)的輸出通過一個非線性函式,那麼整個網路的模型就不再是線性的了。這個非線性函式就是啟用函式。
TF目前提供7種非線性啟用函式,包括tf.nn.relu、tf.sigmoid、tf.tanh是其中比較常用的幾個。同時支援使用自己定義的啟用函式。
3 多層網路解決異或運算
加入隱藏層後,異或問題可以得到很好的解決。這個特徵對於不易提取特徵向量的問題有很大幫助。
2 損失函式定義
神經網路模型的效果和優化目標是通過損失函式(loss function)來定義的。
1 經典損失函式
交叉熵是判斷一個輸出向量和期望向量距離的評判方法之一。交叉熵刻畫的是兩個概率分佈之間的距離。
1.分類問題->輸出n個結點->交叉熵,原來的神經網路輸出被當做置信度生成新的輸出,其滿足概率分佈的所有要求,用交叉熵計算預測的概率分佈和真實答案的概率分佈之間的距離
2.迴歸問題(預測)->1個結點->均方誤差
此外,還可以根據問題來自定義損失函式,總之損失函式是希望答案朝著正確的方向發展,所以如果偏離某一方向,可以給其更大的懲罰,這樣會使得得到的值離期望的答案更近。不同的損失函式對於相同的神經網路產生重要影響。
3 神經網路優化演算法
梯度下降演算法主要用於優化單個引數的取值,而反向傳播演算法給出了一個高效的方式在所有引數上使用梯度下降演算法。這裡只介紹基本概念和主要思想略去數學推導和證明。
要尋找使J(A)最小的A,目前沒有通用方法對任意損失函式直接求解最佳的引數取值,梯度下降演算法迭代更新引數A,不斷沿著梯度的反方向讓引數朝著總損失更小的方向更新。
引數更新的公式為:
其中,
一般隨機生成引數的初始值。
神經網路的優化過程分兩個階段,第一個階段,先通過前向傳播得到預測值,再和真實值比較,得到差距;第二個階段用反向傳播演算法計算損失函式對每一個引數的梯度,再根據梯度和學習率使用梯度下降演算法更新每一個引數。
注意:
梯度下降演算法不能保證全域性最優解,引數的初始值很大程度上影響最後的結果。只有當損失函式為凸函式時才能保證全域性最優解。
還有一個問題是梯度下降演算法計算時間過長,因此採用隨機梯度下降演算法,每次計算一個batch的資料,保證該batch的均值約等於總體的均值更好。
4 進一步優化
1 學習率的設定
學習率決定了引數每次更新的幅度。幅度過大會導致引數在極優值兩側來回波動。既不能過大也不能過小。TF提供了靈活的學習率設定方法——指數衰減法。可以先使用較大的學習率快速得到一個比較優的解,隨著迭代的繼續逐步減小學習率,使得模型在訓練後期更加穩定。
2 過擬合問題
我們想讓模型儘可能對未知資料進行判斷而不是儘量模擬訓練資料的行為。
過擬合指的是,一個模型過為複雜後,他可以很好的記憶每一個訓練資料中隨機噪音的部分而忘記了要去學習訓練資料中通用的趨勢。對於未知資料的判斷很差。避免過擬合問題可以採用正則化。
正則化的思想是在損失函式中加入刻畫模型複雜程度的指標。
假設優化的損失函式為J(A),則優化時不是直接優化J(A),而是優化J(A)+
L1正則化:
L2正則化:
基本思想都是希望通過限制權重大小使模型不能任意你和訓練資料中的隨機噪音。
區別:
L1會讓引數變得稀疏。L2不會。
L1計算公式不可導,L2可導。
實踐中可以同時使用:
TF支援優化帶正則項的損失函式。
3滑動平均模型
使得模型在測試資料中更健壯。
TF中提供了tf.train.ExponentialMovingAverage來實現滑動平均模型。
初始化ExponentialMovingAverage時,需要提供一個衰減率decay,用於控制模型更新的速度。越大模型越穩定。
ExponentialMovingAverage對於每個變數會維護一個影子變數,這個影子變數的初始值就是相應變數的初始值,每次變數更新,影子變數的值會更新為:
附:神經網路樣例程式解決二分類問題
import tensorflow as tf
from numpy.random import RandomState
# 定義訓練資料batch的大小
batch_size = 8
# 定義神經網路的引數
w1 = tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2 = tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))
# 在shape的一個維度上使用None可以方便使用不同的batch大小
x = tf.placeholder(tf.float32,shape=(None,2),name='x-input')
y_ = tf.placeholder(tf.float32,shape=(None,1),name='y-input')
# 定義神經網路前向傳播的過程
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
# 定義損失函式和反響傳播演算法
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y,1e-10,1.0)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
# 通過隨機數生成一個模擬資料集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size,2)
# 定義規則來給出樣本的標籤,x1+x2<1的樣例都被認為是正樣本,其他為負樣本,0:負樣本,1:正樣本
Y = [[int(x1+x2<1)] for (x1,x2) in X]
# 建立一個會話來執行TensorFlow程式
with tf.Session() as sess:
# 初始化變數
init_op = tf.global_variables_initializer()
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
# 設定訓練的輪數
STEPS = 5000
for i in range(STEPS):
# 每次選取batch_size 個樣本進行訓練
start = (i * batch_size)% dataset_size
end = min(start+batch_size,dataset_size)
# 通過選取的樣本訓練神經網路並更新引數
sess.run(train_step,feed_dict={x:X[start:end],y_:Y[start:end]})
if i % 1000 == 0:
total_cross_entropy = sess.run(cross_entropy,feed_dict={x:X,y_:Y})
print("After %d trainint step(s),cross entropy on all data is %g" % (i,total_cross_entropy))
print(sess.run(w1))
print(sess.run(w2))