如何使用Pytorch迅速写一个Mnist数据分类器

一段时间没有更新博文,想着也该写两篇文章玩玩了。而从一个简单的例子作为开端是一个比较不错的选择。本文章会手把手地教读者构建一个简单的Mnist(Fashion-Mnist同理)的分类器,并且会使用相对完整的Pytorch训练框架,因此对于初学者来说应该会是一个方便入门且便于阅读的文章。本文的代码来源于我刚学Pytorch时的小项目,可能在形式上会有引用一些github上的小代码。同时文风可能会和我之前看的一些外国博客有点相近。

本文适用对象: 刚入门的Pytorch新手,想要用Pytorch来完成作业的鱼干。

那么就开始coding吧。

首先,你需要安装好Python 3+,Pytorch 1.0+,我个人使用的是Pytorch1.4,我想1.0以上的版本都可以使用。

然后在想要的位置,新建一个main.py的文件,然后就可以开始敲键盘了。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets
from torchvision import transforms
import torch.utils.data import argparse

第一步自然是导入相应的包。前面的都是Pytorch的包,最后一句导入的argparse便于用来修改训练的参数,这在Pytorch复现深度学习模型时非常常见。

model_names = ['Net','Net1']

parser = argparse.ArgumentParser(description='PyTorch Mnist Training')
parser.add_argument('-a', '--arch', metavar='ARCH', default='Net', choices=model_names,
help='model archtecture: ' + '|'.join(model_names) + '(default:Net)')
parser.add_argument('--epochs', default=5, type=int, metavar='N', help='number of total epochs')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum')
parser.add_argument('-b', '--batchsize', default=32, type=int, metavar='N', help='mini-batch size')
parser.add_argument('--lr', '--learning-rate', default=1e-2, type=float, metavar='LR', help='initial learning rata',
dest='lr')
args=vars(parser.parse_args())

第一行的model_names是一个list,用来存储我们之后会实现的两种网络结构的名字。然后我定义了一个argparse的对象,关于argparse可以自寻一些教程观看,大概只需要知道可以从指令行输入参数即可。在parser中又定义了arch(使用的网络),epochs(迭代轮次),momentum(梯度动量大小),batchsize(一次送入的图片量大小),learning-rate(学习率)参数。之前的model_name也正是用在arch参数中,限定了网络框架将会从此二者中选择其一。

def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") #parameter
batch_size = args["batchsize"]
lr = args["lr"]
momentum = args["momentum"]
num_epochs = args["epochs"]

主函数中,先定义cuda对象,便于使用gpu并行运算。在#parameter中,我们把一些从命令行中获得的参数引入到相应的变量中,以便后续书写。

    #prepare the dataset

    mnist_data = datasets.MNIST("./mnist_data",train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=(0.13,),std=(0.31,))
]))
'''
mnist_data = datasets.FashionMNIST("./fashion_mnist_data", train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(mean=(,), std=(,))
]))
'''
train_loader = torch.utils.data.DataLoader(
mnist_data,batch_size=batch_size,shuffle=True, num_workers=1,pin_memory=True
)
test_loader = torch.utils.data.DataLoader(
mnist_data,batch_size=batch_size,shuffle=True, num_workers=1,pin_memory=True
)

之后将引入Pytorch中datasets包自带的MNIST集,download参数设置为True,以便于本地没有Mnist数据集时直接下载,之后会在当前目录下创建一个mnist_data的文件夹以存放数据,。transform中的transforms.ToTensor()是用于将图片形式的数据转换成tensor类型,而transforms.Normalize(mean=(0.13,),std=(0.31,))则是将tensor类型的数据进行归一化,这里的0.13和0.31可以直接使用。如果你想要使用注释中的FashionMNIST数据集则需要使用的是注释中的内容,当然,mean和std需要另外求解。

