跳转至

Tensor Metrics

导言

在AI训练流程中,明明一个tensor就是一个shape下的数值,但是竟然有一堆指标来处理和解释其含义:

  • l1 norm
  • entropy
  • log_prob
  • logistics

这些指标代表什么,用途为何?如何计算(标量?什么shape),计算前后值域/shape变化如何,在比较精度的场景下是否为合适的典型指标(有代表性,且能比较)

Tensor 指标说明

Tensor 本身只是一个带 shape 的数值数组。 L1 normentropylog_problogits/logistic 的含义取决于这个 tensor 表示什么,以及在哪个维度上计算。

常见分类模型输出:

名称 Shape 含义
logits [B, K] 模型原始输出分数
probs [B, K] softmax 后的概率分布
target [B] 每个样本的真实类别
loss scalar batch 归约后的训练目标

其中:

  • B:batch size
  • K:类别数、词表大小或动作数
  • 最后一维通常是概率分布维度

总览

指标 本质 常见输入 常见输出 值域 是否适合比较精度
L1 norm 数值大小或差异 任意 tensor scalar / 按维度归约 [0, +∞) 适合数值误差、回归
entropy 分布不确定性 概率分布 去掉分布维度 [0, log K] 不适合单独代表准确率
log_prob 样本的对数概率 概率模型输出 [B] 或 scalar 离散时 (-∞, 0] 适合概率模型评价
logits 未归一化分数 模型原始输出 shape 通常不变 (-∞, +∞) 不适合作为最终精度指标
logistic/sigmoid logit 到概率的映射 二分类或多标签 logits shape 不变 (0, 1) 本身不是指标

L1 norm

L1 norm 衡量 tensor 的绝对大小:

L1(x) = sum(|x_i|)

比较两个 tensor 时,通常用 L1 distance:

L1(a, b) = sum(|a_i - b_i|)

如果取平均,就是 MAE:

MAE(a, b) = mean(|a_i - b_i|)

用途

  • 比较两个 tensor 的数值差异
  • 回归任务 loss
  • 模型量化、推理框架转换、不同精度计算的误差检查
  • L1 正则化,鼓励参数稀疏

Shape 变化

假设:

x: [B, C, H, W]

整体 L1:

x.abs().sum()

输出:

scalar

按样本计算:

x.abs().sum(dim=(1, 2, 3))

输出:

[B]

计算平均绝对误差:

x.abs().mean(dim=(1, 2, 3))

输出:

[B]

精度比较

如果比较数值误差,L1/MAE 是合适指标。 但原始 L1=sum(abs(diff)) 会受 tensor 大小影响,shape 越大,L1 往往越大。

更推荐:

MAE = mean(abs(a - b))
relative_error = abs(a - b) / (abs(b) + eps)
max_abs_error = max(abs(a - b))

Entropy

entropy 衡量概率分布的不确定性。

对于概率分布:

p = [p1, p2, ..., pK]

要求:

pi >= 0
sum(pi) = 1

熵定义为:

H(p) = -sum(pi * log(pi))

如果分布很集中,熵低。 如果分布很平均,熵高。

用途

  • 判断分类模型是否自信
  • 强化学习中鼓励探索
  • 半监督学习中筛选高置信度样本
  • 分析 attention 分布是否集中

Shape 变化

分类任务:

probs: [B, K]

计算:

entropy = -(probs * probs.log()).sum(dim=-1)

输出:

[B]

语言模型:

probs: [B, T, V]

对词表维度计算:

entropy = -(probs * probs.log()).sum(dim=-1)

输出:

[B, T]

值域

如果有 K 个类别:

0 <= H(p) <= log(K)

其中:

  • H(p) = 0:完全确定
  • H(p) = log(K):完全均匀

也可以归一化:

H_norm = H(p) / log(K)

值域变为:

[0, 1]

精度比较

entropy 不适合单独作为准确率指标。

原因是:

低熵只说明模型很自信,不说明模型一定正确。
高熵只说明模型不确定,不说明模型一定错误。

例如模型对错误类别给出 0.99 概率,它的熵很低,但预测是错的。

因此,熵适合分析不确定性,不适合单独比较预测精度。


log_prob

log_prob 表示模型给某个样本或标签分配的对数概率:

log_prob(x) = log p_model(x)

分类任务中,如果:

probs = [0.1, 0.7, 0.2]
target = 1

则:

log_prob = log(0.7)

概率越大,log_prob 越接近 0。 概率越小,log_prob 越负。

与 loss 的关系

负对数概率就是 NLL:

NLL = -log_prob(target)

