跳转至

P5-4.1 损失函数(loss function)

Section ID: P5-4.1 Version: v2026.07.20

在 P5-3 章里,我们已经看到:激活函数(activation function)会把非线性(nonlinearity)加入神经网络,并提升表达能力。接下来立刻会跟上的问题就是:

那么,神经网络到底靠什么判断自己当前的输出错了多少?

回答这个问题的标准,就是损失函数(loss function)。

损失函数,就是把模型当前输出与目标之间偏离了多少,压成一个数字的规则。

不过,在教材或框架文档里,lossobjectivecost 有时也会被分得更细一些。通常,loss 指的是样本级偏差或其平均值,而真正被最小化的整体对象,则会被解释为同时包含 batch 平均值与正则化(regularization)的 objective/cost。这一节为了照顾初学者的阅读流程,会先统一用“损失”来讲;但等到后面把梯度(gradient)与优化器(optimizer)连起来时,还会再回头确认:真正被最小化的到底是什么?

如果后面又需要快速回看损失的角色,更适合回到英文概念词汇表里的 loss function 条目

这里先固定下面三句话就够了。

  • 只看到输出,学习还没有真正开始。
  • 必须先把“错得多严重”变成数字,后面的计算才能继续。
  • 这个数字,就是损失(loss)。

需要损失函数的问题

  • 损失(loss)到底把什么变成数字?
  • 为什么在深度学习里,损失函数会成为核心标准?
  • 损失函数与评估指标(metric)有什么不同?
  • 损失值变小,在学习过程中到底意味着什么?

具体有哪些损失函数,以及它们在不同问题类型上的差异,会在 P5-4.2 继续;微分与梯度计算怎样真正接到反向传播(backpropagation),则会在 P5-5.1、P5-5.2 再回来展开。也就是说,这一节首先要抓住的,是损失如何把预测与目标之间的偏差变成学习可以使用的数字

把误差读成数字的判断标准

  • 能把损失函数解释成把当前预测错误变成数字的标准
  • 能区分损失函数与评估指标。
  • 能说明“在学习里让损失变小”到底是什么意思。
  • 能解释:预测值与目标值的差异,会让模型优先更强地修正哪一类错误。

为什么需要损失函数

神经网络会产生输出,但只有输出本身,还不会自动变成学习。模型必须能够对自己提出这个问题:

当前这个结果到底有多糟?

如果这个问题不能被转成数字,下一步就无法展开。

  • 它不知道权重(weight)该往哪个方向改。
  • 它不知道当前模型是否比前一个状态更好。
  • 它也很难比较多个输出候选里到底哪一个更糟。

也就是说,损失函数就是神经网络检查自己结果时所依赖的基准点。

把这个场景换一句话来说,就是:

没有损失,模型就无法自己比较现在是变好了还是变坏了。

损失(loss)在测什么

损失(loss)通常比较的是下面这两个对象:

  • 模型当前给出的输出(prediction)
  • 我们期望的目标(target)

先这样理解就够了:

损失,就是把预测与目标之间的偏差压缩成一个数字。

例如:

  • 正确答案是 10,而预测是 9,损失可能相对较小;
  • 正确答案是 10,而预测是 2,损失可能就更大。

也就是说,损失并不只停在“对 / 错”这种离散判断上,它会进一步把偏离了多少读成一个更连续的数字。

这里读者最容易抓住的关键感觉通常是:

  • 无论是分类还是回归,损失都在处理错的程度
  • 损失往往会给出比 0 或 1 更平滑的数字;
  • 也正因为如此,后面才有办法去计算梯度。

把这个流程再压成很短的一张图,就是下面这样:

flowchart TD
  A["模型预测"]
  B["目标值"]
  C["比较偏差"]
  D["损失值"]
  E["梯度信号"]
  F["参数更新"]

  A --> C
  B --> C
  C --> D
  D --> E
  E --> F

这张图最先要确认的结果是:损失并不只是一个“难看分数”,而是把预测与目标之间的偏差继续传给下一次更新的连接点。

它和 Part 4 里的 metric 有什么不同

