一、什么是损失函数(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 光滑

  • 对异常值更鲁棒(鲁棒性是指系统、模型或算法在面对异常、干扰或不确定性条件下仍能保持稳定运行和预期功能的能力。)

  • 梯度友好

👉 结论

回归任务中最常用、最推荐的损失函数之一

更多推荐