MNIST手写数据集识别并使用visdom绘制精度图

MNIST手写数据集识别练习:

  1. MNIST数据集是在进行深度学习分类时最常用的一个数据集,是手写的0-9十个数,图像大小为(1, 28, 28)。
  2. MNIST数据集都是灰度图所以,通道数为1。
  3. MNIST数据集大概有7w多张图片(6w训练,1w测试)

这段时间观看b站up主 刘二大人 的关于深度学习的讲解,让我获益匪浅。在网络中添加残差模块,使得测试精度再次提升,下面是视频地址。

https://www.bilibili.com/video/BV1Y7411d7Ys?p=11&share_source=copy_web


下面为相关代码:

文中绘制相关曲线使用的是visdom库,优点是根据运行计算出的损失值,实时的进行绘制。
visdom的安装和使用可以浏览下列连接的博客。

https://blog.csdn.net/qq_42962681/article/details/116271548

为了能更加了解网络的结构和参数的传递,标记了大量的注释。

import torch
import torch.nn as nn
from torch.utils.data import DataLoader  # 我们要加载数据集的
from torchvision import transforms  # 数据的原始处理
from torchvision import datasets  # pytorch十分贴心的为我们直接准备了这个数据集
import torch.nn.functional as F  # 激活函数
import torch.optim as optim
import time
import visdom

batch_size = 64
# 我们拿到的图片是pillow,我们要把他转换成模型里能训练的tensor也就是张量的格式
# transform = transforms.Compose([transforms.ToTensor()])
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))  # 你均值和方差都只传入一个参数,就报错了.
    # 这个函数的功能是把输入图片数据转化为给定均值和方差的高斯分布,使模型更容易收敛。图片数据是r,g,b格式,对应r,g,b三个通道数据都要转换。
])

# 加载训练集,pytorch十分贴心的为我们直接准备了这个数据集,注意,即使你没有下载这个数据集
# 在函数中输入download=True,他在运行到这里的时候发现你给的路径没有,就自动下载
train_dataset = datasets.MNIST(root='../datasets/fashion-mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=batch_size)
# 同样的方式加载一下测试集
test_dataset = datasets.MNIST(root='../datasets/fashion-mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(dataset=test_dataset, shuffle=False, batch_size=batch_size)

#为了使预测精度更高,我在这里添加了残差网络模块
# 残差网络示意
class ResidualBlock(nn.Module):
    def __init__(self, channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels
        #这里输入通道与输出通道相等,且经过kernel=3的卷积核,由于w,h填充1,所以图像尺寸不变
        self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)

    def forward(self, x):
        y = F.relu(self.conv1(x))
        y = self.conv2(y)
        return F.relu(x + y)


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 定义了我们第一个要用到的卷积层,因为图片输入通道为1,第一个参数就是1
        # 输出的通道为16,kernel_size是卷积核的大小,这里定义的是5x5的
        # 我们输入的图像为[64,1,28,28]通过卷积层后为[64,1,24,24]
        self.conv1 = nn.Conv2d(1, 16, kernel_size=5)
        # 看懂了上面的定义,下面这个你肯定也能看懂
        self.conv2 = nn.Conv2d(16, 32, kernel_size=5)
        # 再定义一个池化层
        self.mp = nn.MaxPool2d(2)

        # 调用残差网络
        # 输入残差网络中不改变图像尺寸大小,只改变维度
        self.rblock1 = ResidualBlock(16)
        self.rblock2 = ResidualBlock(32)

        # 最后是我们做分类用的线性层
        self.fc = nn.Linear(512, 10)

    # 下面就是计算的过程
    def forward(self, x):
        # 输入的x尺寸 (64, 1, 28, 28)
        batch_size = x.size(0)  # 这里面的0是x大小第1个参数,自动获取batch大小
        #经过卷积层后(64, 16,24, 24)再经过最大池化(64, 16,12, 12)
        x = F.relu(self.mp(self.conv1(x)))
        #经过残差网络通道和图像尺寸不变还是(64, 16,12, 12)
        x = self.rblock1(x)
        #经过卷积层后(64, 32,8, 8)再经过最大池化(64, 32,4, 4)
        x = F.relu((self.mp(self.conv2(x))))
        经过残差网络通道和图像尺寸不变
        x = self.rblock2(x)
        # 为了给我们最后一个全连接的线性层用(64,32*4*4)
        x = x.view(batch_size, -1)  # flatten
        # 经过线性层,确定他是0~9每一个数的概率
        #全连接层后(64,32*4*4)变为(64,10)
        x = self.fc(x)
        return x


model = Net()  # 实例化模型
# 把计算迁移到GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)

# 定义一个损失函数,来计算我们模型输出的值和标准值的差距
criterion = nn.CrossEntropyLoss()
# 定义一个优化器,训练模型咋训练的,就靠这个,他会反向的更改相应层的权重
optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.5)  # lr为学习率

# 全局运行步数
global_step = 0.0
global_step2 = 0.0
# 为训练添加环境变量,之后程序产生的所有数据都会出现在这个环境变量中,visdom就可以监听数据实现可视化
# 而且在visdom中还可以按照环境变量名选择性监听,便于分类管理
viz = visdom.Visdom(env='train-mnist')
# 创建线图初始点。loss是差值图,acc是精度图
viz.line([0.], [0.], win='train_loss', opts=dict(title='train loss'))
viz.line([0.], [0.], win='acc', opts=dict(title='test acc'))

# 设置迭代次数
epochs = 2
for epoch in range(epochs):
    for batch_idx, (data, target) in enumerate(train_loader, 0):
        data, target = data.to(device), target.to(device)

        logits = model(data)
        loss = criterion(logits, target)

        optimizer.zero_grad()
        loss.backward()
        # print(w1.grad.norm(), w2.grad.norm())
        optimizer.step()

        global_step += 1
        viz.line([loss.item()], [global_step], win='train_loss', update='append')

        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                       100. * batch_idx / len(train_loader), loss.item()))

    total = 0.0
    correct = 0.0
    test_loss = 0.0
    for data, target in test_loader:
        data, target = data.to(device), target.to(device)
        outputs = model(data)
        test_loss += criterion(outputs, target).item()
        # test_loss /= len(test_loader.dataset)
        # 我们取概率最大的那个数作为输出

        _, pred = torch.max(outputs.data, dim=1)
        total += target.size(0)
        # 计算正确率
        correct += (pred == target).sum().item()
        global_step2 = global_step2 + 1
        acc = correct / total
        # 打印训练周期数epoch,预测差值loss,精度acc
        # 精度可视化显示
        viz.line([acc], [global_step2], win='acc', update='append')

        # 再加载一次测试集,用于可视化输出每次预测的结果
        x, label = iter(test_loader).next()
        viz.images(x, nrow=16, win='test_x', opts=dict(title='test_x'))
        x, label = x.to(device), label.to(device)
        pred = model(x).argmax(dim=1)

        # 可视化显示预测
        viz.text(str(pred.detach().cpu().numpy()), win='predicted label', opts=dict(title='predicted label'))
        # 可视化显示真实值
        viz.text(str(label.detach().cpu().numpy()), win='groundtruth label', opts=dict(title='groundtruth label'))

    test_loss /= len(test_loader.dataset)
    print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

