一、Precision - Recall 的平衡
1)基礎理論
- 調整閾值的大小,可以調節精准率和召回率的比重;
- 閾值:threshold,分類邊界值,score > threshold 時分類為 1,score < threshold 時分類為 0;
- 閾值增大,精准率提高,召回率降低;閾值減小,精准率降低,召回率提高;
- 精准率和召回率是相互牽制,互相矛盾的兩個變量,不能同時增高;
- 邏輯回歸的決策邊界不一定非是
,也可以是任意的值,可根據業務而定:
,大於 threshold 時分類為 1,小於 threshold 時分類為 0;
- 推廣到其它算法,先計算出一個分數值 score ,再與 threshold 比較做分類判定;
2)舉例說明精准率和召回率相互制約的關系(一)
- 計算結果 score > 0 時,分類結果為 ★;score < 0 時,分類結果為 ●;
- ★ 類型為所關注的事件;
-
情景1:threshold = 0
- 精准率:4 / 5 = 0.80;
- 召回率:4 / 6 = 0.67;
-
情景2:threshold > 0;
- 精准率:2 / 2 = 1.00;
- 召回率:2 / 6 = 0.33;
-
情景3:threshold < 0;
- 精准率:6 / 8 = 0.75;
- 召回率:6 / 6 = 1.00;
3)舉例說明精准率和召回率相互制約的關系(二)
- LogisticRegression() 類中的 predict() 方法中,默認閾值 threshold 為 0,再根據 decision_function() 方法計算的待預測樣本的 score 值進行對比分類:score < 0 分類結果為 0,score > 0 分類結果為 1;
- log_reg.decision_function(X_test):計算所有待預測樣本的 score 值,以向量的數量類型返回結果;
- 此處的 score 值不是概率值,是另一種判斷分類的方式中樣本的得分,根據樣本的得分對樣本進行分類;
-
例
import numpy as np from sklearn import datasets digits = datasets.load_digits() X = digits.data y = digits.target.copy() y[digits.target==9] = 1 y[digits.target!=9] = 0 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666) from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression() log_reg.fit(X_train, y_train)
-
閾值 threshold = 0
y_predict_1 = log_reg.predict(X_test) from sklearn.metrics import confusion_matrix confusion_matrix(y_test, y_predict_1) # 混淆矩陣:array([[403, 2], [9, 36]], dtype=int64) from sklearn.metrics import precision_score precision_score(y_test, y_predict_1) # 精准率:0.9473684210526315 from sklearn.metrics import recall_score recall_score(y_test, y_predict_1) # 召回率:0.8
-
閾值 threshold = 5
decision_score = log_reg.decision_function(X_test) # 更改 decision_score ,經過向量變化得到新的預測結果 y_predict_2; # decision_score > 5,增大閾值為 5;(也就是提高判斷標准) y_predict_2 = np.array(decision_score >= 5, dtype='int') confusion_matrix(y_test, y_predict_2) # 混淆矩陣:array([[404, 1], [ 21, 24]], dtype=int64) precision_score(y_test, y_predict_2) # 精准率:0.96 recall_score(y_test, y_predict_2) # 召回率:0.5333333333333333
# 更改閾值的思路:基於 decision_function() 方法,改變 score 值,簡介更閾值,不再經過 predict() 方法,而是經過向量變化得到新的分類結果;
-
閾值 threshold = -5
decision_score = log_reg.decision_function(X_test) y_predict_3 = np.array(decision_score >= -5, dtype='int') confusion_matrix(y_test, y_predict_3) # 混淆矩陣:array([[390, 15], [5, 40]], dtype=int64) precision_score(y_test, y_predict_3) # 精准率:0.7272727272727273 recall_score(y_test, y_predict_3) # 召回率:0.8888888888888888
-
分析:
- 精准率和召回率相互牽制,相互平衡的,一個升高,另一個就會降低;
- 閾值越大,精准率越高,召回率越低;閾值越小,精准率越低,召回率越高;
- 更改閾值:1)通過 LogisticRegression() 模塊下的 decision_function() 方法得到預測得分;2)不使用 predict() 方法,而是重新設定閾值,通過向量轉化,直接根據預測得分進行樣本分類;
二、精准率 - 召回率曲線(P - R 曲線)
- 對應分類算法,都可以調用其 decision_function() 方法,得到算法對每一個樣本的決策的分數值;
- LogisticRegression() 算法中,默認的決策邊界閾值為 0,樣本的分數值大於 0,該樣本分類為 1;樣本的分數值小於 0,該樣本分類為 0。
- 思路:隨着閾值 threshold 的變化,精准率和召回率跟着相應變化;
- 設置不同的 threshold 值:
decision_scores = log_reg.decision_function(X_test) thresholds = np.arange(np.min(decision_scores), np.max(decision_scores), 0.1)
# 0.1 是區間取值的步長;
1)編碼實現 threshold - Precision、Recall 曲線和 P - R曲線
-
import numpy as np import matplotlib.pyplot as plt from sklearn import datasets digits = datasets.load_digits() X = digits.data y = digits.target.copy() y[digits.target==9] = 1 y[digits.target!=9] = 0 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666) from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression() log_reg.fit(X_train, y_train) decision_scores = log_reg.decision_function(X_test) from sklearn.metrics import precision_score from sklearn.metrics import recall_score precisions = [] recalls = [] thresholds = np.arange(np.min(decision_scores), np.max(decision_scores), 0.1) for threshold in thresholds: y_predict = np.array(decision_scores >= threshold, dtype='int') precisions.append(precision_score(y_test, y_predict)) recalls.append(recall_score(y_test, y_predict))
-
threshold - Precision、Recall 曲線
plt.plot(thresholds, precisions) plt.plot(thresholds, recalls) plt.show()
-
P - R 曲線
plt.plot(precisions, recalls) plt.show()
2)scikit-learn 中 precision_recall_curve() 方法
- 根據 y_test、y_predicts 直接求解 precisions、recalls、thresholds;
from sklearn.metrics import precision_recall_curve
-
from sklearn.metrics import precision_recall_curve precisions, recalls, thresholds = precision_recall_curve(y_test, decision_scores) precisions.shape # (145,) recalls.shape # (145,) thresholds.shape # (144,)
- 現象:thresholds 中的元素個數,比 precisions 和recalls 中的元素個數少 1 個;
- 原因:當 precision = 1、recall = 0 時,不存在 threshold;
-
threshold - Precision、Recall 曲線
plt.plot(thresholds, precisions[:-1]) plt.plot(thresholds, recalls[:-1]) plt.show()
-
P - R 曲線
plt.plot(precisions, recalls) plt.show()
- 途中曲線開始急劇下降的點,可能就是精准率和召回率平衡位置的點;
3)分析
- 不同的模型對應的不同的 Precision - Recall 曲線:
- 外層曲線對應的模型更優;或者稱與坐標軸一起包圍的面積越大者越優。
- P - R 曲線也可以作為選擇算法、模型、超參數的指標;但一般不適用此曲線,而是使用 ROC 曲線;