从宏平均ROC到Obuchowski指数:如何精准评估多分类模型?

picture.image

背景

在机器学习分类问题中,评估模型性能的标准是必须深入思考的重要环节,对于二分类问题,常用的指标有ROC曲线、AUC值等,而在多分类问题中,类似的指标也有其对应的版本,但复杂性也随之增加,今天,作者将带你了解一种用于多分类模型评估的指标—— Obuchowski指数 ,并通过实际代码演示如何计算该指数

Obuchowski指数简介

Obuchowski指数是一种评估多分类问题下分类器性能的工具,它通过考虑各类之间的两两比较,综合计算模型在所有类对上的AUC值,进而为多分类问题提供一个整体的评估,它的计算方式类似于二分类中的AUC,只不过是将各类别之间的分类效果纳入考量

该指数可以为你提供一个更全面的视角去评估多分类模型,因为它不仅关注每个类别的表现,还特别注重模型区分不同类别的能力,主要用于医疗影像或诊断测试的评估,最终评估模型在多个类别上区分不同病情的能力

代码实现

数据读取处理


          
import pandas as pd
          
import numpy as np
          
import matplotlib.pyplot as plt
          
from sklearn.model_selection import train_test_split
          
plt.rcParams['font.family'] = 'Times New Roman'
          
plt.rcParams['axes.unicode_minus'] = False
          

          
df = pd.read_csv('Multiclass Diabetes Dataset.csv')
          
from sklearn.model_selection import train_test_split
          
# 分割数据集
          
X = df.drop(['Class'], axis = 1)
          
y = df['Class']
          

          
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3,
          
                                            stratify=df['Class']) #分离训练集和测试
          
df.head()
      

picture.image

从一个多分类糖尿病数据集中分离出特征和标签,然后将数据集划分为训练集和测试集,以便后续进行模型训练和测试,这个数据集通过生化指标(如尿素、肌酐、HbA1c等)结合人口统计信息(如性别和年龄)来预测个体的糖尿病状态,来自 Mendeley 数据,用于构建糖尿病的多分类预测模型

模型构建


          
from sklearn.ensemble import RandomForestClassifier
          

          
# 初始化随机森林分类器,添加更多参数
          
rf_model = RandomForestClassifier(
          
    n_estimators=100,              # 树的数量
          
    max_depth=5,                  # 树的最大深度
          
    min_samples_split=5,           # 分裂内部节点所需的最小样本数
          
    min_samples_leaf=4,            # 叶节点所需的最小样本数
          
    max_features='sqrt',           # 考虑分裂的最大特征数量,'sqrt' 是默认值用于分类问题
          
    bootstrap=True,                # 是否进行放回抽样
          
    class_weight='balanced',       # 平衡类间权重
          
    random_state=42                # 随机种子,以便结果复现
          
)
          

          
# 训练模型
          
rf_model.fit(X_train, y_train)
      

picture.image

初始化一个带有特定参数的随机森林分类器来训练模型,以便在平衡类别权重的情况下对训练数据 X_train 和 y_train 进行分类

混淆矩阵绘制


          
from sklearn.metrics import confusion_matrix
          
import seaborn as sns
          
# 输出混淆矩阵
          
conf_matrix = confusion_matrix(y_test, pred)
          

          
# 绘制热力图
          
plt.figure(figsize=(10, 7), dpi=1200)
          
sns.heatmap(conf_matrix, annot=True, annot_kws={'size':15}, 
          
            fmt='d', cmap='YlGnBu', cbar_kws={'shrink': 0.75})
          
plt.xlabel('Predicted Label', fontsize=12)
          
plt.ylabel('True Label', fontsize=12)
          
plt.title('Confusion matrix heat map', fontsize=15)
          
plt.savefig('Confusion matrix heat map.pdf', format='pdf', bbox_inches='tight')
          
plt.show()
      

picture.image

生成并可视化随机森林分类模型的混淆矩阵,展示预测结果的正确和错误分类情况

