本文将介绍搭建一个简单的手写字体Mnist分类任务,了解一些基础的torch模块的使用,从较为简单的案例入手,为接下来的学习打下基础(其中不涉及卷积神经网络)。
#首先第一步读取数据
from pathlib import Path
import requests
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist" #mnist数据集文件路径
PATH.mkdir(parents=True, exist_ok=True)
URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"
下载好的mnist数据集为压缩包格式。
from matplotlib import pyplot
import numpy as np
pyplot.imshow(x_train[1].reshape((28, 28)), cmap="gray") #显示数据图片
print(x_train.shape) #打印数据的形状
(50000, 784)
其中,50000表示样本的数量,784(28×28)为每张图片的像素点。mnist数据集是一些手写数字(0~9) 的图片,为灰度图。所以在显示图片需要将其reshape转为二维数据才能显示。如果没有转为二维的话将显示如下错误。
显示一个样本的图片后,进一步了解这x_train[0]的像素的的排列如下。
截取的一部分数据。其中许多的0表示黑色,由于图片为灰度图,中间看得到的白色部分才有数值表示。
降下来将搭建网络模型,在搭建模型前,由于torch要求的数据是Tensor格式,因此需要进行转化。
import torch
import torch.nn.functional as F
#-----------------转为tensor格式
x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid))
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())
通过map映射将array格式转为tensor格式。接下来构建网络模型
from torch import nn
calss Mnist_NN(nn.model):
def __int__(self):
super().__init__()
self.hidden1 = nn.Linear(784, 128)
self.hidden2 = nn.Linear(128, 256)
self.out = nn.Linear(256, 10)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
x = F.relu(self.hidden1(x))
x = self.dropout(x)
x = F.relu(self.hidden2(x))
x = self.dropout(x)
x = self.out(x)
return x
net = Mnist_NN() #实例化网络模型
构建了两层隐藏层网络hidden1和hidden2 。首先第一层hidden1的输入为784,输出为128,所以hidden1为(784,128),同理hidden2为(128,256)。分类任务中输出的结果并不是一个具体的数字,而是当前属于哪一类别的概率最大,Mnist数据集为0~9,因此在这个10个类别中那个概率最大将其作为分类结果。所以输出层为(256,10)。加入dropout防止过拟合。
构建好网络模型之后,下面进一步构建数据集。
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
batch_size = 64
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True) #打包数据集
valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2) #验证数据集不需要打乱
通过DataLoader将完整的数据集按batch_size进行划分送入GPU。 shuffle=True表示需不需要对数据进行打乱。一般训练数据需要打乱,而验证数据则不用。
def get_data(train_ds, valid_ds, bs):
return (
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs *2),
)
loss_func = F.cross_entropy
创建一个get_data函数封装DataLoader。分类问题选用交叉熵损失函数。
模型和数据准备好了,下一步要开始训练了。
import numpy as np
def fit(steps, model, loss_func, 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))
其中,训练阶段需要不断更新权重参数找到最优的模型,而验证阶段不需要更新权重参数即with torch.no_grad()。进入训练需要从DataLoader中取出打包好的数据。
其中优化器部分为
from torch import optim
def get_model():
model = Mnist_NN()
return model, optim.Adam(model.parameters(), lr=0.001)
loss_batch中主要计算损失、更新参数,实现如下:
def loss_batch(model, loss_func, xb, yb, opt=None):
loss = loss_func(model(xb), yb)
loss.backward()
opt.step()
return loss.item(), len(xb)
最后,定义一些函数的参数
train_dl, valid_dl = get_data(train_ds, valid_ds)
model, opt = get_model()
fit(100, model, loss_func, opt, train_dl, valid_dl)
以上就是一个简单的分类模型,利用Mnist数据集,实现数字0~9的分类任务。