前言
- 🍨 本文為🔗365天深度學習訓練營 中的學習紀錄博客
- 🍖 原作者:K同学啊 | 接輔導、項目定制
一、我的環境
-
電腦系統:Windows 10
-
顯卡:NVIDIA Quadro P620
-
語言環境:Python 3.7.0
-
開發工具:Sublime Text,Command Line(CMD)
-
深度學習環境:Tensorflow 2.5.0
二、準備套件
pip install matplotlib
補充:Tensorflow 的安裝方式可參考上一篇學習筆記
# 開源的機器學習框架
import tensorflow as tf
# tensorflow.keras 是 TensorFlow 中的一個 API,用於構建和訓練神經網絡模型
from tensorflow.keras import datasets, layers, models
# 用於繪製數據可視化的庫
import matplotlib.pyplot as plt
三、GPU設置
# 列出系統中的GPU裝置列表
gpus = tf.config.list_physical_devices("GPU")
# 如果有GPU
if gpus:
# 挑選第一個 GPU
gpu0 = gpus[0]
# 僅在需要的時候分配記憶體
tf.config.experimental.set_memory_growth(gpu0, True)
# 將 GPU0 設置為 TensorFlow 中可見的唯一 GPU ,將運算限制在特定的 GPU 上
tf.config.set_visible_devices([gpu0],"GPU")
在 TensorFlow 中,通常情況下 GPU 記憶體是預先分配的,即使不需要全部的 GPU 記憶體,也會分配給 TensorFlow 會話,這可能會導致一些問題,例如在佔用大量 GPU 記憶體的情況下,其他程式可能無法使用 GPU
注意:如果同時運行多個 TensorFlow 程式但卻沒有進行記憶體分配,程式間極有可能會相互干擾進而產出錯誤結果
四、導入數據
# 導入MNIST所需使用的數據
# train_images = 訓練集圖片
# train_labels = 訓練集標籤
# test_images = 測試集圖片
# test_labels = 測試集標籤
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()
# 印出數據形狀
print("訓練集圖片的形狀:", train_images.shape) # (60000, 28, 28)
print("訓練集標籤的形狀:", train_labels.shape) # (60000,)
print("測試集圖片的形狀:", test_images.shape) # (10000, 28, 28)
print("測試集標籤的形狀:", test_labels.shape) # (10000,)
datasets.mnist.load_data() 是 TensorFlow 提供用於加載 MNIST 手寫數字數據集的函式
MNIST 數據集是機器學習中最常用的數據集之一,它包含了 60,000 張訓練圖片和 10,000 張測試圖片,每張圖片都是一個手寫的單個數字(0 到 9)的灰度圖片,並且已經標記了其對應的數字
- train_images 是訓練集圖片的 numpy 數組,形狀為 (60000, 28, 28) ,表示有 60000 張 28x28 的灰度圖片
- train_labels 是訓練集標籤的 numpy 數組,形狀為 (60000,) ,包含了對應每張圖片的數字標籤
- test_images 是測試集圖片的 numpy 數組,形狀為 (10000, 28, 28),表示有 10000 張 28x28 的灰度圖片
- test_labels 是測試集標籤的 numpy 數組,形狀為 (10000,) ,包含了對應每張圖片的數字標籤
五、歸一化
歸一化是將數據按比例縮放到特定範圍的過程,使其落入相同的尺度,例如 [0, 1] 或 [-1, 1]
在機器學習中,通常需要對特徵進行歸一化,以確保所有特徵都在相似的尺度上
歸一化的主要用途包括:
-
加速模型訓練:將特徵歸一化到相似的尺度可以幫助模型更快地收斂,從而加快訓練速度
-
提高模型準確性:歸一化可以幫助模型更好地理解特徵之間的關係,從而提高模型的準確性和泛化能力
-
減少梯度消失或爆炸:將數據歸一化到相似的尺度可以幫助減少梯度下降過程中的梯度消失或爆炸問題
-
提高模型的魯棒性:歸一化可以使模型對於輸入數據的變化更加魯棒,從而提高模型的性能和穩定性
# 對於灰度圖片來說,每個像素的值通常介於 0 和 255 之間,其中 0 表示黑色,255 表示白色
# 因此可以直接將每個像素值除以 255 來完成歸一化
train_images, test_images = train_images / 255.0, test_images / 255.0
補充:
魯棒性(Robustness)指的是系统或模型在面對各種異常或不良情况時仍能保持良好的性能和稳定性的能力
在深度學習中,魯棒性是指模型對於輸入數據的變化、噪音干擾、缺失值、離群值等異常情况的處理能力
具有鲁棒性的模型能夠在不同的數據分布下皆表現良好,不會因為數據的微小變化或異常情況而導致性能急遽下降
這種性能穩定性是非常重要的,因為在現實世界中的數據往往會存在各種噪音、異常值或者缺失值,而一个魯棒的模型能夠更好地處理這些情況,提高模型的可靠性和實用性
六、圖片可視化
# 將訓練集前20張圖片作可視化處理
# 畫出大小為寬20*長10的圖(英吋)
plt.figure(figsize=(20,10))
# 繪製MNIST訓練集前20張圖,並在下方顯示對應的標籤(即圖像表示的數字)
for i in range(20):
# 將figure分成2行10列,繪製第i+1個子圖
plt.subplot(2,10,i+1)
# cmap=plt.cm.binary参数指定使用二進制顏色映射,黑色表示低像素值,白色表示高像素值
plt.imshow(train_images[i], cmap=plt.cm.binary)
# 設置x軸標籤為圖片對應的數字
plt.xlabel(train_labels[i])
# 不顯示x軸刻度
plt.xticks([])
# 不顯示y軸刻度
plt.yticks([])
# 不顯示網格線
plt.grid(False)
# 顯示圖片
plt.show()
七、調整圖片格式
# 調整數據格式
train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))
print('訓練集圖片張量化後的形狀:', train_images.shape) # ((60000, 28, 28, 1)
print('測試集圖片張量化後的形狀:', test_images.shape) # (10000, 28, 28, 1)
在深度學習中,通常使用張量來表示數據
train_images 和 test_images 是圖像數據,每張圖都是一個 28x28 的矩陣,因此需要轉換成張量格式
使用 reshape() 可以將圖像數據從原始的 28x28 調整成一個四維張量,維度為(樣本數量、圖像高度、圖像寬度、通道數量)
補充:
(60000, 28, 28, 1) 表示訓練集中有 60000 個樣本,每個樣本高度為 28x28 像素,且每個像素表示為單通道(灰度圖像),所以通道數量為1
灰度圖像:每個像素點只包含一個灰度值,每個像素的灰度值表示該像素的亮度,數值落在 0(黑色)到 255(白色)間
彩色圖像:由紅色(R)、綠色(G)、藍色(B)三個通道組成的(即RGB圖像),每個像素點在每個通道上都有一個對應的顏色值,可以用一個三元組來表示,分別代表該像素在紅綠藍三個通道上的顏色值
八、建構CNN模型
# 創建並設置模型
# 卷積層:通過卷積操作對圖像進行降維和抽取特徵
# 池化層:非線性形式下的採樣,用於特徵降維、壓縮數據和參數的數量、減小過擬合、提高魯棒性
# 全連接層:將所有的神經元連接起來,在經過幾個卷積層和池化層後,將高維的特徵映射到具體的類別或標籤上
model = models.Sequential([
# 創建一個卷積層,其中包含32個卷積核
# 每個卷積核的大小為3x3
# 使用ReLU激活函數作為該卷積層的激活函數
# 設置輸入形狀為(28, 28, 1),表示輸入圖像的高度為28像素,寬度為28像素,通道數為1(灰度圖像)
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
# 創建一個最大池化層
# 池化窗口設置為2x2,即在每個2x2區域內取最大值
layers.MaxPooling2D((2, 2)),
# 創建一個卷積層,其中包含64個卷積核
# 每個卷積核的大小為3x3
# 使用ReLU激活函數作為該卷積層的激活函數
layers.Conv2D(64, (3, 3), activation='relu'),
# 創建一個最大池化層
# 池化窗口設置為2x2,即在每個2x2區域內取最大值
layers.MaxPooling2D((2, 2)),
# 將多維輸入數據平坦化成一維數組
# 將多維輸入數據(如卷積層的輸出)展平成一個一維數組,以便將其餵入全連接層
layers.Flatten(),
# 全連接層
# 有64個神經元
# 使用ReLU激活函數作為該全連接層的激活函數
layers.Dense(64, activation='relu'),
# 輸出層
# 有10個神經元
# 輸出預期結果
layers.Dense(10)
])
# 印出模型結構
model.summary()
ReLU具有以下優點:
- 避免梯度消失問題:在反向傳播過程中,當輸入大於 0 時,ReLU 函數的梯度為 1,因次可以有效的傳播梯度,避免梯度消失的問題
- 計算速度快:ReLU函數的計算速度比較快,因為他只是簡單的輸出輸入值或零
- 稀疏性:ReLU函數會將負值映射為零,因次具有稀疏性,可以使神經網路的激活值更加稀疏,從而提高模型的泛化能力
九、編譯模型
# 編譯模型的函數
# 編譯模型前要先進行配置,包括優化器(optimizer)、損失函數(loss function)、評估指標(metrics)
model.compile(
# 設置Adam優化器
optimizer='adam',
# 設置損失函數為交叉熵損失函數损失函数(tf.keras.losses.SparseCategoricalCrossentropy())
# from_logits = True,將y_pred轉化為概率(softmax),否則不進行轉換,通常情況下用True會讓結果更穩定
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
# 設置評估指標
metrics=['accuracy'])
優化器(Optimizer):優化器決定了模型如何進行參數更新以最小化損失函數。常見的優化器包括隨機梯度下降(SGD)、Adam、RMSprop等。可以通過指定優化器的名稱或創建優化器對象來設置
損失函數(Loss function):損失函數用於衡量模型在訓練過程中的性能。對於不同的任務類型(如分類、回歸),可以選擇不同的損失函數。例如,對於二分類任務,常用的損失函數是二元交叉熵損失函數(binary crossentropy)
評估指標(Metrics):評估指標用於衡量模型的性能。在訓練過程中,模型的性能將根據評估指標進行監控。通常情況下,分類任務常用的評估指標是準確率(accuracy),回歸任務常用的評估指標是均方誤差(mean squared error)
十、訓練模型
# 訓練模型
history = model.fit(
# 訓練集圖片
train_images,
# 訓練集標籤
train_labels,
# 設置10個epoch(迭代次數),每執行一次迭代都會把所有數據輸入模型完成一次訓練
epochs=10,
# 測試集圖片、測試集標籤
validation_data=(test_images, test_labels))
# 儲存模型,方便下次直接載入進行預測
# 載入方式 model = tf.keras.models.load_model('my_model')
model.save('my_model')
loss: 訓練集上的損失值。是模型在訓練過程中優化的目標,表示模型預測值與真實標籤之間的差異。通常,損失值越低,模型在訓練集上的擬合程度越好
accuracy: 訓練集上的準確率。表示模型在訓練集上正確預測的樣本所佔的比例。通常,準確率越高,模型在訓練集上的性能越好
val_loss: 驗證集上的損失值。用來衡量模型在未見過的數據上的泛化能力。與訓練集上的損失值相比,驗證集上的損失值更能反映模型的真實性能
val_accuracy: 驗證集上的準確率。用來衡量模型在未見過的數據上的分類準確程度
十一、預測
# 顯示出測試圖片的樣子
plt.imshow(test_images[1])
plt.show()
# 對所有測試圖片進行預測
pre = model.predict(test_images)
# 輸出測試圖片的預測結果
print(pre[1])
輸出結果: 表示圖片可能是 0~9 中哪一個數字的機率,數字越大可能性越大,數據中 24.28343 為最大值,因此可以判定預測結果該張圖片最可能是數字 2
十二、總結
借由實作MNIST手寫數字識別程式,我學習了如何預先處理資料、建構模型、訓練模型,預測數據等關鍵步驟,實際操作並嘗試理解每行程式碼真的能讓觀念更加清晰