一、準備工作
導入庫,導入數據集,劃分訓練批次數量,規定訓練硬件(這部分
import torch
from torch import nn # 導入神經網絡模塊
from torch.utils.data import DataLoader # 數據包管理工具,打包數據
from torchvision import datasets # 封裝了很多與圖像相關的模型,和數據集
from torchvision.transforms import ToTensor # 將其他數據類型轉化為張量train_data = datasets.MNIST(root='data',train=True, # 是否讀取下載后數據中的訓練集download=True, # 如果之前下載過則不用下載transform=ToTensor()
)
test_data = datasets.MNIST(root='data',train=False,download=True,transform=ToTensor()
)train_dataloader = DataLoader(train_data,batch_size=256)#是一個類,現在初始化了,但沒開始打包,訓練開始才打包
test_dataloader = DataLoader(test_data,batch_size=256)device = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'Using {device} device')
二、定義神經網絡(重點)
這部分比較重要,我分開講
1、類定義與繼承
class CNN(nn.Module):
這里定義了一個名為CNN
的類,它繼承自 PyTorch 的nn.Module
類。nn.Module
是 PyTorch 中所有神經網絡模塊的基類,通過繼承它,我們可以利用 PyTorch 提供的各種功能,如參數管理、設備遷移等。
2、初始化方法
def __init__(self):super().__init__()
這是類的構造函數,super().__init__()
調用了父類nn.Module
的構造函數,確保父類得到正確初始化。
3、網絡層定義
- 第一個卷積塊(conv1)
self.conv1 = nn.Sequential(nn.Conv2d(in_channels=1, # 輸入通道數,1表示灰度圖像out_channels=16, # 輸出通道數/卷積核數量kernel_size=5, # 卷積核大小5×5stride=1, # 步長為1padding=2, # 填充為2,保持特征圖大小不變),nn.ReLU(), # ReLU激活函數nn.MaxPool2d(kernel_size=2), # 2×2最大池化
)
這個卷積塊接收 1 通道的輸入,通過 16 個 5×5 的卷積核進行卷積操作,然后經過 ReLU 激活和 2×2 的最大池化。
- 第二個卷積塊(conv2)
self.conv2 = nn.Sequential(nn.Conv2d(16,32,5,1,2), # 16→32通道,5×5卷積核nn.ReLU(),nn.Conv2d(32,32,5,1,2), # 32→32通道,5×5卷積核nn.ReLU(),nn.Conv2d(32,32,5,1,2), # 32→32通道,5×5卷積核nn.ReLU(),nn.Conv2d(32,64,5,1,2), # 32→64通道,5×5卷積核nn.ReLU(),nn.Conv2d(64,64,5,1,2), # 64→64通道,5×5卷積核nn.ReLU(),nn.MaxPool2d(kernel_size=2), # 2×2最大池化
)
這個卷積塊包含多個卷積層,逐步增加通道數,并在最后進行一次最大池化。
- 第三個卷積塊(conv3)
self.conv3 = nn.Sequential(nn.Conv2d(64,64,5,1,2), # 64→64通道,5×5卷積核nn.ReLU(),
)
這是一個簡單的卷積塊,保持通道數不變。
- 全連接層(out)
self.out = nn.Linear(64*7*7,10)
這是網絡的輸出層,將卷積得到的特征圖展平后映射到 10 個輸出(可能對應 10 類分類問題)。
4、前向傳播方法
def forward(self, x):x = self.conv1(x) # 通過第一個卷積塊x = self.conv2(x) # 通過第二個卷積塊x = self.conv3(x) # 通過第三個卷積塊x = x.view(x.size(0),-1) # 展平特征圖,保留批次維度output = self.out(x) # 通過全連接層得到輸出return output
forward
方法定義了數據在網絡中的流動路徑,即前向傳播過程。x.view(x.size(0),-1)
將卷積操作得到的多維特征圖展平成一維向量,以便輸入到全連接層。
5、模型實例化
model = CNN().to(device)
創建 CNN 類的實例,并將模型遷移到指定的設備(CPU 或 GPU)上。
完整代碼:
定義神經網絡,通過類的繼承
class CNN(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Sequential(#容器,添加網絡層nn.Conv2d(in_channels=1,out_channels = 16,kernel_size = 5,stride = 1,padding = 2,),nn.ReLU(),nn.MaxPool2d(kernel_size = 2),)self.conv2 = nn.Sequential( # 容器,添加網絡層nn.Conv2d(16,32,5,1,2),nn.ReLU(),nn.Conv2d(32, 32, 5, 1, 2),nn.ReLU(),nn.Conv2d(32, 32, 5, 1, 2),nn.ReLU(),nn.Conv2d(32,64,5,1,2),nn.ReLU(),nn.Conv2d(64, 64, 5, 1, 2),nn.ReLU(),nn.MaxPool2d(kernel_size=2),)self.conv3 = nn.Sequential(nn.Conv2d(64,64,5,1,2),nn.ReLU(),)self.out = nn.Linear(64*7*7,10)def forward(self, x): # 前向傳播x = self.conv1(x)x = self.conv2(x)x = self.conv3(x)x = x.view(x.size(0),-1)output = self.out(x)return output
model = CNN().to(device)
三、模型的訓練
這一段和上一個博客的步驟一樣這里就不多做講解了
不了解的直接看
深度學習----由手寫數字識別案例來認識PyTorch框架-CSDN博客
def train(dataloader, model, loss_fn, optimizer):model.train() # 開啟模型訓練模式,像 Dropout、BatchNorm 層會在訓練/測試時表現不同,需此設置batch_size_num = 1 # 用于計數當前處理到第幾個 batchfor X, y in dataloader: # 從數據加載器中逐個取出 batch 的數據(特征 X、標簽 y )X, y = X.to(device), y.to(device) # 把數據和標簽放到指定計算設備(CPU/GPU)pred = model(X) # 將數據輸入模型,得到預測結果(模型自動做前向傳播計算 )loss = loss_fn(pred, y) # 用損失函數計算預測結果和真實標簽的損失# 以下是反向傳播更新參數的標準流程optimizer.zero_grad() # 清空優化器里參數的梯度,避免梯度累加影響計算loss.backward() # 反向傳播,計算參數的梯度optimizer.step() # 根據梯度,更新模型參數loss = loss.item() # 取出損失張量的數值(脫離計算圖 )# 打印當前 batch 的損失和 batch 編號if batch_size_num % 100 == 0:print(f"loss:{loss:>7f} [number:{batch_size_num}")batch_size_num += 1 # batch 計數加一def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()test_loss,correct = 0,0with torch.no_grad():for X , y in dataloader:X ,y = X.to(device),y.to(device)pred = model.forward(X)#.forward可以被省略test_loss += loss_fn(pred,y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()a = (pred.argmax(1) == y)b = (pred.argmax(1) == y).type(torch.float)test_loss /= num_batchescorrect /= sizeprint(f"test result: \n Accuracy: {(100*correct)}%,Avg loss:{test_loss}")# print(list(model.parameters()))
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr=0.001)#換優化器可以提高準確率,Adam,SGD等# train(train_dataloader,model,loss_fn,optimizer)
# test(test_dataloader,model,loss_fn)
#epochs = 10
for t in range(epochs):print(f"輪次:{t+1}\n----------------------------")train(train_dataloader,model,loss_fn,optimizer)
print("Done")
test(test_dataloader,model,loss_fn)