240619_昇思学习打卡-Day1-损失函数_交叉熵损失函数

本来是参加昇思的打卡活动,今天看的是FCN图像分割,但是写到现在发现今天好像写不完这个,就暂时先把写的用来给那篇笔记引用的交叉熵损失函数博客发出来
在这里插入图片描述

交叉熵损失函数公式

每个样本经过模型后都会得到一个预测值,预测值与真实值的差值就是损失。在模型训练过程中,我们需要关注损失值,使得其无限缩小,即预测值无限接近于真实值,故需要选择合适的损失函数。

损失函数通常位于前向传播和后向传播之间,接受模型的预测值,然后计算出差值后提供给后向传播,以便优化模型参数。

本文主要对交叉熵损失函数进行举例讲解。

交叉熵函数的公式为
H ( p , q ) = − ∑ i = 1 n p ( x i ) l o g ( q ( x i ) ) H(p,q)=−∑_{i=1}^{n}{p(x_i)log(q(x_i))} H(p,q)=i=1np(xi)log(q(xi))

其中log是以e为底的,也就是ln,但我不知道为什么网上的教程全是写的log,刚学习时我还算了半天对不上数值,我这里就写ln了
H ( p , q ) = − ∑ i = 1 n p ( x i ) ln ⁡ ( q ( x i ) ) H(p,q)=−∑_{i=1}^{n}{p(x_i)\ln(q(x_i))} H(p,q)=i=1np(xi)ln(q(xi))
一看公式就头疼,下面举例说明:

二分类问题

现在拿到一张图片,我们要判断他是猫还是不是猫,在经过我们的模型进行预测后,他的预测结果为[0.6,0.4],即:

预测值(q)真实值§
0.61
不是猫0.40

此时模型会判断该图片中的是猫,此时损失函数为
L o s s = − ( 0 ∗ ln ⁡ ( 0.4 ) + 1 ∗ ln ⁡ ( 0.6 ) ) = 0.51 Loss=-(0*\ln(0.4)+1*\ln(0.6))=0.51 Loss=(0ln(0.4)+1ln(0.6))=0.51
此时预测正确,损失值较低,但如果此时有三个样本,情况如下时:

预测值(q)真实值§
0.6 0.3 0.11 0 1
不是猫0.4 0.7 0.90 1 0

参数意思是第一张图预测是猫的概率是0.6,不是猫的概率是0.4,第二张图预测是猫的概率是0.3,不是猫的概率是0.7,以此类推

此时损失函数为
L o s s 1 = − ( 1 ∗ ln ⁡ ( 0.6 ) + 0 ∗ ln ⁡ ( 0.4 ) ) = 0.51 Loss1=-(1*\ln(0.6)+0*\ln(0.4))=0.51 Loss1=(1ln(0.6)+0ln(0.4))=0.51

L o s s 2 = − ( 1 ∗ ln ⁡ ( 0.7 ) + 0 ∗ ln ⁡ ( 0.3 ) ) = 0.36 Loss2=-(1*\ln(0.7)+0*\ln(0.3))=0.36 Loss2=(1ln(0.7)+0ln(0.3))=0.36

L o s s 3 = − ( 1 ∗ ln ⁡ ( 0.1 ) + 0 ∗ ln ⁡ ( 0.9 ) ) = 2.30 Loss3=-(1*\ln(0.1)+0*\ln(0.9))=2.30 Loss3=(1ln(0.1)+0ln(0.9))=2.30

此时对三次预测的Loss求平均为
L o s s = 1 3 ( 0.51 + 0.36 + 2.30 ) = 1.06 Loss=\frac{1}{3}(0.51+0.36+2.30)=1.06 Loss=31(0.51+0.36+2.30)=1.06
在第三次预测中,错误预测且极大的偏离了正确答案,导致Loss值异常高

多分类问题

还是拿到一张图片,我们要判断他是猫、狗、兔其中一类,此时经过我们的模型后他的预测结果如下:

预测值(q)真实值§
0.41
0.40
0.30