多分类任务——宏平均ROC曲线

AUC计算


          
from sklearn import metrics
          
from sklearn.preprocessing import label_binarize
          
# 预测并计算概率
          
ytest_proba_rf = rf_model.predict_proba(X_test)
          

          
# 将y标签转换成one-hot形式
          
ytest_one_rf = label_binarize(y_test, classes=[0, 1, 2])
          

          
# 宏平均法计算AUC
          
rf_AUC = {}
          
rf_FPR = {}
          
rf_TPR = {}
          

          
for i in range(ytest_one_rf.shape[1]):
          
    rf_FPR[i], rf_TPR[i], thresholds = metrics.roc_curve(ytest_one_rf[:, i], ytest_proba_rf[:, i])
          
    rf_AUC[i] = metrics.auc(rf_FPR[i], rf_TPR[i])
          
print(rf_AUC)
          

          
# 合并所有的FPR并排序去重
          
rf_FPR_final = np.unique(np.concatenate([rf_FPR[i] for i in range(ytest_one_rf.shape[1])]))
          

          
# 计算宏平均TPR
          
rf_TPR_all = np.zeros_like(rf_FPR_final)
          
for i in range(ytest_one_rf.shape[1]):
          
    rf_TPR_all += np.interp(rf_FPR_final, rf_FPR[i], rf_TPR[i])
          
rf_TPR_final = rf_TPR_all / ytest_one_rf.shape[1]
          

          
# 计算最终的宏平均AUC
          
rf_AUC_final = metrics.auc(rf_FPR_final, rf_TPR_final)
          
AUC_final_rf = rf_AUC_final  # 最终AUC
          

          
print(f"Macro Average AUC with Random Forest: {AUC_final_rf}")
      

picture.image

通过计算各类的ROC曲线和AUC(曲线下面积),并最终得到宏平均的AUC值,为后续宏平均ROC曲线绘制做准备

可视化


          
plt.figure(figsize=(8, 8), dpi=1200)
          
# 使用不同的颜色和线型
          
plt.plot(rf_FPR[0], rf_TPR[0], color='#1f77b4', linestyle='-', label='Class 0 ROC  AUC={:.4f}'.format(rf_AUC[0]), lw=2)
          
plt.plot(rf_FPR[1], rf_TPR[1], color='#ff7f0e', linestyle='-', label='Class 1 ROC  AUC={:.4f}'.format(rf_AUC[1]), lw=2)
          
plt.plot(rf_FPR[2], rf_TPR[2], color='#2ca02c', linestyle='-', label='Class 2 ROC  AUC={:.4f}'.format(rf_AUC[2]), lw=2)
          
# 宏平均ROC曲线
          
plt.plot(rf_FPR_final, rf_TPR_final, color='#000000', linestyle='-', label='Macro Average ROC  AUC={:.4f}'.format(rf_AUC_final), lw=3)
          
# 45度参考线
          
plt.plot([0, 1], [0, 1], color='gray', linestyle='--', lw=2, label='45 Degree Reference Line')
          
plt.xlabel('False Positive Rate (FPR)', fontsize=15)
          
plt.ylabel('True Positive Rate (TPR)', fontsize=15)
          
plt.title('Random Forest Classification ROC Curves and AUC', fontsize=18)
          
plt.grid(linestyle='--', alpha=0.7)
          
plt.legend(loc='lower right', framealpha=0.9, fontsize=12)
          
plt.savefig('RF_optimized.pdf', format='pdf', bbox_inches='tight')
          
plt.show()
      

picture.image

绘制随机森林的宏平均ROC曲线,并显示不同类别及宏平均的AUC值,结果表明模型在不同类别的分类性能(Class 0 AUC=0.9932,Class 1 AUC=1.0000,Class 2 AUC=0.9887),以及总体的宏平均AUC(0.9969),展示了模型的优秀表现,这部分代码具体解释参考文章——多分类如何绘制ROC曲线--宏平均ROC曲线