在 Part 4 里,我们已经看过 accuracy、precision、recall、F1、RMSE 这类评估指标(metric)。走到这里时,很容易把损失函数和评估指标理解成同一件事。

但它们的角色并不一样。

区分 损失函数(loss function) 评估指标(metric)
主要角色 为学习服务的内部标准 为评估与选择服务的标准
使用时点 在学习过程中持续使用 用于中途检查、early stopping、最终评估
它回答的问题 现在到底错了多少? 实际上表现得有多好?

所以,先把这两件事分开来看会更稳妥:损失函数是模型用来学习的标准,而 metric 是人用来评估和选择模型的标准。

当然,两者也不是完全无关。一个设计得好的损失,通常还是应该和我们真正关心的性能方向有一定对应关系。但它们并不是同一种角色。

为什么损失最后会聚成一个数字

神经网络有很多参数(parameter)。想要修改这些参数,就必须把当前状态先收束成一个可以比较的统一标准。

例如,对多个样本来说:

  • 有些预测只错了一点;
  • 有些预测错得很多;
  • 有些预测几乎已经是对的。

如果把这些状态原样摆着,就很难决定更新方向。因此,损失函数会把多个偏差聚成一个数字。在真实训练代码里,这个数字通常会被整理成 batch 的平均值或总和;有时还会再加上正则化项,变成真正被优化的整体目标函数(objective/cost)。

也就是说,损失函数在学习流程里,承担的是指南针数字的角色。

如果再压成读者可以直接记住的一句话,就是:

损失,是把模型当前走偏的方向压缩成一个数字的信号。

损失变小意味着什么

这里可以先用下面这句话来解释:

损失变小,表示当前模型至少在训练数据上,制造出的预测与目标偏差变少了。

但这里也有一个必须一起记住的提醒:

  • 训练损失(training loss)变小,
  • 并不等于可以立刻断定泛化(generalization)也变好了。

这一点和 Part 4 里关于过拟合(overfitting)与泛化的说明是直接连着的。也就是说,损失非常重要,但不能只靠损失一个数字就判断整个模型。

案例与示例

案例 1. 批次能耗预测

假设我们在预测一个混合批次的总能耗。真实用电量是 5.0kWh,模型预测 A 是 4.8kWh,模型预测 B 是 2.0kWh。人最先会看的是:哪一个更接近真实用量? 只靠这个标准,也能立刻看出预测 A 比预测 B 错得少。但学习必须把这种判断在所有样本上用同一套规则重复下去,所以偏一点偏很多必须被转成一致的数字。损失函数正是把这种差异变成适合学习使用的数字,让模型能够比较:哪个预测更糟,哪个预测相对没那么糟。也就是说,在回归问题里,先要做的不是只问“错没错”,而是把离得有多远变成数字。

比较项 预测 A 预测 B 现在要读到的核心
与真实用量的差距 5.0 - 4.8 = 0.2 5.0 - 2.0 = 3.0 两者都是错的,但偏离程度完全不同。
人最容易先做的判断 几乎正确 错得很多 直觉上能分开,但学习必须把这种差别固定成可重复使用的数字。
从损失视角来看 较小惩罚 较大惩罚 损失函数会把没那么糟的错答更糟得多的错答在数字上拉开。

如果把同样的差距用最简单的规则,例如平方误差(squared error)来读,为什么损失会被拉开也就会更直接。

损失计算示例 预测 A 预测 B 现在要确认的结果
误差(error) 4.8 - 5.0 = -0.2 2.0 - 5.0 = -3.0 两者方向都一样,都是低估了真实值,但大小差很多。
平方误差 (-0.2)^2 = 0.04 (-3.0)^2 = 9.0 B 的损失会比 A 大得多。
更新信号解释 只需要小幅修正的状态 需要大幅纠正的状态 损失数字越大,就越能看清应该优先强烈修正哪一个预测。
人最容易先看的标准 用损失重新阅读后的标准
只看到两者都不是正确答案 即便都是错答,也要把偏得更远的那个在数字上拉得更开
只保留“A 比 B 好”的直觉 这种直觉必须被变成能在所有样本上重复应用的损失数字
在回归里容易觉得“大致接近就行” 学习必须把到底有多接近读成连续值,更新方向才会出现

