实现MNIST手写数字识别-P1周

Pytorch实现MNIST手写数字识别

📌第P1周:实现mnist手写数字识别📌

难度:小白入门⭐
语言:Python3、Pytorch

🍺 要求:
了解Pytorch,并使用Pytorch构建一个深度学习程序
了解什么是深度学习

🍻拔高(可选)
学习文中提到的函数方法
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

device
# device(type='cpu')
train_ds = torchvision.datasets.MNIST('data',
                                      train=True,
                                      transform=torchvision.transforms.ToTensor(), # Change the data type to tensor
                                      download=True)
test_ds  = torchvision.datasets.MNIST('data', 
                                      train=False, 
                                      transform=torchvision.transforms.ToTensor(), # Change the data type to tensor
                                      download=True)
batch_size = 32

train_dl = torch.utils.data.DataLoader(train_ds,
                                       batch_size=batch_size,
                                       shuffle=True)
test_dl = torch.utils.data.DataLoader(test_ds,
                                      batch_size=batch_size)
imgs, labels = next(iter(train_dl))
imgs.shape
# [batch_size, channel, height, weight]
import numpy as np
plt.figure(figsize=(20, 5))
for i, imgs in enumerate(imgs[:20]):
    # squeeze the dimention
    npimg = np.squeeze(imgs.numpy())
    # plot the images
    plt.subplot(2, 10, i+1)
    plt.imshow(npimg, cmap=plt.cm.binary)
    plt.axis("off")

在这里插入图片描述
构建网络

import torch.nn.functional as F

num_classes = 10

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        # Feature Extraction Network
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3) # (input_channel, output_channel, kernel_size)
        self.pool1 = nn.MaxPool2d(2) # Max Pooling Layer
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
        self.pool2 = nn.MaxPool2d(2)
        
        # Classification Network
        self.fc1 = nn.Linear(1600, 64)
        self.fc2 = nn.Linear(64, num_classes)
        
    def forward(self, x):
        x = self.pool1(F.relu(self.conv1(x)))
        x = self.pool2(F.relu(self.conv2(x)))
        
        x = torch.flatten(x, start_dim=1)
        
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        
        return x
from torchinfo import summary
model = Model().to(device)

summary(model)

在这里插入图片描述

# Loss function
loss_fn = nn.CrossEntropyLoss()
learn_rate = 1e-2
# optimizer
opt = torch.optim.SGD(model.parameters(), lr=learn_rate)
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset) # 60000
    num_batches = len(dataloader) # 60000 / 32 = 1875
    
    train_loss, train_acc = 0, 0
    for X, y in dataloader:
        X, y = X.to(device), y.to(device)
        
        # calculate the prediction error
        pred = model(X) # output of the Network
        loss = loss_fn(pred, y) # calculate the Loss based on the loss function
        
        # Backword
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # Record the acc and loss
        train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
        
    train_acc /= size
    train_loss /= num_batches
    
    return train_acc, train_loss
def test(dataloader, model, loss_fn):
    size = len(dataloader.dataset) # 10000
    num_batches = len(dataloader) # 10000 / 32 = 313
    
    test_loss, test_acc = 0, 0
    # Stop the gradient calculation
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # Loss
            pred = model(imgs)
            loss = loss_fn(pred, target)
            
            test_loss += loss.item()
            test_acc += (pred.argmax(1) == target).type(torch.float).sum().item()
    
    test_acc /= size
    test_loss /= num_batches
    
    return test_acc, test_loss
epochs = 5
train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []

for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')

在这里插入图片描述
这里发现第一轮测试集精度高于训练集精度:
训练集的准确率是每个batch之后产生的,而验证集的准确率一般是一个epoch后产生的,验证时的模型是训练一个个batch之后的,有一个滞后性,可以说就是用训练得差不多的模型用来验证,所以准确率要高一点。

  • model.train()
    model.train() is used to start Batch Normalization and Dropout
    If there is a BN layer or Dropout layer in the model, we need to set model to train() mode.
    It ensure that BN layer can use the mean and variance of data from each batch.
  • model.eval()
    When model is at this mode, BN layer is set to use the mean and variance from all data of the training set
import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
# plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
# plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LoveData_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值