深度學習筆記05-使用CNN進行猴痘識別(Tensorflow)

前言

一、我的環境

二、準備套件

三、GPU 設置

四、導入數據

五、數據預處理

六、可視化數據

七、配置數據集

八、建構CNN網路

九、編譯模型

十、訓練模型

十一、模型評估

十二、使用最佳模型權重進行預測

十三、總結


前言


警語  :內文中含有的病例圖片可能引起不適,請斟酌閱讀


一、我的環境

  • 電腦系統:Windows 10

  • 顯卡:NVIDIA Quadro P620

  • 語言環境:Python 3.7.0

  • 開發工具:Sublime Text,Command Line(CMD)

  • 深度學習環境:Tensorflow 2.5.0


二、準備套件

# 提供一些與操作系統交互的功能,例如文件路徑操作等
import os

# 用於圖像處理,例如打開、操作、保存圖像文件
import PIL

# 用於處理文件路徑的模塊,提供一種更加直觀和面向對象的操作文件路徑方式
import pathlib

# 用於繪圖,可以創建各種類型的圖表和圖形
import matplotlib.pyplot as plt

# 數值計算庫,用於處理大型多維數組和矩陣的
import numpy as np

# 開源的機器學習框架
import tensorflow as tf

# 導入 keras 模塊,為 tensorflow 的高級 API 之一,操作起來更加簡單、易用
from tensorflow import keras

# keras.layers,tensorflow 的子模塊,包含建構神經網路模型所需的各種層
# keras.models,tensorflow 的子模塊,包含建構神經網路模型的類和函數
from tensorflow.keras import layers,models

# 是 TensorFlow Keras 中的一個回調函數
# 用於在訓練過程中保存模型的檢查點(checkpoint)
# 檢查點是模型的權重或是整個模型在訓練期間的某個狀態的保存
from tensorflow.keras.callbacks import ModelCheckpoint

三、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") 

四、導入數據

# 設定數據目錄的相對路徑,也可以使用絕對路徑
# D:/AI/ai_note/T4,這邊要注意斜線的方向
data_dir = "T4/"
# 將路徑轉換成 pathlib.Path 對象,更易操作
data_dir = pathlib.Path(data_dir)
# 使用 glob 方法獲取指定目錄下所有以 '.jpg' 為副檔名的文件迭代器
# '*/*.jpg' 是一個通配符模式,表示所有直接位於子目錄中的以 .jpg 結尾的文件
# 第一個星號表示所有目錄
# 第二個星號表示所有檔名
image_count = len(list(data_dir.glob('*/*.jpg')))
# 印出圖片數量
print("圖片總數:",image_count)

# 開張圖來看看
Monkeypox = list(data_dir.glob('Monkeypox/*.jpg'))
img = PIL.Image.open(str(Monkeypox[0]))
plt.imshow(img)
# 關閉座標軸,即不顯示座標軸
plt.axis('off')
# 顯示圖片
plt.show()

共有2142 張圖

 


五、數據預處理

數據集共分為四類,分別為 Monkeypox、Others

# 設置批量大小,即每次訓練模型時輸入到模型中的圖像數量
# 在每次訓練跌代時,模型將同時處理32張圖像
# 批量大小的選擇會影響訓練速度和內存需求
batch_size = 32
# 圖像的高度,在加載圖像數據時,將所有的圖像調整為相同的高度,這裡設定為 224 像素
img_height = 224
# 圖像的寬度,在加載圖像數據時,將所有的圖像調整為相同的寬度,這裡設定為 224 像素
img_width = 224

# 創建訓練集
# 使用 tf.keras.preprocessing.image_dataset_from_directory 函數從目錄中創建一個圖像數據集
# 會得到一個 tf.data.Dataset 對象,其中包含指定目錄中圖像的數據集,以及相應的標籤
# 返回的這個數據集可以直接用於模型的訓練和驗證
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,   # 指定包含圖像的目錄路徑
    validation_split=0.2,   # 用來指定數據集中分割出多少比例數據當作驗證集,0.2 表示 20% 數據會被用來當驗證集
    subset="training",  # 指定是用於訓練還是驗證的數據子集,這裡設定為 training ,表示創建的是訓練數據集
    seed=123,   # 設定亂數種子,確保每次運行程式碼時得到的數據集都是相同的,以保持實驗的可重複性
    image_size=(img_height, img_width), # 指定圖像的大小
    batch_size=batch_size) # 指定每個批次中包含的圖像樣本數量

