Tensorflow 筆記:第四講
神經網絡優化
4.1
√神經元模型:用數學公式表示為:

f 為激活函數。神經網絡是以神經元為基本單元構成的。
√激活函數:引入非線性激活因素,提高模型的表達力。
常用的激活函數有 relu、sigmoid、tanh 等。
① 激活函數 relu: 在 Tensorflow 中,用 tf.nn.relu()表示 relu() 數 學 表 達 式 relu() 數 學 圖 形

② 激活函數 sigmoid:在 Tensorflow 中,用 tf.nn.sigmoid()表示

③ 激活函數 tanh:在 Tensorflow 中,用 tf.nn.tanh()表示
√神經網絡的復雜度:可用神經網絡的層數和神經網絡中待優化參數個數表示
√神經網路的層數:一般不計入輸入層,層數 = n 個隱藏層 + 1 個輸出層
√
神經網路待優化的參數:神經網絡中所有參數
w
的個數
+ 所有參數 b 的個數
例如:

在該神經網絡中,包含 1 個輸入層、1 個隱藏層和 1 個輸出層,該神經網絡的層數為 2 層。在該神經網絡中,參數的個數是所有參數 w 的個數加上所有參數 b 的總數,第一層參數用三行四列的二階張量表示(即 12 個線上的權重 w)再加上 4 個偏置 b;第二層參數是四行兩列的二階張量()即8 個線上的權重 w)再加上 2 個偏置 b。總參數 = 3*4+4 + 4*2+2 = 26。
√損失函數(loss):用來表示預測值(y)與已知答案(y_)的差距。在訓練神經網絡時,通過不斷改變神經網絡中所有參數,使損失函數不斷減小,從而訓練出更高准確率的神經網絡模型。
√常用的損失函數有均方誤差、自定義和交叉熵等。
√均方誤差 mse:n 個樣本的預測值 y 與已知答案 y_之差的平方和,再求平均值。
在 Tensorflow 中用 loss_mse = tf.reduce_mean(tf.square(y_ - y)) 例如:預測酸奶日銷量 y,x1 和 x2 是影響日銷量的兩個因素。
應提前采集的數據有:一段時間內,每日的 x1 因素、x2 因素和銷量 y_。采集的數據盡量多。
在本例中用銷量預測產量,最優的產量應該等於銷量。由於目前沒有數據集,所以擬造了一套數據集。利用 Tensorflow 中函數隨機生成 x1、 x2,制造標准答案 y_ = x1 + x2,為了更真實,求和后還加了正負 0.05 的隨機噪聲。
我們把這套自制的數據集喂入神經網絡,構建一個一層的神經網絡,擬合預測酸奶日銷量的函數。
代碼如下:


運行結果如下:
由上述代碼可知,本例中神經網絡預測模型為 y = w1*x1 + w2*x2,損失函數采用均方誤差。通過使損失函數值(loss)不斷降低,神經網絡模型得到最終參數 w1=0.98,w2=1.02,銷量預測結果為 y = 0.98*x1 + 1.02*x2。由於在生成數據集時,標准答案為 y = x1 + x2,因此,銷量預測結果和標准答案已非常接近,說明該神經網絡預測酸奶日銷量正確。
√自定義損失函數:根據問題的實際情況,定制合理的損失函數。
例如:對於預測酸奶日銷量問題,如果預測銷量大於實際銷量則會損失成本;如果預測銷量小於實際銷量則會損失利潤。在實際生活中,往往制造一盒酸奶的成本和銷售一盒酸奶的利潤是不等價的。因此,需要使用符合該問題的自定義損失函數。

損失函數表示,若預測結果 y 小於標准答案 y_,損失函數為利潤乘以預測結果 y 與標准答案 y_之差;若預測結果 y 大於標准答案 y_,損失函數為成本乘以預測結果 y 與標准答案 y_之差。
用 Tensorflow 函數表示為:
loss = tf.reduce_sum(tf.where(tf.greater(y,y_),COST(y-y_),PROFIT(y_-y)))
① 若酸奶成本為 1 元,酸奶銷售利潤為 9 元,則制造成本小於酸奶利潤,因此希望預測的結果 y 多一些。采用上述的自定義損失函數,訓練神經網絡模型。 代碼如下:
運行結果如下:
由代碼執行結果可知,神經網絡最終參數為 w1=1.03, w2=1.05,銷量預測結果為 y =1.03*x1 + 1.05*x2。由此可見,采用自定義損失函數預測的結果大於采用均方誤差預測的結果,更符合實際需求。
②若酸奶成本為 9 元,酸奶銷售利潤為 1 元,則制造成本大於酸奶利潤,因此希望預測結果 y 小一些。采用上述的自定義損失函數,訓練神經網絡模型。代碼如下:

