卷積神經網絡(CNNs)是現代計算機視覺的基石,可以實現圖像識別、面部檢測和自駕車等應用。這些網絡旨在自動從圖像中提取模式和特徵,使其比傳統的機器學習技術在視覺任務中更為強大。
在本教程中,我們將使用PyTorch實現一個CNN,這是一個既用戶友好又高效的深度學習框架,適用於研究和生產應用。
先備知識:深度學習和PyTorch
在深入研究CNN的細節之前,您必須熟悉深度學習領域以及我們在環境設置期間將使用的Python庫。
深度學習是機器學習的一個子集,其基本模型結構是由輸入、隱藏層和輸出組成的網絡。這樣的網絡可以有一個或多個隱藏層。深度學習最初的直覺是創建受人腦學習啟發的模型:通過稱為神經元的相互連接的細胞。這就是為什麼我們繼續將深度學習模型稱為“神經”網絡。這些分層模型結構需要比其他監督學習模型多得多的數據來從非結構化數據中學習模式。通常我們談論至少數十萬個數據點。
雖然有許多框架和套件可用於實現深度學習算法,但我們將專注於 PyTorch,這是最流行且維護良好的框架之一。除了被產業中的深度學習工程師使用外,PyTorch也是研究人員喜愛的工具。許多深度學習論文都是使用 PyTorch 發表的。它被設計為直觀且用戶友好,與 Python 库 NumPy 有許多共同之處。
如果您需要對這些概念有更深入的了解,請考慮今天報名參加 使用 PyTorch 進行深度學習 課程。
什麼是卷積神經網絡(CNN)?
卷積神經網絡,通常稱為 CNN 或 ConvNet,是一種特定類型的深度神經網絡,非常適合用於計算機視覺任務。CNN 的發明可以追溯到 1980 年代。然而,它們直到 2010 年代才變得主流,這是由於實現圖形處理單元(GPU)所帶來的計算突破。事實上,CNN 的快速普及幫助神經網絡領域重新獲得重要性,導致我們仍然生活在的所謂”神經網絡的第三波”。
CNN 專門受到生物視覺皮層的啟發。皮層有對視覺領域的特定區域敏感的小區域細胞。這個想法是由 Hubel 和 Wiesel 在 1962 年進行的一個引人入勝的實驗所擴展的。
CNN 通過創建由不同的任務特定層組成的複雜神經網絡來複製這一特徵。CNN 被稱為“前饋”,因為信息直接流經模型。與使用反向傳播等技術的其他模型不同,CNN 中沒有反饋連接,其中模型的輸出被餵回到自身。
具體而言,CNN 通常包含以下層:
卷積層
這是 CNN 的第一個構建塊。正如名稱所示,執行的主要數學任務被稱為卷積,這是將滑動窗口函數應用於表示圖像的像素矩陣的過程。應用於矩陣的滑動函數稱為核或過濾器。在卷積層中,應用了幾個相同大小的過濾器,每個過濾器用於識別圖像中的特定模式,例如數字的彎曲、邊緣、數字的整體形狀等。
激活函數
通常,在每次卷積操作之後應用 ReLU 激活函數。此函數有助於網絡學習圖像中特徵之間的非線性關係,使網絡更具魯棒性以識別不同的模式。它還有助於緩解消失梯度問題。
池化層
池化層的目標是從卷積矩陣中提取最重要的特徵。這是通過應用一些聚合操作來完成的,這些操作減少了特徵圖(卷積矩陣)的維度,從而減少了在訓練網絡時使用的內存。池化也有助於減輕過擬合問題。
全連接層
這些層位於卷積神經網絡的最後一層,其輸入對應於最後一個池化層生成的壓平的一維矩陣。對它們應用ReLU激活函數以實現非線性。
卷積神經網絡架構。來源:DataCamp
您可以在我們的教程中閱讀有關CNN背後數學的更詳細解釋,Python中的卷積神經網絡。
為什麼要使用CNN進行圖像分類?
卷積神經網絡在計算機視覺領域中是最具影響力的創新之一。它們的表現遠遠優於傳統的機器學習模型,例如支持向量機(SVM)和決策樹,並產生了最先進的結果。
此外,卷積層賦予了CNN其平移不變特性,使其能夠識別和提取數據中的模式和特徵,無視位置、方向、尺度或平移的變化。
CNN在許多不同的現實案例和應用中得到了成功應用,例如:
- 圖像分類、物體檢測、分割、人臉識別;
- 利用基於CNN的視覺系統的自駕車;
- 使用卷積神經網絡進行晶體結構分類;
- 安全攝像頭系統。
除了圖像分類任務外,CNN是多才多藝的,可以應用於其他領域,如自然語言處理、時間序列分析和語音識別。
使用PyTorch實現CNN
現在您已經熟悉了卷積神經網絡(CNN)的理論,我們準備開始實際操作。在這一部分,我們將使用PyTorch構建和訓練一個簡單的CNN。我們的目標是建立一個能夠對圖像中的數字進行分類的模型。為了訓練和測試我們的模型,我們將使用著名的MNIST數據集,這是一個包含70,000幅手寫數字的28×28灰階圖像的集合。
1. 導入所需的庫
在下面,您可以找到我們將在本教程中使用的庫。基本上,我們將利用PyTorch來構建我們的CNN,並使用PyTorch的計算機視覺模塊torchvision
來下載和加載MNIST數據集。最後,我們還將使用torchmetrics
來評估我們模型的性能。
import numpy as np import pandas as pd import matplotlib.pyplot as plt import torch from torch import optim from torch import nn from torch.utils.data import DataLoader from tqdm import tqdm # !pip install torchvision import torchvision import torch.nn.functional as F import torchvision.datasets as datasets import torchvision.transforms as transforms # !pip install torchmetrics import torchmetrics
2. 加載和預處理數據集
PyTorch還擁有豐富的工具和擴展生態系統,包括torchvision,一個用於計算機視覺的模塊。Torchvision包含幾個可以用於訓練和測試神經網絡的圖像數據集。在我們的教程中,我們將使用MNIST數據集。
首先,我們將下載並將MNIST數據集轉換為張量,這是PyTorch中的核心數據結構,類似於NumPy數組,但具有GPU加速功能。
然後,我們還將使用 DataLoader 來處理訓練和測試數據集的分批和洗牌。可以從資料集創建 PyTorch DataLoader 來載入數據,將其分成批次並對數據執行轉換(如果需要)。然後,它生成一個準備進行訓練的數據樣本。在下面的代碼中,我們加載數據並將其保存在批量大小為 60 張圖片的 DataLoaders 中。
batch_size = 60 train_dataset = datasets.MNIST(root="dataset/", download=True, train=True, transform=transforms.ToTensor()) train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True) test_dataset = datasets.MNIST(root="dataset/", download=True, train=False, transform=transforms.ToTensor()) test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)
可選地,訓練數據集可以進一步劃分為訓練和驗證兩個部分。驗證是深度學習中用於評估模型性能的技術。它有助於檢測模型的過度擬合和欠擬合,尤其有助於優化超參數。但是,為了簡單起見,我們在本教程中不會使用驗證。如果您想了解更多關於驗證的信息,可以查看我們的PyTorch 深度學習入門課程中的詳細解釋。
現在我們有了數據,讓我們看看一個隨機批次的數字是什麼樣子:
def imshow(img): npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show() # 獲取一些隨機訓練圖片 dataiter = iter(dataloader_train) images, labels = next(dataiter) labels # 顯示圖片 imshow(torchvision.utils.make_grid(images))
3. 定義 CNN 架構
為了解決分類問題,我們將利用 nn.Module
類,這是 PyTorch 中用於直觀創建複雜神經網絡架構的基本模塊。
在下面的代碼中,我們創建了一個名為 CNN
的類,該類繼承自 nn.Module
類的屬性。CNN
類將成為具有兩個卷積層、後接全連接層的卷積神經網絡的藍圖。
在 PyTorch 中,我們使用 nn.Conv2d
來定義卷積層。我們傳遞給它輸入和輸出特徵圖的數量。我們還設置了一些卷積層工作的參數,包括內核或過濾器大小和填充。
接下來,我們使用 nn.MaxPool2d
添加一個最大池化層。在這裡,我們在前一個卷積層的輸出上滑動一個不重疊的窗口。在每個位置,我們從窗口中選擇最大值並傳遞到下一層。這個操作減小了特徵圖的空間維度,減少了網絡中的參數數量和計算複雜性。最後,我們添加一個全連接的線性層。
forward()
函數定義了不同層之間的連接方式,在每個卷積層之後添加了幾個 ReLU 激活函數。
class CNN(nn.Module): def __init__(self, in_channels, num_classes): """ Building blocks of convolutional neural network. Parameters: * in_channels: Number of channels in the input image (for grayscale images, 1) * num_classes: Number of classes to predict. In our problem, 10 (i.e digits from 0 to 9). """ super(CNN, self).__init__() # 第1層卷積層 self.conv1 = nn.Conv2d(in_channels=in_channels, out_channels=8, kernel_size=3, padding=1) # 最大池化層 self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # 第2層卷積層 self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, padding=1) # 全連接層 self.fc1 = nn.Linear(16 * 7 * 7, num_classes) def forward(self, x): """ Define the forward pass of the neural network. Parameters: x: Input tensor. Returns: torch.Tensor The output tensor after passing through the network. """ x = F.relu(self.conv1(x)) # 應用第一次卷積和ReLU激活 x = self.pool(x) # 應用最大池化 x = F.relu(self.conv2(x)) # 應用第二次卷積和ReLU激活 x = self.pool(x) # 應用最大池化 x = x.reshape(x.shape[0], -1) # 拉平張量 x = self.fc1(x) # 應用全連接層 return x x = x.reshape(x.shape[0], -1) # 拉平張量 x = self.fc1(x) # 應用全連接層 return x
一旦我們定義了 CNN
類,我們可以創建模型並將其移至將進行訓練和運行的設備。
神經網絡,包括CNN,在GPU上運行時表現更佳,但這可能取決於您的電腦。因此,我們將僅在可用時在GPU上運行模型;否則,我們將使用常規CPU。
device = "cuda" if torch.cuda.is_available() else "cpu" model = CNN(in_channels=1, num_classes=10).to(device) print(model) >>> CNN( (conv1): Conv2d(1, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (fc1): Linear(in_features=784, out_features=10, bias=True) )
4. 訓練CNN模型
現在我們有了模型,是時候開始訓練了。為了進行訓練,我們首先需要確定如何評估模型的表現。由於我們正在處理多類分類問題,我們將使用在PyTorch中作為nn.CrossEntropyLoss
提供的交叉熵損失函數。我們還將使用Adam優化器,這是最流行的優化算法之一。
# 定義損失函數 criterion = nn.CrossEntropyLoss() # 定義優化器 optimizer = optim.Adam(model.parameters(), lr=0.001)
我們將迭代十個epochs和訓練批次來訓練模型,並對每個批次執行一系列常規步驟,如下所示。
num_epochs=10 for epoch in range(num_epochs): # 迭代訓練批次 print(f"Epoch [{epoch + 1}/{num_epochs}]") for batch_index, (data, targets) in enumerate(tqdm(dataloader_train)): data = data.to(device) targets = targets.to(device) scores = model(data) loss = criterion(scores, targets) optimizer.zero_grad() loss.backward() optimizer.step()
Epoch [1/10] 100%|██████████| 1000/1000 [00:13<00:00, 72.94it/s] Epoch [2/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.27it/s] Epoch [3/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.16it/s] Epoch [4/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.00it/s] Epoch [5/10] 100%|██████████| 1000/1000 [00:13<00:00, 75.69it/s] Epoch [6/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.24it/s] Epoch [7/10] 100%|██████████| 1000/1000 [00:12<00:00, 78.23it/s] Epoch [8/10] 100%|██████████| 1000/1000 [00:12<00:00, 78.16it/s] Epoch [9/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.96it/s] Epoch [10/10] 100%|██████████| 1000/1000 [00:12<00:00, 77.93it/s]
5. 評估模型
一旦模型訓練完畢,我們可以在測試數據集上評估其表現。我們將使用準確度,這是分類問題中常用的度量標準。準確度衡量了在數據集中從總對象數中正確分類的案例比例。通過將正確預測的數量除以模型進行的總預測數量來計算。
首先,我們從 torchmetrics 設置了準確度指標。接下來,我們使用模型的 .eval 方法將模型設置為評估模式,因為 PyTorch 模型中的某些層在訓練和測試階段行為不同。我們還添加了一個帶有 torch.no_grad
的 Python 上下文,表示我們將不進行梯度計算。
然後,我們在沒有梯度計算的情況下迭代測試示例。對於每個測試批次,我們獲取模型輸出,取最有可能的類別,並將其與標籤一起傳遞給準確度函數。最後,我們計算指標並打印結果。我們獲得了 0.98 的準確度分數,這意味著我們的模型正確分類了 98% 的數字。不錯!
# 設置多類準確度指標 acc = Accuracy(task="multiclass",num_classes=10) # 遍歷數據集批次 model.eval() with torch.no_grad(): for images, labels in dataloader_test: # 為測試數據批次獲取預測概率 outputs = model(images) _, preds = torch.max(outputs, 1) acc(preds, labels) precision(preds, labels) recall(preds, labels) # 計算總測試準確度 test_accuracy = acc.compute() print(f"Test accuracy: {test_accuracy}") >>> Test accuracy: 0.9857000112533569
您也可以使用其他流行的分類指標,包括召回率和精確率。在我們的 PyTorch 中級深度學習課程 中,我們將告訴您有關這些指標的所有信息以及實際示例。
提高模型性能
儘管我們的 CNN 模型表現出色,但我們可以使用一些策略來進一步提高其準確性、韌性和對新數據的泛化能力。
在這一部分,我們將探討關鍵技術,如資料擴增、超參數調整和遷移學習,以優化我們模型的性能。
資料擴增技術
資料擴增是一種技術,通過隨機創建新的訓練資料來提高模型的準確性。例如,在加載過程中,可以對訓練圖像應用轉換,如調整大小、水平或垂直翻轉、隨機旋轉等。這樣可以創建擴增圖像並將它們分配與原始圖像相同的標籤,從而增加訓練集的大小。
將隨機轉換應用於原始圖像,可以生成更多數據,同時增加訓練集的大小和多樣性。這使得模型對於真實世界圖像中常見的變化和失真更具韌性,並且通過學習忽略隨機轉換來減少過擬合。
然而,對於資料擴增需要謹慎,因為有時候它可能會損害訓練過程。例如,在我們的問題中,如果對數字“6”應用垂直翻轉,它將看起來像數字“9”。將其標記為“6”傳遞給模型將混淆模型並阻礙訓練。這些例子表明,有時候特定的擴增可能會影響標籤。
超參數調整
另一個提高我們模型性能的策略是通過改變模型不同層中涉及的超參數的值。這 超參數調整 需要對神經網絡背後的數學有深入的理解,以及對不同超參數的重要性。
例如,您可以通過改變濾波器的大小或增加填充來調整您的CNN層。您還可以為神經元的初始權重設置不同的值。
由於我們無法提前知道超參數的最佳值,因此需要一定程度的試驗和錯誤。這通常是通過一種稱為網格搜索的技術來完成的,該技術允許您在一個參數值的網格中系統地評估模型。
然而,在使用這種技術時要謹慎,因為它通常計算成本高昂,特別是在處理複雜的神經網絡和大型訓練數據集時。
同樣,您可以通過添加更多的卷積層和線性層來增加模型的複雜性。然而,在添加新層時要謹慎,因為神經元的數量可能會大幅增加,導致更長的訓練時間和潛在的過擬合。
您可以在我們的 PyTorch 深度學習入門課程 中了解更多有關超參數調整的信息。
使用預訓練模型
從頭開始訓練深度學習模型是一個冗長且繁瑣的過程,通常需要大量的訓練數據。相反,我們通常可以使用預訓練模型,即已經在某些任務上進行過訓練的模型。
有時,如果預訓練模型已經能夠解決我們關心的任務,我們可以直接重用預訓練模型。在其他情況下,我們可能需要調整預訓練模型以適應新任務。這被稱為遷移學習。
在PyTorch中使用預訓練模型相當容易。Torchvision提供了一系列針對各種與圖像相關任務的預訓練模型。這些模型是在大規模圖像數據集上預先訓練的,並且很容易獲得。查看我們的使用PyTorch進行圖像深度學習課程,了解您需要了解的一切。
部署CNN模型
在PyTorch中訓練完您高度準確的分類模型後,您現在可以保存模型及其預訓練權重以供將來使用,並與您的團隊共享,確保他們能夠無縫加載它。
要保存模型,我們可以使用torch.save
。 torch模型的常見文件擴展名可以是pt
或pth
。要保存模型的權重,我們將model.state_dict
傳遞給torch.save
,並提供輸出文件名,例如MulticlassCNN.pth
。
要加載一個已保存的模型,我們需要使用相同架構初始化一個新模型。然後,我們使用torch.load
方法加上load state dict
來將參數加載到新模型。
# 保存模型 torch.save(model.state_dict(), 'MulticlassCNN.pth') # 創建一個新模型 loaded_model = CNN(in_channels=1, num_classes=10) # 加載已保存的模型 loaded_model.load_state_dict(torch.load('MulticlassCNN.pth')) print(loaded_model) CNN( (conv1): Conv2d(1, 8, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(8, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (fc1): Linear(in_features=784, out_features=10, bias=True) )
結論
我們已經涵蓋了CNN的完整概述,提供了有關CNN架構每一層的詳細信息。此外,我們還提供了如何在PyTorch中實現CNN的指南,涵蓋了從數據加載和模型設計到模型訓練和評估的主要步驟。最後,我們還分析了幾種改善模型性能的策略。我們將所有這些技能應用到了與多類分類任務相關的現實情境中。
關於深度學習還有很多值得學習的地方,這也許是人工智慧中最令人興奮和需求最大的領域之一。幸運的是,DataCamp在這裡提供幫助。查看我們的專門教材和課程,成為神經網絡專家:
Source:
https://www.datacamp.com/tutorial/pytorch-cnn-tutorial