此时Loss值为
L o s s = − ( 1 ∗ ln ⁡ ( 0.4 ) + 0 ∗ ln ⁡ ( 0.4 ) + 0 ∗ ln ⁡ ( 0.3 ) ) = 0.91 Loss=-(1*\ln(0.4)+0*\ln(0.4)+0*\ln(0.3))=0.91 Loss=(1ln(0.4)+0ln(0.4)+0ln(0.3))=0.91
假设有三张图

预测值(q)真实值§
0.4 0.7 0.21 0 0
0.4 0.2 0.50 1 0
0.3 0.1 0.30 0 1

此时我们错误的预测了两个,可想而知Loss应该很大,以下是计算:
L o s s 1 = − ( 1 ∗ ln ⁡ ( 0.4 ) + 0 ∗ ln ⁡ ( 0.4 ) + 0 ∗ ln ⁡ ( 0.3 ) ) = 0.91 Loss1=-(1*\ln(0.4)+0*\ln(0.4)+0*\ln(0.3))=0.91 Loss1=(1ln(0.4)+0ln(0.4)+0ln(0.3))=0.91

L o s s 2 = − ( 1 ∗ ln ⁡ ( 0.2 ) + 0 ∗ ln ⁡ ( 0.7 ) + 0 ∗ ln ⁡ ( 0.1 ) ) = 1.60 Loss2=-(1*\ln(0.2)+0*\ln(0.7)+0*\ln(0.1))=1.60 Loss2=(1ln(0.2)+0ln(0.7)+0ln(0.1))=1.60

L o s s 3 = − ( 1 ∗ ln ⁡ ( 0.3 ) + 0 ∗ ln ⁡ ( 0.2 ) + 0 ∗ ln ⁡ ( 0.5 ) ) = 1.20 Loss3=-(1*\ln(0.3)+0*\ln(0.2)+0*\ln(0.5))=1.20 Loss3=(1ln(0.3)+0ln(0.2)+0ln(0.5))=1.20

平均Loss为:
L o s s = 1 3 ( 0.91 + 1.60 + 1.20 ) = 1.24 Loss=\frac{1}{3}(0.91+1.60+1.20)=1.24 Loss=31(0.91+1.60+1.20)=1.24

代码实现

# 手写函数计算
import numpy as np

# 二分类交叉熵损失函数
def binary_cross_entropy_loss(y_true, y_pred):
    return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
 
# 多分类交叉熵损失函数
def categorical_cross_entropy_loss(y_true, y_pred):
    num_classes = y_true.shape[1]
    return -np.mean(np.sum(y_true * np.log(y_pred + 1e-9), axis=1))
 
# 示例用法
# 二分类
y_true_binary = np.array([1,0])
y_pred_binary = np.array([0.6,0.4])
loss_binary = binary_cross_entropy_loss(y_true_binary, y_pred_binary)
print("Binary Cross-Entropy Loss:", loss_binary)
 
# 多分类
y_true_categorical = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
y_pred_categorical = np.array([[0.4, 0.4, 0.3], [0.7, 0.2, 0.1], [0.2, 0.5, 0.3]])
loss_categorical = categorical_cross_entropy_loss(y_true_categorical, y_pred_categorical)
print("Categorical Cross-Entropy Loss:", loss_categorical)

'''
输出为
Binary Cross-Entropy Loss: 0.5108256237659907
Categorical Cross-Entropy Loss: 1.2432338126002858
'''

当然,以上只是我们自己根据公式所手动写的交叉熵损失函数,一切都只是我们自以为是,在实际应用过程中,往往直接使用pytorch的torch.nn.CrossEntropyLoss类或者MindSpore的nn.SoftmaxCrossEntropyWithLogits来进行计算,计算结果往往和我们手动计算有一定的偏差,AI是这么解释的:

这段代码实际上运行时,会得到一个与直接计算对数 likelihood 不同的结果,原因在于 nn.CrossEntropyLoss 内部不仅仅计算了单项的 -log(p),而是针对多分类问题进行了综合考虑。具体来说,它是这么操作的:

自动应用了 Softmax:尽管在说明中提到预测值应为未经Softmax处理的原始输出(logits),但在此例中,由于我们直接提供了概率分布,实际上并不符合CrossEntropyLoss的标准使用场景。不过,幸运的是,CrossEntropyLoss函数能够处理概率输入(尽管这不是推荐做法),它会在内部对这些概率进行验证处理。

