小白学Pytorch使用(1):Mnist手写数字集分类
任务背景
使用Pytorch框架搭建神经网络进行Mnist手写数字集分类。数据可以自行下载进行本地链接,也可直接通过代码下载。
mnist数据集下载链接
一、导入库
# pathlib路径操作模块
from pathlib import Path
# requests是⽤Python语⾔编写,基于urllib,采⽤Apache2 Licensed开源协议的 HTTP 库
import requests
# 持续化模块:把Python对象直接保存到文件里,而不需要先把它们转化为字符串再保存,也不需要用底层的文件访问操作,直接把它们写入到一个二进制文件里
import pickle
# 压缩和解压缩模块
import gzip
# 数组及数组运算函数库
import numpy as np
# 2D绘图库
from matplotlib import pyplot
# Pytorch框架
import torch
import torch.nn.functional as F
from torch import nn
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
from torch import optim
二、下载数据集
# 下载mnist手写数字集,Path中更换自己的文件路径
DATA_PATH = Path("D:/咕泡人工智能-配套资料/配套资料/4.第四章 深度学习核⼼框架PyTorch/第二,三章:神经网络实战分类与回归任务/神经网络实战分类与回归任务/data")
# Mnist数据集存储文件夹(可舍去)
PATH = DATA_PATH/'mnist'
# 建立PATH路径,若自己手动建立也可注释掉
PATH.mkdir(parents=True, exist_ok=True)
# Mnist数据集下载网页
# "http://deeplearning.net/data/mnist/"是老师给的下载网页,但我这无法连接
URL = "https://resources.oreilly.com/live-training/inside-unsupervised-learning/-/tree/master/data/mnist_data/"
# 数据集名称mnist.pkl.gz,可打开网页验证
FILENAME = 'mnist.pkl.gz'
# 若本地没有相应文件则进行下载
if not (PATH/FILENAME).exists():
content = requests.get(URL + FILENAME).content # requests.get()请求指定的页面信息,并返回实体主体
(PATH/FILENAME).open('wb').write(content)
三、处理数据集
# 打开压缩包获取训练集、测试集,gzip进行解压
with gzip.open((PATH/FILENAME).as_posix(), 'rb') as f:
((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
# Mnist数据集中训练集50000个,验证集10000个。x为数据,y为对应标签。
# print(x_train.shape, y_train.shape) #(50000, 784) (50000,) 784是mnist数据集每个样本的像素点个数,训练集中50000个784像素点的样本
# print(x_valid.shape, y_valid.shape) #(10000, 784) (10000,)
# 展示训练集中第一张图,尺寸28*28,灰度
pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")
# 数据需要转换为tensor参与后续建模训练
# map()函数的原型是map(function,iterable,……),将function应用于iterable的每一个元素,结果以列表的形式返回。function一般是数据类型,如int、str、tensor等
x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid))
# n数据个数,c像素点个数
n, c = x_train.shape
# print(x_train.shape) #torch.Size([50000, 784])
# torch.utils.data 中的 TensorDataset 基于一系列张量构建数据集。这些张量的形状可以不尽相同,但第一个维度必须具有相同大小,这是为了保证在使用DataLoader时可以正常地返回一个批量的数据。
train_ds = TensorDataset(x_train, y_train)
valid_ds = TensorDataset(x_valid, y_valid)
def get_data(train_ds, valid_ds, bs):
return (
# DataLoader()数据加载,shuffle是否打乱,batch_size分割尺寸。相当于数据打乱后每次随机抽取batch_size个数据作为一个batch
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs * 2),
)
#batch size
bs = 64
四、建立网络结构
#神经网络结构
class Mnist_NN(nn.Module):
def __init__(self):
super().__init__()
#隐藏层1,784*128
self.hidden1 = nn.Linear(784, 128)
#隐藏层2,128*256
self.hidden2 = nn.Linear(128, 256)
#输出层全连接,256*10
self.out = nn.Linear(256, 10)
#前向传播
def forward(self, x):
#relu激活函数,f(x)=max(0,x)
x = F.relu(self.hidden1(x))
x = F.relu(self.hidden2(x))
x = self.out(x)
return x
#net = Mnist_NN()
# print(net)
'''
Mnist_NN(
(hidden1): Linear(in_features=784, out_features=128, bias=True)
(hidden2): Linear(in_features=128, out_features=256, bias=True)
(out): Linear(in_features=256, out_features=10, bias=True)
)
'''
# 打印神经网络每一层名字、权重和偏置
# for name, parameter in net.named_parameters():
# print(name, parameter, parameter.size())
五、优化函数及损失迭代
#传递网络,选择优化器
def get_model():
model = Mnist_NN()
#SGD和Adam两种优化器,SGD随机梯度下降法。本分类任务中Adam效果优于SGD,以下附Pytorch常用优化器介绍链接:"https://www.jianshu.com/p/1a1339c4acd7"
#lr学习率,学习率太小,则收敛得慢。学习率太大,则损失会震荡甚至变大。
# return model, optim.SGD(model.parameters(), lr=0.001)
return model, optim.Adam(model.parameters(), lr=0.001)
# 损失函数
loss_func = F.cross_entropy
# 损失迭代函数
def loss_batch(model, loss_func, xb, yb, opt=None):
# 计算损失值
loss = loss_func(model(xb), yb)
# 若优化器不为空
if opt is not None:
# 反向传播
loss.backward()
# 更新权重参数
opt.step()
# 梯度归零。Pytorch默认梯度叠加,因此将前一轮梯度归零
opt.zero_grad()
# loss.item()损失值
# print(loss) #tensor(2.3090, grad_fn=<NllLossBackward0>)
# print(loss.item(), len(xb)) #2.309035062789917 64
return loss.item(), len(xb)
六、训练/验证函数
#steps训练轮数,model神经网络,loss_func损失函数,opt优化器
def fit(steps, model, loss_func, opt, train_dl, valid_dl):
for step in range(steps):
#训练
model.train()
for xb, yb in train_dl:
loss_batch(model, loss_func, xb, yb, opt)
#验证
model.eval()
#验证过程不进行梯度更新
with torch.no_grad():
losses, nums = zip(*[loss_batch(model, loss_func, xb, yb)for xb, yb in valid_dl])
val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
print('当前step:'+str(step), '验证集损失:'+str(val_loss))
#计算分类准确率
correct = 0
total = 0
for xb, yb in valid_dl:
outputs = model(xb)
_, predicted = torch.max(outputs.data, 1)
total += yb.size(0)
correct += (predicted == yb).sum().item()
print('准确率是:%d%%'%(100*correct/total))
七、主函数
if __name__ == '__main__':
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
#迭代25轮
fit(25, model, loss_func, opt, train_dl, valid_dl)
训练结果展示:
三层全连接网络训练三轮,准确率在97%。
初学者水平,如有不妥请指出,不胜感激!