Scikit-learn e Yellowbrick dando notas diferentes
Estou usando sklearn para calcular a precisão média e roc_auc de um classificador e yellowbrick para plotar as curvas roc_auc e de recall de precisão. O problema é que os pacotes dão notas diferentes nas duas métricas e não sei qual é a correta.
O código usado:
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from yellowbrick.classifier import ROCAUC
from yellowbrick.classifier import PrecisionRecallCurve
from sklearn.datasets import make_classification
from sklearn.metrics import roc_auc_score
from sklearn.metrics import average_precision_score
seed = 42
# provides de data
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0,
n_informative=2, random_state=seed)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
clf_lr = LogisticRegression(random_state=seed)
clf_lr.fit(X_train, y_train)
y_pred = clf_lr.predict(X_test)
roc_auc = roc_auc_score(y_test, y_pred)
avg_precision = average_precision_score(y_test, y_pred)
print(f"ROC_AUC: {roc_auc}")
print(f"Average_precision: {avg_precision}")
print('='*20)
# visualizations
viz3 = ROCAUC(LogisticRegression(random_state=seed))
viz3.fit(X_train, y_train)
viz3.score(X_test, y_test)
viz3.show()
viz4 = PrecisionRecallCurve(LogisticRegression(random_state=seed))
viz4.fit(X_train, y_train)
viz4.score(X_test, y_test)
viz4.show()
O código produz a seguinte saída:
Como pode ser visto acima, as métricas fornecem valores diferentes dependendo do pacote. Na declaração de impressão estão os valores calculados pelo scikit-learn, enquanto nos gráficos aparecem anotados os valores calculados pelo yellowbrick.
Respostas
Como você usa o predictmétodo scikit-learn, suas previsões y_predsão associações difíceis de classe, e não probabilidades:
np.unique(y_pred)
# array([0, 1])
Mas para cálculos ROC e Precision-Recall, este não deve ser o caso; as previsões que você passa para esses métodos devem ser probabilidades, e não classes difíceis. Dos average_precision_score documentos :
y_score: array, shape = [n_samples] ou [n_samples, n_classes]
As pontuações de destino podem ser estimativas de probabilidade da classe positiva, valores de confiança ou medida não limitada de decisões (conforme retornado por “função_de_ decisão” em alguns classificadores).
onde não limite significa exatamente classes não rígidas . Semelhante é o caso de roc_auc_score( docs ).
Corrigir isso com o código a seguir torna os resultados do scikit-learn idênticos aos retornados por Yellowbrick:
y_pred = clf_lr.predict_proba(X_test) # get probabilities
y_prob = np.array([x[1] for x in y_pred]) # keep the prob for the positive class 1
roc_auc = roc_auc_score(y_test, y_prob)
avg_precision = average_precision_score(y_test, y_prob)
print(f"ROC_AUC: {roc_auc}")
print(f"Average_precision: {avg_precision}")
Resultados:
ROC_AUC: 0.9545954595459546
Average_precision: 0.9541994473779806
Como o Yellowbrick lida com todos esses detalhes computacionais internamente (e de forma transparente), ele não sofre com o erro no procedimento manual do scikit-learn feito aqui.
Observe que, no caso binário (como aqui), você pode (e deve) tornar seus gráficos menos confusos com o binary=Trueargumento:
viz3 = ROCAUC(LogisticRegression(random_state=seed), binary=True) # similarly for the PrecisionRecall curve
e que, ao contrário do que migh esperar intuitivamente, para o caso binário, pelo menos, o scoremétodo de ROCAUCvai não voltar a AUC, mas a precisão, como especificado nos documentos :
viz3.score(X_test, y_test)
# 0.88
# verify this is the accuracy:
from sklearn.metrics import accuracy_score
accuracy_score(y_test, clf_lr.predict(X_test))
# 0.88