卷積神經網絡(Convolutional Neural Network, CNN)在python上面的實現

楊學智 🐨@筆記😊
19 min readJan 15, 2024

--

卷積神經網路 (Convolutional Neural Network, CNN)

卷積神經網路 (Convolutional Neural Network, 簡稱 CNN) 是一種常用的深度學習模型,經常用於圖像識別、物件偵測、自然語言處理等領域。CNN 的優勢在於它能夠有效地捕捉圖像中的空間關係,並提取出具有代表性的特徵。

結構:

CNN的主要結構包括卷積層(Convolutional Layer)、池化層(Pooling Layer)、激活函數(Activation Function)和全連接層(Fully Connected Layer)。這些層的組合允許CNN自動學習和提取數據中的高層次特徵。

  1. 卷積層: 卷積層是CNN的核心。通過卷積操作,它能夠在輸入數據中學習和捕捉特徵。卷積操作使用一個可學習的過濾器(也叫卷積核)滑動在輸入數據上,這有助於保留特徵的空間層次結構。
  2. 池化層: 池化層用於減少卷積層輸出的空間維度,同時保留重要的特徵。最常見的池化操作是最大池化,它選擇區域內的最大值作為代表。
  3. 激活函數: 激活函數引入非線性性質,有助於模型更好地擬合複雜的數據。常見的激活函數包括ReLU(Rectified Linear Unit)和Sigmoid。
  4. 全連接層: 全連接層用於將卷積層和池化層的輸出映射到預測空間。在這一層,神經元與前一層的所有神經元都相連,形成全連接結構。

CNN 的基本組成單元是卷積層。卷積層中的每個神經元只與輸入圖像中一小部分像素相連,這稱為局部連接。局部連接有助於減少 CNN 的參數數量,並使其在處理具有不同尺寸、角度和位置的物體時更加有效。

卷積層的另一個重要特徵是權重共享。權重共享是指在卷積核中使用的權重在整個圖像中都相同。權重共享進一步減少了 CNN 的參數數量,並有助於提取空間不變的特徵。

除了卷積層之外,CNN 還經常使用池化層。池化層通過縮小圖像尺寸來減少計算量,同時提取更高級的特徵。

特點:

  1. 權重共享: 卷積操作允許使用相同的過濾器在不同位置進行學習,這減少了參數數量,並有助於捕捉特徵的平移不變性。
  2. 空間層次結構: CNN通過多層次的卷積和池化操作保留了輸入數據的空間層次結構,有助於提取層次化特徵。
  3. 適應性特徵提取: CNN通過學習適應於數據的過濾器,可以自動提取數據中的有用特徵。

CNN 應用實現

使用Keras實現卷積神經網絡 為了介紹您如何使用Keras實現卷積神經網絡,我們將從解決眾所周知的MNIST問題開始。這將為我們提供一個有用的比較點,以對比上週在這個任務上使用的多層感知機。而且,對於這個任務來說,一個小型網絡就足夠了,這在我們希望在本地硬件上運行實驗時尤其重要,因為可能沒有GPU可用。

卷積神經網絡(CNN)架構設計注意事項

現在,我們的數據已經適當地準備好輸入到卷積神經網絡(CNN)中,是時候設計我們的模型了。

對於CNNs,模型設計的問題與多層感知機(Multilayer Perceptron,簡稱MLPs)一樣棘手。事實上,我們引入了更多可能的決策,因為每個卷積層都有不同數量的濾波器、卷積核大小、步長和填充的可能性。此外,現代CNN通常是非常深的網絡,層數比MLPs實際上可能的層數多。

對於需要深度CNN的問題,通常使用現有的架構作為起點,而不是自己設計。這些架構通常是通過廣泛實驗開發的,減輕了很多設計負擔,通常只需要對輸入或輸出層進行一些微小的更改,就可以在您的數據集上完全發揮功能。您可以找到各種不同的架構,這些架構在複雜性、運行時和性能方面有所不同。一些現在經典的圖像分類架構包括:

  • AlexNet
  • VGG
  • ResNet

這些架構的Keras實現可以從各種來源獲取,或者您可以嘗試根據相應的研究論文定義自己的架構!

# Load the MNIST data
(X_train_full, y_train_full), (X_test, y_test) = keras.datasets.mnist.load_data()

# Scale the data to the range 0-1
X_train_full = X_train_full / 255.
X_test = X_test / 255.

# Create validation set from the training set
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, train_size=0.9)

print(f"Shape of X_train: {X_train.shape}")

在輸入到多層感知機(MLP)之前,我們將每個圖像展平為一個向量。然而,對於卷積神經網絡(CNNs)的輸入,我們通常保持原始圖像的尺寸完整,因為它們將從相鄰的像素區域提取特徵。

