欢迎光临散文网 会员登陆 & 注册

Pytorch 手写数字识别MNIST

2023-01-05 15:07 作者:永远的修伊  | 我要投稿

内容来自老师授课,为了方便个人学习,自己手敲了一遍

老师的编写的代码有独特的风格,它将整个workflow分为几三个文件,一个是模型文件(CNN.py),用来定义网络结构(模型),一个是训练集文件(train.py),用来训练模型,还有一个测试集文件(test.py),用来加载训练模型并在测试集上验证。

测试集输出

模型文件 CNN.py

from torch import nn


class SimpleCNN(nn.Module):
   def __init__(self):
       super(SimpleCNN, self).__init__()
       self.layer1 = nn.Sequential(
           nn.Conv2d(1, 16, kernel_size=3),
           nn.BatchNorm2d(16),
           nn.ReLU(inplace=True)
       )

       self.layer2 = nn.Sequential(
           nn.Conv2d(16, 32, kernel_size=3),
           nn.BatchNorm2d(32),
           nn.ReLU(inplace=True),
           nn.MaxPool2d(kernel_size=2, stride=2)
       )

       self.layer3 = nn.Sequential(
           nn.Conv2d(32, 64, kernel_size=3),
           nn.BatchNorm2d(64),
           nn.ReLU(inplace=True)
       )

       self.layer4 = nn.Sequential(
           nn.Conv2d(64, 128, kernel_size=3),
           nn.BatchNorm2d(128),
           nn.ReLU(inplace=True),
           nn.MaxPool2d(kernel_size=2, stride=2)
       )

       self.fc = nn.Sequential(
           nn.Linear(128 * 4 * 4, 1024),
           nn.ReLU(inplace=True),
           nn.Linear(1024, 128),
           nn.ReLU(inplace=True),
           nn.Linear(128, 10)
       )

   def forward(self, x):
       x = self.layer1(x)
       x = self.layer2(x)
       x = self.layer3(x)
       x = self.layer4(x)
       x = x.reshape(x.size(0), -1)
       fc_out = self.fc(x)
       return fc_out


训练模型 train.py

import torch
import CNN
from torch import nn, optim
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader

# 定义超参数
learning_rate = 1e-2  # 学习率
batch_size = 128  # 批处理的样本大小
epoches_num = 20  # 遍历训练集次数

# 下载训练集 MNIST 手写数字识别
train_dataset = datasets.MNIST(root="./data", train=True, transform=transforms.ToTensor(), download=True)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

# 定义model,loss,optimizer
model = CNN.SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

if torch.cuda.is_available():
   print("CUDA is enable! GPU is available!")
   model = model.cuda()
   model.train()

# 开始训练
for epoch in range(epoches_num):
   print('*' * 40)
   running_loss = 0.0
   running_acc = 0.0

   # 训练
   for i, data in enumerate(train_loader, 1):
       img, label = data

       # GPU训练
       if torch.cuda.is_available():
           img = torch.Tensor(img).cuda()
           label = Variable(label).cuda()
       else:
           img = Variable(img)
           label = Variable(label)

       # 前向传播
       out = model(img)
       loss = criterion(out, label)
       running_loss += loss.item() * label.size(0)
       _, pred = torch.max(out, 1)
       num_correct = (pred == label).sum()
       accuracy = (pred == label).float().mean()
       running_acc += num_correct.item()

       # 反向传播
       optimizer.zero_grad()
       loss.backward()
       optimizer.step()

   print(
       f"Finish {epoch + 1} Loss: {running_loss / len(train_dataset) :.6f}, Acc: {running_acc / len(train_dataset) :.6f}")

# 保存模型
torch.save(model, 'cnn.pt')

测试并验证 test.py

import torch
from torch import nn
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader

# 定义超参数
batch_size = 128     # 批的大小

# 下载数据集 MNIST 手写数字测试集
test_dataset = datasets.MNIST(root="./data", train=False, transform=transforms.ToTensor())
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True)

# 加载 Train 模型
model = torch.load("cnn.pt")
criterion = nn.CrossEntropyLoss()
model.eval()
eval_acc = 0
eval_loss = 0

# 测试
for data in test_loader:
   img, label = data
   if torch.cuda.is_available():
       img = Variable(img).cuda()
       label = Variable(label).cuda()
   else:
       img = Variable(img)
       label = Variable(label)

   out = model(img)
   loss = criterion(out, label)
   eval_loss += loss.item() * label.size(0)

   _, pred = torch.max(out, 1)
   num_correct = (pred == label).sum()
   eval_acc += num_correct.item()

print(f'Test Loss:{eval_loss/len(test_dataset) :.6f}, Acc: {eval_acc/len(test_dataset) :.6f}')

当然,写在同一个文件里也没有任何问题,主要是熟悉deep learning中加载数据集、训练网络、模型验证的工作流,作为初学者熟悉基本的操作。因为现有的深度学习框架已经封装了相应的api,比如反向传播、随机梯度下降算法、损失函数、激活函数等等,可能不太care底层的算法,但是需要使用者自定义model,选择超参数,定义网络结构。这里还附上在知乎上学的一份代码。

