输出层的数字、标签格式与损失函数要形成一致约定。看见分类任务就先加 Softmax,可能反而重复了损失函数内部已经做的操作。

常见约定

任务形式 典型模型输出 标签约定
多分类、每样本一个类别 每类一个 Logit 类别索引,或满足要求的类别概率分布
二分类单输出 一个 Logit 与输出对应的 0/1 浮点目标
多标签分类 每个标签一个 Logit 各标签独立的目标向量

PyTorch 的 CrossEntropyLoss 接收未归一化的 Logits;BCEWithLogitsLoss 将 Sigmoid 与二元交叉熵结合,以改善数值稳定性。使用它们时不要先重复做相应激活。CrossEntropyLoss、BCEWithLogitsLoss

一个稳定的多分类算术检查

以下例子只覆盖“一行一个样本、一列一个类别、标签为类别索引”的情况,未实现权重、掩码与复杂维度。

import numpy as np

def cross_entropy_demo(logits, labels):
    logits = np.asarray(logits, dtype=float)
    labels = np.asarray(labels, dtype=int)
    if logits.ndim != 2 or labels.shape != (len(logits),):
        raise ValueError("expected batch-by-class logits and one label per sample")
    if (labels < 0).any() or (labels >= logits.shape[1]).any():
        raise ValueError("class index is outside the class range")
    shifted = logits - logits.max(axis=1, keepdims=True)
    log_probabilities = shifted - np.log(np.exp(shifted).sum(axis=1, keepdims=True))
    return -log_probabilities[np.arange(len(labels)), labels].mean()

scores = np.array([[1000., 1001., 999.], [-1000., -999., -998.]])
target = np.array([1, 2])
value = cross_entropy_demo(scores, target)
assert np.isfinite(value)
assert np.isclose(value, cross_entropy_demo(scores + 12345., target))
assert cross_entropy_demo([[10., 0.]], [0]) < cross_entropy_demo([[0., 10.]], [0])
print("stable logits and class-index checks passed")

平移不变性说明:每个样本所有类别分数同时加一个常数,不改变其 Softmax 概率。数值稳定不等于模型有用;验证准确率、校准、类别表现和数据划分仍需单独评估。

广播可能把错误藏起来

回归中 [B,1] 的预测与 [B] 的标签若意外广播,可能形成并非预期的误差矩阵。不要只依赖“代码没有报错”,应断言预期形状、标签范围和损失是否有限。

分类索引、独热标签、多标签目标的含义不同,不要为了凑形状随意转换。具体实现还需核对所用版本支持的目标格式、类型及归约方式。

来源与核验边界

本篇结合疏锦行机器学习基础班《神经网络》的输出、激活与训练部分,补充输入输出约定和独立数值检查。NumPy 示例已运行,没有训练课程神经网络。视频尚未逐节核验,原课件不公开。