python绘制的ROC曲线与结果不符

python进行XGBoost分析多分类问题时,采用precision、recall、f1-macro、f1-weighted和test score来评价模型并绘制ROC曲线。
结果是以上评价指标都是1,但是ROC曲线并不符合评价指标的结果,并出现了以下错误:

img


请问我要如何解决这个问题?
完整的代码如下:


```python
from xgboost import XGBClassifier
from xgboost import plot_importance
import pandas as pd
from sklearn.model_selection import KFold, cross_val_score as CVS, train_test_split 
from sklearn import metrics
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import GridSearchCV
import numpy as np
from sklearn.metrics import precision_score, recall_score, f1_score,roc_curve, auc
from sklearn.preprocessing import label_binarize


#读取数据和标签
X = pd.read_excel("C:/Users/linyifeng/Desktop/data/SVM/freq_data.xlsx",sheet_name='train_x')
y = pd.read_excel("C:/Users/linyifeng/Desktop/data/SVM/freq_data.xlsx",sheet_name='train_y')
y = y.values.ravel()
#将数据分为训练集、验证集和一部分测试集
X_trainval,X_test,y_trainval,y_test = train_test_split(X,y,test_size=0.2,random_state=777)
le = LabelEncoder()
y_trainval = le.fit_transform(y_trainval)
y_test = le.fit_transform(y_test)

XGB = XGBClassifier(
learning_rate = 0.17,
n_estimators = 90,
max_depth=3,
min_child_weight=2,
gamma=0.07,
subsample=0.8,
colsample_bytree=0.8,
objective= 'multi:softmax',
num_class=7,
seed=27)
XGB.fit(X_trainval,y_trainval)


#评估模型
XGB.fit(X_test,y_test)
y_pred = XGB.predict(X_test)#返回训练后的预测结果
print(y_test)
print(y_pred)
p = precision_score(y_test, y_pred, average='weighted')#计算精确率
print(p)
r = recall_score(y_test, y_pred, average='weighted')#计算召回率
print(r)
f1sore = f1_score(y_test, y_pred, average='macro')#计算f1_macro 分数
print(f1sore)
f1sore_weight = f1_score(y_test, y_pred, average='weighted')#计算f1_weight 分数
print(f1sore_weight)
test_score = XGB.score(X_test,y_test)#计算test score
print("Score on testing set:{:.2f}".format(test_score))

#绘制ROC曲线
import matplotlib.pyplot as plt
y_ROC = y_test
y_ROC = label_binarize(y_ROC,classes=[1,2,3,4,5])#将label二值化
y_pred = label_binarize(y_pred,classes=[1,2,3,4,5])#将label二值化
n_classes = y_ROC.shape[1]#读取二维矩阵的类数,这里为5
y_score = XGB.predict_proba(X_test)#获取每个分类的概率值

fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_ROC[:, i], y_score[:, i])#遍历五种分类的结果来计算五种分类的fpr和tpr
    roc_auc[i] = auc(fpr[i], tpr[i])#计算roc曲线面积
fpr["micro"], tpr["micro"], _ = roc_curve(y_ROC .ravel(), y_score.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
plt.figure()
lw = 2
plt.plot(fpr["micro"], tpr["micro"], color='darkorange',
         lw=lw, label='ROC curve (area = %0.2f)' % roc_auc["micro"])
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.ylim([0.0, 1.0])
plt.xlim([0.0, 1.0])
plt.legend(loc="lower right")
plt.title("Precision-Recall")
plt.show()

```

该回答引用chatGpt
根据您提供的信息,所有评价指标都为1,但是ROC曲线不符合预期,这表明评价指标的结果可能不正确。有以下几个问题需要检查和调整:

对于多类别问题,需要使用label_binarize函数将真实标签和预测标签二值化。您已经使用了该函数,但它在二值化标签时指定的类别不正确。在您的代码中,使用了classes=[1,2,3,4,5],但您的类别应该为7。您可以修改以下两行代码,将classes参数更改为[0,1,2,3,4,5,6]:

