基于Pytorch的mnist的分类

基于Pytorch的mnist的分类

  • 训练集和测试集的划分非常重要。在机器学习模型设计时必须有一个单独的测试集(不用于训练而是用来评估这个模型的性能),才能更容易把模型推广到其他数据集上
    mnist
    mnist中图片包含28x28像素点,可以用一个数组来描述,这个数组可以张成一个向量,长度为784,保证每张照片以相同方式展开。从这个角度看,就是784维向量空间的点,然后可以进行比较。
    在这里插入图片描述

借助Pytorch搭建网络

导入需要的库

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

定义参数

BATCH_SIZE = 523  	# batch_size即每批训练的样本数量
epochs = 20			# 循环次数
DEVICE=torch.device("cuda" if torch.cuda.is_available() else "cpu")    #判断是否能在GPU上进行运算

下载数据集

  • 训练集
train_loader = torch.utils.data.DataLoader(                 # vision.utils : 用于把形似 (3 x H x W) 的张量保存到硬盘中,给一个mini-batch的图像可以产生一个图像格网。
        datasets.MNIST('data', train=True, download=True,
                       transform=transforms.Compose([
                           transforms.ToTensor(),       # 图像转化为Tensor
                           transforms.Normalize((0.1307,), (0.3081,))       # 标准化
                       ])),
        batch_size=BATCH_SIZE, shuffle=True)            # shuffle() 方法将序列的所有元素随机排序

torchvision.dataset作用是下载数据集,第一次调用时会自动从网上获取数据,

  • train来指定获取训练数据集或测试数据集
  • transforms.ToTensor()是将数据转化为tensor
  • transforms.Normalize((0.1307,), (0.3081,))是进行标准化
    torch.utils.data.DataLoader()利用下载的数据集来创建一个读取小批量数据样本的DataLoader实例
    • batch_size是每批训练的样本数量
    • shuffle() 方法将序列的所有元素随机排序
  • 测试集
test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('data', train=False, transform=transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))
                       ])),
        batch_size=BATCH_SIZE, shuffle=True)            # shuffle() 方法将序列的所有元素随机排序

定义网络

  • 网络的定义是最重要的部分,如何设置内部网络结构,往往就决定了你设置模型优劣
  • 这里网络设置的是三个个卷积层,以及两个全连接层,在卷积层以及全连接层之间分别设置了激活函数池化函数分别试验他们各自作用
  • 下面解析各自层之间参数传递关系
    • conv1(1,12,5)由于该图片为黑白图输入通道为1,卷积核为5x5,输出通道为12,输出维度28-5+1=24,输出维度12x24x24
    • 经过池化层维度减半为12,输出shape为12x12x12
    • conv2(12,20,3)输入通道与上一层输出通道对应,卷积核为3,输出维度为12-3+1=10,输出shape为20x10x10
      • conv2(20,40,3)输入通道与上一层输出通道对应,卷积核为3,输出维度为10-3+1=8,输出shape为40x8x8
    • Linear需要接收的是展平后的多维的卷积成的特征图,需要使用view()函数,-1是根据输入数据自动设定
    • nn.Linear(4088, 500),输出为50
    • nn.Linear(500, 10),输出为10,进行10分类
class Net(nn.Module): # 继承model
    def __init__(self):
        super().__init__()
        # 28x28
        self.conv1=nn.Conv2d(1,12,5)         # 12, 24x24
        self.conv2=nn.Conv2d(12, 20,3)       #20, 10x10
        self.conv3=nn.Conv2d(20, 40,3)       #20, 10x10
        self.batchnorm2d = nn.BatchNorm2d(40)
        self.fc1=nn.Linear(40*8*8, 500)
        self.fc2=nn.Linear(500, 10)
    def forward(self, x):      #网络传播结构
        in_size=x.size(0)# in_size 为 batch_size(一个batch中的Sample数)
        # 卷积层 -> relu -> 最大池化
        out = self.conv1(x)     # 24
        out = F.relu(out)
        out = F.max_pool2d(out, 2, 2)  # 12
        out = self.conv2(out)  # 10
        out = F.relu(out)
        out = self.conv3(out)
        out = F.relu(out)
        out =  self.batchnorm2d(out)
        out = out.view(in_size, -1)    # view()函数作用是将一个多行的Tensor,拼接成行。
        # 输出前的预处理
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        # softmax
        out = F.log_softmax(out, dim=1)
        # 返回值 out
        return out

网络实例化与优化器

model = Net().to(DEVICE)
optimizer = optim.Adam(model.parameters())