对于 one-hot 分类任务,NLL 等价于 cross entropy loss:

loss = -log p_model(y_true)

Shape 变化

分类任务:

logits: [B, K]
target: [B]

计算:

log_probs = torch.log_softmax(logits, dim=-1)
target_log_probs = log_probs.gather(
    dim=-1,
    index=target[:, None]
).squeeze(-1)
nll = -target_log_probs
loss = nll.mean()

Shape:

log_probs:        [B, K]
target_log_probs: [B]
nll:              [B]
loss:             scalar

语言模型:

logits: [B, T, V]
target: [B, T]

Shape:

log_probs:        [B, T, V]
target_log_probs: [B, T]
token_nll:        [B, T]
mean_token_nll:   scalar

值域

离散分类中:

p in [0, 1]
log_prob in (-∞, 0]
NLL in [0, +∞)

注意:连续分布中的 log_prob 是 log density,不一定小于 0

精度比较

log_prob/NLL/cross entropy 是概率模型中非常典型的评价指标。

优点是它不仅关心是否预测正确,还关心模型给正确答案的概率有多高。

例如两个模型都预测正确:

模型 A: 正确类概率 0.51
模型 B: 正确类概率 0.99

accuracy 一样,但 NLL 会认为模型 B 更好。

比较时需要保证:

  • 同一数据集
  • 同一标签空间
  • 同一 tokenization
  • 同一长度归一化方式
  • 同一概率建模方式

语言模型中不宜直接比较整句 log_prob,因为句子越长,总 log_prob 越低。 更常用:

mean token NLL
perplexity = exp(mean token NLL)

Logits 与 logistic

logits 是模型输出的未归一化分数:

logits = [2.1, -0.3, 0.7]

它不是概率,因为:

  • 可以为负数
  • 不要求和为 1
  • 没有固定范围

分类任务中通过 softmax 转成概率:

p_i = exp(logit_i) / sum(exp(logit_j))

Shape 通常不变:

logits: [B, K]
probs:  [B, K]

值域变化:

logits: (-∞, +∞)
probs:  [0, 1],且 sum(probs)=1

logistic/sigmoid

logistic 通常指 sigmoid 函数:

sigmoid(z) = 1 / (1 + exp(-z))

用于二分类或多标签分类:

logits:  [B, K]
probs:   [B, K]

值域:

logits: (-∞, +∞)
probs:  (0, 1)

二分类中:

logit(p) = log(p / (1 - p))
p = sigmoid(logit)

训练时常用:

torch.nn.BCEWithLogitsLoss()

它将 sigmoid 和 binary cross entropy 合并,数值更稳定。

精度比较

raw logits 不适合作为最终精度指标。

原因:

  • logits 没有固定值域
  • softmax 对整体平移不敏感
  • 不同模型的 logit scale 可能不同
  • logit 大不一定代表预测更准确,只可能代表更自信

更推荐从 logits 派生指标:

accuracy
top-k accuracy
cross entropy / NLL
AUROC
F1
Brier score
ECE

比较数值一致性

适用于:

  • FP32 vs FP16
  • PyTorch vs ONNX
  • 原模型 vs 量化模型
  • 不同推理后端输出对齐

推荐指标:

MAE
max_abs_error
relative_error
RMSE
cosine_similarity

如果比较的是概率分布,可以补充:

KL divergence
JS divergence
argmax agreement
top-k agreement

Tensor 差异比较指标:KL、JS、Argmax、Top-k

在模型训练、蒸馏、推理对齐、量化评估中,经常需要比较两个 tensor 的差异。例如:

A.shape = [B, ..., C]
B.shape = [B, ..., C]

其中:

  • B:batch 维度
  • ...:可选的空间维、序列维,例如 [T][H, W]
  • C:类别数、词表大小或特征分布维度
  • 通常沿最后一维 C 比较两个 tensor 的分布或预测结果

下面讨论四类常用指标:

KL divergence
JS divergence
argmax agreement
top-k agreement

它们关注的差异不同:

指标 比较对象 是否需要概率分布 越大越好 典型值域
KL divergence 完整分布差异 [0, +∞)
JS divergence 对称分布差异 [0, ln 2][0, 1]
argmax agreement 第一预测是否一致 [0, 1]
top-k agreement 前 k 个候选是否一致 [0, 1]

基本设定

假设有两个 tensor:

x.shape = [B, ..., C]
y.shape = [B, ..., C]

如果 xy 是 logits,需要先转成概率分布:

p = softmax(x, dim=-1)
q = softmax(y, dim=-1)

此时:

