【神经网络】(2) 网络优化,案例:服装图像分类,附python完整代码
各位同學好,今天和大家分享以下TensorFlow2.0深度學習中對神經網絡的優化方法,包括動量、學習率、dropout、交叉驗證、正則化。本節使用數學公式對網絡進行優化,增加網絡的靈活性。
以下代碼按順序組合在一起就是完整代碼,沒有缺失,可以直接運行。
1. 數據導入
導入系統自帶的服裝數據集,查看數據集的相關信息。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, Sequential, layers, optimizers, metrics#(1)獲取數據
(x_com, y_com), (x_test, y_test) = datasets.fashion_mnist.load_data()# 查看數據集合
print('x.shape: ',x_com.shape,'y.shape: ',y_com.shape)  # x.shape:  (60000, 28, 28) y.shape:  (60000,)
print('x_test.shape: ',x_test.shape,'y_test.shape: ',y_test.shape)  # x_test.shape:  (10000, 28, 28) y_test.shape:  (10000,)
print('y[:5]: ',y_com[:5])  # y[:5]:  [9 0 0 3 0]# 繪制圖像
import matplotlib.pyplot as plt
for i in range(10):plt.subplot(2,5,i+1)plt.imshow(x_com[i])plt.xticks([])plt.yticks([])
plt.show()
 
 
2. 網絡構造
堆疊一個6層的網絡,前兩個連接層設置layers.Dropout(),在神經網絡訓練的過程中,在指定Dropout的層中,每個神經元都有20%概率被殺死,被殺的神經元不參與正反向傳播,權重參數保持不變。每一次迭代都是選擇一部分的神經元參與訓練。設置dropout之間要注意訓練部分的策略和驗證部分的策略不一樣,需要分別指定。訓練部分 network(x, training=True),驗證部分?network(x, training=False)
#(2)構造網絡結構
# ==1== 全連接層,6層全連接層
# [b,784]=>[b,512]=>[b,256]=>[b,128]=>[b,64]=>[b,32]=>[b,10]
network = Sequential()
network.add(layers.Dense(512, activation='relu'))  # 堆疊網絡層
network.add(layers.Dropout(0.2))  # 每次前向傳播每條連接都有0.2的幾率被斷開,訓練時需要指定參數network(x, training=True)
network.add(layers.Dense(256, activation='relu'))
network.add(layers.Dropout(0.2))
network.add(layers.Dense(128, activation='relu'))
network.add(layers.Dense(64, activation='relu'))
network.add(layers.Dense(32, activation='relu'))
network.add(layers.Dense(10))  # logits層
# ==2== 設置輸入維度
network.build(input_shape=[None, 28*28])
# ==3== 查看網絡結構
network.summary()
# ==4== 優化器
optimizer = optimizers.Adam(lr=0.01) # 使用動態學習率,剛訓練時梯度下降速度較快,逐漸減慢
# CE變量接收交叉熵損失方法
CE = tf.losses.categorical_crossentropy
 
