原理
交叉熵损失函数是深度学习中分类问题常用的损失函数,特别适用于多分类问题。它通过度量预测分布与真实分布之间的差异,来衡量模型输出的准确性。
交叉熵的数学公式
交叉熵的定义如下:
CrossEntroyLoss=−∑i=1Nyi⋅log(y^i)
\begin{equation}
CrossEntroyLoss = -\sum_{i=1}^{N}y_i \cdot log(\hat{y}_i)
\end{equation}
CrossEntroyLoss=−i=1∑Nyi⋅log(y^i)
- NNN:类别数
- yiy_iyi:真实的标签(用 one-hot 编码表示,只有目标类别对应的位置为 1,其他位置为 0)。
- y^i\hat{y}_iy^i:模型的预测概率,即
softmax
的输出值。
对于单个样本:
Loss=−log(y^c)
\begin{equation}
Loss = -log(\hat{y}_c)
\end{equation}
Loss=−log(y^c)
其中ccc是真实类别的索引。
解释:
- 如果模型的预测概率y^c\hat{y}_cy^c越接近1,则−log(y^c)-log(\hat{y}_c)−log(y^c)越小,损失越大。
- 如果y^c\hat{y}_cy^c越接近0,则−log(y^c)-log(\hat{y}_c)−log(y^c)越大,损失越大。
交叉熵损失和softmax函数的关系
-
模型通常输出logits(未归一化的分数),例如[z1,z2,⋯ ,zN][z_1,z_2,\cdots,z_N][z1,z2,⋯,zN]。
-
softmax函数将logits转化为概率分布:
y^i=zzi∑j=1Nezj \begin{equation} \hat{y}_i = \dfrac{z^{z_i}}{\sum_{j=1}^N e^{z_j}} \end{equation} y^i=∑j=1Nezjzzi -
交叉熵损失结合 softmax,用来计算预测分布与真实分布之间的差异。
在 PyTorch 的 CrossEntropyLoss
中,softmax 和交叉熵是结合在一起实现的,因此你不需要手动调用 softmax。
特性
应用场景:
- 多分类任务,例如图像分类、文本分类等。
- 真实标签通常以整数形式存储(如
0, 1, 2
)。
数值稳定性:
- 由于 softmax 和交叉熵结合在一起,可以避免单独计算 softmax 导致的数值不稳定问题。
Pytorch中的实现
构造函数
PyTorch 提供了 torch.nn.CrossEntropyLoss
:
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduction='mean')
参数说明:
weight
:用于对不同类别赋予不同的权重。ignore_index
:指定忽略某些类别的损失(通常用于处理 padding)。reduction
:决定损失的输出形式:'mean'
(默认):返回损失的均值。'sum'
:返回损失的总和。'none'
:返回每个样本的损失值。
使用示例
1、单样本交叉熵损失
import torch
import torch.nn as nn
# 模型的输出 logits 和真实标签
logits = torch.tensor([[2.0, 1.0, 0.1]]) # 未经过 softmax 的输出
labels = torch.tensor([0]) # 真实标签(类别索引)
# 定义交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(logits, labels)
print("CrossEntropyLoss:", loss.item())
解释:
logits
是未归一化的分数。labels
是类别索引(如类别 0)。- 内部会先对 logits 应用 softmax,再计算交叉熵损失。
计算细节:
a)、给定的数据
- logits: [2.01.00.1]\begin{bmatrix} 2.0 & 1.0 & 0.1 \end{bmatrix}[2.01.00.1]
- 这是模型输出的未归一化分数(logits)。
- labels: [0]\begin{bmatrix} 0 \end{bmatrix}[0]
- 真实标签,表示类别索引(0 表示第一类)。
b)、CrossEntropyLoss 的计算公式,交叉熵损失公式如下:
Loss=−1N∑i=1Nlog(exp(logityi)∑jexp(logitj))
\begin{equation}
Loss = -\dfrac{1}{N} \sum_{i=1}^{N}
log \left(
\dfrac{exp(logit_{y_i})}{\sum_j exp(logit_j)}
\right)
\end{equation}
Loss=−N1i=1∑Nlog(∑jexp(logitj)exp(logityi))
其中:
- NNN: 样本数量(在这里为 1)。
- logitjlogit_jlogitj: 第jjj类的 logit 值。
- yiy_iyi: 样本 iii 的真实类别索引。
c)、具体的步骤
step 1:softmax计算概率分布
softmax函数将logits转换为概率分布:
softmax(zi)=exp(zi)∑jexp(zj)
\begin{equation}
softmax(z_i) = \dfrac{exp(z_i)}{\sum_j exp(z_j)}
\end{equation}
softmax(zi)=∑jexp(zj)exp(zi)
对于logits: [2.01.00.1]\begin{bmatrix} 2.0 & 1.0 & 0.1 \end{bmatrix}[2.01.00.1],计算如下:
- 计算每个元素的指数:
exp(2.0)=e2≈7.389,exp(1.0)=e1≈2.718,exp(0.1)=e0.1≈1.105 \begin{equation} exp(2.0)=e^2 \approx 7.389, \quad exp(1.0)=e^1 \approx 2.718, \quad exp(0.1)=e^{0.1} \approx 1.105 \end{equation} exp(2.0)=e2≈7.389,exp(1.0)=e1≈2.718,exp(0.1)=e0.1≈1.105
- 求和:
sum=7.389+2.718+1.105≈11.212 \begin{equation} sum = 7.389 + 2.718 + 1.105 \approx 11.212 \end{equation} sum=7.389+2.718+1.105≈11.212
- 计算每个类别的概率:
softmax(2.0)=7.38911.212≈0.659,softmax(1.0)=2.71811.212≈0.242,softmax(0.1)=1.10511.212≈0.099 \begin{equation} softmax(2.0)=\dfrac{7.389}{11.212} \approx 0.659,\quad softmax(1.0)=\dfrac{2.718}{11.212} \approx 0.242,\quad softmax(0.1)=\dfrac{1.105}{11.212} \approx 0.099 \end{equation} softmax(2.0)=11.2127.389≈0.659,softmax(1.0)=11.2122.718≈0.242,softmax(0.1)=11.2121.105≈0.099
概率分布为:
[0.6590.2420.099]
\begin{bmatrix} 0.659 & 0.242 & 0.099 \end{bmatrix}
[0.6590.2420.099]
step 2:取真实标签对应的概率
真实标签y=0y=0y=0,对应的概率为第一个类别的softmax输出:
P(y=0)=0.659
\begin{equation} P(y=0)=0.659 \end{equation}
P(y=0)=0.659
step 3:计算交叉熵损失
根据交叉熵公式,损失为:
Loss=−log(P(y=0))=−log(0.659)
\begin{equation}
Loss = -log(P(y=0)) = -log(0.659)
\end{equation}
Loss=−log(P(y=0))=−log(0.659)
计算对数值:
log(0.659)≈−0.416
\begin{equation}
log(0.659) \approx -0.416
\end{equation}
log(0.659)≈−0.416
因此,损失为:
Loss=0.416
\begin{equation}
Loss = 0.416
\end{equation}
Loss=0.416
2、多样本交叉熵损失
logits = torch.tensor(
[[1.5, 0.3, 2.1], [2.0, 1.0, 0.1], [0.1, 2.2, 1.0]]
) # Batch size = 3
labels = torch.tensor([2, 0, 1]) # Batch size = 3
# 定义交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(logits, labels)
print("CrossEntropyLoss:", loss.item())
a)、给定的数据
logits(未归一化的分数):
logits=[1.50.32.12.01.00.10.12.21.0]
logits =
\begin{bmatrix}
1.5 & 0.3 & 2.1 \\
2.0 & 1.0 & 0.1 \\
0.1 & 2.2 & 1.0
\end{bmatrix}
logits=1.52.00.10.31.02.22.10.11.0
labels(真实标签的索引):
labels=[201]
labels =
\begin{bmatrix}
2 & 0 & 1
\end{bmatrix}
labels=[201]
- 第一行对应的类别2
- 第二行对应的类别0
- 第三行对应的类别1
b)、交叉熵损失函数
Loss=−1N∑i=1Nlog(exp(logiti,yi)∑jexp(logiti,j))
\begin{equation}
Loss = -\dfrac{1}{N} \sum_{i=1}^{N}
log \left(
\dfrac{exp(logit_{i,y_i})}{\sum_j exp(logit_{i,j})}
\right)
\end{equation}
Loss=−N1i=1∑Nlog(∑jexp(logiti,j)exp(logiti,yi))
其中:
- N=3N=3N=3: 是批量大小。
- logiti,jlogit_{i,j}logiti,j: 是样本iii对类别jjj的预测分数。
- yiy_iyi: 样本 iii 的真实类别索引。
c)、逐行计算softmax概率和交叉熵损失
step 1:第一行 logits=[1.50.32.1]logits = \begin{bmatrix} 1.5 & 0.3 & 2.1 \end{bmatrix}logits=[1.50.32.1],真实标签 = 2
-
计算softmax:
- 计算每个分数的指数值:
exp(1.5)≈4.481,exp(0.3)≈1.350,exp(2.1)≈8.165 \begin{equation} exp(1.5) \approx 4.481, \quad exp(0.3) \approx 1.350, \quad exp(2.1) \approx 8.165 \end{equation} exp(1.5)≈4.481,exp(0.3)≈1.350,exp(2.1)≈8.165
- 求和
sum=4.481+1.350+8.165≈13.996 \begin{equation} sum = 4.481 + 1.350 + 8.165 \approx 13.996 \end{equation} sum=4.481+1.350+8.165≈13.996
- 计算每个类别的概率
P(0)=4.48113.996≈0.32,P(1)=1.35013.996≈0.096,P(2)=8.16513.996≈0.583 \begin{equation} P(0) = \dfrac{4.481}{13.996} \approx 0.32,\quad P(1) = \dfrac{1.350}{13.996} \approx 0.096,\quad P(2) = \dfrac{8.165}{13.996} \approx 0.583 \end{equation} P(0)=13.9964.481≈0.32,P(1)=13.9961.350≈0.096,P(2)=13.9968.165≈0.583
-
取真实类别2的概率:
P(y=2)=0.583 \begin{equation} P(y=2) = 0.583 \end{equation} P(y=2)=0.583
- 计算损失:
Loss1=−log(0.583)≈0.540 \begin{equation} Loss_1 = -log(0.583) \approx 0.540 \end{equation} Loss1=−log(0.583)≈0.540
step 2:第二行 logits=[2.01.00.1]logits = \begin{bmatrix} 2.0 & 1.0 & 0.1 \end{bmatrix}logits=[2.01.00.1],真实标签 = 0
-
计算softmax:
- 计算每个分数的指数值:
exp(2.0)≈7.389,exp(1.0)≈2.718,exp(0.1)≈1.105 \begin{equation} exp(2.0) \approx 7.389, \quad exp(1.0) \approx 2.718, \quad exp(0.1) \approx 1.105 \end{equation} exp(2.0)≈7.389,exp(1.0)≈2.718,exp(0.1)≈1.105
- 求和
sum=7.389+2.718+1.105≈11.212 \begin{equation} sum = 7.389 + 2.718 + 1.105 \approx 11.212 \end{equation} sum=7.389+2.718+1.105≈11.212
- 计算每个类别的概率
P(0)=7.38911.212≈0.659,P(1)=2.71811.212≈0.242,P(2)=1.10511.212≈0.099 \begin{equation} P(0) = \dfrac{7.389}{11.212} \approx 0.659,\quad P(1) = \dfrac{2.718}{11.212} \approx 0.242,\quad P(2) = \dfrac{1.105}{11.212} \approx 0.099 \end{equation} P(0)=11.2127.389≈0.659,P(1)=11.2122.718≈0.242,P(2)=11.2121.105≈0.099
-
取真实类别0的概率:
P(y=0)=0.659 \begin{equation} P(y=0) = 0.659 \end{equation} P(y=0)=0.659
- 计算损失:
Loss2=−log(0.659)≈0.417 \begin{equation} Loss_2 = -log(0.659) \approx 0.417 \end{equation} Loss2=−log(0.659)≈0.417
step 3:第二行 logits=[0.12.21.0]logits = \begin{bmatrix} 0.1 & 2.2 & 1.0 \end{bmatrix}logits=[0.12.21.0],真实标签 = 1
-
计算softmax:
- 计算每个分数的指数值:
exp(0.1)≈1.105,exp(2.2)≈9.025,exp(1.0)≈2.718 \begin{equation} exp(0.1) \approx 1.105, \quad exp(2.2) \approx 9.025, \quad exp(1.0) \approx 2.718 \end{equation} exp(0.1)≈1.105,exp(2.2)≈9.025,exp(1.0)≈2.718
- 求和
sum=1.105+9.025+2.718≈12.848 \begin{equation} sum = 1.105 + 9.025 + 2.718 \approx 12.848 \end{equation} sum=1.105+9.025+2.718≈12.848
- 计算每个类别的概率
P(0)=1.10512.848≈0.086,P(1)=9.02512.848≈0.703,P(2)=2.71812.848≈0.211 \begin{equation} P(0) = \dfrac{1.105}{12.848} \approx 0.086,\quad P(1) = \dfrac{9.025}{12.848} \approx 0.703,\quad P(2) = \dfrac{2.718}{12.848} \approx 0.211 \end{equation} P(0)=12.8481.105≈0.086,P(1)=12.8489.025≈0.703,P(2)=12.8482.718≈0.211
-
取真实类别1的概率:
P(y=1)=0.703 \begin{equation} P(y=1) = 0.703 \end{equation} P(y=1)=0.703
- 计算损失:
Loss3=−log(0.703)≈0.353 \begin{equation} Loss_3 = -log(0.703) \approx 0.353 \end{equation} Loss3=−log(0.703)≈0.353
d)、批量损失
将每个样本的损失平均:
Loss=Loss1+Loss2+Loss33=0.540+0.417+0.3533≈0.437
\begin{equation}
Loss = \dfrac{Loss_1 + Loss_2 + Loss_3}{3} = \dfrac{0.540 + 0.417 + 0.353}{3} \approx 0.437
\end{equation}
Loss=3Loss1+Loss2+Loss3=30.540+0.417+0.353≈0.437
3、带权重的交叉熵
在某些情况下,类别分布不平衡,可以为不同类别设置权重:
weights = torch.tensor([1.0, 2.0, 3.0]) # 类别权重
criterion = nn.CrossEntropyLoss(weight=weights)
loss = criterion(logits, labels)
print("Weighted CrossEntropyLoss:", loss.item())
4、示例:在神经网络中的应用
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc = nn.Linear(4, 3) # 输入 4 维特征,输出 3 类
def forward(self, x):
return self.fc(x)
# 模型、损失函数和优化器
model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 输入数据和标签
inputs = torch.tensor([[0.5, 1.2, -1.3, 0.8], [0.3, -0.7, 1.0, 1.5]]) # Batch size = 2
labels = torch.tensor([0, 2]) # 两个样本对应的真实类别
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
print("Loss:", loss.item())
# 反向传播和优化
loss.backward()
optimizer.step()
5、总结
-
交叉熵损失函数用于度量预测分布与真实分布之间的差异,是分类问题中的核心工具。
-
在 PyTorch 中,
torch.nn.CrossEntropyLoss
结合了 softmax 和交叉熵计算,使用简单且高效。 -
可以通过参数调整(如权重)来适应不平衡数据集。
整合的代码
import torch
import torch.nn as nn
import torch.optim as optim
def single_instance_CrossEntropyLoss():
# 模型的输出 logits 和真实标签
logits = torch.tensor([[2.0, 1.0, 0.1]]) # 未经过 softmax 的输出
labels = torch.tensor([0]) # 真实标签(类别索引)
# 定义交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(logits, labels)
print("CrossEntropyLoss:", loss.item())
def multi_instance_CrossEntropyLoss():
logits = torch.tensor(
[[1.5, 0.3, 2.1], [2.0, 1.0, 0.1], [0.1, 2.2, 1.0]]
) # Batch size = 3
labels = torch.tensor([2, 0, 1]) # Batch size = 3
# 定义交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(logits, labels)
print("CrossEntropyLoss:", loss.item())
# 定义一个简单的神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc = nn.Linear(4, 3) # 输入 4 维特征,输出 3 类
def forward(self, x):
return self.fc(x)
def apply_deepLearning_CrossEntropyLoss():
# 模型、损失函数和优化器
model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 输入数据和标签
inputs = torch.tensor(
[[0.5, 1.2, -1.3, 0.8], [0.3, -0.7, 1.0, 1.5]]
) # Batch size = 2
labels = torch.tensor([0, 2]) # 两个样本对应的真实类别
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
print("Loss:", loss.item())
# 反向传播和优化
loss.backward()
optimizer.step()
if __name__ == "__main__":
print("*" * 30)
single_instance_CrossEntropyLoss()
multi_instance_CrossEntropyLoss()
apply_deepLearning_CrossEntropyLoss()