这个案例里最后要确认的结果很明确:即便两个预测都错,损失也不会停在“两者都错了”这一层,而会给像 B 这样偏得更远的预测更大的数字,让它更早、更强地被修正。

案例 2. 检测状态分类

分类问题也类似。假设正确答案是细微划痕,而模型输出 A 是 正常 0.51,细微划痕 0.49,模型输出 B 是 正常 0.99,细微划痕 0.01

人很容易说“两者都错了”就直接跳过去。但从学习的角度看,这两个错答的权重不应该一样。A 几乎就快答对了,而 B 却以很高的把握往相反方向判断。因此,损失函数还会把它错得有多自信一起纳入,让模型能够区分:虽然都是错答,但有些错答需要被更强地修正。也就是说,在分类问题里,重要的不只是“对还是错”,还包括模型带着什么样的置信度错了。所以,这个案例要确认的结果是:即便两者都错,像 B 这样几乎不给正确类别概率、而且非常自信地答错的预测,是否会得到更大的损失惩罚。

比较项 输出 A 输出 B 现在要读到的核心
预测标签 正常 正常 如果只看最终标签,两者是同一种错答。
给正确答案细微划痕的概率 0.49 0.01 A 几乎答对了,但 B 几乎没有给正确答案任何机会。
人最容易先做的判断 错了 错了 如果只看对错,这两者之间的差别会消失。
从损失视角来看 更接近小惩罚的错答 惩罚会大得多的错答 即便是同样的错答,损失也会把正确类别概率的差距拉开。

即便不去详细推导 cross-entropy,也能立刻确认这个例子:给正确答案的概率越低,损失就越大。

损失计算示例 输出 A 输出 B 现在要确认的结果
给正确答案细微划痕的概率 0.49 0.01 损失会直接看正确类别到底拿到了多少概率。
cross-entropy 示例 -log(p) -log(0.49) ≈ 0.713 -log(0.01) ≈ 4.605 B 的损失远大于 A。
更新信号解释 可能只需稍微调整边界的状态 必须往正确方向大幅拉回的状态 越是很自信地答错,修正信号就越强。

把这两个案例再画成图形来读,会更容易看出:回归损失会随着离目标值越来越远而变大,而分类损失会随着给正确类别的概率越来越低而变大。

平方误差损失形状

交叉熵损失形状

这两张图的目的并不是让读者背公式,而是看清:损失是沿着哪条轴变大的? 对回归来说,关键轴是与目标值之间的距离;对分类来说,关键轴是给正确类别的概率。

人最容易先看的标准 用损失重新阅读后的标准
把两者都看成同样的错答 同时去看它离正确答案多远,以及它错得有多自信
把 0.51 和 0.99 都只读成“都预测为正常” 把 A 读成“差一点就对的错答”,把 B 读成“强烈朝错误方向确信的错答”
容易觉得两格的对 / 错判断就够了 损失会把错答的程度更细致地分成数字

这个案例里最后也要确认同样明确的结果:在分类里,即便是同一种错答,像 B 这样几乎不给正确类别概率的预测,也会得到更大的损失,并被更早、更强地修正。

这两个案例合在一起所展示的核心是一致的:在回归里,更重要的是偏离了多远;在分类里,更重要的是错得有多自信。这些差异必须更强地反映在损失数字里,下一次更新才会更准确。

把这两个案例收成一句话,损失函数的共同角色其实就是:

把模型当前的偏差变成学习可以使用的数字。

而这一节还必须再往前走一步:在案例层面真正闭合为什么损失需要和 metric 分开存在

问题类型 人最容易先看到的结果 如果只看 metric 或最终对错 损失额外揭示了什么 应该优先修正哪个预测
批次能耗预测 A 比 B 更接近真实用量 如果只知道两者都错,修正优先级还不够清楚 B 比 A 偏离得远得多,因此损失会被拉开很多 预测 B
检测状态分类 A 和 B 都预测成正常,所以都错了 只看 accuracy,它们都可能只是同一个 0 B 几乎没给正确答案细微划痕任何概率,因此损失大得多 输出 B