y_ROC = label_binarize(y_ROC,classes=[0,1,2,3,4,5,6])#将label二值化
y_pred = label_binarize(y_pred,classes=[0,1,2,3,4,5,6])#将label二值化

您的模型是用X_trainval和y_trainval进行训练的,但在评估模型时,您使用了X_test和y_test,这意味着您的模型可能会过度拟合测试集。您可以使用X_trainval和y_trainval来评估模型,并在测试集上进行最终评估,如下所示:

# 评估模型
y_pred = XGB.predict(X_trainval) # 返回训练后的预测结果
p = precision_score(y_trainval, y_pred, average='weighted') # 计算精确率
print(p)
r = recall_score(y_trainval, y_pred, average='weighted') # 计算召回率
print(r)
f1sore = f1_score(y_trainval, y_pred, average='macro') # 计算f1_macro 分数
print(f1sore)
f1sore_weight = f1_score(y_trainval, y_pred, average='weighted') # 计算f1_weight 分数
print(f1sore_weight)
test_score = XGB.score(X_test, y_test) # 计算test score
print("Score on testing set:{:.2f}".format(test_score))

# 绘制ROC曲线
y_ROC = y_trainval
y_ROC = label_binarize(y_ROC, classes=[0,1,2,3,4,5,6]) # 将label二值化
y_pred = label_binarize(y_pred, classes=[0,1,2,3,4,5,6]) # 将label二值化
n_classes = y_ROC.shape[1] # 读取二维矩阵的类数,这里为7
y_score = XGB.predict_proba(X_trainval) # 获取每个分类的概率值

fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_ROC[:, i], y_score[:, i]) # 遍历七种分类的结果来计算七种分类的fpr和tpr
    roc_auc[i] = auc(fpr[i], tpr[i]) # 计算roc曲线面积
