TensorFlow:实战Google深度学习框架(三)深层神经网络
- 第四章 深層神經網絡
- 4.1 深度學習與深層神經網絡
- 4.1.1 線性模型的局限性
- 4.1.2 激活函數實現非線性化
- 4.1.3 多層網絡解決異或問題
- 4.2 損失函數
- 4.2.1 經典損失函數
- 1. 交叉熵(給定兩個概率分布ppp和qqq)
- 2. 均方誤差(MSE,Mean Squared Error)
- 4.2.2 自定義損失函數
- 4.3 神經網絡優化算法
- 4.4 神經網絡的進一步優化
- 4.4.1 學習率的設置
- 4.4.2 過擬合問題
- 4.4.3 滑動平均模型
- 4.1 深度學習與深層神經網絡
第四章 深層神經網絡
本章主要介紹如何設計和優化神經網絡,使其能夠更好的對未知樣本進行預測。
4.1 深度學習與深層神經網絡
深度學習的兩大特性:多層+非線性
4.1.1 線性模型的局限性
線性模型中,模型的輸出就是輸入的加權和,其能解決問題的能力是有限的,無法解決非線性問題(至少是無法通過直線或高維空間的平面來劃分的),當引入非線性激活函數后,就可以實現更復雜的劃分。
4.1.2 激活函數實現非線性化
將每個神經元的輸出都經過一個非線性函數,整個模型就是非線性的模型了,通過激活函數去實現。
激活函數實例:階躍函數、sigmoid函數、tanh函數、ReLU函數等
因為激活函數是非線性的,所以經過激活函數處理之后的每一個節點都將實現復雜的非線性變換。
例子:
a=tf.nn.relu(tf.matmul(x,w1)+biases1) y=tf.nn.relu(tf.matmul(a,w2)+biases2)- TensorFlow提供的激活函數
- Traditional: sigmoid(logistic)、tanh
- RELU Family: RELU、Leaky RELU、PRELU、RRELU
- Exponential Family: ELU、SELU
4.1.3 多層網絡解決異或問題
本節主要講解深度學習的另一重要性質——多層變換
單層感知機無法模擬“異或運算”,加入隱藏層后可以很好的實現異或問題,隱藏層可以被認為從輸入特征中抽取了更高維的特征,深層網絡實際上有組合特征提取的功能,該特性對解決不易提取特征向量的問題(圖像識別、語音識別等)有很大的幫助。
4.2 損失函數
主要介紹如何刻畫神經網絡模型的效果(神經網絡模型的效果以及優化的目標是通過損失函數來定義的)
4.2.1 經典損失函數
神經網絡解決多分類問題最常用的方法是設置n個輸出節點,n為類別個數,對于每個樣例,得到一個n維數組作為輸出結果,每個維度對應一個類別,輸出為“1”代表判斷結果的類別。
判斷輸出向量和期望向量的距離:交叉熵(cross entropy),刻畫了兩個概率分布間的距離,較為常用。
1. 交叉熵(給定兩個概率分布pp和qq)
H(p,q)=?∑xp(x)logq(x)H(p,q)=?∑xp(x)logq(x)?xp(X=x)∈[0,1],且∑xp(X=x)=1?xp(X=x)∈[0,1],且∑xp(X=x)=1
- softmaxsoftmax回歸:將前向傳播結果變成概率分布的常用方法
其本身是一個學習算法來優化分類結果,但TensorFlow中只將其作為處理層——將網絡輸出變為概率分布 - 示例:
假設三分類問題,樣例的正確答案為(1,0,0)(1,0,0),softmax回歸之后的預測答案是(0.5,0.4,0.1)(0.5,0.4,0.1),
則預測和正確結果的交叉熵為:
H((1,0,0),(0.5,0.4,0.1))=?(1×log0.5+0×log0.4+0×log0.1)≈0.3H((1,0,0),(0.5,0.4,0.1))=?(1×log0.5+0×log0.4+0×log0.1)≈0.3
另外一個模型的預測是(0.8,0.1,0.1)(0.8,0.1,0.1):
H((1,0,0),(0.8,0.1,0.1))=?(1×log0.8+0×log0.1+0×log0.1)≈0.1H((1,0,0),(0.8,0.1,0.1))=?(1×log0.8+0×log0.1+0×log0.1)≈0.1
可知第二個模型效果更好
TensorFlow實現交叉熵
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) #其中y代表正確結果,y_代表預測結果分別解釋上述程序的四個運算
1. tf.clip_by_value:將一個張量的數值限定在一個范圍內,可以避免一些錯誤的運算(如log0無效)
clip_by_value(t,clip_value_min,clip_value_max,name=None)
舉例:
import tensorflow as tf v=tf.constant([[1.0,2.0,3.0],[4.0,5.0,6.0]]) with tf.Session() as sess:print(tf.clip_by_value(v,2.5,4.5).eval()) #eval(str [,globals [,locals ]])函數將字符串str當成有效Python表達式來求值,并返回計算結果。[[ 2.5 2.5 3. ][ 4. 4.5 4.5]] #小于2.5的都變為了2.5 #大于4.5的都變為了4.52. tf.log:對張量中的所有元素依次求對數
import tensorflow as tf v=tf.constant([1.0,2.0,3.0]) with tf.Session() as sess:print(tf.log(v).eval())[ 0. 0.69314718 1.09861231]3. tf.matmul:矩陣元素點乘
import tensorflow as tf v1=tf.constant([[1.0,2.0],[3.0,4.0]]) v2=tf.constant([[5.0,6.0],[7.0,8.0]])with tf.Session() as sess:print((v1*v2).eval())#輸出:[[ 5. 12.],[ 21. 32.]](矩陣對應位置元素相乘)print(tf.matmul(v1, v2).eval())#輸出:[[ 19. 22.],[ 43. 50.]](矩陣相乘)通過這三步計算得到每一個樣例中的每一個類別的交叉熵,是一個n×mn×m矩陣:
nn為一個batch中的樣例數量,mm為分類的類別數量。
如何獲得交叉熵:將每行的mm個結果相加得到所有樣例的交叉熵,再對nn行取平均得到一個batch的平均交叉熵
簡化:分類問題的類別數量不變,故可以直接對整個矩陣做平均而不改變計算結果的意義,用tf.reduce_mean實現
import tensorflow as tf v1=tf.constant([[1.0,2.0,3.0],[4.0,5.0,6.0]]) with tf.Session() as sess:print(tf.reduce_mean(v1).eval())output:3.5交叉熵一般會和softmax回歸同時使用,所以TensorFlow對其進行了封裝,可以直接獲得softmax之后的交叉熵函數
cross_entropy=tf.nn.softmax_cross_entropy_with_logits(y,y_)2. 均方誤差(MSE,Mean Squared Error)
回歸問題是對具體數值的預測,解決回歸問題的神經網絡一般只有一個節點,該節點的值就是預測值,回歸問題最常見的的損失函數是均方誤差函數,定義如下:
其中, yiyi是一個batch中的第 ii個數據的正確結果,y′iyi′為預測結果
TensorFlow實現均方誤差函數
mse=tf.reduce_mean(tf.square(y_-y))4.2.2 自定義損失函數
自定義一個損失函數(預測值多于真實值和預測值少于真實值的損失函數不同)
對其中的兩個函數進行說明:
tf.select(condition,a,b) # condition:一個張量tensor,類型為bool # a:一個張量tensor,shape與condition一致,類型一般為float32, float64, int32, int64. # b:一個張量tensor,類型和shape與a一致。# 當condition為True時,選擇第二個參數a,否則選擇第三個參數b(在元素級別進行) tf.greater(a,b) # 比較兩個值的大小,當a>b時,返回True;當a<b時,返回False- 損失函數對模型訓練結果的影響
使用不同的損失函數會對結果造成不同的影響
4.3 神經網絡優化算法
本節將更加具體的介紹如何通過反向傳播和梯度下降來優化單個參數的取值
梯度下降:迭代式更新參數,不斷沿梯度的反方向讓參數朝著總損失更小的方向更新。
損失函數:J(θ)J(θ)
梯度:??θJ(θ)??θJ(θ)
學習率:ηη(learning rate),定義每次更新的幅度,也就是每次參數移動的幅度。
參數更新的公式:
θn+1=θn?η??θnJ(θ)θn+1=θn?η??θnJ(θ)梯度下降法的步驟
- 隨機生成一個參數x的初始值
- 通過梯度和學習率來更新參數x的取值
- 不斷迭代獲得更新后的參數
神經網絡的優化過程:
- 通過前向傳播獲得預測值,并獲得和真實值的差距
- 通過反向傳播計算損失函數對每一個參數的梯度,再根據梯度和學習率使用梯度下降法更新每個參數
梯度下降法的缺點
- 不能保證達到全局最優化,初始值的選取很重要,只有當損失函數為凸函數時,才能達到全局最優解
- 計算時間太長,因為要在全部訓練數據上最小化損失,所以損失函數是在所有訓練數據上的損失和,所以在每輪迭代中都需要計算在全部訓練數據上的損失函數。
隨機梯度下降法:為了加速訓練過程
- 該算法是在每一輪的迭代中,隨機優化某一條訓練數據上的損失函數,使得每一輪的更新速度加快。
- 存在的問題:在某一條數據上損失函數更小,并不代表在全部數據上的損失函數更小,可能都無法達到局部最優。
綜合梯度下降和隨機梯度下降的折中方法:
- 每次計算一小部分的訓練數據(batch)的損失函數,
- 優勢:
- 通過矩陣運算,每次在一個batch上優化神經網絡的參數并不會比單個數據慢太多;
- 每次使用一個batch可以大大減小收斂所需要的迭代次數,同時達到接近梯度下降的效果
TensorFlow訓練神經網絡的過程
batch_size=8#每次讀取一小部分數據作為當前訓練數據來執行反向傳播過程 x=tf.placeholder(tf.float32,shape=(batch_size,2),name='x-input') y_=tf.placeholder(tf.float32,shape=(batch_size,1),name='y-input') #定義神經網絡優化算法 loss=... train_step=tf.train.AdamOptimizer(0.001).minimize(loss)#訓練神經網絡 with tf.Session() as sess:#參數初始化...#迭代更新次數for i in range(STEPS)#準備batch_size個訓練數據,一般將所有訓練數據隨機打亂之后再選取,可以得到更好的優化效果current_X,current_Y=...sess.run(train_step,feed_dict={...})4.4 神經網絡的進一步優化
4.4.1 學習率的設置
學習率——控制參數更新的速度,決定了參數每次更新的幅度
- 幅度過大(學習率過大):可能導致參數在極優秀值附近來回移動
- 幅度過小(學習率過小):降低收斂速度
TensorFlow提供了更加靈活的學習率設置方法——指數衰減法
步驟:
1. 首先使用較大學習率(目的:為快速得到一個比較優的解);
2. 然后通過迭代逐步減小學習率(目的:為使模型在訓練后期更加穩定);
代碼實現:
decayed_learning_rate=learining_rate*decay_rate^(global_step/decay_steps) # decayed_learning_rate為每一輪優化時使用的學習率; # learning_rate: 事先設定的初始學習率; # decay_rate: 衰減系數; # decay_steps: 衰減速度。選擇不同的衰減方式:staircase函數
#繪制staircase函數曲線 import tensorflow as tf import numpy as np import matplotlib.pyplot as pltx=np.arange(2) learning_rate = 0.1 decay_rate = 0.96 global_steps = 1000 decay_steps = 100global_ = tf.Variable(tf.constant(0)) c = tf.train.exponential_decay(learning_rate, global_, decay_steps, decay_rate, staircase=True) d = tf.train.exponential_decay(learning_rate, global_, decay_steps, decay_rate, staircase=False)T_C = [] F_D = []with tf.Session() as sess:for i in range(global_steps):T_c = sess.run(c, feed_dict={global_: i})T_C.append(T_c)F_d = sess.run(d, feed_dict={global_: i})F_D.append(F_d)plt.figure(1) plt.plot(range(global_steps), F_D, 'r-') plt.plot(range(global_steps), T_C, 'b-')plt.show()
初始的學習速率是0.1,總的迭代次數是1000次,如果staircase=True,那就表明每decay_steps次計算學習速率變化,更新原始學習速率,如果是False,那就是每一步都更新學習速率。紅色表示False,藍色表示True。
staircase默認為False:連續的指數衰減學習率,不同的訓練數據有不同的學習率,學習率減小時,對應的訓練數據對模型訓練結果的影響也就小了。
當staircase為True時:(global_step/decay_steps)則被轉化為整數,使得學習率成為一個階梯函數。
此時的decay_steps代表了完整的使用一遍訓練數據所需要的迭代次數(即總樣本數/每個batch中的樣本數)
效果:每完整的過一遍訓練數據,學習率就減小一次,使得訓練數據集中的所有數據對模型訓練有相等的作用。
使用tf.train.exponential_decay的示例:
global_step=tf.Variable(0)#通過exponential_decay生成學習率 learning_rate=tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=True)#使用指數衰減的學習率,在minimize函數中傳入global_step將自動更新global_step參數,從而使得學習率得到相應的更新 learning_step=tf.train.GradientDescentOptimizer(learning_rate)\.minimize(...my loss...,global_step=global_step# 代碼設定初始學習率為0.1,指定`staircase=True`,所以每訓練100輪以后,學習率乘以0.964.4.2 過擬合問題
以上的章節都是求解如何在訓練數據上優化一個給定的損失函數,但是實際中并不是僅僅希望模型盡量模擬訓練數據,而是通過訓練得到的模型能夠給出對未知數據的判斷。
過擬合
過擬合是指當一個模型過為復雜之后,它可以很好的“記憶”每個訓練數據中隨機噪聲的部分,而忽視了整體的規律。也就是參數過多,使得模型對訓練數據有很好的擬合,但是卻沒有學習到通用的規律,使得其對未知數據并不能很好的預測。如何避免過擬合:——正則化(regularization):
在損失函數中加入刻畫模型復雜程度的指標
損失函數:J(θ)J(θ)(θθ指一個神經網絡的所有參數,包括權重和偏置)
優化時優化的函數:J(θ)+λR(w)J(θ)+λR(w)
其中:- R(w)R(w)——刻畫模型的復雜程度;
- L1L1正則化:R(w)=∥w∥1=∑i|wi|R(w)=‖w‖1=∑i|wi|
① 會讓參數變得稀疏,也就是將更多的參數變為0,這樣可以達到類似的特征選取的功能;
② 計算公式不可導,所以優化帶L1L1正則化函數要更加復雜 - L2L2正則化:R(w)=∥w∥22=∑i|w2i|R(w)=‖w‖22=∑i|wi2|
① 不會讓參數變得更稀疏,因為當參數很小時(例如0.0001),則該參數的平方基本上就可以忽略了,于是模型不會進一步將其調整為0。
② 計算公式可導,因為在優化時需要計算損失函數的偏導數,所以對含有L2L2正則化損失函數的優化更加簡潔 - 將L1L1和L2L2正則化同時使用:R(w)=∑iα|wi|+(1?α)w2iR(w)=∑iα|wi|+(1?α)wi2
- L1L1正則化:R(w)=∥w∥1=∑i|wi|R(w)=‖w‖1=∑i|wi|
- λλ——模型復雜損失在總損失中的比例
TensorFlow優化帶正則化的損失函數
- TensorFlow計算給定參數的L1L1正則化項的值:
- TensorFlow計算給定參數的L2L2正則化項的值:
示例:
weights = tf.constant([[1.0,-2.0],[-3.0,4.0]]) with tf.Session() as sess:# 輸出為(|1|+|-2|+|-3|+|4|)*0.5=5,其中0.5為正則項權重print sess.run(tf.contrib.layers.11_regularizer(0.5)(weights))# 輸出為(|1|\^2+|-2|\^2+|-3|\^2+|4|\^2)/2*0.5=7.5# TensorFlow會將L2正則化損失值/2,使得求導結果更加簡潔)print sess.run(tf.contrib.layers.11_regularizer(0.5)(weights))- 當神經網絡參數增多時,這樣的方式可能導致損失函數loss的定義很長,可讀性差,易于出錯,所以可以用集合的方式來計算。
4.4.3 滑動平均模型
在使用隨機梯度下降法訓練神經網絡時,該方法可以使得模型在測試數據上更為健壯
- TensorFlow實現滑動平均模型
1.影子變量
shadow_variable = decay * shadow_variable + (1-decay) * variable # shadow_variable:影子變量 # variable:待更新的變量,也就是變量被賦予的值 # decay:衰減速率(decay越大模型越穩定,因為decay越大,參數更新的速度就越慢,模型越趨于穩定。 ) # decay一般設為接近于1的數(0.99或0.999等)。2.動態設置decay的大小:為了使得模型在訓練前期可以更新的更快,num_updates參數來實現動態設置
代碼示例: import tensorflow as tf# 定義一個變量用于計算滑動平均模型,該變量的初始值為0, # 此處手動設定變量的類型為tf.float32,因為所有需要計算滑動平均模型的變量必須是實數型 v1 = tf.Variable(0, dtype=tf.float32) # 變量初始值為0 # step變量模擬神經網絡中迭代的輪數,可用于動態控制衰減率 step = tf.Variable(0, trainable=False) #train=True 可優化,train=False 不可優化# 定義一個滑動平均的類,初始化時給定衰減率(0.99)和控制衰減率的變量step ema = tf.train.ExponentialMovingAverage(0.99, step) # 定義一個更新變量滑動平均操作 # 需要給定一個列表,每層執行該操作時,列表中的變量都會被更新 # apply()方法添加了訓練變量的影子副本,并保持了其影子副本中訓練變量的移動平均值操作。在每次訓練之后調用此操作,更新移動平均值。 maintain_averages_op = ema.apply([v1]) # v1:更新變量列表with tf.Session() as sess:# 初始化所有變量init_op = tf.global_variables_initializer()sess.run(init_op)# 通過ema.average(v1)獲得滑動平均之后變量的取值# 在初始化之后,變量v1的值和v1的滑動平均都為0print(sess.run([v1, ema.average(v1)])) # 輸出[0.0, 0.0]# 更新變量v1的值為5sess.run(tf.assign(v1, 5))# 更新v1的滑動平均值,衰減率為min{0.99,(1+step)/(10+step)=0.1}=0.1# 所以v1的滑動平均會被更新為0.1*0+0.9*5=4.5sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)])) # 輸出[5.0,4.5](variable,shadow_variable)# 更新step的值為10000sess.run(tf.assign(step,10000))# 更新v1的值為10sess.run(tf.assign(v1, 10))# 更新v1的滑動平均值,衰減率為min{0.99,(1+step)/(10+step)≈0.999}=0.99# 所以v1的滑動平均會被更新為0.99*4.5+0.01*10=4.555sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)])) # 輸出[10.0,4.5559998] 輸出[10.0,4.6094499]# 再次更新滑動平均值,得到新滑動平均0.99*4.555+0.01*10=4.60945sess.run(maintain_averages_op)print(sess.run([v1, ema.average(v1)]))# 輸出[10.0,4.6094499] [0.0, 0.0] [5.0, 4.5] [10.0, 4.5549998] [10.0, 4.6094499]
總結
以上是生活随笔為你收集整理的TensorFlow:实战Google深度学习框架(三)深层神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TensorFlow:实战Google深
- 下一篇: 原来中国科幻的背后是中国制造:《流浪地球