这张表的核心很简单:即使在 metric 或最终对错层面看起来像同一种错答,损失仍然会更细致地区分:到底哪一种错答更危险、更该优先大幅修正。

flowchart TD
  A["看起来像同一种错误的结果"]
  B["人或评估指标(metric)视角"]
  C["两者都只是错了或分数较低"]
  D["损失视角"]
  E["区分偏差大小与置信度差异"]
  F["更大的损失得到更高的修正优先级"]

  A --> B
  B --> C
  A --> D
  D --> E
  E --> F

在这条流程里,读者最后要抓住的结果是:损失不是贴在“对 / 错”后面的辅助分数,而是决定更新优先级的数字。

为什么机器学习与深度学习的教材总会在前面尽早讲损失函数,一个重要原因就是:学习不是简单地反复套规则,而是一个优化(optimization)问题。

深度学习的核心流程,通常可以先读成下面这样:

放入输入
-> 产生输出
-> 计算损失
-> 沿着让损失下降的方向修改参数

也就是说,损失函数不是加在输出后面的可选附件,而是整个学习计算真正被启动的标准。

从课程结构来看,也只有先理解了损失函数,后面这些问题才会变得自然:

  • 为什么需要反向传播(backpropagation);
  • 优化器(optimizer)到底在试图减小什么;
  • 学习率(learning rate)又是在追着什么数字走。

练习与示例

这次练习的目标,是读懂:预测值与目标值之间的差异,怎样以最简单的方式变成损失数字。这里不只看平均损失,还要一起看样本级误差,确认到底是哪个项目把损失拉高了。

输入:

  • 3 个目标值
  • 3 个预测值

输出:

  • 每个样本的平方误差
  • 错得最厉害的样本
  • 平均损失

问题场景:

  • 不能只看最终平均值;还必须一起看每个样本的误差是怎样累积起来的

要确认的概念:

  • 平均损失是由各个样本误差聚合出来的数字;
  • 找出最严重的样本,更容易解释为什么整体损失会变大。

输入(input):

使用上面整理过的样本目标值与预测值。

在看表之前,也可以先自己猜一猜:哪个样本最可能把平均损失拉高。

样本 可以先猜测的损失大小 这样猜的原因
night_shift_batch 中等 目标 3.0 与预测 2.5 的差是 0.5,并不算特别小。
stabilized_batch 最小 目标 1.0 与预测 1.4 的差是 0.4,在三个样本里最小。
restart_delay_batch 最大 目标 2.0 与预测 1.2 的差是 0.8,是三者里拉得最开的。

这张表的目的并不是要求先把平方误差精确算出来,而是先抓住:平均损失不是每个样本都同样贡献,而是会被那些错得更厉害的样本更强地拉动。

如果把这三个 batch 按损失视角整理,就会读成下面这样:

样本 target prediction squared error 现在要读到的结果
night_shift_batch 3.0 2.5 0.25 需要修正,但还不是最优先的 worst case。
stabilized_batch 1.0 1.4 0.16 三个 batch 里错得最轻,损失贡献最小。
restart_delay_batch 2.0 1.2 0.64 它是把平均损失抬得最高的关键错答。

把这三个值再取平均,就得到 mean_loss = 0.35;而错得最严重的 batch,会被读成 restart_delay_batch

这个练习真正重要的是:0.35 这个平均损失数字到底是怎样形成的。

  • 第一个样本错了一些;
  • 第二个样本也错了一些;
  • 第三个样本错得更多。

损失函数会把这些偏差聚成一个数字,让模型能够读出自己当前有多糟。同时,透过样本级误差,也能看出到底是哪个案例把平均损失拉高得最多。

在这个例子里,读者必须真正读到的是:

  • 每个样本错的程度并不一样;
  • 损失函数不会丢掉这种差别,而会把它保留成数字;
  • 多个样本的错答,可以被聚成一个统一的平均标准。

但如果只停在这里,还只是“看到了平均损失数字”。损失示例还应该继续读出:到底应该先更强地修正哪个样本。