之后,定义train_loadertest_loader,将数据集作为可迭代的对象使用。shuffle=True以实现乱序读取数据,一般都会这么设置。num_workerspin_memory都会影响到数据读取速度,前者是会在读取时创建多少个进程,后者是影响到数据读入到GPU中,一般来说,对于这个项目前者设置为1已经够用,后者设置为True和False都不影响。在更大型的项目中,如果设备较好,前者可以设置大一些。

    model = Net1().to(device) if args["arch"]=='Net1' else Net().to(device)
optimizer = torch.optim.SGD(model.parameters(),lr=lr,momentum=momentum)
criteon = nn.CrossEntropyLoss().to(device)

第一行是model实例化,并且会根据args["arch"]选择是用Net还是Net1to(device)会将model放置于device上运行。第二行定义了一个优化器,使用的是SGD,并且放入model的参数、学习率和动量大小。criteon定义损失函数,这边使用的是交叉熵函数,这一损失函数在分类问题中十分常用。

    #train
for epoch in range(num_epochs):
train(model,device,train_loader,optimizer,epoch,criteon)
test(model,device,test_loader,criteon) torch.save(model.state_dict(), "mnist_{}.pth".format(num_epochs))

这就是训练过程,在其中又使用了traintest两个函数(下面会说),根据num_epochs数目进行循环。循环结束后,torch.save将会把模型的参数model.state_dict()mnist_{}.pth的形式存放到当前文件夹下。

def train(model,device,train_loader,optimizer,epoch,criteon):
class_name = model.__class__.__name__ model.train()
loss = 0
for idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
pred = model(data)
if class_name == 'Net':
loss = F.nll_loss(pred, target)
elif class_name == 'Net1':
loss = criteon(pred, target) optimizer.zero_grad()
loss.backward()
optimizer.step() if idx % 100 == 0:
print("train epoch: {}, iteration: {}, loss: {}".format(epoch, idx, loss.item()))

这里定义了train函数的训练过程。class_name中存放了当前使用的模型名字。 model.train()开启训练模式。在for idx, (data, target) in enumerate(train_loader):取出当前数据集的idx,data和种类target。循环中,先把data和target放置于device上,pred = model(data)会进行一次前传,获得相应数据的预测种类pred

对不同的模型,我采用了不同定义损失函数的方式,这里需要结合下面的模型结构来看。optimizer.zero_grad()会将上轮累计的梯度清空,之后loss.backward()梯度反向传播,利用optimizer.step()更新参数。而当if idx % 100 == 0:也就是迭代的数据批次到达100的倍数了,就会输出相关信息。

def test(model,device,test_loader,criteon):
class_name = model.__class__.__name__
model.eval()
total_loss = 0 #caculate total loss
correct = 0
with torch.no_grad():
for idx, (data, target) in enumerate(test_loader):
data, target = data.to(device), target.to(device)
pred = model(data)
if class_name == 'Net':
total_loss += F.nll_loss(pred, target,reduction="sum").item()
elif class_name == 'Net1':
total_loss += criteon(pred, target).item()
correct += pred.argmax(dim=1).eq(target).sum().item() total_loss /= len(test_loader.dataset)
acc = correct/len(test_loader.dataset)
print("Test loss: {}, Accuracy: {}%".format(total_loss,acc*100))

test函数总体结构类似,model.eval()将会把模型调整测试模式,with torch.no_grad():来声明测试模式下不需要积累梯度信息。correct += pred.argmax(dim=1).eq(target).sum().item()则是会计算出预测对了的数目,之后通过total_loss计算总误差和acc计算准确率。

class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1, 20, kernel_size=5,stride=1)
self.conv2 = nn.Conv2d(20,50,kernel_size=5,stride=1)
self.fc1 = nn.Linear(4*4*50, 500)
self.fc2 = nn.Linear(500, 10) def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x,2,2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x,2,2)
x = x.view(-1,4*4*50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
x = F.log_softmax(x,dim=1)
return x

Net不过是一个具有两个卷积层和两个线性全连层的网络。self.conv1 = nn.Conv2d(1, 20, kernel_size=5,stride=1)表示conv1是一个接受1个channel的tensor输出20个channel的tensor,且卷积大小为5,步长为1的卷积层。self.fc1 = nn.Linear(4*4*50, 500)则是接收一个4 * 4 * 50长的一维tensor并且输出长为500的一维tensor。

前传函数forward中,x作为输入的数据,输入后会通过 conv1->relu->pooling->conv2->relu->pooling->view将多维tensor转化成一维tensor->fc1->relu->fc2->log_softmax来获得最终的x的值。这里就需要提train和test函数中的if和elif语句了。使用的时Net时,loss = F.nll_loss(pred, target),这是因为log_softmax之后使用nll_loss和直接使用 nn.CrossEntropyLoss()是等效的,因此:

class Net1(nn.Module):
def __init__(self):
super(Net1,self).__init__()
self.conv_unit=nn.Sequential(
nn.Conv2d(1, 20, kernel_size=5,stride=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2,stride=2),
nn.Conv2d(20,50,kernel_size=5,stride=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.fc_unit=nn.Sequential(
nn.Linear(4*4*50, 500),
nn.ReLU(),
nn.Linear(500, 10)
) def forward(self, x):
x = self.conv_unit(x)
x = x.view(-1,4*4*50)
x = self.fc_unit(x)
return x

Net1中最后并没有使用log_softmax,是因为直接在train的过程中,使用了nn.CrossEntropyLoss()。此外,Net1和Net不同的地方也就是在结构中使用了nn.Sequential()来单元化卷积层和全连层。

if __name__ == '__main__':
main()

之后就可以使用了!

在命令行中使用:

$ python main.py

就会按照默认的参数训练一个Mnist分类器了。

第三轮的效果:

train epoch: 2, iteration: 1300, loss: 0.010509848594665527
train epoch: 2, iteration: 1400, loss: 0.0020529627799987793
train epoch: 2, iteration: 1500, loss: 0.0027058571577072144
train epoch: 2, iteration: 1600, loss: 0.010049819946289062
train epoch: 2, iteration: 1700, loss: 0.0352507084608078
train epoch: 2, iteration: 1800, loss: 0.009431719779968262
Test loss: 0.01797709318200747, Accuracy: 99.42833333333333%

如果希望查看参数列表,则可以在命令行使用:

$ python main.py -h

就会出现:

usage: main.py [-h] [-a ARCH] [--epochs N] [--momentum M] [-b N] [--lr LR]

PyTorch Mnist Training

optional arguments:
-h, --help show this help message and exit
-a ARCH, --arch ARCH model archtecture: Net|Net1(default:Net)
--epochs N number of total epochs
--momentum M momentum
-b N, --batchsize N mini-batch size
--lr LR, --learning-rate LR
initial learning rata

于是如果想要使用Net1,lr为0.001的方式训练,就可以按照这样:

$ python main.py -a Net1 --lr 0.001

第三轮结果:

train epoch: 2, iteration: 1200, loss: 0.03096039593219757
train epoch: 2, iteration: 1300, loss: 0.060124486684799194
train epoch: 2, iteration: 1400, loss: 0.08865253627300262
train epoch: 2, iteration: 1500, loss: 0.13717596232891083
train epoch: 2, iteration: 1600, loss: 0.003894627094268799
train epoch: 2, iteration: 1700, loss: 0.06881710141897202
train epoch: 2, iteration: 1800, loss: 0.03184908628463745
Test loss: 0.0013615453808257978, Accuracy: 98.69666666666667%

至此,你获得了一个Mnist训练器的训练方法。

如何使用Pytorch迅速实现Mnist数据及分类器的更多相关文章

  1. PyTorch 数据集类 和 数据加载类 的一些尝试

    最近在学习PyTorch,  但是对里面的数据类和数据加载类比较迷糊,可能是封装的太好大部分情况下是不需要有什么自己的操作的,不过偶然遇到一些自己导入的数据时就会遇到一些问题,因此自己对此做了一些小实 ...

  2. 使用Tensorflow操作MNIST数据

    MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例.而TensorFlow的封装让使用MNIST数据集变得更加方便.MNIST数据集是NIST数据集的 ...

  3. MNIST数据集和IDX文件格式

    MNIST数据集 MNIST数据集是Yan Lecun整理出来的. NIST是美国国家标准与技术研究院(National Institute of Standards and Technology)的 ...

  4. pytorch 加载mnist数据集报错not gzip file

    利用pytorch加载mnist数据集的代码如下 import torchvision import torchvision.transforms as transforms from torch.u ...

  5. 基于MNIST数据的卷积神经网络CNN

    基于tensorflow使用CNN识别MNIST 参数数量:第一个卷积层5x5x1x32=800个参数,第二个卷积层5x5x32x64=51200个参数,第三个全连接层7x7x64x1024=3211 ...

  6. tensorflow学习笔记——使用TensorFlow操作MNIST数据(2)

    tensorflow学习笔记——使用TensorFlow操作MNIST数据(1) 一:神经网络知识点整理 1.1,多层:使用多层权重,例如多层全连接方式 以下定义了三个隐藏层的全连接方式的神经网络样例 ...

  7. tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)

    续集请点击我:tensorflow学习笔记——使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...

  8. GAN生成式对抗网络(三)——mnist数据生成

    通过GAN生成式对抗网络,产生mnist数据 引入包,数据约定等 import numpy as np import matplotlib.pyplot as plt import input_dat ...

  9. EOFError: Compressed file ended before the end-of-stream marker was reached解决办法(在Windows下查看已下载的MNIST数据文件)

    出现这个问题的原因是因为文件下载到一半就中断了,解决办法是删除datasets中下载到一半的数据包. 下面以我遇到的问题为例: 我下载数据下载到最后一个包就没有反应了,于是我强制终止了运行,可能是因为 ...

随机推荐

  1. Linux/UNIX 上安装 MySQL

    Linux/UNIX 上安装 MySQL Linux平台上推荐使用RPM包来安装Mysql,MySQL AB提供了以下RPM包的下载地址: MySQL - MySQL服务器.你需要该选项,除非你只想连 ...

  2. nc命令的用法

    1.什么是nc netcat(nc)是一个简单而有用的工具,可以使用tcp或者udp进行网络间读写数据,传输文件,接收发送数据,验证网络是否畅通. 2.命令行: 1) -l 用于指定nc将处于侦听模式 ...

  3. vue_webpack初始化项目

    整体架构:此处npm安装过于缓慢,因此使用的是淘宝的镜像cnpm vue+webpack 初始化项目:1.安装vue: cnpm install vue 检验版本: vue -V2.创建一个vue项目 ...

  4. 61)PHP,立即跳转

    一般使用   header(‘Location:’)来进行跳转. ******************************************************************* ...

  5. 企业框架-Spring

    1.什么是Spring Spring是最受欢迎的企业级Java应用程序开发框架,数以百万的来自世界各地的开发人员使用Spring框架来创建性能好.易于测试.可重用的代码. Spring框架是一个开源的 ...

  6. 吴裕雄--天生自然 R语言开发学习:基本图形

    #---------------------------------------------------------------# # R in Action (2nd ed): Chapter 6 ...

  7. 途牛与十八好汉撕X又言和 到底想干啥?

    到底想干啥?" title="途牛与十八好汉撕X又言和 到底想干啥?"> 天下大势,合久必分,分久必合.很多看起来热闹哄哄的"劳燕分飞"事件,最 ...

  8. 使用JavaServer Faces技术的Web模块:hello1 example

    该hello1应用程序是一个Web模块,它使用JavaServer Faces技术来显示问候语和响应.您可以使用文本编辑器查看应用程序文件,也可以使用NetBeans IDE. 此应用程序的源代码位于 ...

  9. CSAPC08台湾邀请赛_T1_skyline

    题目链接:CSAPC08台湾邀请赛_T1_skyline 题目描述 一座山的山稜线由许多片段的45度斜坡构成,每一个片段不是上坡就是下坡. / /​ * / ​/ * /  // ​/ // / 在我 ...

  10. Selenium2自动化——初体验

    一.Windows下的环境搭建 1.安装Python 访问Python官网:https://www.python.org/ 2.安装setuptools与pip setuptools是Python e ...