x, y: [B, ..., C]      # logits
p, q: [B, ..., C]      # probability distribution

其中每个位置上的最后一维满足:

sum(p[..., :]) = 1
sum(q[..., :]) = 1

KL divergence 和 JS divergence 比较的是完整概率分布,因此要求输入是概率分布。

argmax agreement 和 top-k agreement 比较的是排序或预测类别,不要求归一化,所以可以直接用于 logits。


KL divergence

KL divergence,全称 Kullback-Leibler divergence,用来衡量一个概率分布 Q 相对于参考分布 P 的差异。

公式为:

D_KL(P || Q) = sum_i P_i * log(P_i / Q_i)

如果比较的是:

p.shape = [B, ..., C]
q.shape = [B, ..., C]

则沿类别维 C 求和:

kl.shape = [B, ...]

如果再做平均:

kl_mean.shape = []

即得到一个标量。

KL divergence 的值域是:

[0, +∞)

含义:

  • 0 表示两个分布完全相同
  • 值越大,说明两个分布差异越大
  • KL 不对称:
D_KL(P || Q) != D_KL(Q || P)

这点非常重要。

例如:

D_KL(P || Q)

表示以 P 为参考,衡量 QP 的拟合程度。

在知识蒸馏中,如果 P 是 teacher 分布,Q 是 student 分布,那么常用:

D_KL(P_teacher || Q_student)

它会惩罚 student 在 teacher 高概率类别上给出过低概率的情况。

KL divergence 的计算示例:

import torch
import torch.nn.functional as F

log_p = F.log_softmax(x, dim=-1)
log_q = F.log_softmax(y, dim=-1)

p = log_p.exp()
q = log_q.exp()

kl_pq = (p * (log_p - log_q)).sum(dim=-1)
kl_mean = kl_pq.mean()

shape 变化:

x, y       : [B, ..., C]
p, q       : [B, ..., C]
kl_pq      : [B, ...]
kl_mean    : scalar

需要注意:

  • KL 对小概率位置较敏感
  • 如果 P_i > 0Q_i = 0,KL 会趋向无穷
  • 实际计算时通常使用 log_softmax 或加 eps 保持数值稳定

JS divergence

JS divergence,全称 Jensen-Shannon divergence,可以看作 KL divergence 的对称、平滑版本。

定义中先构造中间分布:

M = 0.5 * (P + Q)

然后:

D_JS(P || Q) = 0.5 * D_KL(P || M) + 0.5 * D_KL(Q || M)

它的特点是:

  • 对称:
D_JS(P || Q) = D_JS(Q || P)
  • 有界
  • 比 KL 更稳定
  • 可以衡量两个分布整体上的差异

如果使用自然对数 ln,值域为:

[0, ln 2]

即:

[0, 0.693...]

如果使用以 2 为底的对数,值域为:

[0, 1]

JS divergence 的计算示例:

log_p = F.log_softmax(x, dim=-1)
log_q = F.log_softmax(y, dim=-1)

p = log_p.exp()
q = log_q.exp()

m = 0.5 * (p + q)
log_m = torch.log(m.clamp_min(1e-8))

js = 0.5 * (p * (log_p - log_m)).sum(dim=-1) \
   + 0.5 * (q * (log_q - log_m)).sum(dim=-1)

js_mean = js.mean()

shape 变化:

x, y       : [B, ..., C]
p, q       : [B, ..., C]
m          : [B, ..., C]
js         : [B, ...]
js_mean    : scalar

如果希望将 JS divergence 归一化到 [0, 1],可以除以 ln 2

js_normalized = js / torch.log(torch.tensor(2.0))

含义:

  • 0 表示两个分布完全相同
  • 越接近上界,表示两个分布差异越大
  • 相比 KL,JS 更适合做两个模型输出分布的对称比较

Argmax agreement

Argmax agreement 比较两个 tensor 在每个位置上预测的最大类别是否一致。

它不关心完整概率分布,只关心:

argmax(x) 是否等于 argmax(y)

计算方式:

idx_x = torch.argmax(x, dim=-1)
idx_y = torch.argmax(y, dim=-1)

argmax_agree = (idx_x == idx_y).float()
argmax_agree_mean = argmax_agree.mean()

shape 变化:

x, y                    : [B, ..., C]
idx_x, idx_y            : [B, ...]
argmax_agree            : [B, ...]
argmax_agree_mean       : scalar

值域:

[0, 1]

含义:

  • 1 表示所有位置的 top-1 预测完全一致
  • 0 表示所有位置的 top-1 预测完全不一致
  • 0.9 表示 90% 的位置 top-1 预测一致

