經典卷積神經網絡算法(1):LeNet-5


 

LeNet-5科學家Yann LeCun在1998年發表論文《Gradient based learning applied to document-recognition》上提出的一個神經網絡模型,是最早期的卷積神經網絡,論文中,作者將LeNet-5應用於於灰度圖像的數字識別中獲得了不錯的效果。關於LeNet-5卷積神經網絡原理,在上一篇介紹卷積神經網絡入門博客中已經闡述清楚,本篇中,我們主要對LeNet-5使用TensorFlow進行實現。
LeNet-5網絡結構如下所示:

 

 

接下來,本文就上圖所示LeNet-5結構進行實現。

In [25]:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers, Sequential ,metrics
 

TensorFlow中自帶手寫數字識別圖像數據集,使用datasets模塊進行加載即可。

In [26]:
(x, y), (x_test, y_test) = datasets.mnist.load_data()
 

查看數據的數量和圖像size:

In [27]:
print(x.shape, y.shape)
print(x_test.shape, y_test.shape)
 
(60000, 28, 28) (60000,)
(10000, 28, 28) (10000,)
 

可見,訓練集中包含60000張圖像,測試集中包含10000章圖像,圖像大小為2828。圖像size與上圖LeNet-5卷積網絡中所用3232數據集有所不同,不過沒關系,我們在第一層卷積層中對圖像進行padding即可。

 

使用matplotlib對數據集進行展示,如下所示,圖片上方數字為圖像對應的數字。

In [28]:
index = 1
fig, axes = plt.subplots(4, 3, figsize=(8, 4), tight_layout=True)
for row in range(4):
    for col in range(3):
        axes[row, col].imshow(x[index])
        axes[row, col].axis('off')
        axes[row, col].set_title(y[index])
        index += 1
plt.show()
 
 

剛加載好的圖像是numpy數組形式,元素值在0~255之間,需要進行類型轉換和歸一化。這里我們定義一個preprocess作為預處理函數,在將數據集打包成TensorFlow的dataset形式后,使用map函數調用preprocess對數據進行預處理更加方便。

In [29]:
def preprocess(x, y):
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x,[28,28,1])
    y = tf.cast(y, dtype=tf.int32)
    return x, y
In [30]:
batchs = 32  # 每個簇的大小,批量梯度下降法時每一批的包含圖像的數量
 

打包成TensorFlow到的dataset對象,並隨機打亂數據:

In [31]:
db = tf.data.Dataset.from_tensor_slices((x, y))
db = db.map(preprocess).shuffle(10000).batch(batchs)
 

對測試集同樣打包成dataset,不過測試集可以不隨機打亂:

In [32]:
db_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))
db_test = db_test.map(preprocess).batch(batchs)
In [40]:
net_layers = [ # 卷積部分網絡
    # 第一個卷積層:5*5*6
    # 這個padding在最初的LeNet-5網絡中是沒有的,那時候還沒有padding的概念,為了使這一層輸出與元素LeNet-5網絡保持一致,所以這里添加padding操作
    layers.Conv2D(6, kernel_size=[5,5],padding='same', activation='relu'),  # 6個5*5的卷積核,進行padding
    # 池化層
    layers.MaxPool2D(pool_size=[2, 2], strides=2),  # 池化層大小2*2,步長2
#     layers.ReLU()
    # 第二個池化層:5*5*16
    layers.Conv2D(16, kernel_size=[5,5],padding='valid', activation='relu'),
    # 池化層
    layers.MaxPool2D(pool_size=[2, 2], strides=2),
#     layers.ReLU()
    layers.Flatten(),  # 展平成一維數組
    # 全連接層
    layers.Dense(120,activation='relu'),
    layers.Dense(84,activation='relu'),
    layers.Dense(10,activation='softmax')
]
In [41]:
model = tf.keras.models.Sequential(net_layers) # 將上面創建的層合並打包成模型
model.build(input_shape=(None, 28, 28, 1))     # 指定輸入數據形狀
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)  # 創建優化器
model.compile(optimizer=optimizer,         # 配置模型
           loss='sparse_categorical_crossentropy',  # 指定損失函數
           metrics=['accuracy'])
model.fit(db,epochs=5, validation_data=db_test)  # 訓練模型
 
Epoch 1/5
1875/1875 [==============================] - 46s 25ms/step - loss: 0.5028 - accuracy: 0.8513 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/5
1875/1875 [==============================] - 42s 22ms/step - loss: 0.1257 - accuracy: 0.9619 - val_loss: 0.1051 - val_accuracy: 0.9671
Epoch 3/5
1875/1875 [==============================] - 42s 23ms/step - loss: 0.0928 - accuracy: 0.9716 - val_loss: 0.0798 - val_accuracy: 0.9742
Epoch 4/5
1875/1875 [==============================] - 43s 23ms/step - loss: 0.0746 - accuracy: 0.9766 - val_loss: 0.0666 - val_accuracy: 0.9785
Epoch 5/5
1875/1875 [==============================] - 41s 22ms/step - loss: 0.0638 - accuracy: 0.9801 - val_loss: 0.0580 - val_accuracy: 0.9800
Out[41]:
<tensorflow.python.keras.callbacks.History at 0x7fba79d4b610>
 

可以看到,經過5輪迭代之后,模型的准確率達到98.01%,這在當時已經是相當不錯的成績。
上述代碼對LeNet-5卷積神經網絡進行實現,網絡一共包含7層(激活函數不計算在內),注意,上文中實現的是現代版的LeNet卷積網絡,與最初Yann LeCun論文中描述的LeNet-5在結構上是一致的,不過,現代版的LeNet-5網絡更多使用ReLU激活函數作為中間層激活函數和Softmax在輸出層轉化為概率輸出,另外在池化層現代更多用最大池化,而不是當初的平均池化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM