如何使用Pytorch迅速实现Mnist数据及分类器
如何使用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_loader和test_loader,将数据集作为可迭代的对象使用。shuffle=True以实现乱序读取数据,一般都会这么设置。num_workers和pin_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还是Net1,to(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))
这就是训练过程,在其中又使用了train和test两个函数(下面会说),根据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数据及分类器的更多相关文章
- PyTorch 数据集类 和 数据加载类 的一些尝试
最近在学习PyTorch, 但是对里面的数据类和数据加载类比较迷糊,可能是封装的太好大部分情况下是不需要有什么自己的操作的,不过偶然遇到一些自己导入的数据时就会遇到一些问题,因此自己对此做了一些小实 ...
- 使用Tensorflow操作MNIST数据
MNIST是一个非常有名的手写体数字识别数据集,在很多资料中,这个数据集都会被用作深度学习的入门样例.而TensorFlow的封装让使用MNIST数据集变得更加方便.MNIST数据集是NIST数据集的 ...
- MNIST数据集和IDX文件格式
MNIST数据集 MNIST数据集是Yan Lecun整理出来的. NIST是美国国家标准与技术研究院(National Institute of Standards and Technology)的 ...
- pytorch 加载mnist数据集报错not gzip file
利用pytorch加载mnist数据集的代码如下 import torchvision import torchvision.transforms as transforms from torch.u ...
- 基于MNIST数据的卷积神经网络CNN
基于tensorflow使用CNN识别MNIST 参数数量:第一个卷积层5x5x1x32=800个参数,第二个卷积层5x5x32x64=51200个参数,第三个全连接层7x7x64x1024=3211 ...
- tensorflow学习笔记——使用TensorFlow操作MNIST数据(2)
tensorflow学习笔记——使用TensorFlow操作MNIST数据(1) 一:神经网络知识点整理 1.1,多层:使用多层权重,例如多层全连接方式 以下定义了三个隐藏层的全连接方式的神经网络样例 ...
- tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)
续集请点击我:tensorflow学习笔记——使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...
- GAN生成式对抗网络(三)——mnist数据生成
通过GAN生成式对抗网络,产生mnist数据 引入包,数据约定等 import numpy as np import matplotlib.pyplot as plt import input_dat ...
- EOFError: Compressed file ended before the end-of-stream marker was reached解决办法(在Windows下查看已下载的MNIST数据文件)
出现这个问题的原因是因为文件下载到一半就中断了,解决办法是删除datasets中下载到一半的数据包. 下面以我遇到的问题为例: 我下载数据下载到最后一个包就没有反应了,于是我强制终止了运行,可能是因为 ...
随机推荐
- Linux/UNIX 上安装 MySQL
Linux/UNIX 上安装 MySQL Linux平台上推荐使用RPM包来安装Mysql,MySQL AB提供了以下RPM包的下载地址: MySQL - MySQL服务器.你需要该选项,除非你只想连 ...
- nc命令的用法
1.什么是nc netcat(nc)是一个简单而有用的工具,可以使用tcp或者udp进行网络间读写数据,传输文件,接收发送数据,验证网络是否畅通. 2.命令行: 1) -l 用于指定nc将处于侦听模式 ...
- vue_webpack初始化项目
整体架构:此处npm安装过于缓慢,因此使用的是淘宝的镜像cnpm vue+webpack 初始化项目:1.安装vue: cnpm install vue 检验版本: vue -V2.创建一个vue项目 ...
- 61)PHP,立即跳转
一般使用 header(‘Location:’)来进行跳转. ******************************************************************* ...
- 企业框架-Spring
1.什么是Spring Spring是最受欢迎的企业级Java应用程序开发框架,数以百万的来自世界各地的开发人员使用Spring框架来创建性能好.易于测试.可重用的代码. Spring框架是一个开源的 ...
- 吴裕雄--天生自然 R语言开发学习:基本图形
#---------------------------------------------------------------# # R in Action (2nd ed): Chapter 6 ...
- 途牛与十八好汉撕X又言和 到底想干啥?
到底想干啥?" title="途牛与十八好汉撕X又言和 到底想干啥?"> 天下大势,合久必分,分久必合.很多看起来热闹哄哄的"劳燕分飞"事件,最 ...
- 使用JavaServer Faces技术的Web模块:hello1 example
该hello1应用程序是一个Web模块,它使用JavaServer Faces技术来显示问候语和响应.您可以使用文本编辑器查看应用程序文件,也可以使用NetBeans IDE. 此应用程序的源代码位于 ...
- CSAPC08台湾邀请赛_T1_skyline
题目链接:CSAPC08台湾邀请赛_T1_skyline 题目描述 一座山的山稜线由许多片段的45度斜坡构成,每一个片段不是上坡就是下坡. / / * / / * / // / // / 在我 ...
- Selenium2自动化——初体验
一.Windows下的环境搭建 1.安装Python 访问Python官网:https://www.python.org/ 2.安装setuptools与pip setuptools是Python e ...