计算交叉熵:对于多分类问题,它计算了所有类别的交叉熵之和,但由于我们只关心正确类别的项(由于独热编码),其他类别的贡献在数学上表现为0(因为 (y_i \cdot \log(p_i) = 0) 当 (y_i = 0))。

大概意思就是,我们人类只需要考虑怎么简单的手算出来,代码需要考虑的可就多了(手动狗头)

以下是(为了方便看的、简单的、没有逻辑的、实际应用中不会这么用的)代码计算:

# pytorch
import torch
import torch.nn as nn

# 预测值和真实标签整理成批处理形式
predicted_probs = torch.tensor([[0.4, 0.3, 0.3], [0.7, 0.2, 0.1], [0.2, 0.5, 0.3]])
true_labels = torch.tensor([0, 1, 2])  # 注意,这里直接使用类别索引,而非独热编码

# 初始化交叉熵损失函数
criterion = nn.CrossEntropyLoss()

# 计算损失
loss = criterion(predicted_probs, true_labels)

print('Cross Entropy Loss:', loss.item())


'''
输出值为
Cross Entropy Loss: 1.1469497680664062
存在一定偏差
'''
# MindSpore
import mindspore
from mindspore import nn, Tensor

# 预测概率分布(为了演示,我们直接使用概率分布,但实际应用中应是模型未经softmax处理的输出)
predicted_probs = Tensor([[0.4, 0.3, 0.3], [0.7, 0.2, 0.1], [0.2, 0.5, 0.3]], mindspore.float32)

# 真实标签,转换为独热编码
true_labels_one_hot = Tensor([[1, 0, 0], [0, 1, 0], [0, 0, 1]], mindspore.float32)

# 初始化交叉熵损失函数,MindSpore中通常使用nn.SoftmaxCrossEntropyWithLogits
# 但因为我们已经给出了概率分布,这里直接构造损失计算(这不是标准做法)
# 通常情况下,应该直接用模型的logits和真实类别索引

# 注意:下面的步骤是为了演示,实际中应避免,因为MindSpore的损失函数期望未经softmax的logits
# 为了匹配提供的概率分布,我们这里不直接展示标准的损失函数应用,因为这不符合MindSpore的常规实践

# 由于直接使用概率分布计算交叉熵损失不是MindSpore的标准用法,我们通常不会这样编写代码。
# 通常,应该让模型直接输出logits,然后使用nn.SoftmaxCrossEntropyWithLogits

# 实际上,应该设计模型来输出logits,然后使用下面注释掉的部分
# loss_fn = nn.SoftmaxCrossEntropyWithLogits()  # 这是正确使用方式,但需要logits作为输入
# loss = loss_fn(predicted_logits, true_labels)  # predicted_logits应为模型未经softmax的输出

loss_fn = nn.SoftmaxCrossEntropyWithLogits() 
loss = loss_fn(predicted_probs, true_labels_one_hot) 

# 由于直接处理概率分布不是标准流程,这里不提供直接计算损失的代码
# 请理解,正确实践是使用模型的原始输出(logits)和nn.SoftmaxCrossEntropyWithLogits

print('Cross Entropy Loss:', loss.asnumpy())

'''
输出结果为
Cross Entropy Loss: [1.0330688 1.2679496 1.1398311]
手动求均值之后,发现和pytorch的一致
'''

以下是mindspore下的一个正确举例使用

import mindspore
from mindspore import nn, Tensor

# 假设我们有以下模型输出的logits(未经过softmax的预测值)
model_outputs = Tensor([[2.0, 1.0, 0.1], [-1.0, 3.0, 2.0], [0.5, 0.5, 0.5]], mindspore.float32)

# 真实标签
true_labels = Tensor([[1,0,0], [0,1,0], [0,0,1]], mindspore.int32).astype(mindspore.float32)  # 调整为(batch_size, 1)的形状

# 初始化交叉熵损失函数
criterion = nn.SoftmaxCrossEntropyWithLogits(reduction='mean')

# 计算损失
loss = criterion(model_outputs, true_labels)

print('Cross Entropy Loss:', loss.asnumpy())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值