输出信号 不那么差的读法 更危险的读法 现在更好的下一步判断
mean_loss = 0.35 把它读成三个样本当前平均偏差大约在这个程度 只看 0.35 就以为已经知道该修哪个样本 平均值只是整体摘要,还要一起看样本分解,才能看到更新优先级
worst_sample = restart_delay_batch 读成:三个样本里它把平均损失拉得最高 以为把它当异常点扔掉就好 更好的读法是:它代表了模型当前特别脆弱的一段输入区间,需要优先回看它为什么会偏这么大
night_shift_batch=0.25, stabilized_batch=0.16, restart_delay_batch=0.64 知道三者都有误差,但大小不同 觉得既然都错了,就差不多一起改就行 读出:像 restart_delay_batch 这种更大的错答,会更强地牵动平均损失与后续更新

把这张表也读完后,损失函数就不再只是“算出一个平均数字的过程”,而会更明确地变成筛出哪种错答应该优先被大幅修正的标准

从学习的角度,还要再向前一步。读者最后应该能自己说出这样一句话:重要的不只是平均损失有没有下降,还包括哪一个 batch 还在持续充当 worst case,以及它是否代表了某种输入区间的系统性弱点。因为损失函数一方面是数字聚合规则,另一方面也是修正优先级规则。

换值后重新阅读的轴 会一起变化的损失解释 现在可以继续追问的问题
restart_delay_batch 的 prediction 从 1.2 提高到 1.7 worst case 会被缓和,平均损失也会一起下降 模型当前最大的弱点是否真的被减轻了?
只降低 night_shift_batchstabilized_batch 的误差 平均值可能下降一些,但 worst case 可能原封不动 平均改善是否真的意味着核心错误区间也改善了?
样本数量继续增加 光看平均损失一个数字,更容易看不出到底哪段输入持续有问题 什么时候必须把平均值与样本级分解一起看?

如果把同样的场景用 Python 做一个小实验,就能直接改动看看:修哪个预测时,平均损失和 worst case 会一起下降? 下面的例子把“重点修正 restart_delay_batch”和“只修正原本误差较小的 night_shift_batch”分开比较。

# 这个例子比较各批次的 squared error,观察 mean loss 和 worst case 如何形成预测修正优先级。
samples = [
    {"name": "night_shift_batch", "target": 3.0, "prediction": 2.5},
    {"name": "stabilized_batch", "target": 1.0, "prediction": 1.4},
    {"name": "restart_delay_batch", "target": 2.0, "prediction": 1.2},
]


def squared_error(sample):
    error = sample["prediction"] - sample["target"]
    return error * error


def replace_prediction(samples, name, new_prediction):
    updated = [sample.copy() for sample in samples]
    for sample in updated:
        if sample["name"] == name:
            sample["prediction"] = new_prediction
    return updated


def summarize(label, samples):
    rows = [(sample["name"], squared_error(sample)) for sample in samples]
    mean_loss = sum(loss for _, loss in rows) / len(rows)
    worst_name, worst_loss = max(rows, key=lambda row: row[1])

    print(f"[{label}]")
    for name, loss in rows:
        print(f"{name}: squared_error={loss:.3f}")
    print(f"mean_loss={mean_loss:.3f}")
    print(f"worst_sample={worst_name} ({worst_loss:.3f})")
    print()


summarize("current", samples)

summarize(
    "fix_restart_delay_batch",
    replace_prediction(samples, "restart_delay_batch", 1.7),
)

summarize(
    "fix_night_shift_batch",
    replace_prediction(samples, "night_shift_batch", 3.0),
)

运行后会得到下面这样的输出。

[current]
night_shift_batch: squared_error=0.250
stabilized_batch: squared_error=0.160
restart_delay_batch: squared_error=0.640
mean_loss=0.350
worst_sample=restart_delay_batch (0.640)

[fix_restart_delay_batch]
night_shift_batch: squared_error=0.250
stabilized_batch: squared_error=0.160
restart_delay_batch: squared_error=0.090
mean_loss=0.167
worst_sample=night_shift_batch (0.250)