網絡結構如圖所示,param代表網絡每一層的參數個數。以最后一層為例,330 = 權重參數(32*10) +?偏置參數(10)。
Model: "sequential"
_________________________________________________________________Layer (type)                Output Shape              Param #   
=================================================================dense (Dense)               (None, 512)               401920    dropout (Dropout)           (None, 512)               0         dense_1 (Dense)             (None, 256)               131328    dropout_1 (Dropout)         (None, 256)               0         dense_2 (Dense)             (None, 128)               32896     dense_3 (Dense)             (None, 64)                8256      dense_4 (Dense)             (None, 32)                2080      dense_5 (Dense)             (None, 10)                330       =================================================================
Total params: 576,810
Trainable params: 576,810
Non-trainable params: 0
_________________________________________________________________ 
3. 劃分數據集
使用tf.data.Dataset.from_tensor_slices()將測試數據組成一個數據集,使用.map()對整個數據集使用預處理函數,用于整個神經網絡訓練完成后的測試。在網絡循環過程中,使用K折交叉驗證法對訓練集數據劃分,每次循環從導入的圖像數據(x_com, y_com)中隨機劃分出訓練集和驗證集。
如果按固定比例劃分訓練集和驗證集的話,會導致有一部分數據始終無法用于訓練。先給每一個xy組合生成索引,使用tf.random.shuffle()打亂索引順序,取50k張圖片用于訓練10k張圖片用于驗證,但不改變圖像和標簽之間的對應關系,充分利用有限的數據。將生成的y_train和y_val分別轉換成one_hot編碼,索引對應的數值轉換為1,其他位置的值都變成0,便于后續計算損失。
#(3)劃分數據集
# 預處理函數
def processing(x,y):x = 2 * tf.cast(x, dtype=tf.float32)/255.0 - 1y = tf.cast(y, dtype=tf.int32)return x,y# 構造測試集
ds_test = tf.data.Dataset.from_tensor_slices((x_test, y_test)) 
ds_test = ds_test.map(processing).batch(128)# 迭代器查看數據是否正確
sample = next(iter(ds_test))
print('x_batch:', sample[0].shape, 'y_batch:', sample[1].shape)
# x_batch: (128, 28, 28)  y_batch: (128,)# 網絡循環
for epochs in range(20): # 循環20次print('-----------------------------')    print('epoch:',epochs)    # (5)K折交叉驗證,每次循環使用不同的驗證集和訓練集idx = tf.range(60000) # 訓練集x一共有60k張圖像,生成60k個索引idx = tf.random.shuffle(idx) # 每次迭代都隨機打亂所有的索引    # 使用打亂的索引收集加載進來的x_com和y_com數據x_train, y_train = tf.gather(x_com, idx[:50000]), tf.gather(y_com, idx[:50000])  # 訓練集x_val, y_val = tf.gather(x_com, idx[-10000:]), tf.gather(y_com, idx[-10000:]) # 驗證集# 構造訓練集數據集y_train = tf.one_hot(y_train, depth=10)  # 對y進行onehot編碼,索引對應的值為1,其他為0ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))ds_train = ds_train.map(processing).shuffle(10000).batch(128) # 每次迭代取128組數據,打亂數據# 構造驗證集數據集y_val = tf.one_hot(y_val, depth=10)  # 計算驗證集的損失,看是否出現過擬合現象ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))ds_val = ds_val.map(processing).batch(128)
 
4.? 網絡訓練
使用交叉熵計算模型預測值和真實值之間的損失,交叉熵損失函數自動將logits層的輸出通過softmax函數轉換到0-1之間,并且10個分類的概率和為1。為了防止過擬合發生,對損失函數使用L2正則化,在原來的損失函數基礎上加上權重參數的平方和。L2正則化可以產生趨近0的解。使用tf.nn.l2_loss()對網絡的所有權重參數計算二范數,設置正則化懲罰系數為0.0001,根據公式:
正則化之后,以損失為因變量,權重和偏置為自變量計算梯度。使用學習率動態調整的方法調整網絡權重偏置,optimizer.learning_rate = 0.01*(20-epochs)/20,剛開始訓練時梯度下降較快,最快速度接近最優點,隨著網絡循環的次數增加,梯度下降逐漸緩慢,更較好地逼近最優點。
#(6)網絡訓練# 每次迭代一個batchfor step, (x,y) in enumerate(ds_train):# 將x的shape從[128,28,28]=>[128,28*28]x = tf.reshape(x, [-1,28*28])# 跟蹤梯度with tf.GradientTape() as tape:# 網絡訓練,得到logits層logits = network(x, training=True)# 計算平均交叉熵損失,from_logits自動將實數轉化為概率loss = tf.reduce_mean(CE(y, logits, from_logits=True))# 計算二范數w_regular = []  # 存放權重的二范數# 對所有的權重計算二范數, 正則化項保存為[w1,b1,w2,b2,w3,b3,...]for w in network.trainable_variables[::2]: # 6組權重w_regular.append(tf.nn.l2_loss(w))  # loss是求的整個batch的loss,也要求整個batch的w和b二范數loss_regular = tf.reduce_sum(w_regular)# 對損失正則化,正則化懲罰項設置為0.0001loss = loss + 0.0001 * loss_regular# 動態學習率調整,每次迭代都遞減,剛開始準確率為0.01optimizer.learning_rate = 0.01*(20-epochs)/20# 梯度計算,因變量為loss,自變量為所有的權重和偏置grads = tape.gradient(loss, network.trainable_variables)# 原地更新權重和偏置optimizer.apply_gradients(zip(grads, network.trainable_variables))# 每200個batch打印一次結果if step%200 == 0:print('train', 'batch_loss:', loss.numpy()) # 每一個batch的平均損失
 
