引言:
MNIST(Modified National Institute of Standards and Technology)数据集是一个经典的入门级数据集,用于手写数字识别,涵盖了0到9这十个数字。尽管其数据规模较小,但MNIST数据集因其简单性和广泛应用,成为了众多机器学习初学者和研究人员的首选练习项目。
在这篇博客中,我们将基于pytorch实现对MNIST数据集的高效分类。我们将详细介绍数据预处理、模型选择、训练过程以及最终的模型评估和优化方法,希望通过这一过程能够帮助读者更好地理解和掌握图像分类任务的核心概念和实践技巧。
本篇博客是对唐宇迪ai的学习笔记。
环境介绍:
cuda 12.3
python 3.9.2
pytorch 2.3.1 stable
1. 读取数据集
下载数据
from pathlib import Path
import requests
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"
PATH.mkdir(parents=True, exist_ok=True)
URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"
if not (PATH / FILENAME).exists():
content = requests.get(URL + FILENAME).content
(PATH / FILENAME).open("wb").write(content)
解压数据
import pickle
import 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")
查看数据图
from matplotlib import pyplot
import numpy as np
#numpy是一个处理多为数组的计算库,pytorch是基于numpy之上的机器学习库
pyplot.imshow(x_train[2].reshape((28, 28)), cmap="gray")
#将x_train的第二个样本的元素值重塑为28*28的矩阵图,并用“灰度”来映射该图像
print(x_train.shape)

将变量转化为tensor格式
x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid))
#将这四个变量转化为tensor数组格式
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train[2,1:10], y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())
print(x_valid.shape)
#其中50000代表样本数量,784代表像素点,24*24*1

2. 定义线性神经网络
2.1 定义损失函数
import torch.nn.functional as F
loss_func = F.cross_entropy
#F.cross_entropy是pytorch定义的一个损失函数,这边引用了这个函数
2.2 定义神经网络模型
from torch import nn
class Mnist_NN(nn.Module):
def __init__(self):
super().__init__()
self.hidden1 = nn.Linear(784, 128)
#表示一个全连接层,输入特征数为784,输出128个特征值
self.hidden2 = nn.Linear(128, 256)
self.out = nn.Linear(256, 10)
#输出层,表示结果有10个分类
self.dropout=nn.Dropout(0.5)
#设置dropout=0.5,表示50%的神经元会被丢弃,训练时候50%的神经元输出等于0
def forward(self, x):
#定义一个前向传播算法
x = F.relu(self.hidden1(x))
x = self.dropout(x)
x = F.relu(self.hidden2(x))
x = self.dropout(x)
#Relu激活函数,将负数都映射为0,这一步是再经过隐层之后,在进行激活函数
#经过dropout,会有50%的特征值 = 0,目的是为了避免过拟合(随机杀死神经元)
x = self.out(x)
#此时x代表输出类别
return x
# _init_()为构造函数,创建新实例的时候会自动调用,用于初始化该实例的属性.self表示实例化成对象后这个对象本身
#super()._init_()为多类继承,super().__init__(xxx)继承了父类的xxx属性,这里继承了nn.Module里创建模型的一些参数,例如parameter
#Torch前向传播需要定义,反向传播是自动的
#这里的x为64*784,其中64是batch(样本数量),784是特征数量
#model(xb)会自动调用forward函数
实例化一个网络+打印权重参数
net = Mnist_NN()
#实例化一个网络
print(net)

for name, parameter in net.named_parameters():
print(name, parameter,parameter.size())
#权重和偏置一开始是随机生成的
#打印权重和偏置项

2.3 使用TensorDataset和DataLoader来简化
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
train_ds = TensorDataset(x_train, y_train)
#类似一个封装的效果,将输入特征和输出标签封装成一个统一的数据集对象
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)
#传入封装好的数据集对象,创建一个dataloader对象,batch_size表示样本数量,shuffle代表每个epoch中随机打乱数据,有利于模型训练
#随机打乱+切割样本数量,不但可以加速模型训练,也可以消除一些没有影响的因素,例如顺序性
valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)
#测试集不需要打乱顺序,验证集不参与训练,主要是为了找到错误的的点和不好的部分,并且有利于再现
#train训练集 - 模型训练
#valid验证集 - 用于模型调优,评估性能
#test测试机 - 用于测试模型效果,模型训练结束后使用
def get_data(train_ds, valid_ds, bs):
return (
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs * 2),
)
#定义一个dataloader的函数
#调用方法:train_loader, valid_loader = get_data(train_dataset, valid_dataset, batch_size)
2.4 训练和评估神经网络
import numpy as np
def fit(steps, model, loss_func, opt, train_dl, valid_dl):
#steps迭代轮数;model模型;loss_func损失函数,计算损失;opt为优化器,用于更新模型参数
#train_dl和valid_dl提供训练批次
for step in range(steps):
model.train()
#开启训练模式,会启用dropout和batch normalization(批量归一化)
for xb, yb in train_dl:
loss_batch(model, loss_func, xb, yb, opt)
#计算损失函数,并且计算损失和更新参数w,b,这一步是反向传播
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))
print(xb.shape)
print(yb.shape)
![]()
2.5 优化器
from torch import optim
#optim是torch中的一个模块,包含了各种优化算法,例如SGD和Adam
def get_model():
#定义一个优化器
model = Mnist_NN()
return model, optim.Adam(model.parameters(), lr=0.001)
#返回一个神经网络实例和优化器实例,model.parameters()是需要被优化的参数
#这一步会根据预定义的优化算法(例如SGD或Adam),使用参数的梯度来调整参数的值,以最小化损失函数
2.6 计算损失+反向传播
#计算一个批次的损失,opt默认是None
def loss_batch(model, loss_func, xb, yb, opt=None):
loss = loss_func(model(xb), yb)
#计算损失
if opt is not None:
loss.backward()
#反向传播,计算loss对模型的梯度
opt.step()
#根据学习率和梯度更新参数
opt.zero_grad()
#pytroch中梯度默认是累加的,清零
return loss.item(), len(xb)
#返回损失值,以及输入数据的样本数
3. 模型训练
#模型训练
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
fit(10, model, loss_func, opt, train_dl, valid_dl)

4. 结果展示
correct=0
total=0
for xb,yb in valid_dl:
outputs = model(xb)
print(outputs.shape)
print(outputs[0])
print(outputs.data[0]) #不返回梯度
_,predicted = torch.max(outputs.data,1)
#_为预测类别的最大值,predicted为索引
print(_)
print(yb == predicted)
total += yb.size(0)
print(total)
correct += (predicted == yb).sum().item()
#计预测正确的样本数量,并累加到 correct 变量中。(predicted == yb).sum().item() 会返回预测正确的样本数量。
print('Accuracy of the network on the 10000 test images: %d' %(100*correct/total))
#最后打印出模型在验证集上的准确率,计算方法是 100 * correct / total
#torch.max(input, dim, max=None, max_indices=None) -> (Tensor, LongTensor),返回输入张量给定维度上每行的最大值,并同时返回每个最大值的位置索引。
#.item() 方法用于从 PyTorch 张量中提取标量值

结语:
在训练过程中,我们通过多次迭代,不断调整模型参数,并使用验证集来评估模型的性能,防止过拟合和欠拟合的发生。最终,我们的模型在验证集上达到了令人满意的准确率。
本人也是在学习AI的道路上,欢迎大家提出问题和分享答案。
4387

被折叠的 条评论
为什么被折叠?