# 創建驗證集
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",    # 指定是用於訓練還是驗證的數據子集,這裡設定為 validation ,表示創建的是驗證集
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size)

# class_names 是一個包含數據集中所有類別名稱的列表
class_names = train_ds.class_names
# 印出類別
print(class_names)

驗證集跟測試集間的說明:

  1. 驗證集在梯度下降中的作用: 驗證集沒有直接參與模型參數的訓練更新過程,在梯度下降中,只有訓練集的數據被用來計算損失函數並更新模型參數,從而使模型更好地擬合訓練數據

  2. 驗證集在人工調參過程中的作用: 驗證集的存在參與了一個「人工調參」的過程。在每個訓練輪次(epoch)結束後,使用驗證集來評估模型的性能,例如計算準確率、損失等指標,根據這些指標,可以做出一些決策,比如是否提前停止訓練(early stopping),或者調整模型的超參數(如學習率、批量大小等)

  3. 驗證集在防止過擬合上的作用: 驗證集的存在有助於防止模型對訓練集的過度擬合,通過監控模型在驗證集上的表現,我們可以確保模型在訓練過程中不僅僅是在記憶訓練集,而是學習到了普適的特徵和模式,這有助於提高模型的泛化能力,使其在面對新的未見過的數據時也能表現良好

總的來說,驗證集在訓練過程中扮演了關鍵的角色,雖然它並沒有直接參與參數的訓練更新,但通過在每個epoch後對模型性能的評估,它能夠幫助我們進行模型的調優和防止過擬合


六、可視化數據

# 創建一個圖形對象,設置圖形大小寬度 20 英寸、高度 10 英寸
plt.figure(figsize=(20, 10))
# train_ds.take(1) 從訓練集中取出一個批次(batch)的數據
# 遍歷訓練數據集的第一個批次(batch)中的每張圖片
# images 是一個包含圖片數據的張量
# labels 是一個包含對應標籤的張量
for images, labels in train_ds.take(1):
	# 遍歷當前批次(batch)的前 20 張圖
    for i in range(20):
    	# 創建一個子圖,將圖形劃分成 5 行 10 列,當前子圖的索引為 i+1
        ax = plt.subplot(5, 10, i + 1)

		# 顯示第 i 張圖片
        # images[i] 是一個張量,使用 .numpy() 方法將其轉換為 NumPy 數組,
        # 再使用 .astype("uint8") 方法將其轉換為無符號整型 8 位整數類型
        plt.imshow(images[i].numpy().astype("uint8"))
        # 設置當前子圖的標題為當前圖片的類別名稱
        # labels[i] 視當前圖片對應的標籤,通過索引 class_names 列表獲取對應的類別名稱 
        plt.title(class_names[labels[i]])
        # 關閉座標軸的顯示
        plt.axis("off")
# 顯示圖片
plt.show()

# 從 train_ds 中迭代獲取一個批次(batch)的圖像數據和標籤數據,印出他們的形狀
for image_batch, labels_batch in train_ds:
    print('圖像數據的形狀:', image_batch.shape)
    print('標籤數據的形狀:', labels_batch.shape)
    break  # 只印一批次(batch)即可

 (32, 224, 224, 3)  :印出當前批次 ( batch ) 的圖像數據的形狀,image_batch 是一個張量,.shape 屬性表示該張量的形狀,即每個維度的大小,最后一维指的是彩色通道RGB

(32,)  :印出當前批次 ( batch ) 的標籤數據的形狀,labels_batch 是一個張量,.shape 屬性表示該張量的形狀,即每個維度的大小


七、配置數據集

# AUTOTUNE 表示自動調整緩衝區大小以優化性能
AUTOTUNE = tf.data.AUTOTUNE

# 使用 cache() 方法將訓練集緩存到內存中,這樣可以加快數據加載速度
# 當模型多次迭代訓練數據集時,可以重複使用已經加載到內存中的數據,而不必重新從磁盤加載
# 使用 shuffle() 方法對訓練數據集進行洗牌操作,打亂數據集中的樣本順序
# 參數 1000 指定了洗牌時使用的緩衝區大小,即每次從數據集中隨機選擇的樣本數量
# 通過洗牌操作,可以增加模型的訓練效果,使模型更好的學習數據的分布
# 使用 prefetch() 方法預取數據,以便在訓練過程中盡可能的減少數據加載時間
# 此方法可以在訓練模型的同時異步加載數據,從而提高 GPU 的利用率,減少數據加載造成的訓練時間損失
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)