定义训练函数

  • 将训练的所有操作封装到函数Train中,这样在最后直接调用函数就显得非常简洁
  • 采用的损失函数是torch.functional.nll_loss,后面讨论环节也会分析不同loss函数的结果差异
  • with torch.no_grad()表示不反向求导。训练过程需要反向求导(去更新优化模型参数),测试过程不需要反向求导。
  • print(‘Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}’.format(
    epoch_i, batch_idx * len(data), len(train_loader.dataset),
    100. * batch_idx / len(train_loader), loss.item()))采用了比较简洁方式将结果输出
def train(model, device, train_loader, optimizer, epoch):
    model.eval()
    for  epoch_i in range(epoch+1):
        for batch_idx, (data, target) in enumerate(train_loader):
            data, target = data, target      # CPU转GPU
            optimizer.zero_grad()               # 优化器清零
            output = model(data)                # 由model,计算输出值
            loss = F.nll_loss(output, target)   # 计算损失函数loss
            loss.backward()                     # loss反向传播
            optimizer.step()                    # 优化器优化
            if(batch_idx+1)%30 == 0:            # 输出结果
                print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                    epoch_i, batch_idx * len(data), len(train_loader.dataset),
                    100. * batch_idx / len(train_loader), loss.item()))
def test(model, device, test_loader):
    test_loss = 0                           # 损失函数初始化为0
    correct = 0                             # correct 计数分类正确的数目
    with torch.no_grad():           
        for data, target in test_loader:    # 遍历所有的data和target
            data, target = data.to(device), target.to(device)   # CPU -> GPU
            output = model(data)            # output为预测值,由model计算出
            test_loss += F.nll_loss(output, target, reduction='sum').item()     ### 将一批的损失相加
            pred = output.max(1, keepdim=True)[1]       ### 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)   # 总损失除数据集总数
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))

函数调用

train(model, DEVICE, train_loader, optimizer, epoch)
test(model, DEVICE, test_loader)

结果分析

构建不同卷积层对输出结果分析

  • 在将循环次数设置为20下,然后取平均值
    • 两层卷积网,在测试集上平均损失为 0.0363,
    • 三层卷积网,在测试集上平均损失为0.0296
    • 对结果进行分析,对于精度略有提升,还可以进行更多层的对比
  • 两层卷积层、
class Net(nn.Module): # 继承model
    def __init__(self):
        super().__init__()
        # 28x28
        self.conv1=nn.Conv2d(1,12,5)         
        self.conv2=nn.Conv2d(12, 20,3)       
        self.fc1=nn.Linear(20*10*10, 500)
        self.fc2=nn.Linear(500, 10)
    def forward(self, x):      #网络传播结构
        # 卷积层 -> relu -> 最大池化
        out = self.conv1(x)     # 24
        out = F.relu(out)
        out = F.max_pool2d(out, 2, 2)  # 12
        out = self.conv2(out)  # 10
        out = F.relu(out)
        out = out.view(in_size=x.size(0), -1)    # view()函数作用是将一个多行的Tensor,拼接成行。
        # 输出前的预处理结束,提取完特征信息
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        # softmax
        out = F.log_softmax(out, dim=1)
        # 返回值 out
        return out
Test set: Average loss: 0.0363, Accuracy: 9889/10000 (99%)

  • 三层卷积层
class Net(nn.Module): # 继承model
    def __init__(self):
        super().__init__()
        # 28x28
        self.conv1=nn.Conv2d(1,12,5)         # 12, 24x24
        self.conv2=nn.Conv2d(12, 20,3)       #20, 10x10
        self.conv3=nn.Conv2d(20, 40,3)       #20, 10x10
        self.batchnorm2d = nn.BatchNorm2d(40)
        self.fc1=nn.Linear(40*8*8, 500)
        self.fc2=nn.Linear(500, 10)
    def forward(self, x):      #网络传播结构
        in_size=x.size(0)# in_size 为 batch_size(一个batch中的Sample数)
        # 卷积层 -> relu -> 最大池化
        out = self.conv1(x)     # 24
        out = F.relu(out)
        out = F.max_pool2d(out, 2, 2)  # 12
        out = self.conv2(out)  # 10
        out = F.relu(out)
        out = self.conv3(out)
        out = F.relu(out)
        out =  self.batchnorm2d(out)
        out = out.view(in_size, -1)    # view()函数作用是将一个多行的Tensor,拼接成行。
        # 输出前的预处理
        out = self.fc1(out)
        out = F.relu(out)
        out = self.fc2(out)
        # softmax
        out = F.log_softmax(out, dim=1)
        # 返回值 out
        return out