[fix_night_shift_batch]
night_shift_batch: squared_error=0.000
stabilized_batch: squared_error=0.160
restart_delay_batch: squared_error=0.640
mean_loss=0.267
worst_sample=restart_delay_batch (0.640)

这里真正被操作的值,是 replace_prediction(...) 里的最后一个数字。修正 restart_delay_batch 时,平均损失会明显下降,worst case 也会改变;而只修 night_shift_batch 时,平均值虽然也会下降一些,但最大的弱点依然没变。因此,这段代码帮助读者确认:损失不是一个单纯的平均数,而是会把哪一类错误该优先被大幅修正显露出来的信号。

按批次修正候选比较平均损失与最坏情况

从图里看,会更容易发现:修正 restart_delay_batch 时,平均损失和 worst 损失会一起下降。相反,只修正 night_shift_batch 时,平均损失会减少,但红色柱子,也就是最大的损失,仍然留在原地。所以,这个图额外强化的重点是:要把平均有没有下降最大的弱点有没有缩小分开来看。

如果把同样的输出重新译回运营判断,会更直接。

示例场景 只急着看结果时容易做出的解释 更好的解释
night_shift_batchstabilized_batch 两者都只错一点,因此优先级差别不大 两者都只是轻微修正候选,但仍可读出 night_shift_batchstabilized_batch 更早需要处理
restart_delay_batch 反正它也被平均进去了,整体调整自然会顺便解决 它是把损失拉得最高的关键样本,因此更像模型特别脆弱输入区间的代表信号
只看 mean_loss 这一个数字 只要整体损失下降就足够了 即便平均下降,也要一起看哪个样本还在持续充当 worst case,修正方向才会更清楚

损失小,就一定是好模型吗

读者很容易觉得:只要损失数字变小,一切问题就都解决了。但还要一起记住下面几点:

  • 看损失时,不只要看数字本身有没有变小,还要一起看它是在训练数据上变小,还是在验证数据上也保持住了;
  • 即便是同样小的损失,如果不区分它来自哪个数据区间,就很容易漏掉过拟合;
  • 只看训练损失去相信模型,很可能会忽略过拟合。

也就是说,损失是学习过程中的核心标准,但它仍然必须和 Part 4 里看过的 validation、test、metric 一起阅读。

正因为如此,深度学习也会直接继承 Part 4 的机器学习共通原则。

什么时候要先用损失函数视角来读

当你发现“模型已经输出了结果”这一句还不足以说明学习到底发生了什么时,就应该先把损失函数这一节拿出来。

先出现的问题场景 为什么应先切到损失函数视角 紧接着该问的下一个问题
已经有输出,但还无法用数字比较哪里错了 可以先建立起:预测与目标之间的偏差怎样被转成可学习的信号 接下来要继续看,不同问题类型为什么会用不同的损失
metric 与学习标准混在一起 可以把“人用来读的指标”和“模型内部用来学的标准”拆开 接下来要看回归与分类各自最自然的损失是什么
反向传播或优化器的说明看起来很突兀 因为只有先有损失,才能闭合“到底在减小什么” 接下来要看损失如何被传回到各层
同样是错答,但错的程度差异很重要 可以说明:为什么不能只停在对 / 错,而要保留偏差大小 接下来要看不同损失怎样解释这些偏差差异

检查清单

  • 能说明损失函数(loss function)到底是把什么变成数字的标准吗?
  • 能区分损失与评估指标(metric)的角色差异吗?
  • 能说明损失函数是把预测与目标之间的偏差变成一个数字的规则吗?
  • 能解释为什么只有输出还不算学习开始,还必须再加上损失这个数字吗?
  • 能说明损失是模型学习时的内部标准,而 metric 是人用来读与选择模型的外部标准吗?
  • 能把“学习会沿着让损失下降的方向调整参数”这件事说清楚吗?
  • 能说明损失下降并不自动保证泛化也变好吗?
  • 当你看到“模型已经输出了结果”,但仍然说不清它靠什么标准在学时,能先想到损失函数视角吗?
  • 能掌握:问题类型差异会在下一节继续,而损失如何传回前面各层会在下一章继续吗?

出处与参考资料