運行結果如下:

由執行結果可知,神經網絡最終參數為 w1=0.96,w2=0.97,銷量預測結果為 y =0.96*x1 + 0.97*x2。因此,采用自定義損失函數預測的結果小於采用均方誤差預測的結果,更符合實際需求。
√交叉熵(Cross Entropy):表示兩個概率分布之間的距離。交叉熵越大,兩個概率分布距離越遠,兩個概率分布越相異;交叉熵越小,兩個概率分布距離越近,兩個概率分布越相似。
交叉熵計算公式:
用 Tensorflow 函數表示為
ce= -tf.reduce_mean(y_* tf.log(tf.clip_by_value(y, 1e-12, 1.0))) 例如:
兩個神經網絡模型解決二分類問題中,已知標准答案為 y_ = (1, 0),第一個神經網絡模型預測結果為
y1=(0.6, 0.4),第二個神經網絡模型預測結果為 y2=(0.8, 0.2),判斷哪個神經網絡模型預測的結果更接近標准答案。
根據交叉熵的計算公式得:
H1((1,0),(0.6,0.4)) = -(1*log0.6 + 0*log0.4) ≈ -(-0.222 + 0) = 0.222
H2((1,0),(0.8,0.2)) = -(1*log0.8 + 0*log0.2) ≈ -(-0.097 + 0) = 0.097
由於 0.222>0.097,所以預測結果 y2 與標准答案 y_更接近,y2 預測更准確。
√softmax 函數:將 n 分類的 n 個輸出(y1,y2…yn)變為滿足以下概率分布要求的函數。

softmax 函數應用:在 n 分類中,模型會有 n 個輸出,即 y1,y2…yn,其中 yi 表示第 i 種情況出現的可能性大小。將 n 個輸出經過 softmax 函數,可得到符合概率分布的分類結果。
√在 Tensorflow 中,一般讓模型的輸出經過 sofemax 函數,以獲得輸出分類的概率分布,再與標准答案對比,求出交叉熵,得到損失函數,用如下函數實現:
ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cem = tf.reduce_mean(ce)
4.2
√學習率 learning_rate:表示了每次參數更新的幅度大小。學習率過大,會導致待優化的參數在最小值附近波動,不收斂;學習率過小,會導致待優化的參數收斂緩慢。在訓練過程中,參數的更新向着損失函數梯度下降的方向。參數的更新公式為:

假設損失函數為 loss = (w + 1)2。梯度是損失函數 loss 的導數為 ∇=2w+2。如參數初值為 5,學習率為 0.2,則參數和損失函數更新如下:
1 次 參 數 w:5 5 - 0.2 * (2 * 5 + 2) = 2.6
2 次 參 數 w:2.6 2.6 - 0.2 * (2 * 2.6 + 2) = 1.16
3 次 參 數 w:1.16 1.16 – 0.2 * (2 * 1.16 + 2) = 0.296
4 次 參 數 w:0.296
由圖可知,損失函數 loss 的最小值會在(-1,0)處得到,此時損失函數的導數為 0,得到最終參數 w =-1。
代碼如下:
運行結果如下:
由結果可知,隨着損失函數值的減小,w 無限趨近於-1,模型計算推測出最優參數 w = -1。
√學習率的設置
學習率過大,會導致待優化的參數在最小值附近波動,不收斂;學習率過小,會導致待優化的參數收斂緩慢。
例如:
① 對於上例的損失函數 loss = (w + 1)2。則將上述代碼中學習率修改為 1,其余內容不變。實驗結果如下:
由運行結果可知,損失函數 loss 值並沒有收斂,而是在 5 和-7 之間波動。
② 對於上例的損失函數 loss = (w + 1)2。則將上述代碼中學習率修改為 0.0001,其余內容不變。實驗結果如下:

由運行結果可知,損失函數 loss 值緩慢下降,w 值也在小幅度變化,收斂緩慢。
√指數衰減學習率:學習率隨着訓練輪數變化而動態更新
其中,LEARNING_RATE_BASE 為學習率初始值,LEARNING_RATE_DECAY 為學習率衰減率,global_step 記錄了當前訓練輪數,為不可訓練型參數。學習率 learning_rate 更新頻率為輸入數據集總樣本數除以每次喂入樣本數。若 staircase 設置為 True 時,表示 global_step/learning rate step 取整數,學習率階梯型衰減;若 staircase 設置為 false 時,學習率會是一條平滑下降的曲線。
例如:
在本例中,模型訓練過程不設定固定的學習率,使用指數衰減學習率進行訓練。其中,學習率初值設置為 0.1,學習率衰減率設置為 0.99,BATCH_SIZE 設置為 1。
代碼如下:


