损失函数
损失函数是用于判断 模型当前轮次预测效果好坏 和 指导模型该如何提高预测效果的函数
分类损失
对于分类算法,的核心任务就是衡量模型预测的类别/概率 与真实类别之间的差距
损失函数的工作流程
-
模型预测
模型对某一输入样本进行预测,输出通常为一个或一组概率值,例如这张图片有 0.8的可能是猫,0.15的可能是狗,0.05的可能是鸟
-
比对真实值
和该样本的真实标签比对,例如该样本标签是猫
-
计算惩罚
损失函数会比较模型预测概率和真实标签,然后计算出一个损失值
- 如果模型以高概率预测对了(预测80%是猫,真实也是猫),损失值就很小
- 如果模型以高概率预测错了(预测80%是狗,真实却是猫),损失值就非常大
-
模型学习
训练过程中,优化器,例如 Adam、SGD,会去调整模型的内部参数,进而让所有样本的平均损失值变得尽可能小
1. 二元分类问题下的损失函数
task:二选一
1.1. 二元交叉熵损失 Binary Cross-Entropy Loss
-
二元分类的黄金标准和默认选择,它衡量的是模型预测的概率分布与真实的0或1分布之间的距离
-
工作原理
- 模型最后一层需要一个 Sigmoid 激活函数 ,输出一个0到1之间的概率值 p,代表样本属于正类(类别1)的概率
- 当真实标签是 1 时, 的值是 如果模型预测的概率 p 接近 0, 就接近 1,损失就很小;反之,损失则很大
- 当真实标签是 0 时,情况相反
-
什么时候使用
几乎所有的二元分类问题,例如是否患有糖尿病
1.2. Hinge Loss
-
这是 SVM 使用的
它的目标不仅仅是 分对,而是要以尽可能大的间隔 Margin 分对
-
工作原理
- 它要求模型的输出不是概率,而是一个原始的得分(可正可负)
- 如果一个样本被正确分类,并且与决策边界的距离足够远(大于等于1的间隔),那么它的损失就是 0
- 如果样本被错误分类,或者虽然分对了但在间隔之内,损失就会呈线性增加
-
主要在训练支持向量机时使用,在深度学习中不常用
2. 多类别分类
任务是 N选一,即类别之间互斥,例如手写数字识别(0-9中选一个),或者CIFAR-10中的图像分类(10个类别中选一个)
分类交叉熵损失 Categorical Cross-Entropy Loss
-
二元交叉熵向多类别的自然扩展,也是多类别分类的默认选择
-
工作原理
- 模型最后一层需要一个 Softmax 激活函数,它将模型的N个输出得分转换成一个N维的概率分布(所有概率加起来等于1)
- 真实标签通常是一个独热编码 (One-Hot Encoding) 的向量,例如对于类别2(在0,1,2,3四个类别中),标签是 [0, 0, 1, 0]
- 损失函数只关心模型赋予那正确类别的概率值。损失值 ,也就是这个分类的熵 例如,如果模型预测 [0.1, 0.2, 0.6, 0.1],真实标签是类别2,那么损失就是 -log(0.6)
-
何时使用
几乎所有的多类别分类问题
-
Pyotrch中使用:
torch.nn.CrossEntropyLoss注意: PyTorch的
CrossEntropyLoss内部已经集成了Softmax操作。因此,神经网络的最后一层不需要加Softmax激活函数,直接输出原始的、未经激活的得分(logits)
3.多标签分类
任务是 N选K,即一个样本可以同时属于多个类别,类别之间不互斥。例如,一部电影的标签可以是“喜剧”、“爱情”、“科幻”
二元交叉熵损失 Binary Cross-Entropy Loss
-
核心
将 N选K 问题分解为 N 个独立的二元分类问题
-
工作原理
- 对于N个类别,模型最后一层会有N个输出节点
- 与多类别分类不同,这N个节点各自独立地通过一个Sigmoid激活函数,输出 N 个 0 到 1 之间的概率值。每个概率值代表 “样本属于该类别的概率”
- 然后,对这N个输出节点,分别计算它们的二元交叉熵损失,最后将所有损失相加或求平均
-
何时使用
当一个样本有多个标签时。例如,图像场景识别(一张图里可以同时有“天空”、“草地”、“狗”)
总结
| 任务类型 | 类别关系 | 模型最后一层激活 | 首选损失函数 |
|---|---|---|---|
| 二元分类 | 二选一 | Sigmoid | 二元交叉熵 |
| 多类别分类 | N选一 (互斥) | Softmax | 分类交叉熵 |
| 多标签分类 | N选K (不互斥) | N个独立的Sigmoid | 二元交叉熵 |
注意
在绝大多数情况下,交叉熵损失(包括其二元和分类形式)都是你最好的朋友,它性能好,收敛稳定
回归损失
与分类损失函数衡量 类别猜得对不对 不同,回归损失函数的核心任务是衡量预测值与真实值之间的距离或误差有多大
工作原理
假设你和朋友在玩一个猜数字游戏,你的目标是猜一个朋友心里想的数字,假设朋友想的是50
-
真实值 :朋友心中想的数字50
-
预测值 :你猜的数字,例如45
-
残差:真实值和预测值之间的差距
-
损失函数
一个计算惩罚的规则,根据你猜的误差(5)来给你一个惩罚分数(损失值)。如果你猜得非常准(误差接近0),惩罚就非常小;反之,惩罚很大
-
模型学习
在训练过程中,模型(比如线性回归)会不断调整自己的内部参数(斜率和截距),努力找到一组参数,使得在所有数据点上的平均惩罚分数(总损失)最小
均方误差 MSE / L2 损失
最常用的回归损失函数,也是线性回归等模型的默认选择
-
函数描述
计算每个点的误差 平方 求所有点的均值
-
特点
- 优点
- 数学性质好:处处可微,是一个平滑的凸函数 ,方便优化器进行梯度下降
- 惩罚大的误差:由于平方项会放大大误差,因此模型会尽力去减小这个大误差
- 缺点
- 对异常值敏感: 如果数据中有一个异常点,它的巨大误差在平方后会不成比例地支配总损失,可能导致模型为了迁就这个异常点而偏离正常数据的趋势。因此数据清洗时,可能要花大力气去清掉异常值
- 单位不直观,单位是目标变量单位的平方,例如预测房价任务中的单位是:
- 优点
-
何时使用
数据中没有太多异常值,或者你希望模型特别关注并修正那些大的误差时
平均绝对误差 Mean Absolute Error - MAE / L1 损失
非常直观且稳健的损失函数
-
核心思想:计算每个点的误差的绝对值,对所有误差都无差别地惩罚
-
函数描述
计算每个点的误差的绝对值,然后求所有点的平均值
-
特点
- 优点
- 对异常值稳健:即使有异常点,它巨大的误差也只是被线性地计入总损失,不会像MSE那样被放大,因此模型不会过分关注它
- 单位直观:损失值的单位与目标变量的单位相同(例如,“万元”)
- 缺点
- 数学性质稍差:在误差为 0 的点导数不连续,这可能在优化后期导致学习率需要更精细的调整
- 优点
-
何时使用:数据中存在或怀疑存在异常值时
均方根误差 RMSE
- 函数描述:
- 特点:
- 它保留了MSE对大误差惩罚的特性,并且单位与MAE一样直观(例如,“万元”)
- 它仍然像MSE一样对异常值敏感
- 何时使用:
- 它通常不作为模型的训练损失函数(因为优化RMSE等价于优化MSE),而是作为一个评估指标,因为它比MSE更易于解释
Huber 损失 Huber Loss / 平滑平均绝对误差
对应 评价指标 里的
- 核心思想: 集合MSE和MAE之的优势,并在两者之间取得平衡
- 工作原理:
- 它有一个超参数
- 当误差很小(绝对值小于δ)时,它表现得像 MSE(二次函数),平滑且能精细调整
- 当误差很大(绝对值大于δ)时,它表现得像 MAE(线性函数),从而降低了对异常值的敏感度
- 特点:
- 优点:既对异常值稳健,又在误差较小时保持平滑的梯度
- 缺点:需要额外调整一个超参数 δ
- 何时使用:当你想要一个既稳健又高效的损失函数时,Huber损失是一个绝佳的选择。
总结
| 损失函数 | 核心特点 | 对异常值 | 何时使用 |
|---|---|---|---|
| MSE (L2) | 平方误差,惩罚大错误 | 非常敏感 | 默认选择,数据干净,希望修正大误差时 |
| MAE (L1) | 绝对误差,一视同仁 | 非常稳健 | 数据中存在异常值时 |
| RMSE | MSE的平方根 | 非常敏感 | 主要作为评估指标,因其单位直观 |
| Huber Loss | 混合体,小误差用MSE,大误差用MAE | 比较稳健 | 想要两全其美,既稳健又高效时 |
实现方式
在 Scikit-learn 中
与分类类似,损失函数通常是模型内置的。例如 LinearRegression 默认使用MSE
但你可以使用 metrics 模块来评估模型在不同损失函数下的表现,不是指定
from sklearn.metrics import mean_squared_error, mean_absolute_error
# 假设 y_true 和 y_pred 是 NumPy 数组
y_true = [3, -0.5, 2, 7]
y_pred = [2.5, 0.0, 2, 8]
# 计算 MSE
mse = mean_squared_error(y_true, y_pred)
# 计算 MAE
mae = mean_absolute_error(y_true, y_pred)
# 计算 RMSE
rmse = mean_squared_error(y_true, y_pred, squared=False) # 或者 np.sqrt(mse)
在 PyTorch 中
PyTorch中需要显式定义训练时使用的损失函数
import torch
import torch.nn as nn
# 创建损失函数实例
criterion_mse = nn.MSELoss()
criterion_mae = nn.L1Loss() # MAE在PyTorch中被称为L1Loss
criterion_huber = nn.HuberLoss(delta=1.0)
# 准备数据
predictions = torch.tensor([2.5, 0.0, 2.0, 8.0])
targets = torch.tensor([3.0, -0.5, 2.0, 7.0])
# 计算损失
loss_mse = criterion_mse(predictions, targets)
loss_mae = criterion_mae(predictions, targets)
loss_huber = criterion_huber(predictions, targets)
交叉熵损失函数
直观理解
现在做出一个假设:
AI模型是一位 学生,而损失函数是一位 老师,负责给学生的每次预测打分
-
学生的工作:
对一个问题,给出它属于每个可能类别的置信度(概率)
例如:对于一张图片,学生认为这张图有80%的可能是猫,15%是狗,5%是鸟
-
老师的工作:
-
获取标准答案,即真实标签
-
查看学生对正确答案的置信度:例子中为 80%
-
根据这个置信度给出一个惩罚分数,也就是损失值
- 如果学生对正确答案的置信度很高(假设99%),老师会很满意,给一个非常小的惩罚
- 如果置信度很低(假设1%),说明学生错得非常离谱且自信,这会得到一个巨大的惩罚分数
-
交叉熵损失函数就是老师的打分标准,它通过 ,即这个概率的熵来实现奖惩机制
函数描述与作用
交叉熵衡量的是两个概率分布之间的距离
在分类问题中,就是模型预测的概率分布和真实的概率分布之间的距离
二元分类
-
真实标签 y 是 或
-
模型预测 是样本为 的概率
-
则表达式为
即 当 时,就是 ;当 时,就是
多类别分类
-
真实标签 是一个独热编码向量 (One-Hot): 例如 表示正确类别是第 2 类
-
模型预测 是一个概率向量: 例如
-
则 损失函数为:
即对所有类别 求和
因为 向量中只有一个 ,其他都是 ,所以这个求和公式最终会简化为只看正确类别那一项:
在上面的例子中,就是
作用
-
量化预测错误:提供一个明确的、连续的数值来表示模型当前预测的糟糕程度
-
提供学习方向“:它是一个可微的函数,这意味着可以计算出损失 respecto to 模型的每一个参数的梯度
这个梯度会告诉优化器(例如Adam):“你应该把这个参数调高还是调低,才能让损失变小一点。”
-
惩罚不确定性与自信的错误:如前所述,它对那些错得离谱的预测给予不成比例的巨大惩罚,迫使模型尽快修正这些严重错误
实现方式
在 Scikit-learn 中
在Scikit-learn中,通常不会直接选择交叉熵损失函数。对于像逻辑回归这样的分类模型,交叉熵损失是其内置的、默认的优化目标
调用 .fit() 方法训练一个逻辑回归模型时,它内部的求解器就在最小化交叉熵损失,只需创建和训练模型即可
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# 1. 准备数据
X, y = make_classification(n_samples=100, n_features=10, n_classes=2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 2. 创建模型
# 交叉熵损失是这个模型内在的优化目标
model = LogisticRegression()
# 3. 训练模型
# .fit() 的过程就是在最小化交叉熵
model.fit(X_train, y_train)
# 可用 log_loss 来计算损失值,但这只是评估,不是训练过程的一部分
from sklearn.metrics import log_loss
predictions_proba = model.predict_proba(X_test)
loss_value = log_loss(y_test, predictions_proba)
在 PyTorch 中
PyTorch中,需要显式定义和使用损失函数
-
对于二元分类:使用
torch.nn.BCEWithLogitsLoss。 版本更稳定,因为它内部包含了Sigmoid函数,这意味着模型最后一层不能有Sigmoid激活函数import torch import torch.nn as nn criterion = nn.BCEWithLogitsLoss() model_output = torch.randn(10, 1) # 10个样本,模型的原始输出 (logits) true_labels = torch.randint(0, 2, (10, 1)).float() # 10个真实标签 (0或1) loss = criterion(model_output, true_labels) -
对于多类别分类:使用
torch.nn.CrossEntropyLoss。这个函数内部集成了 Softmax 和计算负对数损失两个步骤,这意味着模型最后一层绝对不能有Softmax激活函数,直接输出原始得分(logits)即可import torch import torch.nn as nn criterion = nn.CrossEntropyLoss() # 3个样本,模型对4个类别的原始输出 (logits) model_output = torch.randn(3, 4) # 3个真实标签 (不是one-hot,直接是类别索引) true_labels = torch.tensor([1, 0, 3]) loss = criterion(model_output, true_labels)
损失值达到多少,模型才算优秀
损失值本身没有一个优秀的绝对标准,它的意义在于相对比较和趋势分析
核心指标:看趋势,而非绝对值
-
训练损失:在训练过程中,这个值必须持续、稳定地下降。这是模型正在学习的最直接信号
-
验证损失:这个值应该先下降,然后趋于平稳。如果它在训练损失持续下降的情况下开始回升,那就发出了一个强烈的过拟合信号
最终评判: 评价指标
损失值是给机器看的,而准确率 (Accuracy)、精确率 (Precision)、召回率 (Recall)、F1分数才是人类看的指标。一个优秀的损失值,最终会体现在这些评估指标上。例如,一个损失为0.1的模型,其准确率可能是95%;另一个损失为0.05的模型,准确率可能是97%