Test set: Average loss: 0.0296, Accuracy: 9898/10000 (99%)

卷积层之间的通道数设置

  • 卷积层之间的通道数设置为超参,如何设置,影响着结果,以两层网络为例,改变第一层卷积网络输出通道,看测试集上的结果,循环次数设置为20
    • 输出通道为10,11,12,13,14,测试集上平均损失为
      • 0.0315,0.0313,0.03630,0386,0.0284
      • 在经调试,第一层通道设置为14较好
        self.conv1=nn.Conv2d(1,10,5)         # 改变输出通道10,11,12,13,14
        self.conv2=nn.Conv2d(10, 20,3)       #对应上一层输出通道
        self.fc1=nn.Linear(20*10*10, 500)
        self.fc2=nn.Linear(500, 10)

卷积层间的relu以及pooling

  • 在卷积层后将sigmoid()都改成使用F.relu(和nn.relu产不多),AlexNet相对于LeNet有一改变就是使用了relu函数,使用了relu克服sigmoid在反向求导时,导数不可算的情况
  • 池化层作用:池化层能够克服过拟合。池化层的存在,会不断提取一定范围内最强烈的特征,并且缩小张量的大小,使得大范围内的特征组合也能够捕捉到,pooling的结果是使得特征减少,参数减少,但pooling的目的并不仅在于此。pooling目的是为了保持某种不变性(旋转、平移、伸缩等),常用的有mean-pooling,max-pooling和Stochastic-pooling三种,该文建立的网络中采取的是最大池化
    • 特征提取的误差主要来自两个方面:
      • 邻域大小受限造成的估计值方差增大;
      • 卷积层参数误差造成估计均值的偏移
    • 一般来说,mean-pooling能减小第一种误差,更多的保留图像的背景信息;
    • max-pooling能减小第二种误差,更多的保留纹理信息;
    • Stochastic-pooling则介于两者之间,通过对像素点按照数值大小赋予概率,再按照概率进行亚采样,在平均意义上,与mean-pooling近似,在局部意义上,则服从max-pooling的准则。
  • 在设置了最大池化与不进行池化,测试集上平均损失进行对比,循环次数设置为20

损失函数

  • 损失函数有许多,在模型中选择了不同的loss函数对于结果也通样是有影响的,在该文中选择的是torch.functional.null_loss,
    • 用于多分类的负对数似然损失函数(Negative Log Likelihood)
      l o s s ( x , l a b e l ) = − X l a b e l loss(x,label)=-X_{label} loss(x,label)=Xlabel
      事实上,nn.CrossEntropyLoss 也是调用这个函数。注意这里的和交叉熵损失里的不一样,nll是经过 logSoftMax运算后的数值

小结

  • 对于不同的问题设计结构,再进行调参,进行有目标的,设计,得到的效果也会更好,在此文中仅仅只是对模型中部分进行了对比测试,具体的参数也只是进行小范围,以测试集上的效果为目标进行调参

总结与感悟

  • 在学习了Pytorch相关资料后,实现了mnist分类任务,从开始的一点不会,通过查资料,慢慢读懂一部分程序,这个过程还是学到了不少,但是由于里面内容是很丰富,还有许多问题理解得不够深入仅仅是会用,在下一步学习中,多思考,多查资料,多记笔记,也要提升自己的编程能力。
  • 参考
    https://blog.csdn.net/qq_41683065/article/details/91374189
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个基于PyTorchMNIST识别的代码示例: 首先,导入必要的库和模块: ``` import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms ``` 然后,定义网络模型。这里我们定义了一个简单的卷积神经网络,包含两个卷积层和两个全连接层: ``` class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 4 * 4, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(nn.functional.relu(self.conv1(x))) x = self.pool(nn.functional.relu(self.conv2(x))) x = x.view(-1, 16 * 4 * 4) x = nn.functional.relu(self.fc1(x)) x = nn.functional.relu(self.fc2(x)) x = self.fc3(x) return x net = Net() ``` 接着,定义损失函数和优化器: ``` criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) ``` 然后,加载MNIST数据集,并进行预处理: ``` transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2) testset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False, num_workers=2) ``` 最后,开始训练模型: ``` for epoch in range(5): # 训练5轮 running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 100 == 99: # 每100个batch输出一次训练状态 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 100)) running_loss = 0.0 print('Finished Training') ``` 最后,计算测试集上的准确率: ``` correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % ( 100 * correct / total)) ``` 这就是一个基于PyTorchMNIST识别的完整代码示例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pinn山里娃

原创不易请多多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值