沖沖沖😊
here😊
文章目錄
- PyTorch多層感知機模型構建與MNIST分類訓練筆記
- 🎯 1. 任務概述
- ?? 2. 環境設置
- 2.1 導入必要庫
- 2.2 GPU配置
- 🧠 3. 模型構建
- 3.1 模型定義關鍵點
- 3.2 損失函數選擇
- 3.3 模型初始化與設備選擇
- 🔧 4. 優化器配置
- 4.1 隨機梯度下降優化器
- 🔄 5. 訓練循環實現
- 5.1 訓練函數設計
- 5.2 測試函數設計
- 📦 6. 數據準備
- 6.1 加載MNIST數據集
- 🚀 7. 訓練執行
- 7.1 訓練循環主體
- 7.2 訓練過程輸出(部分)
- 📊 8. 結果可視化
- 8.1 損失曲線繪制
- 8.2 準確率曲線繪制
PyTorch多層感知機模型構建與MNIST分類訓練筆記
🎯 1. 任務概述
解決MNIST手寫數字分類問題,創建一個簡單的多層感知機(MLP)模型
- 使用
torch.nn.Linear
層構建模型- 使用ReLU作為激活函數
- 包含兩個全連接隱藏層(120和84個神經元)和輸出層(10個神經元對應10個數字類別)
- 模型輸入為展平后的28×28=784像素圖像
?? 2. 環境設置
2.1 導入必要庫
import torch
from torch import nn
import os
2.2 GPU配置
# os.environ["CUDA_VISIBLE_DEVICES"] = "3,4,6" # 只使用空閑的GPU
🧠 3. 模型構建
3.1 模型定義關鍵點
class Model(nn.Module):def __init__(self):super().__init__()# 第一層輸入展平后的特征長度28乘28,創建120個神經元self.liner_1 = nn.Linear(28*28, 120)# 第二層輸入的是前一層的輸出,創建84個神經元self.liner_2 = nn.Linear(120, 84)# 輸出層接受第二層的輸入84,輸出分類個數10self.liner_3 = nn.Linear(84, 10)def forward(self, input):x = input.view(-1, 28*28) # 將輸入展平為二維(1,28,28)->(28*28)x = torch.relu(self.liner_1(x))x = torch.relu(self.liner_2(x))x = self.liner_3(x)return x
📝 模型結構說明:
- 輸入層:將28×28圖像展平為784維向量
- 隱藏層1:120個神經元,使用ReLU激活
- 隱藏層2:84個神經元,使用ReLU激活
- 輸出層:10個神經元對應10個數字類別
3.2 損失函數選擇
loss_fn = nn.CrossEntropyLoss() # 交叉熵損失函數
'''
注意兩個參數
1. weight: 各類別的權重(處理不平衡數據集)
2. ignore_index: 忽略特定類別的索引
另外,它要求實際類別為數值編碼,而不是獨熱編碼
'''
🔍 為什么選擇交叉熵損失?
- 適用于多分類問題
- 內部集成了Softmax計算,簡化實現流程
- 對錯誤分類有較強的懲罰
3.3 模型初始化與設備選擇
device = "cuda" if torch.cuda.is_available() else "cpu"
model = Model().to(device)
# print(device) # 可選:打印使用的設備
💡 GPU加速提示:
使用.to(device)
將模型移動到GPU可顯著加快訓練速度,特別是對于大模型和大數據集
🔧 4. 優化器配置
4.1 隨機梯度下降優化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.005)
🔧 關鍵參數解析:
params
: 需要優化的模型參數(通常為model.parameters()
)lr=0.005
: 學習率,控制參數更新步長的超參數- 其他可選參數:
momentum
(動量),weight_decay
(L2正則化)
🔄 5. 訓練循環實現
5.1 訓練函數設計
def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset) # 獲取當前數據集樣本總數量num_batches = len(dataloader) # 獲取當前data loader總批次數# train_loss用于累計所有批次的損失之和, correct用于累計預測正確的樣本總數train_loss, correct = 0, 0for X, y in dataloader:X, y = X.to(device), y.to(device)# 進行預測,并計算當前批次的損失pred = model(X)loss = loss_fn(pred, y)# 利用反向傳播算法,根據損失優化模型參數optimizer.zero_grad() # 先將梯度清零loss.backward() # 損失反向傳播,計算模型參數梯度optimizer.step() # 根據梯度優化參數with torch.no_grad():# correct用于累計預測正確的樣本總數correct += (pred.argmax(1) == y).type(torch.float).sum().item()# train_loss用于累計所有批次的損失之和train_loss += loss.item()# train_loss 是所有批次的損失之和,所以計算全部樣本的平均損失時需要除以總的批次數train_loss /= num_batches# correct 是預測正確的樣本總數,若計算整個epoch總體正確率,需要除以樣本總數量correct /= sizereturn train_loss, correct
5.2 測試函數設計
def test(dataloader, model):size = len(dataloader.dataset)num_batches = len(dataloader)test_loss, correct = 0, 0with torch.no_grad():for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizereturn test_loss, correct
📊 數據加載器相關方法區別:
方法 返回內容 適用場景 len(dataset)
數據集總樣本數(如100) 數據統計、劃分 len(dataloader)
總批次數(如4) 訓練循環控制 len(dataloader.dataset)
等同于 len(dataset)
需要訪問原始數據時
📦 6. 數據準備
6.1 加載MNIST數據集
import torchvision
from torchvision.transforms import ToTensortrain_ds = torchvision.datasets.MNIST("data/", train=True, transform=ToTensor(), download=True)
test_ds = torchvision.datasets.MNIST("data/", train=False, transform=ToTensor(), download=True)train_dl = torch.utils.data.DataLoader(train_ds, batch_size=64, shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds, batch_size=64)
🚀 7. 訓練執行
7.1 訓練循環主體
# 對全部的數據集訓練50個epoch(一個epoch表示對全部數據訓練一遍)
epochs = 50
train_loss, train_acc = [], []
test_loss, test_acc = [], []for epoch in range(epochs):# 調用train()函數訓練epoch_loss, epoch_acc = train(train_dl, model, loss_fn, optimizer)# 調用test()函數測試epoch_test_loss, epoch_test_acc = test(test_dl, model)train_loss.append(epoch_loss)train_acc.append(epoch_acc)test_loss.append(epoch_test_loss)test_acc.append(epoch_test_acc)# 定義一個打印模板template = ("epoch:{:2d},train_loss:{:.6f},train_acc:{:.1f}%,""test_loss:{:.5f},test_acc:{:.1f}%")print(template.format(epoch, epoch_loss, epoch_acc*100, epoch_test_loss, epoch_test_acc*100))print("Done")
7.2 訓練過程輸出(部分)
epoch: 0,train_loss:2.157364,train_acc:46.7%,test_loss:1.83506,test_acc:63.7%
epoch: 1,train_loss:1.222660,train_acc:74.3%,test_loss:0.74291,test_acc:81.8%
epoch: 2,train_loss:0.612381,train_acc:84.0%,test_loss:0.49773,test_acc:86.3%
...
epoch:48,train_loss:0.110716,train_acc:96.9%,test_loss:0.12003,test_acc:96.4%
epoch:49,train_loss:0.108877,train_acc:97.0%,test_loss:0.11783,test_acc:96.5%
Done
📈 訓練趨勢分析:
- 初始準確率:46.7%(訓練集),63.7%(測試集)
- 最終準確率:97.0%(訓練集),96.5%(測試集)
- 過擬合現象輕微:訓練集和測試集性能差距僅0.5%
📊 8. 結果可視化
8.1 損失曲線繪制
import matplotlib.pyplot as pltplt.plot(range(1, epochs+1), train_loss, label="train_loss")
plt.plot(range(1, epochs+1), test_loss, label="test_loss", ls="--")
plt.xlabel("epoch")
plt.legend()
plt.show()
注釋:損失曲線顯示訓練初期損失快速下降,后期趨于平穩
8.2 準確率曲線繪制
plt.plot(range(1, epochs+1), train_acc, label="train_acc")
plt.plot(range(1, epochs+1), test_acc, label="test_acc")
plt.xlabel("epoch")
plt.legend()
plt.show()
注釋:準確率曲線穩步上升,最終達到96.5%的測試準確率