softmax 和 交叉熵损失函数

尝试使用 typora

--typora的基本使用方法(快捷键和基础设置)_typora基本操作-CSDN博客

--Typora添加数学公式_typora输入公式-CSDN博客

--【科研学术】Typora之markdown公式,汇总,看这一篇就够了~_typora公式加粗斜体-CSDN博客

softmax

为了解决分类问题而出现,实现为

$$
\pmb{o=Wx+b}\\ \hat{y}=softmax(\pmb{o})\space其中\space\hat{y}_i=\frac{exp(o_j)}{\sum_k{exp(o_k)}}
$$

对于所有j总有 0\leq\hat{y}_j\leq1,且总和为1。并且在预测过程中,选择最有可能的类别。

$$
\underset{j}{argmax\space \hat{y}_j}=\underset{j}{argmax\space\hat{o}_j}
$$

使用了softmax计算模型的输出,然后将此输出送入交叉熵损失。 从数学上讲,这是一件完全合理的事情。 然而,从计算角度来看,指数可能会造成数值稳定性问题。

softmax函数\hat y_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)},其中\hat y_j是预测的概率分布。o_j是未规范化的预测\mathbf{o}的第j个元素。 如果o_k中的一些数值非常大, 那么\exp(o_k)可能大于数据类型容许的最大数字,即上溢(overflow)。 这将使分母或分子变为inf(无穷大), 最后得到的是0、infnan(不是数字)的\hat y_j。 在这些情况下,我们无法得到一个明确定义的交叉熵值。

解决这个问题的一个技巧是: 在继续softmax计算之前,先从所有o_k中减去\max(o_k)。 这里可以看到每个o_k按常数进行的移动不会改变softmax的返回值:

$$
\begin{aligned} \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))}. \end{aligned}
$$

在减法和规范化步骤之后,可能有些o_j - \max(o_k)具有较大的负值。 由于精度受限,\exp(o_j - \max(o_k))将有接近零的值,即下溢(underflow)。 这些值可能会四舍五入为零,使\hat y_j为零, 并且使得\log(\hat y_j)的值为-inf。 反向传播几步后,可能会发现自己面对一屏幕可怕的nan结果。

尽管我们要计算指数函数,但最终在计算交叉熵损失时会取它们的对数。 通过将softmax和交叉熵结合在一起,可以避免反向传播过程中可能会困扰我们的数值稳定性问题。 如下面的等式所示,避免计算\exp(o_j - \max(o_k)), 而可以直接使用o_j - \max(o_k),因为\log(\exp(\cdot))被抵消了

$$
\begin{aligned} \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)}. \end{aligned}
$$

我们也希望保留传统的softmax函数,以备我们需要评估通过模型输出的概率。 但是,我们没有将softmax概率传递到损失函数中, 而是[在交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其对数], 这是一种类似"LogSumExp技巧"的聪明方式。

交叉熵损失函数

交叉熵损失函数是分类问题最常用的损失函数,它是所有标签分布的预期损失值。

$$
l(y,\hat{y})=-\sum_{j=1}^{q}{y_ilog\hat{y}_j}
$$

import torch
from torch import nn
from d2l import torch as d2l
​
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
​
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
​
#权重初始化
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)
​
net.apply(init_weights);
​
#定义损失函数为交叉熵损失函数
loss = nn.CrossEntropyLoss(reduction='none')
​
#优化器为随机梯度下降
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
​
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

补充:

plt.subplots是一个用于创建带有多个子图的函数。它的作用是一次性创建多个子图,并将它们以矩阵的形式排列在一个画窗中。可以通过指定行数和列数来控制子图的布局。

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):  #@save
    """绘制图像列表"""
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            # PIL图片
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸡不叫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值