本代码基于《动手学深度学习》Pytorch版,第四章多层感知机,第三节多层感知机的简洁实现。对代码进行修改,增加注释,供学习使用。
导入相关库
import matplotlib_inline
import matplotlib.pyplot as plt
import IPython
import torch
from torch import nn
import torchvision
def use_svg_display():
matplotlib_inline.backend_inline.set_matplotlib_formats('svg')
设置Matplotlib图形的轴属性,并在轴上启用网格线
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()
在动画中绘制图表,在训练过程中实时显示数据,在动画中绘制图表指在计算机动画或交互式图形应用程序中实时显示数据变化过程
class Animator:
def __init__(self, xlabel = None, ylabel = None, legend = None, xlim = None, ylim = None, xscale = 'linear',
yscale = 'linear', fmts = ('-', 'm--', 'g-.', 'r:'), nrows = 1, ncols = 1, figsize = (3.5, 2.5)):
if legend is None:
legend = []
use_svg_display()
self.fig, self.axes = plt.subplots(nrows, ncols, figsize = figsize)
if nrows * ncols == 1:
self.axes = [self.axes, ]
self.config_axes = lambda: set_axes(self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
self.X, self.Y, self.fmts = None, None, fmts
def add(self, x, y):
if not hasattr(y, '__len__'):
y = [y]
n = len(y)
if not hasattr(x, '__len__'):
x = [x] * n
if not self.X:
self.X = [[] for _ in range(n)]
if not self.Y:
self.Y = [[] for _ in range(n)]
for i, (a, b) in enumerate(zip(x, y)):
if a is not None and b is not None:
self.X[i].append(a)
self.Y[i].append(b)
self.axes[0].cla()
for x, y, fmt in zip(self.X, self.Y, self.fmts):
self.axes[0].plot(x, y, fmt)
self.config_axes()
IPython.display.display(self.fig)
IPython.display.clear_output(wait = True)
累加数值
class Accumulator:
def __init__(self, n):
self.data = [0.0] * n
def add(self, *args):
self.data = [a + float(b) for a, b in zip(self.data, args)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
计算预测正确的数量
def accuracy(y_hat, y):
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = y_hat.argmax(axis = 1)
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum())
评估在任意模型上对任意数据迭代器可访问的数据集的精度
def evaluate_accuracy(net, data_iter):
if isinstance(net, nn.Module):
net.eval()
metric = Accumulator(2)
with torch.no_grad():
for x, y in data_iter:
metric.add(accuracy(net(x), y), y.numel())
return metric[0] / metric[1]
加载并处理Fashion-MNIST数据集并构建迭代器
def dataset(batch_size, resize = None):
trans = [torchvision.transforms.ToTensor()]
if resize:
trans.insert(0, torchvision.transforms.Resize(resize))
trans = torchvision.transforms.Compose(trans)
train = torchvision.datasets.FashionMNIST(root = 'C:\\Users\\kongbai\\study\\数据集\\fashionMNIST', train = True, transform = trans, download = True)
test = torchvision.datasets.FashionMNIST(root = 'C:\\Users\\kongbai\\study\\数据集\\fashionMNIST', train = False, transform = trans, download = True)
return torch.utils.data.DataLoader(train, batch_size, shuffle = True), torch.utils.data.DataLoader(test, batch_size, shuffle = False)
def init_weights(m):
if type(m) == nn.Linear:
# type(obj)获取对象的类型信息,返回对象的类型信息,检查变量的数据类型,也可用来创建新的类型(类)
# 创建新的类型(类),通过传递三个参数来实现,类名、基类元组和属性字典,创建新类型时,基类元组中的类必须是已定义好的类
# 在实际编程中,通常使用class来定义类,而不是使用type(),type()更多的是在元编程场景中使用
nn.init.normal_(m.weight, std = 0.01)
# init模块,提供各种初始化神经网络权重的方法,对于训练深度学习模型非常重要,可影响模型的收敛速度和最终性能
# nn.init.xavier_uniform_使用均匀分布进行Xavier初始化
# nn.init.xavier_normal_使用正态分布进行Xavier初始化
# nn.init.kaiming_uniform_使用均匀分布进行He初始化,也称Kaiming初始化
# nn.init.kaiming_normal_使用正态分布进行He初始化
# nn.init.constant_使用常数值进行初始化
# nn.init.normal_使用正态分布进行初始化
# nn.init.uniform_使用均匀分布进行初始化
# 初始化方法的选择取决于具体的网络结构和激活函数
# 在某些情况下,可使用默认的初始化方法,PyTorch已经为一些层提供了推荐的初始化方法
# 在训练过程中,可尝试不同的初始化方法,以找到最适合特定任务的初始化策略
# nn.init.normal_(tensor, mean = 0.0, std = 1.0, inplace = False)
# 使用指定的均值和标准差,从正态分布中随机采样值来初始化张量的权重
# tensor需要初始化的张量
# mean正态分布的均值,默认为0.0
# std正态分布的标准差,默认为1.0
# inplace是否在原张量上进行修改,如为True,则直接修改原张量,如为False,则返回新的张量,默认为False
训练模型一轮
def train_epoch(net, train, loss, updater):
if isinstance(net, torch.nn.Module):
net.train()
metric = Accumulator(3)
for x, y in train:
y_hat = net(x)
l = loss(y_hat, y)
if isinstance(updater, torch.optim.Optimizer):
updater.zero_grad()
l.mean().backward()
updater.step()
else:
l.sum().backward()
updater(x.shape[0])
metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
return metric[0] / metric[2], metric[1] / metric[2]
训练模型,在训练集迭代器访问到的数据上训练模型,利用Animator可视化训练进度,每个迭代周期结束时,利用测试集迭代器访问到的数据对模型进行评估
def train(net, train, test, loss, n, updater):
animator = Animator(xlabel = 'epoch', xlim = [1, n], ylim = [0.3, 0.9],
legend = ['train loss', 'train acc', 'test acc'])
for epoch in range(n):
train_metrics = train_epoch(net, train, loss, updater)
test_acc = evaluate_accuracy(net, test)
animator.add(epoch + 1, train_metrics + (test_acc,))
train_loss, train_acc = train_metrics
assert train_loss < 0.5, train_loss
assert train_acc <= 1 and train_acc > 0.7, train_acc
assert test_acc <= 1 and test_acc > 0.7, test_acc
实现
定义模型
net = nn.Sequential(
nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10)
)
# Sequential()创建顺序模型,快速构建和管理神经网络模型
# 顺序模型是一种简单且常用的神经网络结构,将多个层按顺序堆叠在一起,使得输入数据依次通过这些层进行处理
# 将多个神经网络层组合成一个整体,便于管理和使用,方便地构建深度学习模型,无需手动将每一层连接起来
# 参数是一个包含多个神经网络层的列表或元组,每个层都是一个PyTorch的nn.Module类实例
# Sequential()中的层是按照添加顺序依次执行的,因此输入数据会依次通过这些层
# 如需修改顺序模型中的某个层,可直接通过索引访问该层并进行修改
# 如需将顺序模型转换为其他类型的模型(如自定义模型),可使用ModuleList()或ModuleDict()实现更灵活的层组合
初始化模型参数
batch_size = 256
lr = 0.1
n = 10
net.apply(init_weights)
# apply(fn)递归地应用一个函数到模型的所有子模块(包括模型本身)
# 将一个函数应用到模型的所有子模块。这对于批量初始化权重、修改模型结构等操作非常方便
# fn函数,接受一个参数(子模块),并对其进行某种操作。这个函数可以是用户自定义的,也可以是内置的
# apply()是一个递归操作,会遍历模型的所有子模块,因此,在使用时要确保函数fn对所有子模块都是安全的
# 如果只对某些特定的子模块应用函数,可考虑使用children()或named_children()来获取子模块列表,然后手动应用函数
运行结果
Sequential(
(0): Flatten(start_dim=1, end_dim=-1)
(1): Linear(in_features=784, out_features=256, bias=True)
(2): ReLU()
(3): Linear(in_features=256, out_features=10, bias=True)
)
构建交叉熵损失函数
loss = nn.CrossEntropyLoss(reduction = 'none')
构建随机梯度下降优化算法
trainer = torch.optim.SGD(net.parameters(), lr = lr)
train_iter, test_iter = dataset(batch_size)
train(net, train_iter, test_iter, loss, n, trainer)
运行结果