事實上,大多數標準CNNs期望以四維數組作為輸入:(batch_size,width,height,n_channels)

最後一維n_channels對應於每個圖像像素的值數量。RGB彩色圖像通常具有三個通道,分別對應紅色、綠色和藍色主色的強度值。結合這些值可以產生豐富的顏色陣列。

請注意,MNIST圖像是灰度的,因此我們每個圖像像素只有一個值。雖然這使得額外的維度有些多餘,但我們的模型仍然期望在輸入中包含它。讓我們使用np.expand_dims添加它。

# Add an extra dimension to our data
X_train = np.expand_dims(X_train, -1)
X_valid = np.expand_dims(X_valid, -1)
X_test = np.expand_dims(X_test, -1)

print(f"New shape of X_train: {X_train.shape}")
New shape of X_train: (54000, 28, 28, 1)

定義我們的卷積神經網絡模型

這些現有模型擁有眾多層次,需要使用GPU以實現高效運行。相反,我們將定義一個小型、簡單的卷積神經網絡(CNN),可以更有效地在我們的本地硬體上運行,並且在處理MNIST問題時仍然表現卓越。

現在讓我們使用Keras的Sequential來定義模型,然後我們將詳細說明每個組件。

model = keras.Sequential([

# Specify the input shape
keras.Input(shape=(28, 28, 1)),

# Conv and pool block 1
keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
keras.layers.MaxPooling2D(pool_size=(2, 2)),

# Conv and pool block 2
keras.layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
keras.layers.MaxPooling2D(pool_size=(2, 2)),

# Flatten and classify using dense output layer
keras.layers.Flatten(),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation="softmax"),
])
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 26, 26, 32) 320

max_pooling2d (MaxPooling2D (None, 13, 13, 32) 0
)

conv2d_1 (Conv2D) (None, 11, 11, 64) 18496

max_pooling2d_1 (MaxPooling (None, 5, 5, 64) 0
2D)

flatten (Flatten) (None, 1600) 0

dropout (Dropout) (None, 1600) 0

dense (Dense) (None, 10) 16010

=================================================================
Total params: 34,826
Trainable params: 34,826
Non-trainable params: 0
_________________________________________________________________
image From: (Vincent Dumoulin, Francesco Visin, via Wikimedia Commons)
(Michael Plotke, via Wikimedia Commons)

