零实现
导入所需要的包:
import torch
from IPython import display
from d2l import torch as d2l
定义数据集参数、模型参数:
batch_size = 256 # 每次随机读取256张图片
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# 将展平每个图片将其视为长度为784的向量,数据集存在10个类别
num_inputs = 784
num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
实现Softmax操作:
# 实现Softmax
def softmax(X):X_exp = torch.exp(X)partition = X_exp.sum(1, keepdim=True) #列数为特征数,行数为样本数return X_exp / partition #广播机制# 尝试进行Softmax操作
X = torch.normal(0, 1, (2,5))
X_prob = softmax(X)
X_prob, X_prob.sum(1)# 实现Softmax回归模型
def net(X):return softmax(torch.matmul(X.reshape(-1,W.shape[0]),W)+b)
定义交叉熵函数:
# 创建一个数据y_hat,其中包含2个样本在3个类别的预测概率,使用y作为y_hat中概率的索引
y = torch.tensor([0,2])
y_hat = torch.tensor([[0.1, 0.3, 0.6],[0.3, 0.2, 0.5]])
y_hat[[0, 1], y]
# 交叉熵函数
def cross_entropy(y_hat, y):return -torch.log(y_hat[range(len(y_hat)),y])
cross_entropy(y_hat, y)
将预测类别于真实元素进行比较:
torch.argmax(input, dim=None, keepdim=False):用于返回指定维度中最大值的索引。通常用于分类任务中从预测输出中找到概率最大的类别
.dtype:
.dtype
是张量的属性,用于返回该张量的 数据类型 (data type)。每个张量都有一个数据类型,用于定义其中存储元素的类型,例如浮点数、整数或布尔值。
tensor.type(dtype=None):不传入参数时,返回一个字符串,表示张量的类型;传入参数时,返回一个新的张量,该张量的类型与指定类型匹配。
x = torch.tensor([1.0, 2.0, 3.0]) # 默认 float32 类型 print(x.type()) # 输出: torch.FloatTensorx_int = x.type(torch.int64) print(x_int) # 输出: tensor([1, 2, 3]) print(x_int.type()) # 输出: torch.LongTensor (int64 的别名)
net.eval():设置为评估模式。
def accuracy(y_hat, y):#计算预测争取的数量# 判断 y_hat 是否为多维张量(例如二维)if len(y_hat.shape)>1 and y_hat.shape[1] > 1:# 如果是多类别分类(第二维大于 1),通过argmax获取每行中概率或分数最大的类别索引y_hat = y_hat.argmax(axis=1)cmp = y_hat.type(y.dtype)==y # 比较预测结果和真实标签是否相等return float(cmp.type(y.dtype).sum()) # 返回预测正确的总数量accuracy(y_hat, y) / len(y)def evaluate_accuracy(net, data_iter):#计算在指定数据集上的模型精度# 如果是 PyTorch 模型,设置为评估模式if isinstance(net, torch.nn.Module):net.eval() metric = Accumulator(2) # 初始化累加器,存储 [正确预测数, 总样本数]for X, y in data_iter:metric.add(accuracy(net(X), y), y.numel()) # 累加每批数据的预测结果return metric[0] / metric[1] # 返回精度:正确预测数 / 总样本数
Accumulator实例:
class Accumulator: #在n个变量上累加def __init__(self, n):self.data = [0.0] * ndef 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]evaluate_accuracy(net, test_iter)
定义训练过程:
net.train():设置为训练模式。
torch.optim.Optimizer.step():用于执行模型参数更新。基于之前计算好的梯度(通过反向传播获得),按照优化算法的规则调整模型参数的值,以最小化损失函数。
def train_epoch_ch3(net, train_iter, loss, updater):if isinstance(net, torch.nn.Module):net.train()metric = Accumulator(3)for X, y in train_iter:y_hat = net(X)l = loss(y_hat, y) #计算损失if isinstance(updater, torch.optim.Optimizer):updater.zero_grad() # 清除梯度l.backward() # 反向传播计算梯度updater.step() # 根据梯度更新模型参数metric.add(float(l) * len(y), # 累加当前批次的损失accuracy(y_hat, y), # 累加当前批次的正确预测数y.size().numel()) # 累加当前批次的样本数else: # 如果是自定义优化器l.sum().backward()updater(X.shape[0]) # 自定义的更新函数,可能需要批次大小作为参数metric.add(float(l.sum()), accuracy(y_hat),y.numel())return metric[0] / metric[2], metric[1] / metric[2]
定义一个在动画中绘制数据的实用程序类:
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 = []d2l.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:d2l.set_axes(self.axes[0],xlabel, ylabel,xlim, ylim,xscale, yscale,legend)self.X, self.Y, self.fmt = None, None, fmtsdef add(self, x, y):if not hasattr(y, "__len__"):y = [y]n = len(y)
训练函数:
# 训练函数
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):# 进行可视化animator = Aminator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3,],legend=['train loss','train acc','test acc'])for epoch in range(num_epochs):train_metrics = train_epoch_ch2(net, train_iter, loss, updater)test_acc = evaluate_accuracy(net, test_iter)animator.add(epoch+1, train_metrics+(test_acc,))train_loss, train_acc = train_metrics# 小批量随机梯度下降来优化训练算法
lr = 0.1
def updater(batch_size):return d2l.sgd([W,b],lr,batch_size)num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater(10))
简洁实现
导入所需要的包:
import torch
from IPython import display
from d2l import torch as d2l
初始化数据集、模型参数、损失函数以及训练优化算法:网络加入高斯噪声,增强泛化性。
torch.nn.init.normal_(tensor, mean=0.0, std=1.0):正态分布(高斯分布)随机初始化张量的值
nn.Sequential(*modules):用于将多个模块(如线性层、激活函数等)按顺序组合成一个模型。适合简单的前向计算场景。
nn.Flatten(start_dim=1, end_dim=-1):将输入张量展平成二维张量,适用于线性层输入。
nn.Linear(in_features, out_features, bias=True):实现一个线性层(全连接层)
nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean'):计算分类任务中的交叉熵损失(适用于多分类问题)。
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False):实现随机梯度下降(SGD)优化算法,用于更新模型参数。net.parameters():返回模型的可训练参数的迭代器。
batch_size = 256 # 每次随机读取256张图片
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)net = nn.Sequential(nn.Flatten(),nn.Linear(784, 100))
def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)net.apply(init_weights);loss = nn.CrossEntropyLoss()trainer = torch.optim.SGD(net.parameters(),lr=0.1)
用之前定义的训练函数训练模型:
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater(10))