Argmax agreement 的特点:

  • 只看最终预测类别
  • 不看置信度
  • 不看非最大类别的分布
  • 对 logits 和 softmax 后的概率结果相同,因为 softmax 不改变排序

例如:

x = [10.0, 1.0, 0.0]
y = [2.0, 1.9, 1.8]

二者 argmax 都是第 0 类,因此 argmax agreement 为 1

但两个分布的置信度差异很大,KL/JS 可能不小。


Top-k agreement

Top-k agreement 比较两个 tensor 的前 k 个预测类别是否一致。

常见定义有几种,最常用的是 top-k overlap ratio:

top-k agreement = |TopK(x) ∩ TopK(y)| / k

即两个 top-k 集合的交集占比。

例如:

TopK(x) = {1, 3, 5}
TopK(y) = {3, 5, 8}

则:

overlap = {3, 5}
top-k agreement = 2 / 3

计算示例:

k = 5

topk_x = torch.topk(x, k=k, dim=-1).indices
topk_y = torch.topk(y, k=k, dim=-1).indices

overlap = (
    topk_x.unsqueeze(-1) == topk_y.unsqueeze(-2)
).any(dim=-1).float().mean(dim=-1)

topk_agree_mean = overlap.mean()

shape 变化:

x, y                 : [B, ..., C]
topk_x, topk_y       : [B, ..., k]
overlap              : [B, ...]
topk_agree_mean      : scalar

值域:

[0, 1]

含义:

  • 1 表示两个 tensor 的 top-k 集合完全一致
  • 0 表示两个 tensor 的 top-k 集合完全没有交集
  • 0.6 表示平均有 60% 的 top-k 候选相同

Top-k agreement 比 argmax agreement 更宽松。

例如:

Top1(x) != Top1(y)

但如果两个模型的高分候选集合接近,那么:

top-k agreement 仍然可能较高

这在大词表语言模型、检索系统、推荐系统中很常见。

还可以定义更严格的 top-k 指标。

第一种是 top-k exact set agreement:

TopK(x) 集合是否完全等于 TopK(y) 集合
topk_exact = (overlap == 1.0).float()
topk_exact_mean = topk_exact.mean()

第二种是 ordered top-k agreement:

TopK(x) 的顺序是否完全等于 TopK(y)
ordered_topk_agree = (topk_x == topk_y).all(dim=-1).float()
ordered_topk_agree_mean = ordered_topk_agree.mean()

严格程度如下:

ordered top-k agreement
    > top-k exact set agreement
        > top-k overlap ratio

其中 ordered top-k agreement 最严格,top-k overlap ratio 最平滑。


四类指标的对比

假设:

x, y shape = [B, ..., C]

整体 shape 变化如下:

指标 输入 shape 中间 shape 输出 shape 标量汇总
KL divergence [B, ..., C] [B, ..., C] [B, ...] mean/sum
JS divergence [B, ..., C] [B, ..., C] [B, ...] mean/sum
argmax agreement [B, ..., C] [B, ...] [B, ...] mean
top-k agreement [B, ..., C] [B, ..., k] [B, ...] mean

值域对比:

指标 值域 最优值 方向
KL divergence [0, +∞) 0 越小越相似
JS divergence [0, ln 2] 0 越小越相似
JS normalized [0, 1] 0 越小越相似
argmax agreement [0, 1] 1 越大越相似
top-k agreement [0, 1] 1 越大越相似

该如何解读

KL divergence 适合回答:

两个完整概率分布是否接近?

它关注概率质量的偏移,尤其关注参考分布中高概率位置是否被另一个分布正确覆盖。

适用于:

  • 知识蒸馏
  • teacher/student 分布对齐
  • 语言模型 logits 分布比较
  • 量化前后概率分布偏移分析

JS divergence 适合回答:

两个分布整体差异有多大?

它比 KL 更稳定、更对称,适合做模型间输出分布差异的通用度量。

适用于:

  • 两个模型输出分布比较
  • 量化前后分布漂移
  • 不希望区分参考方和被比较方的场景

Argmax agreement 适合回答:

最终 top-1 决策是否一致?

它不关心置信度,只关心最终预测类别。

适用于:

  • 分类结果一致性
  • 量化模型和原模型 top-1 是否一致
  • 推理优化前后预测类别是否改变

Top-k agreement 适合回答:

高概率候选集合是否一致?

它比 argmax 更宽松,能够反映候选排序的稳定性。

适用于:

  • 语言模型 top-k token 对齐
  • 检索候选集一致性
  • 推荐系统候选集稳定性
  • 大类别分类中 top-k 预测对齐