運行結果如下:

由結果可以看出,隨着訓練輪數增加學習率在不斷減小。
4.3
√滑動平均:記錄了一段時間內模型中所有參數 w 和 b 各自的平均值。利用滑動平均值可以增強模型的泛化能力。
√滑動平均值(影子)計算公式:
√用 Tesnsorflow 函數表示為:

其中,MOVING_AVERAGE_DECAY 表示滑動平均衰減率,一般會賦接近 1 的值,global_step 表示當前訓練了多少輪。
√ema_op = ema.apply(tf.trainable_variables())
其中,ema.apply()函數實現對括號內參數求滑動平均,tf.trainable_variables()函數實現把所有待訓練參數匯總為列表。
√with tf.control_dependencies([train_step, ema_op]):
train_op = tf.no_op(name='train')
其中,該函數實現將滑動平均和訓練過程同步運行。
查看模型中參數的平均值,可以用
ema.average()函數。例如:在神經網絡模型中,將 MOVING_AVERAGE_DECAY 設置為 0.99,參數 w1 設置為 0,w1 的滑動平均值設置為 0。
①開始時,輪數 global_step 設置為 0,參數 w1 更新為 1,則 w1 的滑動平均值為:w1 滑動平均值=min(0.99,1/10)*0+(1– min(0.99,1/10)*1 = 0.9
②當輪數 global_step 設置為 100 時,參數 w1 更新為 10,以下代碼 global_step 保持為 100,每次執行滑動平均操作影子值更新,則滑動平均值變為:w1 滑動平均值=min(0.99,101/110)*0.9+(1– min(0.99,101/110)*10 = 0.826+0.818=1.644
③再次運行,參數 w1 更新為 1.644,則滑動平均值變為:w1 滑動平均值=min(0.99,101/110)*1.644+(1– min(0.99,101/110)*10 = 2.328
④再次運行,參數 w1 更新為 2.328,則滑動平均值:w1 滑動平均值=2.956
代碼如下:


運行程序,結果如下:

從運行結果可知,最初參數 w1 和滑動平均值都是 0;參數 w1 設定為 1 后,滑動平均值變為 0.9; 當迭代輪數更新為 100 輪時,參數 w1 更新為 10 后,滑動平均值變為 1.644。隨后每執行一次,參數w1的滑動平均值都向參數 w1 靠近。可見,滑動平均追隨參數的變化而變化。
4.4
√過擬合:神經網絡模型在訓練數據集上的准確率較高,在新的數據進行預測或分類時准確率較低,說明模型的泛化能力差。
√正則化:在損失函數中給每個參數 w 加上權重,引入模型復雜度指標,從而抑制模型噪聲,減小過擬合。
使用正則化后,損失函數 loss 變為兩項之和:loss = loss(y 與 y_) + REGULARIZER*loss(w)
其中,第一項是預測結果與標准答案之間的差距,如之前講過的交叉熵、均方誤差等;
第二項是正則化計算結果。
例如:
用 300 個符合正態分布的點 X[x0, x1]作為數據集,根據點 X[x0, x1]計算生成標注 Y_,將數據集標注為紅色點和藍色點。標注規則為:當 x02 + x12 < 2 時,y_=1,標注為紅色;當 x02 + x12 ≥2 時,y_=0,標注為藍色。我們分別用無正則化和有正則化兩種方法,擬合曲線,把紅色點和藍色點分開。在實際分類時,
如果前向傳播輸出的預測值 y 接近 1 則為紅色點概率越大,接近 0 則為藍色點概率越大,輸出的預測值 y 為 0.5 是紅藍點概率分界線。
在本例子中,我們使用了之前未用過的模塊與函數:
√matplotlib 模塊:Python 中的可視化工具模塊,實現函數可視化 終端安裝指令:sudo pip install matplotlib
√函數 plt.scatter():利用指定顏色實現點(x,y)的可視化 plt.scatter (x 坐標, y 坐標, c=”顏色”) plt.show()
√收集規定區域內所有的網格坐標點: xx, yy = np.mgrid[起:止:步長, 起:止:步長] #找到規定區域以步長為分辨率的行列網格坐標點grid = np.c_[xx.ravel(), yy.ravel()] #收集規定區域內所有的網格坐標點 √plt.contour()函數:告知 x、y 坐標和各點高度,用 levels 指定高度的點描上顏色plt.contour (x 軸坐標值, y 軸坐標值, 該點的高度, levels=[等高線的高度]) plt.show()
本例代碼如下:




執行代碼,效果如下:
首先,數據集實現可視化,x 2 + x 2 < 2 的點顯示紅色, x 2 + x 2 ≥2 的點顯示藍色,如圖所示:

接着,執行無正則化的訓練過程,把紅色的點和藍色的點分開,生成曲線如下圖所示:

最后,執行有正則化的訓練過程,把紅色的點和藍色的點分開,生成曲線如下圖所示:

對比無正則化與有正則化模型的訓練結果,可看出有正則化模型的擬合曲線平滑,模型具有更好的泛化能力。
4.5 搭建模塊化神經網絡八股
√前向傳播:由輸入到輸出,搭建完整的網絡結構描述前向傳播的過程需要定義三個函數:
√def forward(x, regularizer):
w=
b=
y=
return y
第一個函數
forward()完成網絡結構的設計,從輸入到輸出搭建完整的網絡結構,實現前向傳播過程。
該函數中,參數 x
為輸入
,regularizer
為正則化權重,返回值為預測或分類結果 y。
√def get_weight(shape, regularizer):
w = tf.Variable( )
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(regularizer)(w))
return w
第二個函數 get_weight()對參數 w 設定。該函數中,參數 shape 表示參數 w 的形狀,regularizer 表示正則化權重,返回值為參數 w。其中,tf.variable()給 w 賦初值,tf.add_to_collection()表示將參數 w 正則化損失加到總損失 losses 中。
√def get_bias(shape):
b = tf.Variable( )
return b
第三個函數 get_bias()對參數 b 進行設定。該函數中,參數 shape 表示參數 b 的形狀,返回值為參數b。其中,tf.variable()表示給 b 賦初值。
√反向傳播:訓練網絡,優化網絡參數,提高模型准確性。
√def backward():
x = tf.placeholder()
y_ = tf.placeholder()
y = forward.forward(x, REGULARIZER)
global_step = tf.Variable(0, trainable=False)
loss =
函數 backward()中,placeholder()實現對數據集 x 和標准答案 y_占位,forward.forward()實現前向傳播的網絡結構,參數 global_step 表示訓練輪數,設置為不可訓練型參數。
在訓練網絡模型時,常將正則化、指數衰減學習率和滑動平均這三個方法作為模型優化方法。
√在 Tensorflow 中,正則化表示為: 首先,計算預測結果與標准答案的損失值
①MSE: y 與 y_的差距(loss_mse) = tf.reduce_mean(tf.square(y-y_))
②交叉熵:ce = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))y 與 y_的差距(cem) = tf.reduce_mean(ce)
③自定義:y 與 y_的差距其次,總損失值為預測結果與標准答案的損失值加上正則化項loss = y 與 y_的差距 + tf.add_n(tf.get_collection('losses'))
√在 Tensorflow 中,指數衰減學習率表示為:
learning_rate = tf.train.exponential_decay( LEARNING_RATE_BASE, global_step, 數據集總樣本數 / BATCH_SIZE, LEARNING_RATE_DECAY, staircase=True) train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
√在 Tensorflow 中,滑動平均表示為:
ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
ema_op = ema.apply(tf.trainable_variables())
with tf.control_dependencies([train_step, ema_op]):
train_op = tf.no_op(name='train')
其中,滑動平均和指數衰減學習率中的 global_step 為同一個參數。
√用 with 結構初始化所有參數
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
sess.run(init_op) for i in range(STEPS):
sess.run(train_step, feed_dict={x: , y_: })
if i % 輪 數 == 0:
print
其中,with 結構用於初始化所有參數信息以及實現調用訓練過程,並打印出 loss 值。
√判斷 python 運行文件是否為主文件if name ==' main__': backward()
該部分用來判斷 python 運行的文件是否為主文件。若是主文件,則執行 backword()函數。例如:用 300 個符合正態分布的點 X[x0, x1]作為數據集,根據點 X[x0, x1]的不同進行標注 Y_,將數據集標注為紅色和藍色。
標注規則為:當 x 2 + x 2 < 2 時,y_=1,點 X 標注為紅色;當 x 2 + x 2 ≥2 時, y_=0,點 X 標注為藍色。
我們加入指數衰減學習率優化效率,加入正則化提高泛化性,並使用模塊化設計方法,把紅色點和藍色點分開。代碼總共分為三個模塊: 生成數據集(generateds.py) 、前向傳播(forward.py) 、反向傳播(backward.py)。
①生成數據集的模塊(generateds.py)
②前向傳播模塊(forward.py)
③反向傳播模塊(backward.py)




運行代碼,結果如下:

由運行結果可見,程序使用模塊化設計方法,加入指數衰減學習率,使用正則化后,紅色點和藍色點的分割曲線相對平滑,效果變好。