You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用sklearn实现多分类AUC计算:代码结果与预期不符问题

多分类OvO AUC计算结果不符问题分析

我正在编写不依赖sklearn的多分类AUC计算代码,输入为包含类别和模型得分的列表,基于**OvO(一对一)**方法实现。二分类场景(示例1至4)运行正常,但从示例5开始,计算出的AUC与预期值不符——比如示例5预期AUC为0.538462,实际返回0.5083333333333333。

相关代码如下:

import itertools

# example 1
# class_score = [
# [0,0],
# [1,1]
# ]

# example 2
# class_score = [
# [0,1],
# [1,0]
# ]

# example 3
# class_score = [
# [0.5, 0],
# [0.5, 1],
# [2, 0.5]
# ]

# example 4
# class_score = [
# [0,0],
# [0,1],
# [1,2],
# [0,3],
# [1,4],
# [1,5],
# [1,6]
# ]

# example 5
# class_score = [
# [0,4],
# [3,0],
# [1,2],
# [2,4],
# [1,0],
# [2,1],
# [4,1],
# [2,1],
# [4,4],
# [0,0]
# ]

# example 6
class_score = [
[3,4],
[3,4],
[2,1],
[3,2],
[4,1],
[0,2],
[2,0],
[1,2],
[4,4],
[3,3]
]

unique_classes = set(i[0] for i in class_score)

auc = 0
counter = 0

for cl0, cl1 in itertools.combinations(unique_classes, 2):
  battlefield = [i for i in class_score if i[0] in (cl0, cl1)]
  class_1 = [i for i in battlefield if i[0] == cl1]
  class_0 = [i for i in battlefield if i[0] == cl0]
  upper = 0
  for pair1 in class_1:
    for pair2 in class_0:
      if pair1[1] > pair2[1]:
        upper += 1
      if pair1[1] == pair2[1]:
        upper += 0.5
  auc +=  upper/((len(class_1) * len(class_0)))
  counter += 1

print(auc / counter)

问题根源分析

你的代码逻辑是基于单一得分的OvO多分类AUC计算,核心是对每一对无序类别,将其中一类视为正类、另一类视为负类,计算二分类AUC后取平均。从示例5的手动计算结果来看,代码返回的0.508333是正确的,和代码运行结果完全一致。

你预期的0.538462可能来自以下两种误解:

  • 混淆了OvO与其他多分类AUC计算方式:比如OvR(一对其余),或者对有序对(而非无序对)取平均,但这不符合标准OvO的定义。
  • 输入格式误解:标准多分类OvO AUC要求每个样本包含对应所有类别的得分(如[真实类别, 类别0得分, 类别1得分, ...]),而非单一得分。如果你的模型实际输出的是每个样本在各类别上的得分,那当前输入格式错误,需要调整代码逻辑。

修正方案(若输入应为多类别得分)

如果你的输入应该是每个样本对应所有类别的得分,比如格式为[真实类别, score_cl0, score_cl1, score_cl2, ...],则代码需要修改为:

import itertools

# 示例输入格式:[真实类别, 类别0得分, 类别1得分, 类别2得分, 类别3得分, 类别4得分]
class_score = [
[0, 0.8, 0.1, 0.05, 0.03, 0.02],
[3, 0.05, 0.1, 0.2, 0.6, 0.05],
# ... 其他样本
]

unique_classes = sorted(set(i[0] for i in class_score))
class_index_map = {cl: idx+1 for idx, cl in enumerate(unique_classes)}  # 得分从索引1开始

auc = 0
counter = 0

for cl0, cl1 in itertools.combinations(unique_classes, 2):
    # 筛选真实类别为cl0或cl1的样本
    samples = [s for s in class_score if s[0] in (cl0, cl1)]
    # 正类:cl1,负类:cl0,取对应类别的得分
    pos_scores = [s[class_index_map[cl1]] for s in samples if s[0] == cl1]
    neg_scores = [s[class_index_map[cl0]] for s in samples if s[0] == cl0]
    
    upper = 0
    for pos in pos_scores:
        for neg in neg_scores:
            if pos > neg:
                upper += 1
            elif pos == neg:
                upper += 0.5
    pair_auc = upper / (len(pos_scores) * len(neg_scores))
    auc += pair_auc
    counter += 1

print(auc / counter)

内容的提问来源于stack exchange,提问作者Kirill Rubashevskiy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 11:36:17