网上做兼职网站正规,商丘网站建设推广渠道,餐饮 公司 网站建设,无锡嘉饰茂建设网站的公司文章目录 前置知识nn.CrossEntropyLoss() 交叉熵损失参数数学公式带权重的公式#xff08;weight#xff09;标签平滑#xff08;label_smoothing#xff09; 要点 附录参考链接 前置知识
深度学习#xff1a;关于损失函数的一些前置知识#xff08;PyTorch Loss#x… 文章目录 前置知识nn.CrossEntropyLoss() 交叉熵损失参数数学公式带权重的公式weight标签平滑label_smoothing 要点 附录参考链接 前置知识
深度学习关于损失函数的一些前置知识PyTorch Loss
nn.CrossEntropyLoss() 交叉熵损失 torch.nn.CrossEntropyLoss(weightNone, size_averageNone, ignore_index-100, reduceNone, reductionmean, label_smoothing0.0) This criterion computes the cross entropy loss between input logits and target. 该函数计算输入 logits 和目标之间的交叉熵损失。 参数
weight (Tensor, 可选): 一个形状为 ( C ) (C) (C) 的张量表示每个类别的权重。如果提供了这个参数损失函数会根据类别的权重来调整各类别的损失适用于类别不平衡的问题。默认值是 None。size_average (bool, 可选): 已弃用。如果 reduction 不是 none则默认情况下损失是取平均True否则是求和False。默认值是 None。ignore_index (int, 可选): 如果指定了这个参数则该类别的索引会被忽略不会对损失和梯度产生影响。默认值是 -100。reduce (bool, 可选): 已弃用。请使用 reduction 参数。默认值是 None。reduction (str, 可选): 指定应用于输出的归约方式。可选值为 none、mean、sum。none 表示不进行归约mean 表示对所有样本的损失求平均sum 表示对所有样本的损失求和。默认值是 mean。label_smoothing (float, 可选): 标签平滑值范围在 [0.0, 1.0] 之间。默认值是 0.0。标签平滑是一种正则化技术通过在真实标签上添加一定程度的平滑来避免过拟合。
数学公式 附录部分会验证下述公式和代码的一致性。 假设有 N N N 个样本每个样本属于 C C C 个类别之一。对于第 i i i 个样本它的真实类别标签为 y i y_i yi模型的输出 logits 为 x i ( x i 1 , x i 2 , … , x i C ) \mathbf{x}_i (x_{i1}, x_{i2}, \ldots, x_{iC}) xi(xi1,xi2,…,xiC)其中 x i c x_{ic} xic 表示第 i i i 个样本在第 c c c 类别上的原始输出分数logits。
交叉熵损失的计算步骤如下
Softmax 函数 对 logits 进行 softmax 操作将其转换为概率分布 p i c exp ( x i c ) ∑ j 1 C exp ( x i j ) p_{ic} \frac{\exp(x_{ic})}{\sum_{j1}^{C} \exp(x_{ij})} pic∑j1Cexp(xij)exp(xic) 其中 $ p_{ic} $ 表示第 $ i $ 个样本属于第 $ c $ 类别的预测概率。负对数似然Negative Log-Likelihood 计算负对数似然 ℓ i − log ( p i y i ) \ell_i -\log(p_{iy_i}) ℓi−log(piyi) 其中 ℓ i \ell_i ℓi 是第 i i i 个样本的损失 p i y i p_{iy_i} piyi 表示第 i i i 个样本在真实类别 y i y_i yi 上的预测概率。总损失 计算所有样本的平均损失 reduction 参数默认为 mean L 1 N ∑ i 1 N ℓ i 1 N ∑ i 1 N − log ( p i y i ) \mathcal{L} \frac{1}{N} \sum_{i1}^{N} \ell_i \frac{1}{N} \sum_{i1}^{N} -\log(p_{iy_i}) LN1i1∑NℓiN1i1∑N−log(piyi) 如果 reduction 参数为 sum总损失为所有样本损失的和 L ∑ i 1 N ℓ i ∑ i 1 N − log ( p i y i ) \mathcal{L} \sum_{i1}^{N} \ell_i \sum_{i1}^{N} -\log(p_{iy_i}) Li1∑Nℓii1∑N−log(piyi) 如果 reduction 参数为 none则返回每个样本的损失 ℓ i \ell_i ℓi 组成的张量。 L [ ℓ 1 , ℓ 2 , … , ℓ N ] [ − log ( p i y 1 ) , − log ( p i y 2 ) , … , − log ( p i y N ) ] \mathcal{L} [\ell_1, \ell_2, \ldots, \ell_N] [-\log(p_{iy_1}), -\log(p_{iy_2}), \ldots, -\log(p_{iy_N})] L[ℓ1,ℓ2,…,ℓN][−log(piy1),−log(piy2),…,−log(piyN)]
带权重的公式weight
如果指定了类别权重 w ( w 1 , w 2 , … , w C ) \mathbf{w} (w_1, w_2, \ldots, w_C) w(w1,w2,…,wC)则总损失公式为 L 1 N ∑ i 1 N w y i ⋅ ℓ i ∑ i 1 N w y i ⋅ ( − log ( p i y i ) ) ∑ i 1 N w y i \mathcal{L} \frac{1}{N} \sum_{i1}^{N} w_{y_i} \cdot \ell_i \frac{\sum_{i1}^{N} w_{y_i} \cdot (-\log(p_{iy_i}))}{\sum_{i1}^{N} w_{y_i}} LN1i1∑Nwyi⋅ℓi∑i1Nwyi∑i1Nwyi⋅(−log(piyi))
其中 w y i w_{y_i} wyi 是第 i i i 个样本真实类别的权重。
标签平滑label_smoothing
如果标签平滑label smoothing参数 α \alpha α 被启用目标标签 y i \mathbf{y}_i yi 会被平滑处理 y i ′ ( 1 − α ) ⋅ y i α C \mathbf{y}_i (1 - \alpha) \cdot \mathbf{y}_i \frac{\alpha}{C} yi′(1−α)⋅yiCα
其中 y i \mathbf{y}_i yi 是原始的 one-hot 编码目标标签 y i ′ \mathbf{y}_i yi′ 是平滑后的标签。
总的损失公式会相应调整 ℓ i − ∑ c 1 C y i c ′ ⋅ log ( p i c ) \ell_i - \sum_{c1}^{C} y_{ic} \cdot \log(p_{ic}) ℓi−c1∑Cyic′⋅log(pic)
其中 y i c y_{ic} yic 是第 i i i 个样本在第 c c c 类别上的标签为原标签 y i y_i yi 经过 one-hot 编码后 y i \mathbf{y}_i yi 中的值。对于一个 one-hot 编码标签向量 y i c y_{ic} yic 在样本属于类别 c c c 时为 1否则为 0。
要点
nn.CrossEntropyLoss() 接受的输入是 logits这说明分类的输出不需要提前经过 softmax。如果提前经过 softmax则需要使用 nn.NLLLoss()负对数似然损失。import torch
import torch.nn as nn
import torch.nn.functional as F# 定义输入和目标标签
logits torch.tensor([[2.0, 0.5], [0.5, 2.0]]) # 未经过 softmax 的 logits
target torch.tensor([0, 1]) # 目标标签# 使用 nn.CrossEntropyLoss 计算损失接受 logits
criterion_ce nn.CrossEntropyLoss()
loss_ce criterion_ce(logits, target)# 使用 softmax 后再使用 nn.NLLLoss 计算损失
log_probs F.log_softmax(logits, dim1)
criterion_nll nn.NLLLoss()
loss_nll criterion_nll(log_probs, target)print(fLoss using nn.CrossEntropyLoss: {loss_ce.item()})
print(fLoss using softmax nn.NLLLoss: {loss_nll.item()})# 验证两者是否相等
assert torch.allclose(loss_ce, loss_nll), The losses are not equal, which indicates a mistake in the assumption.
print(The losses are equal, indicating that nn.CrossEntropyLoss internally applies softmax.)Loss using nn.CrossEntropyLoss: 0.2014133334159851Loss using softmax nn.NLLLoss: 0.2014133334159851The losses are equal, indicating that nn.CrossEntropyLoss internally applies softmax.拓展 F.log_softmax() F.log_softmax 等价于先应用 softmax 激活函数然后对结果取对数 log()。它是将 softmax 和 log 这两个操作结合在一起以提高数值稳定性和计算效率。具体的数学定义如下 log_softmax ( x i ) log ( softmax ( x i ) ) log ( exp ( x i ) ∑ j exp ( x j ) ) x i − log ( ∑ j exp ( x j ) ) \text{log\_softmax}(x_i) \log\left(\text{softmax}(x_i)\right) \log\left(\frac{\exp(x_i)}{\sum_j \exp(x_j)}\right) x_i - \log\left(\sum_j \exp(x_j)\right) log_softmax(xi)log(softmax(xi))log(∑jexp(xj)exp(xi))xi−log(j∑exp(xj)) 在代码中F.log_softmax 的等价操作可以用以下步骤实现 计算 softmax。计算 softmax 的结果的对数。 import torch
import torch.nn.functional as F# 定义输入 logits
logits torch.tensor([[2.0, 1.0, 0.1], [1.0, 3.0, 0.2]])# 计算 log_softmax
log_softmax_result F.log_softmax(logits, dim1)# 分开计算 softmax 和 log
softmax_result F.softmax(logits, dim1)
log_result torch.log(softmax_result)print(Logits:)
print(logits)print(\nLog softmax (using F.log_softmax):)
print(log_softmax_result)print(\nSoftmax result:)
print(softmax_result)print(\nLog of softmax result:)
print(log_result)# 验证两者是否相等
assert torch.allclose(log_softmax_result, log_result), The results are not equal.
print(\nThe results are equal, indicating that F.log_softmax is equivalent to softmax followed by log.)Logits:tensor([[2.0000, 1.0000, 0.1000],[1.0000, 3.0000, 0.2000]]) Log softmax (using F.log_softmax):tensor([[-0.4170, -1.4170, -2.3170],[-2.1791, -0.1791, -2.9791]]) Softmax result:tensor([[0.6590, 0.2424, 0.0986],[0.1131, 0.8360, 0.0508]]) Log of softmax result:tensor([[-0.4170, -1.4170, -2.3170],[-2.1791, -0.1791, -2.9791]]) The results are equal, indicating that F.log_softmax is equivalent to softmax followed by log.从结果中可以看到 F.log_softmax 的结果等价于先计算 softmax 再取对数。nn.CrossEntropyLoss() 实际上默认reduction‘mean’计算的是每个样本的平均损失已经做了归一化处理所以不需要对得到的结果进一步除以 batch_size 或其他某个数除非是用作 loss_weight。下面是一个简单的例子import torch
import torch.nn as nn# 定义损失函数
criterion nn.CrossEntropyLoss()# 定义输入和目标标签
input1 torch.tensor([[2.0, 0.5], [0.5, 2.0]], requires_gradTrue) # 批量大小为 2
target1 torch.tensor([0, 1]) # 对应的目标标签input2 torch.tensor([[2.0, 0.5], [0.5, 2.0], [2.0, 0.5], [0.5, 2.0]], requires_gradTrue) # 批量大小为 4
target2 torch.tensor([0, 1, 0, 1]) # 对应的目标标签# 计算损失
loss1 criterion(input1, target1)
loss2 criterion(input2, target2)print(fLoss with batch size 2: {loss1.item()})
print(fLoss with batch size 4: {loss2.item()})Loss with batch size 2: 0.2014133334159851Loss with batch size 4: 0.2014133334159851可以看到这里的 input2 实际上等价于 torch.cat([input1, input1], dim0)target2 等价于 torch.cat([target1, target1], dim0)简单拓展了 batch_size 大小但最终的 Loss 没变这也就验证了之前的说法。目标标签 target 期望两种格式 类别索引: 类别的整数索引而不是 one-hot 编码。范围在 [ 0 , C ) [0, C) [0,C) 之间其中 C C C 是类别数。如果指定了 ignore_index则该类别索引也会被接受即便可能不在类别范围内 使用示例 # Example of target with class indices
import torch
import torch.nn as nnloss nn.CrossEntropyLoss()
input torch.randn(3, 5, requires_gradTrue)
target torch.empty(3, dtypetorch.long).random_(5)
output loss(input, target)
output.backward()类别概率: 类别的概率分布适用于需要每个批次项有多个类别标签的情况如标签平滑等。 使用示例 # Example of target with class probabilities
import torch
import torch.nn as nnloss nn.CrossEntropyLoss()
input torch.randn(3, 5, requires_gradTrue)
target torch.randn(3, 5).softmax(dim1)
output loss(input, target)
output.backward()The performance of this criterion is generally better when target contains class indices, as this allows for optimized computation. Consider providing target as class probabilities only when a single class label per minibatch item is too restrictive. 通常情况下当目标为类别索引时该函数的性能更好因为这样可以进行优化计算。只有在每个批次项的单一类别标签过于限制时才考虑使用类别概率。
附录 用于验证数学公式和函数实际运行的一致性 import torch
import torch.nn.functional as F# 假设有两个样本每个样本有三个类别
logits torch.tensor([[1.5, 2.0, 0.5], [1.0, 0.5, 2.5]], requires_gradTrue)
targets torch.tensor([1, 2])# 根据公式实现 softmax
def softmax(x):return torch.exp(x) / torch.exp(x).sum(dim1, keepdimTrue)# 根据公式实现 log-softmax
def log_softmax(x):return x - torch.log(torch.exp(x).sum(dim1, keepdimTrue))# 根据公式实现负对数似然损失NLLLoss
def nll_loss(log_probs, targets):N log_probs.size(0)return -log_probs[range(N), targets].mean()# 根据公式实现交叉熵损失
def custom_cross_entropy(logits, targets):log_probs log_softmax(logits)return nll_loss(log_probs, targets)# 使用 PyTorch 计算交叉熵损失
criterion torch.nn.CrossEntropyLoss(reductionmean)
loss_torch criterion(logits, targets)# 使用根据公式实现的交叉熵损失
loss_custom custom_cross_entropy(logits, targets)# 打印结果
print(PyTorch 计算的交叉熵损失:, loss_torch.item())
print(根据公式实现的交叉熵损失:, loss_custom.item())# 验证结果是否相等
assert torch.isclose(loss_torch, loss_custom), 数学公式验证失败# 带权重的交叉熵损失
weights torch.tensor([0.7, 0.2, 0.1])
criterion_weighted torch.nn.CrossEntropyLoss(weightweights, reductionmean)
loss_weighted_torch criterion_weighted(logits, targets)# 根据公式实现带权重的交叉熵损失
def custom_weighted_cross_entropy(logits, targets, weights):log_probs log_softmax(logits)N logits.size(0)weighted_loss -log_probs[range(N), targets] * weights[targets]return weighted_loss.sum() / weights[targets].sum()loss_weighted_custom custom_weighted_cross_entropy(logits, targets, weights)# 打印结果
print(PyTorch 计算的带权重的交叉熵损失:, loss_weighted_torch.item())
print(根据公式实现的带权重的交叉熵损失:, loss_weighted_custom.item())# 验证结果是否相等
assert torch.isclose(loss_weighted_torch, loss_weighted_custom, atol1e-6), 带权重的数学公式验证失败# 标签平滑的交叉熵损失
alpha 0.1
criterion_label_smoothing torch.nn.CrossEntropyLoss(label_smoothingalpha, reductionmean)
loss_label_smoothing_torch criterion_label_smoothing(logits, targets)# 根据公式实现标签平滑的交叉熵损失
def custom_label_smoothing_cross_entropy(logits, targets, alpha):N, C logits.size()log_probs log_softmax(logits)one_hot torch.zeros_like(log_probs).scatter(1, targets.view(-1, 1), 1)smooth_targets (1 - alpha) * one_hot alpha / Closs - (smooth_targets * log_probs).sum(dim1).mean()return lossloss_label_smoothing_custom custom_label_smoothing_cross_entropy(logits, targets, alpha)# 打印结果
print(PyTorch 计算的标签平滑的交叉熵损失:, loss_label_smoothing_torch.item())
print(根据公式实现的标签平滑的交叉熵损失:, loss_label_smoothing_custom.item())# 验证结果是否相等
assert torch.isclose(loss_label_smoothing_torch, loss_label_smoothing_custom, atol1e-6), 标签平滑的数学公式验证失败PyTorch 计算的交叉熵损失: 0.45524317026138306根据公式实现的交叉熵损失: 0.4552431106567383PyTorch 计算的带权重的交叉熵损失: 0.5048722624778748根据公式实现的带权重的交叉熵损失: 0.50487220287323PyTorch 计算的标签平滑的交叉熵损失: 0.5469098091125488根据公式实现的标签平滑的交叉熵损失: 0.5469098091125488输出没有抛出 AssertionError验证通过。
参考链接
CrossEntropyLoss - Docs