使用TensorFlow和Keras,輕松搭建并訓練你的第一個神經(jīng)網(wǎng)絡
AI技術發(fā)展迅猛,利用各種先進的AI模型,可以打造聊天機器人、仿人機器人、自動駕駛汽車等。AI已經(jīng)成為發(fā)展最快的技術,而對象檢測和物體分類是最近的趨勢。
本文將介紹使用卷積神經(jīng)網(wǎng)絡從頭開始構建和訓練一個圖像分類模型的完整步驟。本文將使用公開的Cifar-10數(shù)據(jù)集來訓練這個模型。這個數(shù)據(jù)集是獨一無二的,因為它包含了像汽車、飛機、狗、貓等日常所見物體的圖像。通過對這些物體進行神經(jīng)網(wǎng)絡訓練,本文將開發(fā)出智能系統(tǒng)來對現(xiàn)實世界中的這些東西進行分類。它包含了6萬多張32x32大小的10種不同類型的物體圖像。在本教程結束時,你將擁有一個可以根據(jù)物體的視覺特征來判斷對象的模型。
圖1 數(shù)據(jù)集樣本圖像|圖片來自datasets.activeloop
本文將從頭開始講述所有內(nèi)容,所以如果你還沒有學習過神經(jīng)網(wǎng)絡的實際實現(xiàn),也完全沒問題。
以下是本教程的完整工作流程:
- 導入必要的庫
- 加載數(shù)據(jù)
- 數(shù)據(jù)的預處理
- 建立模型
- 評估模型的性能
圖2 完整的流程
導入必要的庫
首先必須安裝一些模塊才能開始這個項目。本文將使用Google Colab,因為它提供免費的GPU訓練。
以下是安裝所需庫的命令:
$ pip install tensorflow, numpy, keras, sklearn, matplotlib
將庫導入到Python文件中。
from numpy import *
from pandas import *
import matplotlib.pyplot as plotter
# 將數(shù)據(jù)分成訓練集和測試集。
from sklearn.model_selection import train_test_split
# 用來評估我們的訓練模型的庫。
from sklearn.metrics import classification_report, confusion_matrix
import keras
# 加載我們的數(shù)據(jù)集。
from keras.datasets import cifar10
# 用于數(shù)據(jù)增量。
from keras.preprocessing.image import ImageDataGenerator
# 下面是一些用于訓練卷積Nueral網(wǎng)絡的層。
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.layers import Conv2D, MaxPooling2D, GlobalMaxPooling2D, Flatten
- Numpy:它用于對包含圖像的大型數(shù)據(jù)集進行高效的數(shù)組計算。
- Tensorflow:它是一個由谷歌開發(fā)的開源機器學習庫。它提供了許多函數(shù)來建立大型和可擴展的模型。
- Keras:另一個在TensorFlow之上運行的高級神經(jīng)網(wǎng)絡API。
- Matplotlib:這個Python庫可以創(chuàng)建圖表,提供更好的數(shù)據(jù)可視化。
- Sklearn:它提供了對數(shù)據(jù)集執(zhí)行數(shù)據(jù)預處理和特征提取任務的功能。它包含內(nèi)置的函數(shù),可以找到模型的評估指標,如準確率、精確度、誤報、漏報等。
現(xiàn)在,進入數(shù)據(jù)加載的步驟。
加載數(shù)據(jù)
本節(jié)將加載數(shù)據(jù)集并執(zhí)行訓練-測試數(shù)據(jù)的拆分。
加載和拆分數(shù)據(jù):
# 類的數(shù)量
nc = 10
(training_data, training_label), (testing_data, testing_label) = cifar10.load_data()
(
(training_data),
(validation_data),
(training_label),
(validation_label),
) = train_test_split(training_data, training_label, test_size=0.2, random_state=42)
training_data = training_data.astype("float32")
testing_data = testing_data.astype("float32")
validation_data = validation_data.astype("float32")
cifar10數(shù)據(jù)集是直接從Keras數(shù)據(jù)集庫中加載的。并且這些數(shù)據(jù)也分為訓練數(shù)據(jù)和測試數(shù)據(jù)。訓練數(shù)據(jù)用于訓練模型,以便它可以識別其中的模式。而測試數(shù)據(jù)對模型來說是不可見的,它被用來檢查其性能,即相對于總的數(shù)據(jù)點,有多少數(shù)據(jù)點被正確預測。
training_label包含了與training_data中的圖像對應的標簽。
然后使用內(nèi)置sklearn的train_test_split函數(shù)將訓練數(shù)據(jù)再次拆分成驗證數(shù)據(jù)。驗證數(shù)據(jù)用于選擇和調整最終的模型。最后,所有的訓練、測試和驗證數(shù)據(jù)都轉換為32位的浮點數(shù)。
現(xiàn)在,數(shù)據(jù)集的加載已經(jīng)完成。在下一節(jié)中,本文將對其執(zhí)行一些預處理步驟。
數(shù)據(jù)的預處理
數(shù)據(jù)預處理是開發(fā)機器學習模型時的第一步,也是最關鍵的一步。跟隨本文一起看看如何做到這一點。
# 歸一化
training_data /= 255
testing_data /= 255
validation_data /= 255
# 熱編碼
training_label = keras.utils.to_categorical(training_label, nc)
testing_label = keras.utils.to_categorical(testing_label, nc)
validation_label = keras.utils.to_categorical(validation_label, nc)
# 輸出數(shù)據(jù)集
print("Training: ", training_data.shape, len(training_label))
print("Validation: ", validation_data.shape, len(validation_label))
print("Testing: ", testing_data.shape, len(testing_label))
輸出:
Training: (40000, 32, 32, 3) 40000
Validation: (10000, 32, 32, 3) 10000
Testing: (10000, 32, 32, 3) 10000
該數(shù)據(jù)集包含10個類別的圖像,每個圖像的大小為32x32像素。每個像素都有一個0-255的值,我們需要在0-1之間對其進行歸一化以簡化計算過程。之后,我們將把分類標簽轉換為單熱編碼標簽。這樣做是為了將分類數(shù)據(jù)轉換為數(shù)值數(shù)據(jù),這樣我們就可以毫無問題地應用機器學習算法。
現(xiàn)在,進入CNN模型的構建。
建立CNN模型
CNN模型分三個階段工作。第一階段由卷積層組成,從圖像中提取相關特征。第二階段由池化層組成,用于降低圖像的尺寸。它也有助于減少模型的過度擬合。第三階段由密集層組成,將二維圖像轉換為一維數(shù)組。最后,這個數(shù)組被送入全連接層,進行最后的預測。
以下是代碼:
model = Sequential()
model.add(
Conv2D(32, (3, 3), padding="same", activatinotallow="relu", input_shape=(32, 32, 3))
)
model.add(Conv2D(32, (3, 3), padding="same", activatinotallow="relu"))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), padding="same", activatinotallow="relu"))
model.add(Conv2D(64, (3, 3), padding="same", activatinotallow="relu"))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(96, (3, 3), padding="same", activatinotallow="relu"))
model.add(Conv2D(96, (3, 3), padding="same", activatinotallow="relu"))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dropout(0.4))
model.add(Dense(256, activatinotallow="relu"))
model.add(Dropout(0.4))
model.add(Dense(128, activatinotallow="relu"))
model.add(Dropout(0.4))
model.add(Dense(nc, activatinotallow="softmax"))
本文應用了三組圖層,每組包含兩個卷積層、一個最大池化層和一個丟棄層。Conv2D層接收input_shape為(32,32,3),必須與圖像的尺寸相同。
每個Conv2D層還需要一個激活函數(shù),即relu。激活函數(shù)是用于增加系統(tǒng)中的非線性。更簡單地說,它決定神經(jīng)元是否需要根據(jù)某個閾值被激活。有許多類型的激活函數(shù),如ReLu、Tanh、Sigmoid、Softmax等,它們使用不同的算法來決定神經(jīng)元的激發(fā)。
之后,添加了平坦層和全連接層,在它們之間還有幾個Dropout層。Dropout層隨機地拒絕一些神經(jīng)元對網(wǎng)層的貢獻。它里面的參數(shù)定義了拒絕的程度。它主要用于避免過度擬合。
下面是一個CNN模型架構的示例圖像。
圖3 Sampe CNN架構|圖片來源:Researchgate
編譯模型
現(xiàn)在,本文將編譯和準備訓練的模型。
# 啟動Adam優(yōu)化器
opt = keras.optimizers.Adam(lr=0.0001)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# 獲得模型的摘要
model.summary()
輸出:
圖4 模型摘要
本文使用了學習率為0.0001的Adam優(yōu)化器。優(yōu)化器決定了模型的行為如何響應損失函數(shù)的輸出而變化。學習率是訓練期間更新權重的數(shù)量或步長。它是一個可配置的超參數(shù),不能太小或太大。
擬合模型
現(xiàn)在,本文將把模型擬合到我們的訓練數(shù)據(jù),并開始訓練過程。但在此之前,本文將使用圖像增強技術來增加樣本圖像的數(shù)量。
卷積神經(jīng)網(wǎng)絡中使用的圖像增強技術將增加訓練圖像,而不需要新的圖像。它將通過在圖像中產(chǎn)生一定量的變化來復制圖像。它可以通過將圖像旋轉到一定程度、添加噪聲、水平或垂直翻轉等方式來實現(xiàn)。
augmentor = ImageDataGenerator(
width_shift_range=0.4,
height_shift_range=0.4,
horizontal_flip=False,
vertical_flip=True,
)
# 在augmentor中進行擬合
augmentor.fit(training_data)
# 獲得歷史數(shù)據(jù)
history = model.fit(
augmentor.flow(training_data, training_label, batch_size=32),
epochs=100,
validation_data=(validation_data, validation_label),
)
輸出:
圖5 每個時期的準確度和損失
ImageDataGenerator()函數(shù)用于創(chuàng)建增強的圖像。fit()用于擬合模型。它以訓練和驗證數(shù)據(jù)、Batch Size和Epochs的數(shù)量作為輸入。
Batch Size是在模型更新之前處理的樣本數(shù)量。一個關鍵的超參數(shù)必須大于等于1且小于等于樣本數(shù)。通常情況下,32或64被認為是最好的Batch Size。
Epochs的數(shù)量代表了所有樣本在網(wǎng)絡的前向和后向都被單獨處理了多少次。100個epochs意味著整個數(shù)據(jù)集通過模型100次,模型本身運行100次。
我們的模型已經(jīng)訓練完畢,現(xiàn)在我們將評估它在測試集上的表現(xiàn)。
評估模型性能
本節(jié)將在測試集上檢查模型的準確性和損失。此外,本文還將繪制訓練和驗證數(shù)據(jù)的準確率與時間之間和損失與時間之間的關系圖。
model.evaluate(testing_data, testing_label)
輸出:
313/313 [==============================] - 2s 5ms/step - loss: 0.8554 - accuracy: 0.7545
[0.8554493188858032, 0.7545000195503235]
本文的模型達到了75.34%的準確率,損失為0.8554。這個準確率還可以提高,因為這不是一個最先進的模型。本文用這個模型來解釋建立模型的過程和流程。CNN模型的準確性取決于許多因素,如層的選擇、超參數(shù)的選擇、使用的數(shù)據(jù)集的類型等。
現(xiàn)在我們將繪制曲線來檢查模型中的過度擬合情況。
def acc_loss_curves(result, epochs):
acc = result.history["accuracy"]
# 獲得損失和準確性
loss = result.history["loss"]
# 聲明損失和準確度的值
val_acc = result.history["val_accuracy"]
val_loss = result.history["val_loss"]
# 繪制圖表
plotter.figure(figsize=(15, 5))
plotter.subplot(121)
plotter.plot(range(1, epochs), acc[1:], label="Train_acc")
plotter.plot(range(1, epochs), val_acc[1:], label="Val_acc")
# 給予繪圖的標題
plotter.title("Accuracy over " + str(epochs) + " Epochs", size=15)
plotter.legend()
plotter.grid(True)
# 傳遞值122
plotter.subplot(122)
# 使用訓練損失
plotter.plot(range(1, epochs), loss[1:], label="Train_loss")
plotter.plot(range(1, epochs), val_loss[1:], label="Val_loss")
# 使用 ephocs
plotter.title("Loss over " + str(epochs) + " Epochs", size=15)
plotter.legend()
# 傳遞真值
plotter.grid(True)
# 打印圖表
plotter.show()
acc_loss_curves(history, 100)
輸出:
圖6 準確度和損失與歷時的關系
在本文的模型中,可以看到模型過度擬合測試數(shù)據(jù)集。(藍色)線表示訓練精度,(橙色)線表示驗證精度。訓練精度持續(xù)提高,但驗證誤差在20個歷時后惡化。
總結
本文展示了構建和訓練卷積神經(jīng)網(wǎng)絡的整個過程。最終得到了大約75%的準確率。你可以使用超參數(shù)并使用不同的卷積層和池化層來提高準確性。你也可以嘗試遷移學習,它使用預先訓練好的模型,如ResNet或VGGNet,并在某些情況下可以提供非常好的準確性。