fpr["micro"], tpr["micro"], _ = roc_curve(y_ROC.ravel(), y_score.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
plt.figure()
lw = 2
plt.plot(fpr["micro"], tpr["micro"], color='darkorange',
         lw=lw, label='ROC curve (area = %0.2f)' % roc_auc["micro"])
for i in range(n_classes):
    plt.plot(fpr[i], tpr[i], lw=lw,
             label='ROC curve of class %d (area = %0.2f)' % (i, roc_auc[i]))
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.ylim([0.0, 1.0])
plt.xlim([0.0, 1.0])
plt.legend(loc="lower right")
plt.title("ROC Curve")
plt.show()

您可以尝试使用网格搜索(Grid Search)来调整模型的参数,以改善模型的性能和泛化能力。例如,您可以使用以下代码:

# 网格搜索
param_grid = {'learning_rate': [0.1, 0.17],
              'n_estimators': [50, 90],
              'max_depth': [3, 5],
              'min_child_weight': [1, 2],
              'gamma': [0, 0.07],
              'subsample': [0.8, 1],
              'colsample_bytree': [0.8, 1]}
grid_search = GridSearchCV(estimator=XGB, param_grid=param_grid, cv=5, n_jobs=-1)
grid_search.fit(X_trainval, y_trainval)
print("Best parameters: ", grid_search.best_params_)
print("Best score: ", grid_search.best_score_)

希望这些建议能帮助您解决问题。

看看你的曲线的数据,和预期是不是不同,或者缺少数据

打印y_test和y_pred的结果为:

img


上面是y_test,下面是y_pred,确实都预测对了啊

基于最新版ChatGPT4的回答,望采纳!!!有其他问题也可以询问我哦、”(最新版更智能,功能更加强大)

你的问题出在ROC曲线的绘制部分。ROC曲线主要用于二分类问题,当你需要绘制多分类问题的ROC曲线时,需要将其分解为多个二分类问题,并计算每个类的ROC曲线。在你的代码中,有以下几个问题需要修正:

  1. 你的数据有7个类别,但在将标签二值化时,只使用了5个类别。你需要将所有类别都包含在内。将以下代码:
pythonCopy code
y_ROC = label_binarize(y_ROC,classes=[1,2,3,4,5])#将label二值化
y_pred = label_binarize(y_pred,classes=[1,2,3,4,5])#将label二值化

修改为:

pythonCopy code
unique_classes = np.unique(y_test)
y_ROC = label_binarize(y_ROC, classes=unique_classes)  # 将label二值化
y_pred = label_binarize(y_pred, classes=unique_classes)  # 将label二值化
n_classes = y_ROC.shape[1]  # 读取二维矩阵的类数
  1. 在计算ROC曲线时,应当使用真实标签(y_ROC)与预测概率(y_score)进行计算,而不是使用预测的标签(y_pred)。以下是修改后的代码:

    for i in range(n_classes):
        fpr[i], tpr[i], _ = roc_curve(y_ROC[:, i], y_score[:, i])  # 遍历各个分类的结果来计算各个分类的fpr和tpr
        roc_auc[i] = auc(fpr[i], tpr[i])  # 计算roc曲线面积
    fpr["micro"], tpr["micro"], _ = roc_curve(y_ROC.ravel(), y_score.ravel())
    roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
    

在进行了这些修改后,你的代码应该可以正确绘制ROC曲线。注意,你可能需要为每个类别绘制单独的ROC曲线,或者计算一个总体的平均ROC曲线。在这个例子中,我们计算了一个微观平均ROC曲线(micro-averaged ROC curve)。

参考GPT,根据代码中的警告信息,这个问题可能是由于测试集中没有某个分类的样本所导致的。可以通过检查测试集中每个分类的样本数量来确认这一点。如果存在某个分类样本数量为0,需要重新调整训练集和测试集的划分方式,以确保每个分类都有足够的样本。

此外,对于多分类问题,应该将评价指标的average参数设置为'macro'或'weighted'。如果使用'binary'或None,可能会导致评价指标的结果异常或不准确。需要注意的是,对于不同的指标,其average参数的设置可能有所不同。

最后,对于ROC曲线的绘制,需要将标签进行二值化处理,这里需要确保标签的二值化方式与模型训练时一致。此外,对于多分类问题,需要分别计算每个分类的ROC曲线,并绘制宏观平均ROC曲线。

参考GPT和自己的思路:

根据你提供的代码和问题描述,可能是因为ROC曲线的绘制中数据出现了问题。在代码中,您使用了label_binarize()函数将预测的标签和测试集的标签都进行了二值化处理。这个操作可能会导致数据出现错误。ROC曲线绘制需要的是概率值,而不是二值化标签。所以,您可以尝试不进行二值化处理,使用predict_proba()函数获取每个分类的概率值来绘制ROC曲线。

具体修改如下:

  1. 将第59、60行代码删除:
y_ROC = label_binarize(y_ROC,classes=[1,2,3,4,5])#将label二值化
y_pred = label_binarize(y_pred,classes=[1,2,3,4,5])#将label二值化
  1. 第62行代码使用predict_proba()函数获取每个分类的概率值:
y_score = XGB.predict_proba(X_test)#获取每个分类的概率值

修改完代码之后,再次运行绘制ROC曲线的代码,查看绘制的结果是否符合预期。

参考GPT和自己的思路:这个错误是由于模型预测的结果中没有正样本造成的,这可能是由于标签数据中某些类别没有出现在测试集中导致的。为了解决这个问题,可以使用label_binarize函数将多类别标签转换为二进制格式,以确保所有类别都有样本出现在测试集中。

另外,由于多分类问题的ROC曲线通常是通过对每个类别分别绘制ROC曲线来绘制的,你需要将label_binarize函数中的classes参数修改为所有可能的类别,而不仅仅是[1, 2, 3, 4, 5]。

以下是修改后的代码:

#读取数据和标签
X = pd.read_excel("C:/Users/linyifeng/Desktop/data/SVM/freq_data.xlsx",sheet_name='train_x')
y = pd.read_excel("C:/Users/linyifeng/Desktop/data/SVM/freq_data.xlsx",sheet_name='train_y')
y = y.values.ravel()
#将数据分为训练集、验证集和一部分测试集
X_trainval,X_test,y_trainval,y_test = train_test_split(X,y,test_size=0.2,random_state=777)
le = LabelEncoder()
y_trainval = le.fit_transform(y_trainval)
y_test = le.fit_transform(y_test)

XGB = XGBClassifier(
learning_rate = 0.17,
n_estimators = 90,
max_depth=3,
min_child_weight=2,
gamma=0.07,
subsample=0.8,
colsample_bytree=0.8,
objective= 'multi:softmax',
num_class=7,
seed=27)
XGB.fit(X_trainval,y_trainval)


#评估模型
XGB.fit(X_test,y_test)
y_pred = XGB.predict(X_test)#返回训练后的预测结果
print(y_test)
print(y_pred)
p = precision_score(y_test, y_pred, average='weighted')#计算精确率
print(p)
r = recall_score(y_test, y_pred, average='weighted')#计算召回率
print(r)
f1sore = f1_score(y_test, y_pred, average='macro')#计算f1_macro 分数
print(f1sore)
f1sore_weight = f1_score(y_test, y_pred, average='weighted')#计算f1_weight 分数
print(f1sore_weight)
test_score = XGB.score(X_test,y_test)#计算test score
print("Score on testing set:{:.2f}".format(test_score))

#绘制ROC曲线
import matplotlib.pyplot as plt
y_ROC = y_test
y_ROC = label_binarize(y_ROC,classes=np.unique(y))#将label二值化
y_pred = label_binarize(y_pred,classes=np.unique(y))#将label二值化
n_classes = y_ROC.shape[1]#读取二维矩阵的类数,这里为7
y_score = XGB.predict_proba(X_test)#获取每个分类的概率值

fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_ROC[:, i], y_score[:, i])
roc_auc[i] = auc(fpr[i], tpr[i])