5. 網絡驗證
在上面已經對驗證集的目標y_val進行了onehot編碼,在和預測值計算完損失值后,需要將one_hot類型轉回標簽類型,和預測值比較結果是否一致,來計算準確率。驗證集最后一層的輸出結果logits經過tf.nn.softmax()函數轉換成相應的概率值,tf.argmax()找出最大概率所在的下標,即所屬的分類。看真實值和預測值是否相同來統計預測對了的個數。
    #(7)驗證,前向傳播total_correct = 0  # 每次迭代有多少預測對了total_num = 0  # 每次迭代一共有多少參與預測for step,(x,y) in enumerate(ds_val):  # 每次驗證一個batch# x的shape由[128,28,28]變成[b,28*28]x = tf.reshape(x, [-1,28*28])# x從第一層傳播到最后一層logits = network(x, training=False)  #得出的是實數,需轉換成概率# 計算驗證集交叉熵損失loss = tf.reduce_mean(CE(y, logits, from_logits=True))# 計算完之后,將one_hot類型轉換回標簽類型,進行準確度計算labels = [] # 存放轉換后的標簽for label in y:labels.append(tf.argmax(label))labels = tf.convert_to_tensor(labels, dtype=tf.int32) # 轉變數據類型int32# 將圖片屬于每一層的實數結果轉換為概率prob = tf.nn.softmax(logits, axis=1)# 找到概率最大值所在的索引位置,指定axis=1,轉變每一張圖片屬于10個分類的概率predict = tf.argmax(prob, axis=1, output_type=tf.int32)  # 默認輸出tf.int64,而y是tf.int32類型# 對比預測值和真實值是否相同,計算有幾個相同的correct = tf.equal(labels, predict)correct = tf.reduce_sum(tf.cast(correct, tf.int32))  #True代表1# 記錄預測對了的概率total_correct += int(correct)  #從tensor類型變成數值類型total_num += x.shape[0]# 每100個batch打印一次準確率和損失if step%100 == 0:accuracy = total_correct/total_numprint('val', 'batch_accuracy:', accuracy)print('val', 'batch_loss:', loss.numpy())# 每一次循環打印一次驗證結果accuracy = total_correct/total_numprint('val', 'eopch_accuracy:', accuracy)print('-----------------------------')
 
6. 網絡測試
測試方法和驗證方法基本相同,實數轉概率,找下標值,統計相同個數,不過多介紹。
#(8)網絡完成后測試,記錄的是最后一次的權重
# 前向傳播
test_correct = 0
test_num = 0for step, (x, y) in enumerate(ds_test):# 改變輸入x的shapex = tf.reshape(x,shape=[-1, 28*28])# x從[128,28*28]=>[128,10]logits = network(x) # 得到每張圖片屬于10個分類的實數prob = tf.nn.softmax(logits, axis=1) # 將每一個張圖片屬于10分類的實數變成概率,且和為1predict = tf.argmax(prob, axis=1, output_type=tf.int32) # 找到概率最大的下標# 計算準確率correct = tf.reduce_sum(tf.cast(tf.equal(y, predict), tf.int32))  #有多少預測對了# 計算概率test_correct += int(correct)test_num += x.shape[0]# 整個test集的準確率
accuracy = test_correct / test_num
print('test_accuracy: ', accuracy) 
7. 預測
這里采用測試集中的x數據來做預測,next(iter(ds_test)),設置一個迭代器,每次運行從中取出一個batch的測試數據。同樣也是將網絡最后一層logits層的輸出的實數,轉為概率,再找概率最大的下標,得到預測結果。最后將預測的結果和真實值的前10個數比較一下,結果相同。
#(9)預測,這里使用的ds_test中的特征值x來預測
sample = next(iter(ds_test))
predict = []
for step,x in enumerate(sample[0]):  #sampel[0]中保存x數據# 改變輸入x的shapex = tf.reshape(x,shape=[-1, 28*28])# x從[128,28*28]=>[128,10]logits = network(x) # 得到每張圖片屬于10個分類的實數prob = tf.nn.softmax(logits, axis=1) # 將每一個張圖片屬于10分類的實數變成概率,且和為1pred = tf.argmax(prob, axis=1) # 找到概率最大的下標predict.append(int(pred))print('預測值:', predict[:10])
print('真實值:', sample[1][:10].numpy()) 
8. 結果展示
這里只迭代20次,隨著次數增加,準確率會不斷提高。
-----------------------------
epoch: 19
train batch_loss: 0.4036927
train batch_loss: 0.29839486
val batch_accuracy: 0.9140625
val batch_loss: 0.27389172
val eopch_accuracy: 0.898
-----------------------------
test_accuracy:  0.8713預測值: [9, 2, 1, 1, 6, 1, 4, 6, 5, 7]
真實值: [9 2 1 1 6 1 4 6 5 7]
                            總結
以上是生活随笔為你收集整理的【神经网络】(2) 网络优化,案例:服装图像分类,附python完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【深度学习】(7) 交叉验证、正则化,自
 - 下一篇: 【神经网络】(3) 卷积神经网络(CNN