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&#​34;, 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('*&#​39; * 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&#​39;)


测试并验证 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&#​34;, train=False, transform=transforms.ToTensor()) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True) # 加载 Train 模型 model = torch.load("cnn.pt&#​34;) 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/&#​34;, 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/&#​34;, 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&#​34;, interpolation='none&#​39;) #     plt.title(f&#​34;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&#​39;)            torch.save(optimizer.state_dict(), './optimizer.pth&#​39;) # 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&#​34;) plt.scatter(test_counter, test_losses, color="red&#​34;) plt.legend(["Train_loss&#​34;, "test_loss&#​34;], loc="upper right&#​34;) plt.xlabel('number of training examples seen') plt.ylabel('negative log likehood loss&#​39;) 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&#​39;, interpolation='none&#​39;)    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&#​39;) continuted_network.load_state_dict(network_state_dict) optimizer_state_dict = torch.load('optimizer.pth&#​39;) 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&#​34;) plt.scatter(test_counter, test_losses, color="red&#​34;) plt.legend(['Train Loss&#​39;, 'Test Loss&#​39;], loc='upper right&#​39;) plt.xlabel('number of training examples seen') plt.ylabel('negative log likelihood loss&#​39;) plt.show()