softmax回归的简洁实现
2024-09-27 15:25:41 # 深度学习入门 # 线性神经网络

简洁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import torch
import torch.nn
from d2l import torch as d2l
#1. 加载训练集,测试集数据
batch_size = 256
train_iter, test_iter = d2l.load_mini_fasion_data(256)
#2. 定义模型
net = nn.Sequentail(nn.Flatten(), nn.Linear(28*28, 10))
#3. 初始化模型参数
def init_params(layer):
if type(layer) == nn.Linear:
nn.init.normal_(layer.weight, std = 0.01)
net.apply(init_params)
#4. 损失函数定义
loss = nn.crossEntropy(reduction = 0)
#5.优化算法定义
trainer = torch.optim.sgd(net.parameters, lr = 0.01)
#6. 开始训练
num_epochs = 10
d2l.train_epoch(net, train_iter, test_iter, loss, num_epochs, trainer)

使用数学方法实现的softmax函数的缺陷

我们已经知道softmax函数的具体定义为:
$$
\hat{y}{j}=\frac{\exp \left(o{j}\right)}{\sum_{k} \exp \left(o_{k}\right)}
$$
现在思考这样一个问题,如果oj的值为100,会发生什么?

考虑到float32的表示范围,e^100根本无法表示,此时会产生数值上溢!

一种解决办法是将softmax改进,将每一项都减去最大的项,最后的结果会保持不变,即:
$$
\hat{y}_j = \frac{\exp(o_j - \max(o_k)) \exp(\max(o_k))}{\sum_k \exp(o_k - \max(o_k)) \exp(\max(o_k))}
= \frac{\exp(o_j - \max(o_k))}{\sum_k \exp(o_k - \max(o_k))}.
$$
这样做又会带来一个新的问题,假设现在oj - max(o_k)的值为-100, 那么又会产生数值下溢问题,再几次反向传播后,很容易出现梯度变为NAN的问题

由于softmax函数输出的是每个类别的预测概率,这些中间变量最终会通过交叉熵损失函数(crossEntropy)转换为损失值,而交叉熵损失函数又需要使用到log函数,log和e组合到了一起,就让事情变得简单起来,我们可以直接省去softmax函数的计算,直接将交叉熵损失函数变为下列形式:
$$
\log(\hat{y}_j) = \log\left( \frac{\exp(o_j - \max(o_k))}{\sum_k \exp(o_k - \max(o_k))} \right)
= \log(\exp(o_j - \max(o_k))) - \log\left( \sum_k \exp(o_k - \max(o_k)) \right)
= o_j - \max(o_k) - \log\left( \sum_k \exp(o_k - \max(o_k)) \right).
$$

我们没有将softmax概率传递到损失函数中, 而是在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数