这里为测试的准确度,已经达到了99%的准确率,效果已经比较好了,并且网络只迭代了两次
在这里插入图片描述


visdom绘制的结果示意:

这是测试时的结果,左侧为标签数值,右侧是通过网络的预测值,通过对比可以看出我们的网络精度还是比较高的。
在这里插入图片描述
这是训练的损失函数,这里取得是单独的batch的损失进行绘制,而不是使用迭代次数epoch
在这里插入图片描述
这是测量的精度
在这里插入图片描述


遇到的问题:

使用visdom.images()命令显示图片时,不能成功显示。也查询了很多方法没能解决,有了解的朋友,希望可以告知。共同进步!
在这里插入图片描述

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: mnist手写数字数据集识别一般使用交叉熵损失函数,因为交叉熵损失函数能很好地度量两个概率分布之间的距离。在mnist数据集识别中,我们通常将真实的像类别看作一个“真实概率分布”,将模型预测出的像类别看作一个“预测概率分布”。使用交叉熵损失函数可以帮助我们衡量这两个概率分布之间的距离,从而帮助我们更好地评估模型的预测结果。 ### 回答2: MNIST手写数据集是一个常用的用于机器学习任务的数据集,其中包含了手写数字的灰度像。识别这些手写数字的任务可以被视为一个多分类问题,因为我们需要将每个像分为10个不同的类别,分别代表数字0到9。 交叉熵损失函数是一种常用的用于多分类问题的损失函数。其优点在于它能够衡量预测结果与真实结果之间的差异,并且能够产生一个连续的、可导的损失值。在MNIST数据集识别任务中,交叉熵损失函数可以用来衡量预测结果与实际标签之间的差异。 具体来说,交叉熵损失函数计算预测结果的概率分布与实际标签的概率分布之间的差异。对于每个样本,神经网络会输出一个概率分布,表示该样本属于每个类别的概率。而实际标签是一个one-hot编码的向量,表示该样本的真实类别。交叉熵损失函数会计算这两个概率分布的差异,并尽量将其最小化。 使用交叉熵损失函数的好处是它能够有效地推动神经网络学习出更接近于真实标签分布的预测结果。通过最小化交叉熵损失,神经网络可以迭代地调整权重和偏置,使得预测结果与实际标签尽可能接近。这使得神经网络识别MNIST手写数据集的任务中能够取得较好的性能。 因此,交叉熵损失函数是因其能够衡量预测结果与实际标签之间的差异,并对模型进行有效的训练而选择作为MNIST手写数据集识别任务的损失函数。 ### 回答3: MNIST手写数据集是一个非常经典的机器学习问题,旨在将手写数字像正确分类为0到9中的一个数字。为了解决这个问题,我们需要选择适当的损失函数来衡量模型预测和真实标签之间的差异。 交叉熵损失函数是一种常用的选择,因为它在分类问题中表现良好。它基于信息论中的概念,可以量化预测分布与真实分布之间的差异。 对于MNIST数据集来说,每个像都有一个真实的标签,表示正确的数字。在训练过程中,我们的模型会输出一个预测的概率分布,对应着0到9这十个数字的可能性。 交叉熵损失函数通过计算预测分布和真实标签之间的交叉熵来衡量模型的性能。具体而言,对于每个样本,交叉熵损失函数会计算预测标签的概率分布和真实标签的one-hot编码之间的差异。 交叉熵损失函数有以下几个优点: 1. 它对错误分类的惩罚越大,因此可以帮助模型更加关注错误分类的样本,从而改进模型的分类准确性。 2. 由于它是非负的,并且在模型接近最优解时会取得最小值,因此在训练过程中可以作为损失函数的优化目标。 3. 它的导数形式简洁,方便使用梯度下降等优化算法进行模型的训练。 总而言之,交叉熵损失函数MNIST手写数据集识别问题中被广泛使用,因为它是一种简单且有效的损失函数,可以帮助提高模型的分类准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值