Compute micro-average ROC curve and ROC area
fpr["micro"], tpr["micro"], _ = roc_curve(y_ROC.ravel(), y_score.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])

Plot ROC curve
plt.figure()
lw = 2
plt.plot(fpr["micro"], tpr["micro"], color='darkorange',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc["micro"])
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.ylim([0.0, 1.0])
plt.xlim([0.0, 1.0])
plt.legend(loc="lower right")
plt.title("ROC Curve")
plt.show()


根据代码,您使用了 label_binarize 函数将原始标签进行了二值化,但是没有指定 classes 参数,导致在二值化后的标签中,可能存在某些类别没有被二值化到,从而引起计算 AUC 时的错误。建议指定 classes 参数以确保每个类别都被二值化。例如:

classes = np.unique(y_trainval)  # 从训练集的标签中获取所有类别
y_ROC = label_binarize(y_test, classes=classes)
y_pred = label_binarize(y_pred, classes=classes)

另外,您可以查看预测结果中每个类别的数量,以确保每个类别都有被预测到。可以使用 pandas 库中的 value_counts 函数:

print(pd.Series(y_pred).value_counts())

如果某些类别的数量为 0,说明模型预测时未预测到这些类别,可能需要对模型进行调整或增加更多的训练数据。此外,这个问题可能是因为您的标签数据在处理过程中没有正确转换为分类数据,因此评价指标结果为1。在您的代码中,您使用了LabelEncoder对标签数据进行编码,但在训练集和测试集上使用了不同的标签编码。此外,对于多类问题,您应该使用"macro"或"weighted"而不是"binary"选项来计算精确度、召回率和F1分数。
建议您尝试以下修改:
1.将标签数据转换为分类数据,使用相同的标签编码

le = LabelEncoder()
y = le.fit_transform(y)

2.计算精确度、召回率和F1分数时,使用"macro"或"weighted"选项

p = precision_score(y_test, y_pred, average='weighted')
r = recall_score(y_test, y_pred, average='weighted')
f1sore = f1_score(y_test, y_pred, average='macro')
f1sore_weight = f1_score(y_test, y_pred, average='weighted')

3.检查ROC曲线计算中的类别数量是否与实际情况相符

n_classes = len(np.unique(y))

请尝试以上建议,看看是否可以解决您的问题。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 关于该问题,我找了一篇非常好的博客,你可以看看是否有帮助,链接:python 画 roc曲线图

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^