# 對驗證集進行和訓練集相同的緩存操作
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

八、建構CNN網路

# 表示模型的輸出類別數量為 2 ,在這種情況下,模型的輸出層具有 2 個神經元,每個神經元對應一個類別
num_classes = 2

# 創建一個序列模型,這是一種線性堆疊模型,其中各個層按照他們被添加到模型中的順序來堆疊
model = models.Sequential([
	# 創建了一個對圖像進行重新縮放的預處理層
    # 將每個像素的數值除以 255,將像素值縮放到範圍 [0,1],即歸一化
    # 輸入的 img_height 和 img_width 分別表示圖像的高度和寬度,3 表示圖像的通道數,通常為 RGB 彩色圖像
    layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
    # 創建一個卷積層
    # 16:表示該卷積層使用了 16 個卷積核
    # (3, 3):指定了每個卷積核的大小為 3x3
    # 使用ReLU(Rectified Linear Unit)作為激活函數,它是一種常用的非線性函數,能夠使模型具有更好的學習能力
    # 設置輸入形狀為(img_height, img_width, 3)
    # 表示輸入圖像的高度為img_height像素,寬度為img_width像素,通道數為3(RGB 彩色圖像)
    # 這個參數只需要在模型的第一層中指定,後續的層將自動推斷輸入形狀
    layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)),
    # 創建一個平均池化層
    # 將原始特徵圖中的每個 2x2 區域的數值取平均,從而實現尺寸的縮小和特徵的抽取
    # (2, 2):指定了池化窗口的大小為 2x2 ,即將原始特徵圖劃分為 2x2 的子區域
    # 這個池化操作對每個子區域內的數值進行平均,得到新的特徵圖
    layers.AveragePooling2D((2, 2)),         
    # 創建第二個卷積層
    # 32 個卷積核
    # 每個卷積核的大小是 3x3
    # 本層沒有指定輸入形狀(input_shape),因為這是模型中的中間層,將繼承前一層的輸出形狀作為輸入形狀
    layers.Conv2D(32, (3, 3), activation='relu'), 
	# 創建第二個平均池化層
    # 作用是對輸入的特徵圖進行下採樣,以減少特徵圖的空間尺寸,同時保留主要的特徵
    # 指定池化窗口的大小為 2x2 ,這意味著對於每個 2x2 的區域,將計算其內像素的平均值作為輸出
    layers.AveragePooling2D((2, 2)),
    # 隨機失活層
    # 在模型訓練過程中以指定的概率丟棄(關閉)一定比例的神經元,以防止過擬合
    # 0.3 表示丟棄的神經元概率為 30%
    # 意味著每次訓練迭代中,有 30% 的神經元會被隨機丟棄,以防止模型過度擬合訓練數據
    layers.Dropout(0.3),  
    # 創建第三個卷積層
    # 64個卷積核
    # 每個卷積核的大小為 3x3
    layers.Conv2D(64, (3, 3), activation='relu'), 
    # 隨機失活層
    # 在模型訓練過程中以指定的概率丟棄(關閉)一定比例的神經元,以防止過擬合
    # 0.3 表示丟棄的神經元概率為 30%
    # 意味著每次訓練迭代中,有 30% 的神經元會被隨機丟棄,以防止模型過度擬合訓練數據
    layers.Dropout(0.3),  
    # 展平層
    # 將多維輸入數據平坦化成一維數組
    # 將多維輸入數據(如卷積層的輸出)展平成一個一維數組,以便將其餵入全連接層
    layers.Flatten(),
    # 全連接層
    # 包含 128 個神經元
    # 使用 ReLU 激活函數
    # 用於將之前卷積層和池化層提取的特徵進一步轉換和提取  
    layers.Dense(128, activation='relu'), 
    # 輸出層
    # 包含了 num_classes 個神經元
    # 其數量通常等於分類問題中的類別數量
    # 這一層的激活函數通常沒有指定,因為它用於輸出每個類別的原始分數或概率值
    layers.Dense(num_classes) 
])