Keras對於Conv2D層的文檔可以在這裡找到(https://keras.io/api/layers/convolution_layers/convolution2d/)。

我們必須指定過濾器(filters)的數量和核大小(kernel size),就像我們上面圖示所做的那樣。改變步長(stride)和填充(padding)也是可能的,不過這些通常設置為合理的默認值。

池化層 為了減少網絡中的參數數量,這些層通過對由pool_size定義的輸入窗口進行最大值池化來降低維度。

全連接分類器 在通過網絡的卷積塊適當提取圖像特徵後,我們將所有內容展平,並使用全連接分類器生成對各類別的概率。

指定優化器和損失函數

儘管深度學習優化器的詳細介紹超出了本文的範疇,但今天我們將使用Adam優化器(Keras文檔),該優化器通過調整整個訓練過程中每個參數的學習率來擴展常規的SGD。擁有這些適應性學習率可以極大地加快我們的訓練過程,並減輕調整初始學習率的重要性。

# Compile model with loss, optimiser, and metrics
model.compile(loss='sparse_categorical_crossentropy',
optimizer="adam",
metrics=['accuracy'])

培訓和評估模型

根據您的硬件設備,以下的培訓單元可能需要幾分鐘的時間才能運行。這些卷積層需要大量的計算!如果您時間緊迫,您可以僅檢查輸出結果。

"""Running this cell could take several minutes"""

# Train the model for 10 epochs with batch size 128
batch_size = 128
epochs = 10
history = model.fit(X_train, y_train, batch_size=batch_size,
epochs=epochs, validation_data=(X_valid, y_valid))
Epoch 1/10
422/422 [==============================] - 20s 46ms/step - loss: 0.3636 - accuracy: 0.8896 - val_loss: 0.0990 - val_accuracy: 0.9737
Epoch 2/10
422/422 [==============================] - 19s 45ms/step - loss: 0.1123 - accuracy: 0.9659 - val_loss: 0.0668 - val_accuracy: 0.9822
Epoch 3/10
422/422 [==============================] - 20s 47ms/step - loss: 0.0843 - accuracy: 0.9736 - val_loss: 0.0523 - val_accuracy: 0.9850
Epoch 4/10
422/422 [==============================] - 22s 52ms/step - loss: 0.0687 - accuracy: 0.9787 - val_loss: 0.0470 - val_accuracy: 0.9865
Epoch 5/10
422/422 [==============================] - 19s 46ms/step - loss: 0.0618 - accuracy: 0.9810 - val_loss: 0.0424 - val_accuracy: 0.9880
Epoch 6/10
422/422 [==============================] - 19s 45ms/step - loss: 0.0543 - accuracy: 0.9829 - val_loss: 0.0408 - val_accuracy: 0.9890
Epoch 7/10
422/422 [==============================] - 20s 48ms/step - loss: 0.0500 - accuracy: 0.9847 - val_loss: 0.0401 - val_accuracy: 0.9888
Epoch 8/10
422/422 [==============================] - 20s 48ms/step - loss: 0.0479 - accuracy: 0.9850 - val_loss: 0.0380 - val_accuracy: 0.9897
Epoch 9/10
422/422 [==============================] - 20s 48ms/step - loss: 0.0432 - accuracy: 0.9869 - val_loss: 0.0354 - val_accuracy: 0.9900
Epoch 10/10
422/422 [==============================] - 20s 48ms/step - loss: 0.0411 - accuracy: 0.9873 - val_loss: 0.0331 - val_accuracy: 0.9905
# Evaluate the classifier on the test data.
loss, accuracy = model.evaluate(X_test, y_test)
print(f"Accuracy on test data: {accuracy:.4f}")
313/313 [==============================] - 3s 9ms/step - loss: 0.0280 - accuracy: 0.9906
Accuracy on test data: 0.9906
# Sample several test examples
X_test_sample = X_test[3:6]

# Get probability of each class from model
y_proba = model.predict(X_test_sample)
y_pred = np.argmax(y_proba, axis=-1)
print(y_pred)

plot_examples(X_test[3:], n_rows=1, n_cols=3)

在這項任務中使用多層感知機(MLP)時,我們達到了約98%的準確度。隨著卷積神經網絡(CNN)在圖像特徵提取方面的改進,我們在使用更少參數的同時略微提高了性能。在更難的任務中,圖像未居中且大小未標準化,改進通常更加顯著。

CNN 圖片分類應用

這是一個基本的卷積神經網絡(CNN),包含三個卷積層,後接最大池化層和全連接層。您可以嘗試不同的架構、超參數和正則化技術,以提升模型的性能。此外,對於更高級的用戶,整合 keras.applications 中的預訓練模型並進行對 CIFAR10 的微調是一個令人興奮的擴展。

以下是使用Keras定義、訓練和評估卷積神經網路(CNN)解決CIFAR10問題的簡化範例:

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split

# Load CIFAR 10 dataset
(X_train_full, y_train_full), (X_test, y_test) = keras.datasets.cifar10.load_data()

# Scale the data to the range 0-1
X_train_full = X_train_full / 255.
X_test = X_test / 255.

# Create validation set from the training set
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, train_size=0.9)

# Define the CNN model
model = keras.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(128, (3, 3), activation='relu'),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(10, activation='softmax')
])

# Compile the model
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])

# Train the model
history = model.fit(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid))

# Evaluate the model on the test set
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f'Test Accuracy: {test_acc}')

# Optionally, you can visualize training history using matplotlib
import matplotlib.pyplot as plt

plt.plot(history.history['accuracy'], label='accuracy')
plt.plot(history.history['val_accuracy'], label = 'val_accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.show()
from sklearn.metrics import classification_report

# Get model predictions on the test set
y_pred = model.predict(X_test)
y_pred_classes = tf.argmax(y_pred, axis=1).numpy()

# Convert one-hot encoded labels to single integers
y_test_classes = y_test.squeeze()

# Generate classification report
report = classification_report(y_test_classes, y_pred_classes, target_names=cifar10_labels)

# Print the classification report
print("Classification Report:\n", report)
313/313 [==============================] - 4s 11ms/step
Classification Report:
precision recall f1-score support

airplane 0.82 0.69 0.75 1000
automobile 0.85 0.78 0.81 1000
bird 0.68 0.54 0.60 1000
cat 0.51 0.47 0.49 1000
deer 0.55 0.82 0.66 1000
dog 0.69 0.53 0.60 1000
frog 0.67 0.84 0.74 1000
horse 0.74 0.78 0.76 1000
ship 0.83 0.80 0.82 1000
truck 0.80 0.81 0.80 1000

accuracy 0.71 10000
macro avg 0.72 0.71 0.70 10000
weighted avg 0.72 0.71 0.70 10000

--

--

楊學智 🐨@筆記😊

20多年IT程式設計經驗,雙碩士 (澳洲雪梨大學 The University of Sydney數據科學碩士,昆士蘭大學The University of Queensland 商業分析碩士) 興趣於機器學習、神經網絡,深度學習,多模態融合的分析和整理相關的實際應用方面的筆記和經驗。希望能與有相似興趣的人分享知識。