【动手学深度学习Pytorch】2. Softmax回归代码
零实现
导入所需要的包:
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.FloatTensor x_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] * 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]
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, fmts
def 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))