从零实现Softmax回归:d2l-ai项目实战解析
引言
Softmax回归是机器学习中最基础也最重要的分类算法之一,它是逻辑回归在多分类问题上的自然扩展。本文将带你从零开始实现一个完整的Softmax回归模型,并应用于经典的Fashion-MNIST数据集。
Softmax函数原理
Softmax函数的核心作用是将一组实数转换为概率分布。给定一个输入向量x,Softmax的计算过程分为三步:
- 对每个元素进行指数运算:exp(x_i)
- 计算所有元素的指数和(称为配分函数)
- 每个元素的指数值除以配分函数
数学表达式为: $$ \mathrm{softmax}(x_i) = \frac{\exp(x_i)}{\sum_{j=1}^n \exp(x_j)} $$
这种转换确保了输出值在0到1之间,并且所有输出值的和为1,完美符合概率分布的要求。
实现细节
1. Softmax函数实现
我们首先实现Softmax函数。需要注意的是,直接计算指数可能会遇到数值不稳定的问题(特别是对于很大的输入值),但在基础实现中我们先不考虑这个问题:
def softmax(X):
X_exp = d2l.exp(X) # 对每个元素求指数
partition = d2l.reduce_sum(X_exp, 1, keepdims=True) # 计算每行的和
return X_exp / partition # 应用广播机制进行归一化
2. 模型定义
Softmax回归模型的结构相对简单:
class SoftmaxRegressionScratch(d2l.Classifier):
def __init__(self, num_inputs, num_outputs, lr, sigma=0.01):
super().__init__()
self.save_hyperparameters()
# 初始化权重矩阵 (784×10) 和偏置 (10)
self.W = d2l.randn(num_inputs, num_outputs) * sigma
self.b = d2l.zeros(num_outputs)
# 设置需要计算梯度
self.W.requires_grad = True
self.b.requires_grad = True
3. 前向传播
前向传播过程将输入图像展平后与权重矩阵相乘,再加上偏置,最后应用Softmax函数:
def forward(self, X):
X = d2l.reshape(X, (-1, self.W.shape[0])) # 展平图像
return softmax(d2l.matmul(X, self.W) + self.b)
4. 交叉熵损失函数
交叉熵损失是多分类问题中最常用的损失函数,它衡量预测概率分布与真实分布之间的差异:
def cross_entropy(y_hat, y):
return -d2l.reduce_mean(d2l.log(y_hat[list(range(len(y_hat))), y]))
这里使用了高级索引技巧直接从预测概率矩阵中选取对应真实标签的概率值。
训练过程
我们使用Fashion-MNIST数据集进行训练,该数据集包含10类服装物品的28×28灰度图像:
data = d2l.FashionMNIST(batch_size=256)
model = SoftmaxRegressionScratch(num_inputs=784, num_outputs=10, lr=0.1)
trainer = d2l.Trainer(max_epochs=10)
trainer.fit(model, data)
模型评估
训练完成后,我们可以评估模型在验证集上的表现,并可视化一些分类错误的样本:
X, y = next(iter(data.val_dataloader()))
preds = d2l.argmax(model(X), axis=1)
wrong = d2l.astype(preds, y.dtype) != y
# 可视化错误分类样本
data.visualize([X[wrong], y[wrong]], labels=...)
数值稳定性问题
在实际应用中,直接实现Softmax可能会遇到数值不稳定的情况:
- 当输入值很大时(如100),exp(100)会超出浮点数的表示范围
- 当输入值很小时(如-100),exp(-100)会下溢为0
解决方案通常是在计算Softmax前,先从所有输入中减去最大值:
def stable_softmax(X):
X_max = d2l.reduce_max(X, axis=1, keepdims=True)
X_exp = d2l.exp(X - X_max) # 减去最大值防止溢出
partition = d2l.reduce_sum(X_exp, axis=1, keepdims=True)
return X_exp / partition
总结
通过本文,我们完整实现了Softmax回归模型,包括:
- Softmax函数的原理与实现
- 模型结构与参数初始化
- 交叉熵损失函数的计算
- 完整的训练与评估流程
虽然这个实现足够教学使用,但在实际项目中,我们通常会使用深度学习框架内置的优化实现,它们不仅效率更高,而且包含了各种数值稳定性的处理。
扩展思考
- 如何改进交叉熵损失的计算效率?
- 当类别数量非常大时(如语言模型中的词汇表),Softmax计算会变得非常昂贵,有什么解决方案?
- 为什么在医疗诊断等场景中,仅返回最可能的标签可能不够?
- 学习率和批量大小如何影响模型的训练动态?
通过深入思考这些问题,你可以更好地理解Softmax回归的局限性和改进方向。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考