Pytorch——利用Pytorch进行手写数字分类模型搭建

176 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情


在上一篇文章中,我们介绍神经网络的基本概念,包括感知器的基本概念,前向计算,反向传播,分类与回归,过拟合和欠拟合,正则化问题。

今天,我们来利用神经网络解决分类和回归问题


  • 2.1 Pytorch完成手写数字分类模型搭建

    • 数据集为黑白图,大小为28x28
    • 其中,包括60000张训练图片和10000张测试图片
    • 数据集下载:yann.lecun.com/exdb/mnist/
    • 分类问题采用Loss:交叉熵损失
    • CrossEntropyLoss
      • 1.png
    • 本质是0-9的十分类问题

回归问题和分类问题最本质的区别是一个是预测连续的值,一个是预测离散的值。

  • 2.1.1 数据集的加载

手写数字识别的数据集已经集成在torchvision这个包中,在这个包中还有cifar10,coco等一系列数据集,我们可以调用它来完成对数据集的加载

train_data = dataset.MNIST(root="mnist",
                           train=True,
                           transform=transforms.ToTensor(),
                           download=True)

在下载完数据集之后,可以看到一个mnist的文件夹,在这个文件夹下包含了原始的数据和处理过的数据

采用batchsize的方式,选择一小部分的数据来丢掉网络里来进行训练,这一小部分数据就叫做batchsize,利用这一小部分数据来对网络进行训练,进行参数的调整

所以我们在训练图片的时候,通常采用min batch的方式

#batchsize
train_loader = data_utils.DataLoader(dataset=train_data,
                                     batch_size=64,
                                     shuffle=True)

test_loader = data_utils.DataLoader(dataset=test_data,
                                     batch_size=64,
                                     shuffle=True)
  • 2.1.2 定义网络
import torch
class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv =torch.nn.Sequential(
            torch.nn.Conv2d(1, 32, kernel_size=5, padding=2),
            torch.nn.BatchNorm2d(32),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)
        )
        self.fc = torch.nn.Linear(14 * 14 * 32, 10)
    def forward(self, x):
        out = self.conv(x)
        out = out.view(out.size()[0], -1)
        out = self.fc(out)
        return out
cnn = CNN()
  • 2.1.3 定义损失函数和优化器
# loss

loss_func = torch.nn.CrossEntropyLoss()

#optimizer

optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)
  • 2.1.4 训练数据
#training
for epoch in range(10):
    for i, (images, labels) in enumerate(train_loader):
        images = images.cuda()
        labels = labels.cuda()

        outputs = cnn(images)
        loss = loss_func(outputs, labels)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print("epoch is {}, ite is "
          "{}/{}, loss is {}".format(epoch+1, i,
                                     len(train_data) // 64,
                                     loss.item()))

可以看到,在一个epoch内,需要迭代937次才能将全部样本训练完,loss处于不断的收敛的过程,从最开始的十几降到0.02

  • 2.1.5 计算测试集的准确率
#eval/test
loss_test = 0
accuracy = 0
for i, (images, labels) in enumerate(test_loader):
    # images = images.cuda()
    # labels = labels.cuda()
    outputs = cnn(images)
    #[batchsize]
    #outputs = batchsize * cls_num
    loss_test += loss_func(outputs, labels)
    _, pred = outputs.max(1)
    accuracy += (pred == labels).sum().item()

accuracy = accuracy / len(test_data)
loss_test = loss_test / (len(test_data) // 64)

print("epoch is {}, accuracy is {}, "
      "loss test is {}".format(epoch + 1,
                               accuracy,
                               loss_test.item()))
  • 2.1.6 保存模型
torch.save(cnn, "mnist_model.pkl")
  • 2.1.7 利用保存的模型进行推理
import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
from CNN import CNN
# net

test_data = dataset.MNIST(root="mnist",
                          train=False,
                          transform=transforms.ToTensor(),
                          download=False)

test_loader = data_utils.DataLoader(dataset=test_data,
                                    batch_size=64,
                                    shuffle=True)

cnn = torch.load("mnist_model.pkl")
# cnn = cnn.cuda()
# loss
# eval/test
loss_test = 0
accuracy = 0

import cv2



for i, (images, labels) in enumerate(test_loader):
    outputs = cnn(images)
    _, pred = outputs.max(1)
    accuracy += (pred == labels).sum().item()

    #batchsize * 1 * 28 * 28

    for idx in range(images.shape[0]):
        im_data = images[idx]
        im_label = labels[idx]
        im_pred = pred[idx]

        im_data = im_data.numpy()
        im_data = im_data.transpose(1, 2, 0)


        print("label", im_label)
        print("pred", im_pred)
        cv2.imshow("imdata", im_data)
        cv2.waitKey(0)


accuracy = accuracy / len(test_data)
print(accuracy)

运行结果:

0.9833
  • 2.2 模型的性能评价——交叉验证

    • 简单交叉验证:将数据集划分成:train/test
    • 2-折交叉验证:将数据集划分成:train/test,先将train作为训练集,将tset作为测试集,然后将test作为训练集,将train作为测试集
    • k-折交叉验证:将数据分成k份,每次使用k-1次来训练,1份用来做测试,然后循环k次,并结合k次的训练结果进行评价
    • 留一交叉验证(数据缺失的情况下使用):保留一份样本,使用剩下的样本进行训练,优点是训练的时候使用了基本上所有的数据,缺点是需要进行多次的训练,训练的周期比较久。

9JQ4ZCQY3M({Q$KEN%9BFQX.png