暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

深度学习案例分享 | 手写数字识别 - PyTorch 实现

程序员学长 2022-08-16
872

大家好,我是小寒。

今天我们来分享第二个深度学习案例:手写数字识别。

MNIST 手写数字识别数据集来自美国国家标准与技术研究所(National Institute of Standards and Technology,NIST)。这个数据集由250个不同人手写的数字构成, 其中50%来自高中生, 50%来自美国人口普查局(the Census Bureau) 的工作人员。MNIST 是图像分类中使用最广泛的数据集。

1.加载数据集

MNIST 手写数字识别数据集中的图像是一个28*28 的灰度图像。我们通过 pytorch 的内置函数将 MNIST 下载并读到内存中。
# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0到1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.MNIST(root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.MNIST(root="../data", train=False, transform=trans, download=True)

MNIST由10个类别的图像组成,每个类别由训练数据集(train dataset)中的 6000 张图像和测试数据集(test dataset)中的1000 张图像组成。
len(mnist_train), len(mnist_test)

60000,10000

可视化数据集

我们来看一下数据集中的图像样本是什么样的。

通过如下方式,我们来可视化的展示训练集中前几个样本。

# 显示数据集
mnist_show = torchvision.datasets.MNIST(root="../data", train=True, transform=torchvision.transforms.ToTensor(), download=True)
images, label = next(iter(data.DataLoader(mnist_show, 20, shuffle=True)))
#多张图合并成一张图片
images_example = torchvision.utils.make_grid(images,nrow=5)
images_example = images_example.numpy().transpose(1,2,0) # 将图像的通道值置换到最后的维度,符合图像的格式
plt.imshow(images_example )
plt.show()

2.模型架构

这里我们使用只有一个隐藏层的多层感知机(MLP)来进行手写数字的识别。

模型架构如下图所示。

这里定义了两层的的神经网络,第⼀层是隐藏层,它包含 256 个隐藏单元,并使⽤了 ReLU 激活函数。第⼆层是输出层
#这里使用多层感知机来进行手写数字的识别
net = nn.Sequential(nn.Flatten(),
                    nn.Linear(784, 256),
                    nn.ReLU(),
                    nn.Linear(256, 10))

3.初始化模型参数

以均值为 0 和标准差为 0.1的高斯分布来随机初始化权重。

# PyTorch 不会隐式地调整输⼊的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整⽹络输⼊的形状
def init_weights(m):
    if type(m) == nn.Linear:
        #以均值为 0 和标准差为 0.1 随机初始化权重。
        nn.init.normal_(m.weight, std=0.1)

4.定义损失函数

由于是分类问题,所以损失函数使用交叉熵损失。

loss = nn.CrossEntropyLoss(reduction='none')

5.定义优化器

使用 SGD(批量梯度下降算法)来进行优化,这里设置学习率为0.01

trainer = torch.optim.SGD(net.parameters(), lr=0.01)

6.训练和预测

class Accumulator:
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n
    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]
    def reset(self):
        self.data = [0.0] * len(self.data)
    def __getitem__(self, idx):
         return self.data[idx]

print("随机初始化后,测试集的准确率为")
print(evaluate_accuracy(net, test_iter))

#训练
def train_epoch_ch3(net, train_iter, loss, updater):
    """训练模型⼀个迭代周期(定义⻅第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        #进行预测
        y_hat = net(X)
        #计算损失
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
        # 使⽤PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:  # 使⽤定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
    metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

train_losss=[]
train_accs=[]

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
        """训练模型"""
        for epoch in range(num_epochs):
            train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
            train_loss, train_acc = train_metrics
            print(f'epoch {epoch + 1}, loss {float(train_loss):f}')
            train_losss.append(train_loss)
            train_accs.append(train_acc)

num_epochs = 15
print("开始训练:")
train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

输出:

随机初始化后,测试集的准确率为
0.0997
开始训练:
epoch 1, loss 1.719787
epoch 2, loss 1.028229
epoch 3, loss 0.708345
epoch 4, loss 0.633207
epoch 5, loss 0.545152
epoch 6, loss 0.517245
epoch 7, loss 0.435589
epoch 8, loss 0.378610
epoch 9, loss 0.398382
epoch 10, loss 0.355470
epoch 11, loss 0.292634
epoch 12, loss 0.303913
epoch 13, loss 0.297712
epoch 14, loss 0.316524
epoch 15, loss 0.227925

7.评估及测试

def show_image(num_epochs,train_ls,train_acc):
    plt.plot(np.arange(1, num_epochs + 1), train_ls,label='train_loss')
    plt.plot(np.arange(1, num_epochs + 1), train_acc,label='train_accuracy')
    plt.xlabel('epoch')
    plt.legend()
    plt.show()
show_image(num_epochs,train_losss,train_accs)

test_acc = evaluate_accuracy(net, test_iter)
print(f'test_acc {float(test_acc):f}')

输出:

test_acc 0.910300

损失变化和准确率变化如下图所示。

最后

今天简单介绍了一个如何用 Pytorch 来实现手写数字识别。

如果需要完整代码+数据集,可以加我微信: algo_code

接下来我们会分享更多的 深度学习案例以及python相关的技术,欢迎大家关注。

最后,最近新建了一个 python 学习交流群,会时不时的分享 python相关学习资料,也可以问问题,非常棒的一个群

进群方式:加我微信,备注 “python”

最后




这期文章就分享到这里,如果觉得不错,转发、在看、点赞安排起来吧
你知道的越多,你的思维越开阔。我们下期再见。



往期回顾


深度学习案例分享 | 房价预测 - PyTorch 实现

    




如果对本文有疑问可以加作者微信直接交流。进技术交流群的可以拉你进群。

文章转载自程序员学长,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论