ministpytorch.py

import torch
import torchvision
from torch.utils.data import DataLoader

# load dataset
n_epochs = 3
batch_size_train = 64
batch_size_test = 1000
learning_rate = 0.01
momentum = 0.5
log_interval = 10
random_seed = 1
torch.manual_seed(random_seed)  # 实验可重复

train_loader = torch.utils.data.DataLoader(
   torchvision.datasets.MNIST("./data/", train=True, download=True,
                              transform=torchvision.transforms.Compose([
                                  torchvision.transforms.ToTensor(),
                                  torchvision.transforms.Normalize((0.1307,), (0.3081,))
                              ])),
   batch_size=batch_size_train, shuffle=True
)

test_loader = torch.utils.data.DataLoader(
   torchvision.datasets.MNIST("./data/", train=False, download=True,
                              transform=torchvision.transforms.Compose([
                                  torchvision.transforms.ToTensor(),
                                  torchvision.transforms.Normalize((0.1307,), (0.3081,))
                              ])),
   batch_size=batch_size_test, shuffle=True
)

examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
# print(example_targets)
# print(example_data.shape)

import matplotlib.pyplot as plt

# fig = plt.figure()
# for i in range(6):
#     plt.subplot(2, 3, i + 1)
#     plt.tight_layout()
#     plt.imshow(example_data[i][0], cmap="gray", interpolation='none')
#     plt.title(f"Ground Truth: {example_targets[i]}")
#     plt.xticks([])
#     plt.yticks([])
# plt.show()

# create a neural network
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class Net(nn.Module):
   def __init__(self):
       super(Net, self).__init__()
       self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
       self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
       self.conv2_drop = nn.Dropout2d()
       self.fc1 = nn.Linear(320, 50)
       self.fc2 = nn.Linear(50, 10)


   def forward(self,x):
       x = F.relu(F.max_pool2d(self.conv1(x), 2))
       x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
       x = x.view(-1, 320)
       x = F.relu(self.fc1(x))
       x = F.dropout(x, training=self.training)
       x = self.fc2(x)
       return F.log_softmax(x)


network = Net()
optimizer = optim.SGD(network.parameters(), lr=learning_rate, momentum=momentum)

train_losses = []
train_counter = []
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]

def train(epoch):
   network.train()
   for batch_idx, (data, target) in enumerate(train_loader):
       optimizer.zero_grad()
       output = network(data)
       loss = F.nll_loss(output, target)
       loss.backward()
       optimizer.step()
       if batch_idx % log_interval == 0:
           print(f"Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} "
                 f"{100. * batch_idx / len(train_loader) :.0f}%]\tLoss:{loss.item() :.6f}")
           train_losses.append(loss.item())
           train_counter.append(
               (batch_idx*64) + ((epoch-1) * len(train_loader.dataset))
           )
           torch.save(network.state_dict(), './model.pth')
           torch.save(optimizer.state_dict(), './optimizer.pth')

# train(1)


def test():
   network.eval()
   test_loss = 0
   correct = 0
   with torch.no_grad():
       for data, target in test_loader:
           output = network(data)
           test_loss += F.nll_loss(output, target, size_average=False).item()
           pred = output.data.max(1, keepdim=True)[1]
           correct += pred.eq(target.data.view_as(pred)).sum()
   test_loss /= len(test_loader.dataset)
   test_losses.append(test_loss)
   print(f"\nTest set: Avg. loss:{test_loss :.4f}, Accuracy: {correct}/{len(test_loader.dataset)} "
         f"({100. * correct / len(test_loader.dataset) :.0f}%)")


test()

for epoch in range(1,n_epochs + 1):
   train(epoch)
   test()


import matplotlib.pyplot as plt

fig = plt.figure
plt.plot(train_counter, train_losses, color="blue")
plt.scatter(test_counter, test_losses, color="red")
plt.legend(["Train_loss", "test_loss"], loc="upper right")
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likehood loss')
plt.show()



examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
with torch.no_grad():
   output = network(example_data)
fig = plt.figure()
for i in range(6):
   plt.subplot(2, 3, 1+i)
   plt.tight_layout()
   plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
   plt.title(f'Prediction: {output.data.max(1, keepdim=True)[1][i].item()}')
   plt.xticks([])
   plt.yticks([])
plt.show()


continuted_network = Net()
continuted_optimizer = optim.SGD(network.parameters(), lr=learning_rate,momentum=momentum)


network_state_dict = torch.load('model.pth')
continuted_network.load_state_dict(network_state_dict)
optimizer_state_dict = torch.load('optimizer.pth')
continuted_optimizer.load_state_dict(optimizer_state_dict)

for i in range(4, 9):
   test_counter.append(i*len(train_loader.dataset))
   train(i)
   test()


fig = plt.figure()
plt.plot(train_counter, train_losses, color="blue")
plt.scatter(test_counter, test_losses, color="red")
plt.legend(['Train Loss', 'Test Loss'], loc='upper right')
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likelihood loss')
plt.show()



Pytorch 手写数字识别MNIST的评论 (共 条)

分享到微博请遵守国家法律