参考实现

下面给出一个统一实现,假设类别维在最后一维:

import torch
import torch.nn.functional as F


def compare_tensors(x, y, k=5, from_logits=True, eps=1e-8):
    """
    x, y: [B, ..., C]
    return:
        kl:              [B, ...]
        js:              [B, ...]
        argmax_agree:    [B, ...]
        topk_overlap:    [B, ...]
    """

    assert x.shape == y.shape
    C = x.shape[-1]
    k = min(k, C)

    if from_logits:
        log_p = F.log_softmax(x, dim=-1)
        log_q = F.log_softmax(y, dim=-1)
        p = log_p.exp()
        q = log_q.exp()
    else:
        p = x.clamp_min(eps)
        q = y.clamp_min(eps)

        p = p / p.sum(dim=-1, keepdim=True)
        q = q / q.sum(dim=-1, keepdim=True)

        log_p = torch.log(p)
        log_q = torch.log(q)

    # KL(P || Q)
    kl = (p * (log_p - log_q)).sum(dim=-1)

    # JS(P || Q)
    m = 0.5 * (p + q)
    log_m = torch.log(m.clamp_min(eps))

    js = 0.5 * (p * (log_p - log_m)).sum(dim=-1) \
       + 0.5 * (q * (log_q - log_m)).sum(dim=-1)

    # Argmax agreement
    idx_x = torch.argmax(x, dim=-1)
    idx_y = torch.argmax(y, dim=-1)

    argmax_agree = (idx_x == idx_y).float()

    # Top-k overlap ratio
    topk_x = torch.topk(x, k=k, dim=-1).indices
    topk_y = torch.topk(y, k=k, dim=-1).indices

    topk_overlap = (
        topk_x.unsqueeze(-1) == topk_y.unsqueeze(-2)
    ).any(dim=-1).float().mean(dim=-1)

    return {
        "kl": kl,
        "kl_mean": kl.mean(),
        "js": js,
        "js_mean": js.mean(),
        "argmax_agree": argmax_agree,
        "argmax_agree_mean": argmax_agree.mean(),
        "topk_overlap": topk_overlap,
        "topk_overlap_mean": topk_overlap.mean(),
    }

总结

这四个指标不是互相替代关系,而是观察差异的角度不同。

如果关心完整分布差异,使用:

KL divergence
JS divergence

如果关心最终决策是否一致,使用:

argmax agreement

如果关心候选集合是否稳定,使用:

top-k agreement

通常建议组合使用:

JS divergence + argmax agreement + top-k agreement

原因是:

  • JS divergence 反映整体分布漂移
  • argmax agreement 反映最终 top-1 决策一致性
  • top-k agreement 反映高分候选集合稳定性

例如在量化模型评估中:

JS 很小,argmax agreement 很高

说明量化后模型输出基本保持一致。

如果:

JS 较大,但 argmax agreement 很高

说明最终预测类别没变,但置信度或非 top-1 分布发生了明显变化。

如果:

JS 很小,但 argmax agreement 较低

说明两个分布整体很接近,但 top-1 位置可能经常发生细微交换,常见于多个类别分数非常接近的情况。

比较分类效果

推荐:

accuracy
top-k accuracy
precision
recall
F1
confusion matrix
cross entropy / NLL

如果关注概率校准:

Brier score
ECE

比较回归效果

推荐:

MAE / L1
MSE
RMSE
MAPE
relative_error

比较语言模型

推荐:

mean token NLL
perplexity
average log_prob

任务型评价可以补充:

exact match
BLEU
ROUGE
pass@k
win rate

常见误区

  1. L1 越小只说明数值更接近,不一定说明任务效果更好。
  2. entropy 低只说明模型更自信,不一定说明模型更正确。
  3. log_prob 适合概率模型,但比较时必须保证数据和归一化方式一致。
  4. logits 不是概率,也不是最终精度指标。
  5. 任何指标都要先明确计算维度和 reduction 方式。
  6. 标量指标通常来自对 batch、类别、序列或空间维度的归约。
  7. 比较语言模型时,不要直接比较不同长度文本的总 log_prob。

简要结论

  • L1 norm:看数值差异,适合数值误差和回归。
  • entropy:看分布不确定性,不适合单独代表准确率。
  • log_prob:看模型给目标的概率,是 NLL 和 cross entropy 的基础。
  • logits:模型原始分数,需要经过 softmax 或 sigmoid 才能解释为概率。
  • 比较精度前,必须先明确 tensor 的语义、计算维度和归约方式。