pytorch實(shí)現(xiàn)手寫數(shù)字識(shí)別

image.png

簡介

一個(gè)典型的神經(jīng)網(wǎng)絡(luò)訓(xùn)練過程包括以下幾點(diǎn):

  • 1.定義一個(gè)包含可訓(xùn)練參數(shù)的神經(jīng)網(wǎng)絡(luò)
  • 2.迭代整個(gè)輸入
  • 3.通過神經(jīng)網(wǎng)絡(luò)處理輸入
  • 4.計(jì)算損失(loss)
  • 5.反向傳播梯度到神經(jīng)網(wǎng)絡(luò)的參數(shù)
  • 6.更新網(wǎng)絡(luò)的參數(shù),典型的用一個(gè)簡單的更新方法:weight = weight - learning_rate *gradient

原理

源碼

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 為了實(shí)現(xiàn)手寫數(shù)字識(shí)別(MNIST數(shù)據(jù)集)使用PyTorch,我們需要完成以下幾個(gè)步驟:
# 加載和預(yù)處理MNIST數(shù)據(jù)集。
# 定義神經(jīng)網(wǎng)絡(luò)模型。
# 設(shè)置損失函數(shù)和優(yōu)化器。
# 訓(xùn)練模型。
# 測試模型性能。


# 數(shù)據(jù)預(yù)處理
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

# 下載訓(xùn)練集和測試集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

# 創(chuàng)建DataLoader
batch_size = 64
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# 定義簡單的卷積神經(jīng)網(wǎng)絡(luò)
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # --------卷積層的作用是將輸入圖像中的像素值與卷積核中的像素值進(jìn)行相乘,得到特征圖。-----------------
        # in_channels=1: 輸入通道數(shù)。對于MNIST數(shù)據(jù)集,每個(gè)圖像都是灰度圖,因此輸入通道數(shù)為1。
        # out_channels=32: 輸出通道數(shù)。這是卷積層產(chǎn)生的特征圖的數(shù)量。這里設(shè)置為32。
        # kernel_size=3: 卷積核(濾波器)的大小。這里是3x3的卷積核。
        # stride=1: 步幅(步長)。表示卷積操作在輸入上移動(dòng)的步數(shù)。這里設(shè)置為1,意味著每次卷積操作會(huì)在輸入上向右和向下各移動(dòng)一步。
        # padding=1: 填充。在輸入周圍添加額外的像素,以控制輸出的空間維度。這里設(shè)置為1,這意味著在輸入圖像的上下左右各填充一個(gè)像素,使得輸入和輸出的高度和寬度相同
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)   
        # in_channels=32: 輸入通道數(shù)。這個(gè)值必須等于前一層的輸出通道數(shù),即第一層卷積層的輸出通道數(shù)32。
        # out_channels=64: 輸出通道數(shù)。這是第二層卷積層產(chǎn)生的特征圖的數(shù)量。這里設(shè)置為64。
        # kernel_size=3: 卷積核的大小仍然是3x3。
        # stride=1: 步幅依然是1。
        # padding=1: 填充同樣是1,保持輸出空間維度不變。
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
        # --------最大池化層的作用是從每個(gè)2x2的區(qū)域中選擇最大的值,從而減少特征圖的空間維度,同時(shí)保留最重要的信息。-----------------
        # kernel_size=2: 池化窗口的大小。這里是2x2的窗口。
        # stride=2: 步幅(步長)。表示池化操作在輸入上移動(dòng)的步數(shù)。這里設(shè)置為2,意味著每次池化操作會(huì)在輸入上向右和向下各移動(dòng)兩步。
        # padding=0: 填充。在輸入周圍添加額外的像素,以控制輸出的空間維度。這里設(shè)置為0,沒有填充。
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)

        # in_features=64 * 7 * 7: 輸入特征的數(shù)量。這個(gè)值計(jì)算如下:
        # 經(jīng)過兩個(gè)卷積層后,特征圖的通道數(shù)變?yōu)?4。
        # 每個(gè)卷積層后面有一個(gè)最大池化層,將特征圖的空間維度減半。
        # 初始輸入圖像大小為28x28,經(jīng)過第一次卷積和池化后變?yōu)?4x14,再經(jīng)過第二次卷積和池化后變?yōu)?x7。
        # 因此,最終的特征圖大小為64x7x7。
        # out_features=128: 輸出特征的數(shù)量。這里設(shè)置為128,表示第一層全連接層有128個(gè)神經(jīng)元。
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        # in_features=128: 輸入特征的數(shù)量,等于前一層全連接層的輸出特征數(shù)量128。
        # out_features=10: 輸出特征的數(shù)量。這里設(shè)置為10,因?yàn)镸NIST數(shù)據(jù)集有10個(gè)類別(數(shù)字0到9),因此最后一層全連接層的輸出對應(yīng)于每個(gè)類別的概率。
        self.fc2 = nn.Linear(128, 10)
        # ReLU激活函數(shù): 引入非線性,增強(qiáng)模型的學(xué)習(xí)能力。
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.pool(self.relu(self.conv1(x)))
        x = self.pool(self.relu(self.conv2(x)))
        x = x.view(-1, 64 * 7 * 7)
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 初始化模型、損失函數(shù)和優(yōu)化器
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓(xùn)練模型
def train_model(model, train_loader, criterion, optimizer, num_epochs=5):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for images, labels in train_loader:
            optimizer.zero_grad()
            outputs = model(images)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')

# 測試模型
def test_model(model, test_loader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for images, labels in test_loader:
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = 100 * correct / total
    print(f'Test Accuracy: {accuracy:.2f}%')

# 開始訓(xùn)練和測試
train_model(model, train_loader, criterion, optimizer)
test_model(model, test_loader)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容