Obuchowski指数


          
from sklearn.metrics import roc_auc_score
          
from itertools import combinations
          

          
def calculate_obuchowski_index(model, X_test, y_test):
          
    """
          
    计算随机森林模型的Obuchowski指数。
          
    
          
    参数:
          
    - model: 训练好的随机森林模型
          
    - X_test: 测试集的特征
          
    - y_test: 测试集的标签
          
    
          
    返回:
          
    - Obuchowski指数
          
    """
          
    # 获取类别数量
          
    unique_classes = np.unique(y_test)
          
    n_classes = len(unique_classes)
          

          
    # 获取模型的预测概率
          
    y_pred_proba = model.predict_proba(X_test)
          

          
    auc_sum = 0
          
    # 遍历所有类的两两组合,计算每个类对之间的AUC
          
    for class_a, class_b in combinations(unique_classes, 2):
          
        # 为每个类创建二进制标签
          
        y_binary_a = np.where(y_test == class_a, 1, 0)
          
        y_binary_b = np.where(y_test == class_b, 1, 0)
          

          
        # 计算每个类别的预测概率
          
        y_pred_a = y_pred_proba[:, class_a]
          
        y_pred_b = y_pred_proba[:, class_b]
          

          
        # 计算 AUC
          
        auc_a = roc_auc_score(y_binary_a, y_pred_a)
          
        auc_b = roc_auc_score(y_binary_b, y_pred_b)
          

          
        # 累加两个类别对的AUC
          
        auc_sum += (auc_a + auc_b) / 2
          

          
    # 计算Obuchowski指数
          
    obuchowski_index = auc_sum / (n_classes * (n_classes - 1) / 2)
          
    
          
    return obuchowski_index
          

          
# 假设你已经训练好了模型并有X_test和y_test
          
obuchowski_index = calculate_obuchowski_index(rf_model, X_test, y_test)
          
print(f"Obuchowski指数: {obuchowski_index}")
      

picture.image

通过计算各类别之间所有可能的两两组合的AUC值,Obuchowski指数评估模型在多分类问题上的表现。当前结果中的Obuchowski指数为0.9939,表明模型具有非常优秀的整体分类性能。该指数范围在0到1之间,接近1的值表明模型在区分不同类别时表现出色,尤其是在处理类别不平衡问题时,Obuchowski指数相较于传统的AUC更具解释力和优势

宏平均ROC曲线和Obuchowski指数差异

计算方式

  • 宏平均ROC曲线通过对每个类别与其余类别的二分类ROC曲线进行平均,不考虑类别之间的差异
  • Obuchowski指数则是基于类别的两两比较(比如类别A对类别B,类别A对类别C等)分别计算ROC曲线,考虑了类别之间的区分度,并且可以加权平均

适用场景

  • 宏平均ROC曲线主要用于评估模型在多类别分类任务中的整体性能,适用于通用的机器学习问题
  • Obuchowski指数更适用于在不同类别之间有特定区分要求的场景,例如医学领域,强调类别对之间的区分

往期推荐

SCI图表复现:整合数据分布与相关系数的高级可视化策略

复现顶刊Streamlit部署预测模型APP

树模型系列:如何通过XGBoost提取特征贡献度

不止 SHAP 力图:LIME 实现任意黑盒模型的单样本解释

特征选择:Lasso和Boruta算法的结合应用

从基础到进阶:优化SHAP力图,让样本解读更直观

SCI图表复现:优化SHAP特征贡献图展示更多模型细节

多模型中的特征贡献度比较与可视化图解

picture.image

picture.image

picture.image

微信号|deep_ML

欢迎添加作者微信进入Python、ChatGPT群

进群请备注Python或AI进入相关群
无需科学上网、同步官网所有功能、使用无限制

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

个人观点,仅供参考

0
0
0
0
评论
未登录
暂无评论