深度学习Day03 损失函数
一、什么是损失函数(Loss Function)
在机器学习和深度学习中,损失函数(Loss Function)用来衡量模型预测结果与真实结果之间的差距。
简单来说:
损失函数回答的问题是:模型这一次预测“错得有多离谱”。
模型在训练过程中会不断调整参数,使得损失函数的值尽可能小,这个过程就是我们常说的“模型训练”或“模型优化”。
二、损失函数的作用
损失函数在模型训练中起着核心作用,主要体现在三个方面:
1、衡量模型好坏
损失值越大,说明预测结果与真实值差距越大;
损失值越小,说明模型预测越准确。
2、为模型提供优化方向
通过对损失函数求梯度,模型可以知道:
参数应该往哪个方向调整,才能让预测结果更接近真实值。
3、连接预测结果与反向传播
损失函数是反向传播(Backpropagation)的起点,
没有损失函数,模型就无法学习。
三、损失函数的两大类型
根据任务类型的不同,常见的损失函数可以分为 两大类:分类任务的损失函数(Classification Loss)和回归任务的损失函数(Regression Loss)。
3.1 分类任务的损失函数(Classification Loss)
分类问题的目标是从多个类别中预测一个离散类别,例如:
-
垃圾邮件识别
-
图像分类
-
情感分析
这类损失函数更关注:
模型对正确类别的“置信程度”
常见的分类损失函数包括:
-
交叉熵损失(Cross Entropy Loss)
-
二分类交叉熵(Binary Cross Entropy)
3.1.1 交叉熵损失
交叉熵损失一般用于多分类问题。
多分类问题:
比如 3 个类别{0, 1, 2},每个样本只能属于其中一个。
def multi():
"""
演示多分类交叉熵损失函数的使用方法
该函数展示了如何使用PyTorch的CrossEntropyLoss计算多分类问题的损失值。
支持两种真实值格式:类别索引和one-hot编码格式。
参数:
无
返回值:
无
注意事项:
- 当使用类别索引时,数据类型必须是torch.int64 (Long类型)
- 当使用one-hot编码时,数据类型必须是浮点数类型
- 预测值通常需要是浮点数类型并设置requires_grad=True以支持梯度计算
"""
# 真实值
# 可以是类别。注意:类型必须是长整数Long,否则会报错:expected scalar type Long but found Int
# y_true = torch.tensor([1,2],dtype=torch.int64)
# 也可以是热编码之后的数据。注意:类型必须是小数,只要是小数就行
y_true = torch.tensor([[0,1,0], [0,0,1]],dtype=torch.float32)
# 预测值
y_pred = torch.tensor([[0.1,0.6,0.3], [0.7,0.1,0.2]],requires_grad=True,dtype=torch.float32)
# 创建损失函数实例对象
# CrossEntropyLoss = 线性求和结果 + softmax激活函数
loss_obj = nn.CrossEntropyLoss()
# 计算损失值
loss_value = loss_obj(y_pred,y_true)
print(loss_value)
真实值y_true有两种写法:
✅ 写法一:类别索引(最推荐)
y_true = torch.tensor([1, 2], dtype=torch.int64)
含义:
-
第 1 个样本真实类别是 1
-
第 2 个样本真实类别是 2
⚠️ 必须是 torch.int64(Long)
否则会报错:
expected scalar type Long but found Int
✅ 写法二:One-hot 编码(你代码里用的)
y_true = torch.tensor([[0,1,0], [0,0,1]], dtype=torch.float32)
含义:
-
[0,1,0]→ 类别 1 -
[0,0,1]→ 类别 2
⚠️ 注意点:
-
必须是浮点数
-
PyTorch 新版本才支持 one-hot 形式
-
实战中 不推荐,容易出兼容和数值问题
预测值 y_pred
y_pred = torch.tensor([[0.1,0.6,0.3], [0.7,0.1,0.2]], requires_grad=True)
关键点 🔥:
-
不是概率
-
不用 softmax
-
这是模型的 原始输出 logits
👉 PyTorch 会自动做这一步:
logits → LogSoftmax → NLLLoss
也就是 CrossEntropyLoss = 线性求和结果 + softmax激活函数
损失函数本体 CrossEntropyLoss()
loss_obj = nn.CrossEntropyLoss()
loss_value = loss_obj(y_pred, y_true)
📌 数学形式(简化版):
$$L = -\log \left( \frac{e^{z_{true}}}{\sum e^{z_i}} \right)$$
重点
模型输出的y_pred是原始输出logits,数值意义只是表示“哪个类更大”的相对大小,就比如老师给学生的评分:
语文:85 数学:40 英语:60此时:你已经知道语文最好,但这还不是“概率”。
那么,CrossEntropyLoss 到底干了什么?
简单来说,CrossEntropyLoss = LogSoftmax + NLLLoss
众所周知,Softmax是一个激活函数,它可以将logits转换成概率,它的数学公式如下:
$$softmax(zi) = \left( \frac{e^{z_{true}}}{\sum e^{z_i}} \right)$$
这里举一个例子来讲解一下CrossEntropyLoss的过程:
假如这里有一个logits
logits = [2.3, -1.2, 0.5] 真实类别 = 0对它使用CrossEntropyLoss求损失值
第一步:使用LogSoftmax
$$\log \left( \frac{e^{z_{true}}}{\sum e^{z_i}} \right)$$
得到:
[-0.248, -3.76, -1.66]第二步:NLLLoss(负对数似然)
NLLLoss 只取“真实类别对应的那个值”,再取负号。因为真实类别为0,因此:
loss = -(-0.248) = 0.248注意:
若要对y_pred使用CrossEntropyLoss来求损失值时,就不能单独对y_pred使用Softmax,因为CrossEntropyLoss 内部做了一次 softmax,重复做Softmax会对结果产生不好的影响。
3.1.2 二分类交叉熵
二分类交叉熵通常用于二分类问题求损失值。
二分类问题:
只有{0, 1}两个类别,例如:是否是垃圾邮件、是否点击广告。
def binary():
"""
演示二分类交叉熵损失函数的使用方法
该函数展示了如何使用PyTorch的BCELoss计算二分类问题的损失值。
真实值和预测值都应该是概率形式,取值范围在[0,1]之间。
参数:
无
返回值:
无
注意事项:
- 真实值应该是0或1的标签
- 预测值应该是模型输出的概率值
- 通常需要在模型输出后添加sigmoid激活函数
"""
# 真实概率值,要么是0,要么是1
y_true = torch.tensor([0,1,0],dtype=torch.float32)
# 预测概率值
y_pred = torch.tensor([0.6901,0.5459,0.2469],requires_grad=True,dtype=torch.float32)
# 创建损失函数实例对象
loss = nn.BCELoss()
# 计算损失值
loss = loss(y_pred,y_true)
print("二分类的损失值",loss)
真实值 y_true
✔️ 必须是:
0 或 1
浮点数(float)
预测值 y_pred
含义:
已经是 概率(因为代码里使用的是BCELoss,所以默认为概率)
数值范围 [0, 1]
⚠️ 这意味着:
模型最后一层 已经做过 sigmoid
Linear → Sigmoid → 概率 → BCELoss在二分类任务中,如果使用
BCELoss,模型的预测值必须已经被解释为“属于正类的概率”,
因此其取值范围必须在[0, 1]之间。但如果使用BCEWithLogitsLoss,则无需提前对y_pred使用sigmoid,因为BCEWithLogitsLoss里包含了sigmoid。
损失函数
loss = nn.BCELoss()
loss(y_pred, y_true)
📌 数学形式:
$$L = - \left[ y \log(p) + (1-y) \log(1-p) \right]$$
重点
问题类型 损失函数 y_pred 要不要激活 y_true 形式 二分类 BCELoss 要 sigmoid 0/1 二分类 BCEWithLogitsLoss ❌ 不要 0/1 多分类 CrossEntropyLoss ❌ 不要 类别索引
3.2 回归任务的损失函数(Regression Loss)
回归问题的目标是预测一个连续数值,例如:
-
房价预测
-
温度预测
-
销量预测
这类损失函数主要关注的是:
预测值与真实值之间的“数值误差”
不像分类那样谈“概率”,这里就是纯数值误差。
常见的回归损失函数包括:
-
均方误差(MSE, Mean Squared Error)
-
平均绝对误差(MAE, Mean Absolute Error)
3.2.1 L1 损失(MAE)
数学定义
$$ \text{MAE} = \frac{1}{n} \sum |y_{pred} - y_{true}| $$
也就是:
误差的绝对值,再求平均
y_true = torch.tensor([2.0,2.0,2.0])
y_pred = torch.tensor([1.0,1.0,1.9], requires_grad=True)
loss = nn.L1Loss()
loss = loss(y_pred, y_true)
逐个样本的误差是:
真实值 预测值 绝对误差 2.0 1.0 1.0 2.0 1.0 1.0 2.0 1.9 0.1 MAE = (1.0 + 1.0 + 0.1) / 3 ≈ 0.7
L1 的特点
✅ 优点:
-
对异常值(outlier)不敏感
-
大误差不会被无限放大
❌ 缺点:
-
在 0 点不可导
-
梯度是常数 → 优化不够平滑
-
容易卡在“非最优点”
👉 结论:
L1 很少单独作为主损失函数,更多用于正则化
3.2.2 L2 损失(MSE)
数学定义
$$ \text{MSE} = \frac{1}{n} \sum (y_{pred} - y_{true})^2 $$
也就是:
误差平方,再求平均
loss = nn.MSELoss()
loss = loss(y_pred, y_true)
还是刚才那组数据:
误差 平方误差
1.0 1.0 1.0 1.0 0.1 0.01 MSE = (1 + 1 + 0.01) / 3 ≈ 0.67
L2 的特点
✅ 优点:
-
函数光滑
-
梯度连续
-
非常利于梯度下降
❌ 缺点:
-
平方会放大大误差
-
对异常值极其敏感
-
可能导致:
-
梯度爆炸
-
训练不稳定
-
👉 结论:
比 L1 更常用,但在有噪声/异常值时风险大
3.2.3 Smooth L1(Huber Loss)
由于L1和L2自身都有硬伤,所以有结论说:
小误差用 L2,大误差用 L1
Smooth L1 就是为了实现这个结论的思想而被设计出来的损失函数。
Smooth L1(Huber Loss)的定义是一个分段函数:
$$
\text{SmoothL1}(x) =
\begin{cases}
0.5x^2 & |x| < \delta \\
\delta(|x| - 0.5\delta) & |x| \ge \delta
\end{cases}
$$
在 PyTorch 中默认:
δ = 1
令 x = y_pred − y_true(预测误差)
| 误差大小 | Smooth L1 在干什么 | 对应那句“思想” |
|---|---|---|
| |x| < 1 | 用 平方损失(像 MSE) | 小误差用 L2 |
| |x| ≥ 1 | 用 线性损失(像 MAE) | 大误差用 L1 |
👉 关键点只有一个:先看“误差有多大”
y_true = torch.tensor([0,3])
y_pred = torch.tensor([0.6, 0.4])
loss = nn.SmoothL1Loss()
误差是:
第一个:|0.6 - 0| = 0.6(小误差 → L2)
第二个:|0.4 - 3| = 2.6(大误差 → L1)
👉 自动切换策略
Smooth L1 的特点
✅ 优点:
-
比 L2 稳定
-
比 L1 光滑
-
对异常值更鲁棒(鲁棒性是指系统、模型或算法在面对异常、干扰或不确定性条件下仍能保持稳定运行和预期功能的能力。)
-
梯度友好
👉 结论:
回归任务中最常用、最推荐的损失函数之一
更多推荐
所有评论(0)