mnist数据集作为一个经典的数据集,不少机器学习深度学习的初学者都对其下过手,它干净的数据集、训练难度也不是很高,作为初学练手,很适合了。今天我也来拜会一下这位经典大哥,看看到底感觉如何。
本次训练使用RNN的网络模型,其实其他模型也可以,但是这两天不是正好温习RNN嘛,就刚好学以致用嘛。
请套路先生上场吧:
1.导包定参
当你新建完一个python文件不管是py文件还是ipynb文件,不管会不会,先把常用的包导入进来再说,常定义几个超参数也请上来:
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
#定义超参数
EPOCH = 10
BATCH_SIZE = 64
LR = 0.001
TIME_STEP = 28
INPUT_SIZE = 28
2.数据加载
先把数据加载进来,虽然mnist数据集可以在线下载,但可能比较慢,我更喜欢先把数据下载到本地再加载进来,这一块的处理和cifar-10那篇就很像了。可以去回顾一下:
#数据加载
train_data = datasets.MNIST (root='./mnist',train=True,transform=transforms.ToTensor(),download=True )
test_data = datasets.MNIST(root='./mnist',train=False,
transform=transforms.ToTensor(),download=True )
print(train_data)
print(test_data)
我们把数据的一些特征打印出来看一眼:
Dataset MNIST
Number of datapoints: 60000
Root location: ./mnist
Split: Train
StandardTransform
Transform: ToTensor()
Dataset MNIST
Number of datapoints: 10000
Root location: ./mnist
Split: Test
StandardTransform
Transform: ToTensor()
可以看到训练集60000个样本,测试集10000个,现在已经都被我们转为tensor格式了。
下面该干嘛了,数据预处理哈,归一化、分批啥的都可以搞起了。
3.数据预处理
图片数据可视化
数据是图像的话我比较喜欢先搞一张出来看看:
plt.imshow(train_data.train_data[0].numpy(),cmap='gray')
plt.show()
可以看到:
这一下就直观多了,我们继续:
数据归一化
test_x = test_data.test_data.type(torch.FloatTensor)[:2000]/255#归一化
test_y = test_data.test_labels.numpy()[:2000]
数据分批
数据分批的套路和之前cifar-10的处理也基本一致:
#数据分批
train_loader = DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True)
test_loader = DataLoader(dataset=test_data,batch_size=BATCH_SIZE,shuffle=True)
#了解一下数据特征,图片是28*28个像素
#print(type(train_loader))
dataiter= iter(train_loader)
imgs,labs = next(dataiter)
print(imgs)#imgs.size()=torch.Size([64, 1, 28, 28])
print(labs)#图片的标签,就是类别,mnist一共有10个类别,故其取值分布在[0,9]之间
4.定义网络模型
#定义网络
class RNN(nn.Module):
def __init__(self):
super(RNN,self).__init__()
#输入是两个月的数据input_size=2,输出是下一个月的数据output_size=1,中间隐藏层的层数任意指定,指定为4
input_size,hidden_size,output_size = INPUT_SIZE,64,10
num_layers = 1
#self.rnn = nn.RNN(input_size,hidden_size,num_layers,batch_first=True)
self.rnn = nn.LSTM(input_size,hidden_size,num_layers,batch_first=True)
#self.rnn = nn.GRU(input_size,hidden_size,num_layers,batch_first=True)
self.out = nn.Linear(hidden_size,output_size)
def forward(self,x):
r_out,_ = self.rnn(x)
out = self.out(r_out[:,-1,:])
#b,s,h = x.shape #batch,seq,hidden
# x = x.view(b*s,h)#转化为线性层的输入
# x = self.out(x)
#x = x.view(b,s,-1)#(99, 1, 1)
return out
5.设置使用GPU
#设置使用GPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
#使用定义好的RNN
model = RNN()
#模型和输入数据都需要to device
mode = model.to(device)
6.定义损失函数及优化器
#定义损失函数,分类问题使用交叉信息熵
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr= LR)
7.模型训练与测试
模型训练一般包含几个步骤:
- 取出数据及标签并均送入GPU或CPU
- 前向传播
- 计算损失函数
- 清空上一轮梯度
- 反向传播
- 参数更新
- 周期打印loss值或tensorboard数据保存绘图
- 保存模型参数(非必要)
- 在训练的同时进行测试,得到准确率(可单独进行)
#开始训练
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('cifar-10')
for epoch in range(EPOCH):
#60000/BATCH_SIZE
for step,data in enumerate(train_loader):
#取出数据及标签
inputs,labels = data
inputs = inputs.view(-1,28,28)
#数据及标签均送入GPU或CPU
inputs,labels = inputs.to(device),labels.to(device)
#前向传播
outputs = model(inputs)
#计算损失函数
loss = criterion(outputs,labels)
#清空上一轮的梯度
optimizer.zero_grad()
#反向传播
loss.backward()
#参数更新
optimizer.step()
#测试,计算准确率
if step%100 == 0:
writer.add_scalar("Train/Loss", loss.item(), epoch*len(train_loader)+step)
test_outputs = model(test_x.cuda())
pred_y = torch.max(test_outputs,1)[1].data.cpu().numpy()
accuracy = float((pred_y == test_y).astype(int).sum())/ float(test_y.size)
print ('Epoch: {}, Step: {}, loss: {}, accuracy: {}'.format(epoch, step, loss.item(), accuracy))
writer.add_scalar("Accuracy", 100.0*accuracy, epoch*len(train_loader)+step)
#print('epoch{} loss:{:.4f}'.format(epoch+1,loss.item()))
我们EPOCH=10时,看一下开头和结尾的记录:
Epoch: 0, Step: 0, loss: 2.3028504848480225, accuracy: 0.092
Epoch: 0, Step: 100, loss: 1.3167051076889038, accuracy: 0.549
Epoch: 0, Step: 200, loss: 0.7903525829315186, accuracy: 0.743
Epoch: 0, Step: 300, loss: 0.6392905712127686, accuracy: 0.806
Epoch: 0, Step: 400, loss: 0.47715359926223755, accuracy: 0.8445
Epoch: 0, Step: 500, loss: 0.3677961826324463, accuracy: 0.827
Epoch: 0, Step: 600, loss: 0.4239515960216522, accuracy: 0.8695
Epoch: 0, Step: 700, loss: 0.10473807901144028, accuracy: 0.891
Epoch: 0, Step: 800, loss: 0.28696632385253906, accuracy: 0.9055
Epoch: 0, Step: 900, loss: 0.09985525161027908, accuracy: 0.904
可以发现第一轮训练模型的loss就收敛的很快了,准确率迅速上升;
再看最后一轮:
Epoch: 9, Step: 0, loss: 0.029277682304382324, accuracy: 0.9715
Epoch: 9, Step: 100, loss: 0.028733856976032257, accuracy: 0.9735
Epoch: 9, Step: 200, loss: 0.045807115733623505, accuracy: 0.9715
Epoch: 9, Step: 300, loss: 0.09034416824579239, accuracy: 0.973
Epoch: 9, Step: 400, loss: 0.022646501660346985, accuracy: 0.97
Epoch: 9, Step: 500, loss: 0.01956302672624588, accuracy: 0.968
Epoch: 9, Step: 600, loss: 0.19199049472808838, accuracy: 0.974
Epoch: 9, Step: 700, loss: 0.1159246563911438, accuracy: 0.97
Epoch: 9, Step: 800, loss: 0.03719437122344971, accuracy: 0.9655
Epoch: 9, Step: 900, loss: 0.05097261443734169, accuracy: 0.9715
最后会有一点反复了,我们使用了tensorboard,可以对训练过程进行可视化:
效果还行,但还可以优化。
8.模型验证
按上面的结果,我们可以找几个样本来验证一下:
# 从测试集选出30个,进行验证
test_x = test_x.cuda()
test_output = model(test_x[:30].view(-1, 28, 28))
pred_y = torch.max(test_output, 1)[1].data.cpu().numpy()
print('预测数字', pred_y)
print('实际数字', test_y[:30])
看一下结果:
预测数字 [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 8 4 9 6 6 5 4 0 7 4 0 1]
实际数字 [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4 9 6 6 5 4 0 7 4 0 1]
对比看一下,准确率还是挺高的。模型还可以继续优化,包括可以试一下RNN和GRU,包括调整一下超参,都是顺手一改的事,这里就不再赘述了。
小小总结
经典数据集的训练总能让人收益很多,首先它能让你对整个训练过程更加熟悉,而且由于网上有大量前人的笔记,这对初学者来说也是很友好的,拿这样的小作业练手,能让我们对框架使用、模型特性、原理技巧都会有更深的理解。
加油,跑起来,就有了风。