深度学习之卷积神经网络(4)LeNet-5实战
深度學習之卷積神經網絡(4)LeNet-5實戰
- 加載數據集
- 創建網絡
- 訓練階段
- 測試階段
- 完整代碼
?1990年代,Yann LeCun等人提出了用于手寫數字和機器打印字符圖片識別的神經網絡,被命名為LetNet-5 [1]。LetNet-5的提出,使得卷積神經網絡在當時能夠成功被商用,廣泛應用在郵政編碼、支票號碼識別任務中。下圖是LetNet-5的網絡結構圖,它接受 32×3232×3232×32大小的數字、字符圖片,經過第一個卷積層得到 [b,28,28,6][b,28,28,6][b,28,28,6]形狀的張量,經過一個向下采樣層,張量尺寸縮小到 [b,14,14,6][b,14,14,6][b,14,14,6],經過第二個卷積層,得到 [b,10,10,16][b,10,10,16][b,10,10,16]形狀的張量,同樣經過下采樣層,張量尺寸縮小到 [b,5,5,16][b,5,5,16][b,5,5,16],在經過全連接層之前,先將張量打成 [b,400][b,400][b,400]的張量,送入輸出節點數分別為120、84的兩個全連接層,得到 [b,84][b,84][b,84]的張量,最后通過Gaussian connection層。
[1] Y. Lecun, L. Bottou, Y. Bengio 和 P. Haffner, “Gradient-based learning applied to document recognition,” 出處 Proceedings of the IEEE, 1998.
LeNet-5網絡結構
?現在看來,LeNet-5網絡層數較少(2個卷積層和2個全連接層),參數量較少,計算代價較低,尤其在現代GPU的加持下,數分鐘即可訓練好LeNet-5網絡。
?我們在LeNet-5的基礎上進行了少許調整,使得它更容易在現代深度學習框架上實現。首先我們將輸入X\boldsymbol XX形狀由32×3232×3232×32調整為28×2828×2828×28,然后將2個下采樣層實現為最大池化層(降低特征圖的高、寬,后面會介紹),最后利用全連接層替換掉Gaussian connection層。下文統一稱修改的網絡也為LeNet-5網絡。網絡結構圖如圖所示:
加載數據集
?我們基于MNIST手寫數字圖片數據集訓練LeNet-5網絡,并測試其最終準確度。前面已經介紹了如何在TensorFlow中加載MNIST數據集。詳見深度學習(18)神經網絡與全連接層一: 數據加載
創建網絡
?首先通過Sequential容器創建LeNet-5,代碼如下:
運行結果如下圖所示:
?通過summary()函數統計出每層的參數量,打印出網格結構信息和每層參數數量詳情,如下表所示,我們可以與全連接網絡的參數量表進行比較。
| 參數量 | 60 | 880 | 48120 | 10164 | 850 |
可以看到,卷積層的參數量非常少,主要的參數量集中在全連接層。由于卷積層將輸入層特征維度降低很多,從而使得全連接層的參數量不至于過大,整個模型的參數量約60K,而全連接網絡參數量達到了34萬個,因此通過卷積神經網絡可以顯著降低網絡參數量,同時增加網絡深度。
訓練階段
?在訓練階段,首先將數據集中shape為[b,28,28][b,28,28][b,28,28]的輸入X\boldsymbol XX增加一個維度,調整shape為[b,28,28,1][b,28,28,1][b,28,28,1],送入模型進行前向計算,得到輸出張量output,shape為[b,10][b,10][b,10]。我們新建交叉熵損失函數類(沒錯,損失函數也能使用類方式)用于處理分類任務,通過設定from_logits=True標志位將softmax激活函數實現在損失函數中,不需要手動添加損失函數,提升數值計算穩定性。代碼如下:
訓練部分實現如下:
獲得損失值后,通過TensorFlow的梯度記錄器tf.GradientTape()來計算損失函數loss對網絡參數network.trainable_variables之間的梯度,并通過optimizer對象自動更新網絡權值參數。代碼如下:
重復上述步驟若干次后即可完成訓練工作
測試階段
?在測試階段,由于不需要記錄梯度信息,代碼一般不需要寫在with tf.GradientTape() as tape環境中。前向計算得到的輸出經過softmax函數后,代表了網絡預測當前圖片輸入x\boldsymbol xx屬于類別i的概率P(x標簽是i│x),i∈[0,9]P(x標簽是i│x),i∈[0,9]P(x標簽是i│x),i∈[0,9]。通過argmax函數選取概率最大的元素所在的索引,作為當前x的預測類別,與真實標注y比較,通過計算比較結果中間True的數量并求和來統計預測正確的樣本的個數,最后除以總樣本的個數,得出網絡的測試準確度。代碼如下:
?在數據集上面循環訓練30個Epoch后,網絡的訓練準確度達到了98.1%,測試準確度也達到了97.7%。對于非常簡單的手寫數字識別任務,古老的LeNet-5網絡已經可以取得很好地效果,但是稍微復雜一點的任務,比如色彩動物圖片識別,LeNet-5性能就會急劇下降。
完整代碼
import osfrom Chapter08 import metrics from Chapter08.metrics import loss_meteros.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers, Sequential, losses, optimizers, datasets# 加載MNIST數據集 def preprocess(x, y):# 預處理函數x = tf.cast(x, dtype=tf.float32) / 255y = tf.cast(y, dtype=tf.int32)return x, y# 加載MNIST數據集 (x, y), (x_test, y_test) = keras.datasets.mnist.load_data() # 創建數據集 batchsz = 128 train_db = tf.data.Dataset.from_tensor_slices((x, y)) train_db = train_db.map(preprocess).shuffle(60000).batch(batchsz).repeat(10) test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)) test_db = test_db.batch(batchsz)network = Sequential([ # 網絡容器layers.Conv2D(6, kernel_size=3, strides=1), # 第一個卷積層,6個3×3卷積核layers.MaxPooling2D(pool_size=2, strides=2), # 高寬各減半的池化層layers.ReLU(), # 激活函數layers.Conv2D(16, kernel_size=2, strides=1), # 第二個卷積層,16個3×3卷積核layers.MaxPooling2D(pool_size=2, strides=2), # 高寬各減半的池化層layers.ReLU(), # 激活函數layers.Flatten(), # 打平層,方便全連接層處理layers.Dense(120, activation='relu'), # 全連接層,120個節點layers.Dense(84, activation='relu'), # 全連接層,84個節點layers.Dense(10), # 全連接層,10個節點 ])# build一次網格模型,給輸入x的形狀,其中4為隨意給的batchsize network.build(input_shape=(4, 28, 28, 1)) # 統計網絡信息 network.summary()# 創建損失函數的類,在實際計算時直接調用實例即可 criteon = losses.CategoricalCrossentropy(from_logits=True)optimizer = optimizers.Adam(lr=0.01)# 訓練部分實現如下 # 構建梯度記錄環境 # 訓練20個epochdef train_epoch(epoch):for step, (x, y) in enumerate(train_db): # 循環優化with tf.GradientTape() as tape:# 插入通道維度,=>[b,28,28,1]x = tf.expand_dims(x, axis=3)# 向前計算,獲得10類別的概率分布,[b,784] => [b,10]out = network(x)# 真實標簽one-hot編碼,[b] => [b,10]y_onehot = tf.one_hot(y, depth=10)# 計算交叉熵損失函數,標量loss = criteon(y_onehot, out)# 自動計算梯度grads = tape.gradient(loss, network.trainable_variables)# 自動更新參數optimizer.apply_gradients(zip(grads, network.trainable_variables))if step % 100 == 0:print(step, 'loss:', loss_meter.result().numpy())loss_meter.reset_states()# 計算準確度if step % 100 == 0:# 記錄預測正確的數量,總樣本數量correct, total = 0, 0for x, y in test_db: # 遍歷所有訓練集樣本# 插入通道維度,=>[b,28,28,1]x = tf.expand_dims(x, axis=3)# 向前計算,獲得10類別的概率分布,[b,784] => [b,10]out = network(x)# 真實的流程時先經過softmax,再argmax# 但是由于softmax不改變元素的大小相對關系,故省去pred = tf.argmax(out,axis=-1)y = tf.cast(y, tf.int64)# 統計預測樣本總數correct += float(tf.reduce_sum(tf.cast(tf.equal(pred, y), tf.float32)))# 統計預測樣本總數total += x.shape[0]# 計算準確率print('test acc:', correct/total)def train():for epoch in range(30):train_epoch(epoch)if __name__ == '__main__':train()總結
以上是生活随笔為你收集整理的深度学习之卷积神经网络(4)LeNet-5实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac连接局域网Windows共享打印机
- 下一篇: 部分 Win10 / Win11 家庭版