# 印出模型結構
model.summary()


九、編譯模型

# 編譯模型的函數
# 編譯模型前要先進行配置,包括優化器(optimizer)、損失函數(loss function)、評估指標(metrics)
# 創建了一個 Adam 優化器的實例,設置了學習率為  0.0001
# Adam 優化器是一種常用的梯度下降優化算法,用於更新模型的權重以最小化訓練過程中的損失函數
opt = tf.keras.optimizers.Adam(learning_rate=0.0001)
# 設置了模型的優化器、損失函數和評估指標
# 優化器使用了上面定義的 Adam 優化器
# 損失函數使用了稀疏分類交叉熵(SparseCategoricalCrossentropy)
# from_logits=True ,表示模型的輸出是未經過 softmax 轉換的原始分數或概率值
# 評估指標指定了準確率(accuracy),用於在訓練過程中監控模型的性能
model.compile(optimizer=opt,
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'])

十、訓練模型

# 指定訓練過程中的迭代次數,即訓練模型的輪數
epochs = 50

# 創建了一個名為 checkpointer 的 ModelCheckpoint 物件
# 用於在訓練過程中監視驗證集的準確率,並保存在驗證集上獲得最佳性能的模型權重
checkpointer = ModelCheckpoint(
    # 保存模型權重的檔案路徑和名稱
    # 模型的權重將保存在名為 'best_model.h5' 的檔案中
    'best_model.h5', 
    # 指定要監視的指標,這裡是驗證集的準確率
    # ModelCheckpoint 將根據驗證集的準確率表現來決定是否保存模型權重
    monitor='val_accuracy', 
    # 這是日誌輸出的詳細程度
    # 設置為 1 時,會輸出保存模型信息的日誌
    verbose=1,  
    # 指示是否只保存在驗證集上獲得最佳性能的模型權重
    # 如果設置為 True,則只有當監視的指標在新的 epoch 中有改善時,才會保存模型權重。這有助於避免保存過擬合的模型
    save_best_only=True,   
    # 指示是否只保存模型的權重而不保存模型的結構
    # 如果設置為 True,則只保存模型的權重,而不保存模型的結構
    # 這在需要在不同的模型結構之間共享權重時很有用
    save_weights_only=True)

# checkpointer 物件創建完成後,可以將其傳遞給 fit() 方法的 callbacks 參數,以便在訓練過程中使用它
# 使用 fit 方法開始訓練模型
# 訓練過程中的歷史記錄將保存在 history 變量中,可用於後續分析和可視化
history = model.fit(
    train_ds,   # 訓練集
    validation_data=val_ds, # 驗證集
    epochs=epochs,   # 指定訓練的迭代次數
    callbacks=[checkpointer])   # 使用監測物件

在訓練過程中,ModelCheckpoint 會在每個 epoch 結束時檢查驗證集的準確率,並在發現性能提升時將模型權重保存到 best_model.h5 檔案中,如上圖框起來部分

挑一筆輸出做說明:Epoch 00010: val_accuracy did not improve from 0.79206  

  • 這個訊息是來自於訓練模型時的 ModelCheckpoint 回調函數
  • Epoch 00010: 這表示這個訊息是在第 10 個訓練時期(epoch)生成的
  • val_accuracy did not improve from 0.79206: 這部分指的是模型在驗證集上的準確率沒有改善,在這個訓練時期結束時,驗證集的準確率仍然是 0.79206(即 79.206%),而沒有進一步提升

總的來說,這個訊息表示在第 10 個訓練時期結束時,模型在驗證集上的準確率沒有達到比之前更好的表現,因此沒有保存模型的權重


十一、模型評估

# 從訓練歷史中提取準確率
# acc 是訓練集準確率
# val_acc 是驗證集準確率
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

# 從訓練歷史中提取損失
# loss 是訓練集損失
# val_loss 是驗證集損失
loss = history.history['loss']
val_loss = history.history['val_loss']

# 定義了迭代次數範圍
epochs_range = range(epochs)

# 創建一個新的圖形,指定圖形的大小
plt.figure(figsize=(12, 4))
# 在圖形中創建 1 行 2 列的子圖,並選擇第一個子圖
plt.subplot(1, 2, 1)
# 繪製訓練集準確率隨迭代次數變化的曲線
plt.plot(epochs_range, acc, label='Training Accuracy')
# 繪製驗證集準確率隨迭代次數變化的曲線
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
# 添加圖例,位置位於右下角
plt.legend(loc='lower right')
# 添加子圖的標題
plt.title('Training and Validation Accuracy')

# 在圖形中創建 1 行 2 列的子圖,並選擇第二個子圖
plt.subplot(1, 2, 2)
# 繪製訓練集損失函數的曲線
plt.plot(epochs_range, loss, label='Training Loss')
# 繪製驗證集損失函數的曲線
plt.plot(epochs_range, val_loss, label='Validation Loss')
# 添加圖例,位置位於右上角
plt.legend(loc='upper right')
# 添加子圖的標題
plt.title('Training and Validation Loss')

# 顯示圖片
plt.show()

 

損失率(Loss)和準確率(Accuracy)是在機器學習和深度學習中常用的兩個指標,用於評估模型的性能

  1. 損失率(Loss):損失率是模型在訓練過程中對於每個樣本的預測結果與實際標籤之間的差異的度量,它是一個數值,表示模型的預測與真實值之間的差距有多大,通常,損失率越低表示模型的預測越接近真實值,在深度學習中,常見的損失函數包括均方誤差(Mean Squared Error,MSE)、交叉熵(Cross Entropy)等
  2. 準確率(Accuracy):準確率是模型在測試集或驗證集上預測正確的樣本數量與總樣本數量之間的比例,它是一個百分比,通常用來衡量模型的整體性能,準確率越高表示模型預測正確的樣本比例越高,模型的性能越好

這兩個指標通常是相互關聯的:當損失率下降時,準確率通常會提高,因為模型的預測更加準確,因此,在訓練模型時,我們通常會盡量降低損失率,以提高準確率


十二、使用最佳模型權重進行預測

# 加載效果最好的模型權重
model.load_weights('best_model.h5')

# 選擇想預測的照片
img = PIL.Image.open("T4/Others/NM15_02_11.jpg")  
# 將原始圖像調整為模型所需的大小
img = img.resize((img_height, img_width))
# 將 PIL 圖像對象轉換為 NumPy 數組(即將圖像像素轉換為數值表示)
# TensorFlow 中的圖像處理功能通常接受 NumPy 數組作為輸入
img_array = tf.keras.preprocessing.image.img_to_array(img)
# 將 NumPy 數組轉換為 TensorFlow 張量,同時添加一個額外的維度
# 添加到第一個維度,以便能夠將單個圖像作為單個樣本餵入模型進行預測。
img_array = tf.expand_dims(img_array, 0)

# 進行預測
# 產出一個包含預測概率的陣列
predictions = model.predict(img_array)
# 使用 np.argmax(predictions) 獲取概率最高的類別索引
# 利用 class_names 陣列找到對應的類別名稱
print("預測結果為:",class_names[np.argmax(predictions)])

預測結果:正確!


十三、總結

經過本次實做,學到了如何使用 ModelCheckpoint 以及他的重要性:

  1. 模型訓練的重要性: ModelCheckpoint 讓我意識到了在深度學習中,模型訓練是一個反覆運行的過程,而模型的效果往往會隨著訓練的進行而改變,因此,及時保存模型在訓練過程中表現最好的權重是至關重要的,這有助於避免訓練過程中的意外中斷或資源浪費

  2. 監控與保存模型: 使用 ModelCheckpoint 可以方便地監控模型在驗證集上的表現,並保存表現最佳的模型權重,通過設置參數如 monitor、save_best_only 等,可以根據訓練過程中的效果自動選擇保存模型的時間點,這大大提高了訓練效率和模型的穩定性

  3. 防止過擬合: 通過在訓練過程中保存驗證集上表現最佳的模型權重,ModelCheckpoint 有助於防止模型在訓練過程中過度擬合訓練集,及時停止訓練或回溯到表現較佳的模型權重,有助於提高模型的泛化能力

  4. 優化超參數調整: 除了保存最佳模型權重外,ModelCheckpoint 還可以幫助我們優化模型的超參數調整過程,通過監控模型在驗證集上的表現,可以根據效果調整學習率、批次大小等超參數,從而進一步提升模型性能

結論,ModelCheckpoint 是一個強大的工具,能夠幫助我們更好地管理和優化模型訓練過程,提高模型的效果和效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值