WeHelp
PyTorch 可以應用在迴歸預測模型、分類預測模型、影像辨識、物件偵測、生成式模型等等領域。
  1. 房屋價格預測
  2. 乳癌良性 / 惡性分類
  3. 紅酒產地分類
  4. CNN 影像辨識
  5. R-CNN 物件偵測
  6. GAN 生成對抗網路
CNN 影像辨識
以下以 **MNIST 手寫數字辨識**為例,使用 PyTorch 建立一個簡單的 CNN,完成資料準備、模型訓練與測試評估。 ## 一、實務案例說明 **目標:** 輸入一張大小為 `28 × 28` 的灰階手寫數字影像,辨識其為 `0~9` 中的哪一個數字。 **資料集:** - MNIST - 訓練資料:60,000 張 - 測試資料:10,000 張 - 影像通道:1,灰階 - 分類數量:10 類 --- ## 二、安裝套件 ```bash pip install torch torchvision ``` --- ## 三、完整實作程式碼 ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, random_split from torchvision import datasets, transforms # -------------------------------------------------- # 1. 基本設定 # -------------------------------------------------- device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print("使用裝置:", device) torch.manual_seed(42) BATCH_SIZE = 64 EPOCHS = 5 LEARNING_RATE = 0.001 # -------------------------------------------------- # 2. 資料前處理 # -------------------------------------------------- transform = transforms.Compose([ transforms.ToTensor(), # MNIST 常用的平均值與標準差 transforms.Normalize((0.1307,), (0.3081,)) ]) # 下載並載入 MNIST 訓練資料 full_train_dataset = datasets.MNIST( root="./data", train=True, download=True, transform=transform ) # 官方測試資料 test_dataset = datasets.MNIST( root="./data", train=False, download=True, transform=transform ) # 將訓練資料切成訓練集與驗證集 train_size = int(0.9 * len(full_train_dataset)) val_size = len(full_train_dataset) - train_size train_dataset, val_dataset = random_split( full_train_dataset, [train_size, val_size], generator=torch.Generator().manual_seed(42) ) # 建立 DataLoader train_loader = DataLoader( train_dataset, batch_size=BATCH_SIZE, shuffle=True ) val_loader = DataLoader( val_dataset, batch_size=BATCH_SIZE, shuffle=False ) test_loader = DataLoader( test_dataset, batch_size=BATCH_SIZE, shuffle=False ) # -------------------------------------------------- # 3. 建立 CNN 模型 # -------------------------------------------------- class CNNModel(nn.Module): def __init__(self): super().__init__() self.features = nn.Sequential( # 輸入:1 x 28 x 28 nn.Conv2d( in_channels=1, out_channels=32, kernel_size=3, padding=1 ), nn.ReLU(), nn.MaxPool2d(kernel_size=2), # 形狀:32 x 14 x 14 nn.Conv2d( in_channels=32, out_channels=64, kernel_size=3, padding=1 ), nn.ReLU(), nn.MaxPool2d(kernel_size=2) # 形狀:64 x 7 x 7 ) self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(64 * 7 * 7, 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, 10) ) def forward(self, x): x = self.features(x) x = self.classifier(x) return x model = CNNModel().to(device) print(model) # -------------------------------------------------- # 4. 損失函數與最佳化器 # -------------------------------------------------- criterion = nn.CrossEntropyLoss() optimizer = optim.Adam( model.parameters(), lr=LEARNING_RATE ) # -------------------------------------------------- # 5. 定義訓練函式 # -------------------------------------------------- def train_one_epoch(model, data_loader, criterion, optimizer, device): model.train() total_loss = 0.0 correct = 0 total = 0 for images, labels in data_loader: images = images.to(device) labels = labels.to(device) # 清除上一批次的梯度 optimizer.zero_grad() # 前向傳播 outputs = model(images) # 計算損失 loss = criterion(outputs, labels) # 反向傳播 loss.backward() # 更新模型參數 optimizer.step() total_loss += loss.item() * images.size(0) predictions = outputs.argmax(dim=1) correct += (predictions == labels).sum().item() total += labels.size(0) average_loss = total_loss / total accuracy = correct / total return average_loss, accuracy # -------------------------------------------------- # 6. 定義評估函式 # -------------------------------------------------- def evaluate(model, data_loader, criterion, device): model.eval() total_loss = 0.0 correct = 0 total = 0 # 評估時不需要計算梯度 with torch.no_grad(): for images, labels in data_loader: images = images.to(device) labels = labels.to(device) outputs = model(images) loss = criterion(outputs, labels) total_loss += loss.item() * images.size(0) predictions = outputs.argmax(dim=1) correct += (predictions == labels).sum().item() total += labels.size(0) average_loss = total_loss / total accuracy = correct / total return average_loss, accuracy # -------------------------------------------------- # 7. 執行模型訓練 # -------------------------------------------------- best_val_accuracy = 0.0 for epoch in range(EPOCHS): train_loss, train_accuracy = train_one_epoch( model, train_loader, criterion, optimizer, device ) val_loss, val_accuracy = evaluate( model, val_loader, criterion, device ) print( f"Epoch [{epoch + 1}/{EPOCHS}] | " f"Train Loss: {train_loss:.4f}, " f"Train Acc: {train_accuracy:.4f} | " f"Val Loss: {val_loss:.4f}, " f"Val Acc: {val_accuracy:.4f}" ) # 儲存驗證集表現最好的模型 if val_accuracy > best_val_accuracy: best_val_accuracy = val_accuracy torch.save( model.state_dict(), "best_mnist_cnn.pth" ) # -------------------------------------------------- # 8. 載入最佳模型並進行測試 # -------------------------------------------------- model.load_state_dict( torch.load("best_mnist_cnn.pth", map_location=device) ) test_loss, test_accuracy = evaluate( model, test_loader, criterion, device ) print("\n測試結果") print(f"Test Loss: {test_loss:.4f}") print(f"Test Accuracy: {test_accuracy:.4f}") print(f"Test Accuracy: {test_accuracy * 100:.2f}%") ``` --- ## 四、CNN 模型架構說明 本範例的模型結構如下: ```text 輸入影像:1 × 28 × 28 Conv2d(1, 32, 3) ReLU MaxPool2d(2) ↓ 32 × 14 × 14 Conv2d(32, 64, 3) ReLU MaxPool2d(2) ↓ 64 × 7 × 7 Flatten Linear(64 × 7 × 7, 128) ReLU Dropout(0.5) Linear(128, 10) ``` ### 1. 卷積層 `Conv2d` 卷積層用於擷取影像特徵,例如: - 邊緣 - 線條 - 轉折 - 局部形狀 第一層從單一灰階通道產生 32 個特徵圖,第二層再將特徵數量增加到 64。 ### 2. 池化層 `MaxPool2d` 池化層會降低影像的空間大小: ```text 28 × 28 → 14 × 14 → 7 × 7 ``` 這可以: - 減少計算量 - 降低模型參數數量 - 增加模型對小幅位移的容忍度 ### 3. ReLU 激活函數 ```python nn.ReLU() ``` ReLU 可以增加模型的非線性表達能力,使 CNN 能學習較複雜的影像特徵。 ### 4. Dropout ```python nn.Dropout(0.5) ``` 訓練時隨機忽略部分神經元,降低過度擬合的可能性。 ### 5. 最後一層 ```python nn.Linear(128, 10) ``` 輸出 10 個數值,分別代表輸入影像屬於數字 `0~9` 的分數。 這裡不需要自行加上 `Softmax`,因為: ```python nn.CrossEntropyLoss() ``` 內部已經包含適當的 LogSoftmax 計算。 --- ## 五、訓練流程中的關鍵步驟 ### 1. 前向傳播 ```python outputs = model(images) ``` 模型接收影像後,計算每個類別的預測分數。 ### 2. 計算損失 ```python loss = criterion(outputs, labels) ``` 使用交叉熵損失函數,比較模型預測結果與正確答案之間的差異。 ### 3. 反向傳播 ```python loss.backward() ``` 根據損失函數計算各個參數的梯度。 ### 4. 更新參數 ```python optimizer.step() ``` 使用 Adam 優化器更新模型參數,使損失逐漸下降。 ### 5. 清除梯度 ```python optimizer.zero_grad() ``` PyTorch 預設會累積梯度,因此每一批資料開始前都要先清除上一批次的梯度。 --- ## 六、重要設定說明 | 設定 | 本範例值 | 說明 | |---|---:|---| | Batch size | 64 | 每次更新模型使用 64 張影像 | | Epochs | 5 | 將完整訓練資料重複訓練 5 次 | | Learning rate | 0.001 | 控制模型參數更新幅度 | | Optimizer | Adam | 常用且容易訓練的最佳化器 | | Loss function | CrossEntropyLoss | 適合多分類問題 | | Validation split | 10% | 用於選擇最佳模型 | | Dropout | 0.5 | 降低過度擬合 | 在一般電腦或 GPU 上,這個模型通常可以達到約 **98%~99% 的 MNIST 測試準確率**,實際結果會受到硬體、隨機種子與訓練次數影響。 --- ## 七、模型評估的意義 最後使用沒有參與訓練的測試資料: ```python test_loss, test_accuracy = evaluate( model, test_loader, criterion, device ) ``` 其中: - `Test Loss`:模型在測試資料上的平均損失 - `Test Accuracy`:預測正確的比例 例如: ```text Test Accuracy: 98.40% ``` 表示 10,000 張測試影像中,大約有 9,840 張被正確分類。 實務上除了準確率,也可以進一步計算: - Precision - Recall - F1-score - Confusion Matrix 以了解模型是否特別容易混淆某些數字,例如 `4` 和 `9`。
相關學習地圖、教學課程
Python 人工智慧
建議完成「Python 資料工程」教程後,繼續學習以下課程。