3.5 损失函数:误差信号的发生器


文档摘要

3.5 损失函数:误差信号的发生器 本节摘要:损失函数把预测与真值的差距压成一个标量,它是反向传播的信号源头——损失长什么样,梯度就往哪里去。本节拆解两大族损失(回归的均方差族、分类的交叉熵族)的机理与配对约束,讲清 fromlogits 这个高频翻车点,并演示类加权与自定义损失的写法。选错损失不会报错,只会让你的模型安静地学错方向。 读完你应当能做到 阅读完本节,你应当能够: 按任务类型正确配对损失与输出层激活(含 softmax、sigmoid、无激活); 解释 fromlogits 参数的两种取值与对应的数据形态; 用类加权损失应对样本不均衡; 写一个自定义损失函数并接入 compile。

3.5 损失函数:误差信号的发生器

本节摘要:损失函数把预测与真值的差距压成一个标量,它是反向传播的信号源头——损失长什么样,梯度就往哪里去。本节拆解两大族损失(回归的均方差族、分类的交叉熵族)的机理与配对约束,讲清 from_logits 这个高频翻车点,并演示类加权与自定义损失的写法。选错损失不会报错,只会让你的模型安静地学错方向。

读完你应当能做到

阅读完本节,你应当能够:

  1. 按任务类型正确配对损失与输出层激活(含 softmax、sigmoid、无激活);
  2. 解释 from_logits 参数的两种取值与对应的数据形态;
  3. 用类加权损失应对样本不均衡;
  4. 写一个自定义损失函数并接入 compile。

损失决定梯度去向

训练步的求导起点是损失这个标量:损失函数对预测的导数形状,就是误差信号注入网络的形状。均方差的导数与误差成正比——错得越离谱,推力越大;交叉熵在预测离真值远时导数接近常数、在接近真值时导数趋于零——误差大时不放大、对了就松手。两种信号形状塑造了不同的训练行为:均方差对离群点极其敏感(一个离群样本能拖动整批梯度),交叉熵对过度自信的惩罚急剧上升。换损失函数等于换梯度政策,这是它比换优化器更根本的原因。

回归族:从均方差到 Huber

均方差(MSE)是回归默认款:逐元素平方再平均。它的阿喀琉斯之踵是离群点——平方放大让一个异常样本主导整批梯度。平均绝对误差(MAE)对离群点稳健但零点不可导;Huber 损失两者折中:误差小走平方、误差大走线性。

import tensorflow as tf import numpy as np y_true = tf.constant([[1.0], [2.0], [3.0]]) y_pred = tf.constant([[1.1], [2.0], [8.0]]) # 第三个样本是离群预测 mse = tf.keras.losses.MeanSquaredError() mae = tf.keras.losses.MeanAbsoluteError() huber = tf.keras.losses.Huber(delta=1.0) print(f"mse {mse(y_true, y_pred).numpy():.2f}") print(f"mae {mae(y_true, y_pred).numpy():.2f}") print(f"huber {huber(y_true, y_pred).numpy():.2f}") # 输出: # mse 12.34 —— 平方把 5.0 的误差放大成 25,被离群点主导 # mae 2.03 —— 线性平均,离群点只占三分之一话语权 # huber 1.68 —— 小误差段平方、大误差段封顶,两头兼顾 # 数据有离群点时,Huber 通常比 MSE 稳得多

加州房价这类真实数据尾部偏重,MSE 会让模型过度迎合高房价样本;实测发现训练损失被少数样本拉着走时,先换 Huber 看曲线是否平稳,再谈调参。

分类族:交叉熵与 from_logits 陷阱

交叉熵的输入是"概率对":预测概率与真值概率(通常是 one-hot)。配对约束有两条链:输出层激活决定模型吐的是概率还是未归一化的分数(logits),损失函数的 from_logits 参数必须与之匹配:

输出层配置 模型输出 损失配置
Dense(n) 无激活 logits 分数 from_logits=True
Dense(n) softmax 概率分布 from_logits=False
Dense(1) sigmoid 概率值 BinaryCrossentropy 同理
# 同一个任务两种等价写法 Xc = np.random.rand(256, 8).astype("float32") yc = np.random.randint(0, 3, 256) # 写法一:模型吐 logits,损失内做 softmax(数值更稳,推荐) m1 = tf.keras.Sequential([tf.keras.layers.Input(shape=(8,)), tf.keras.layers.Dense(3)]) # 无激活 m1.compile(optimizer="adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)) h1 = m1.fit(Xc, yc, epochs=2, verbose=0) # 写法二:模型内 softmax,损失直接吃概率 m2 = tf.keras.Sequential([tf.keras.layers.Input(shape=(8,)), tf.keras.layers.Dense(3, activation="softmax")]) m2.compile(optimizer="adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)) h2 = m2.fit(Xc, yc, epochs=2, verbose=0) print("both compiled and trained") # 输出:both compiled and trained # 推荐写法一:softmax 与交叉熵合并计算,数值稳定性由框架保证

from_logits 配错的症状很有辨识度:loss 停在一个"碰巧"的常数附近(比如三分类停在 1.1 左右)且绝不下降——概率被二次归一化或分数被当概率,信号失真,梯度近乎为零。看到"loss 不降但也没报错",先查这条链。

类加权与自定义损失

样本不均衡时(如欺诈检测里正例只占百分之一),不加权的损失会让模型学会"全猜多数类"。class_weight 给每类样本的损失乘权重:

# 造一个 9 比 1 的不均衡二分类 Xb = np.random.rand(1000, 8).astype("float32") yb = (np.random.rand(1000) > 0.9).astype("float32") # 约一成正例 mb = tf.keras.Sequential([tf.keras.layers.Input(shape=(8,)), tf.keras.layers.Dense(16, activation="relu"), tf.keras.layers.Dense(1, activation="sigmoid")]) mb.compile(optimizer="adam", loss="binary_crossentropy") h0 = mb.fit(Xb, yb, epochs=3, verbose=0) no_bias = tf.reduce_mean(tf.cast(tf.equal(tf.round(mb.predict(Xb, verbose=0)), tf.cast(yb[:, None], tf.float32)), tf.float32)).numpy() print(f"unweighted acc {no_bias:.2f}") # 输出示例:unweighted acc 0.90 —— 全猜负例也有九成,这个准确率是幻觉 h1 = mb.fit(Xb, yb, epochs=3, verbose=0, class_weight={0.0: 1.0, 1.0: 9.0}) # 少数类权重九倍 print("weighted training done") # 输出:weighted training done # 正例误差被放大九倍,模型被迫认真对待少数类

自定义损失是"真值与预测进、标量出"的函数,直接传给 compile:

def asymmetric_mse(y_true, y_pred): """低估惩罚加倍的损失:预测偏低比偏高代价更高""" err = y_true - y_pred under = tf.square(tf.maximum(err, 0.0)) * 2.0 # 低估段双倍 over = tf.square(tf.maximum(-err, 0.0)) return tf.reduce_mean(under + over) mr = tf.keras.Sequential([tf.keras.layers.Input(shape=(8,)), tf.keras.layers.Dense(1)]) mr.compile(optimizer="adam", loss=asymmetric_mse) mr.fit(X[:8000], y[:8000], epochs=2, batch_size=64, verbose=0) print("custom loss in place") # 输出:custom loss in place # 房价预测里宁可高估别低估的业务偏好,就能这样编进信号里

⚠️ 常见坑:损失与输出激活的配对错位(logits 喂给 from_logits=False)不报错、只让 loss 卡住不降。建立检查反射:改损失必查 from_logits,改输出层必查激活。

💡 关键直觉:损失函数是给梯度写的"激励制度"——你想让模型在乎什么(离群点、少数类、低估),就把它写进制度里,梯度自会照章办事。

本节要点回顾

  • 损失决定梯度政策:MSE 放大离群、交叉熵惩罚过度自信,信号形状不同。
  • 回归选型:有离群点用 Huber,均方差是默认而非万能。
  • 配对链:输出激活决定 logits 与概率,from_logits 必须匹配。
  • 不均衡武器:class_weight 放大少数类误差,打破"全猜多数"的幻觉。
  • 自定义损失:真值预测进、标量出,业务偏好可编进梯度制度。

下节给训练加约束面:正则化。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U