準(zhǔn)確率與召回率
Scikit-Learn 提供了一些函數(shù)去計算分類器的指標(biāo),包括準(zhǔn)確率和召回率。
>>> from sklearn.metrics import precision_score, recall_score >>> precision_score(y_train_5, y_pred) # == 4344 / (4344 + 1307) 0.76871350203503808 >>> recall_score(y_train_5, y_train_pred) # == 4344 / (4344 + 1077) 0.79136690647482011
當(dāng)你去觀察精度的時候,你的“數(shù)字 5 探測器”看起來還不夠好。當(dāng)它聲明某張圖片是 5 的時候,它只有 77% 的可能性是正確的。而且,它也只檢測出“是 5”類圖片當(dāng)中的 79%。
通常結(jié)合準(zhǔn)確率和召回率會更加方便,這個指標(biāo)叫做“F1 值”,特別是當(dāng)你需要一個簡單的方法去比較兩個分類器的優(yōu)劣的時候。F1 值是準(zhǔn)確率和召回率的調(diào)和平均。普通的平均值平等地看待所有的值,而調(diào)和平均會給小的值更大的權(quán)重。所以,要想分類器得到一個高的 F1 值,需要召回率和準(zhǔn)確率同時高。
公式 3-3 F1 值
$$ F1 = \frac{2}{\frac{1}{precision} + \frac{1}{recall}} = 2 * \frac{precison * recall}{precison + recall} = \frac{TP}{TP + \frac{FN + FP}{2}} $$
為了計算 F1 值,簡單調(diào)用f1_score()
>>> from sklearn.metrics import f1_score >>> f1_score(y_train_5, y_pred) 0.78468208092485547
F1 支持那些有著相近準(zhǔn)確率和召回率的分類器。這不會總是你想要的。有的場景你會絕大程度地關(guān)心準(zhǔn)確率,而另外一些場景你會更關(guān)心召回率。舉例子,如果你訓(xùn)練一個分類器去檢測視頻是否適合兒童觀看,你會傾向選擇那種即便拒絕了很多好視頻、但保證所保留的視頻都是好(高準(zhǔn)確率)的分類器,而不是那種高召回率、但讓壞視頻混入的分類器(這種情況下你或許想增加人工去檢測分類器選擇出來的視頻)。另一方面,加入你訓(xùn)練一個分類器去檢測監(jiān)控圖像當(dāng)中的竊賊,有著 30% 準(zhǔn)確率、99% 召回率的分類器或許是合適的(當(dāng)然,警衛(wèi)會得到一些錯誤的報警,但是幾乎所有的竊賊都會被抓到)。
不幸的是,你不能同時擁有兩者。增加準(zhǔn)確率會降低召回率,反之亦然。這叫做準(zhǔn)確率與召回率之間的折衷。
準(zhǔn)確率/召回率之間的折衷
為了弄懂這個折衷,我們看一下SGDClassifier是如何做分類決策的。對于每個樣例,它根據(jù)決策函數(shù)計算分?jǐn)?shù),如果這個分?jǐn)?shù)大于一個閾值,它會將樣例分配給正例,否則它將分配給反例。圖 3-3 顯示了幾個數(shù)字從左邊的最低分?jǐn)?shù)排到右邊的最高分。假設(shè)決策閾值位于中間的箭頭(介于兩個 5 之間):您將發(fā)現(xiàn)4個真正例(數(shù)字 5)和一個假正例(數(shù)字 6)在該閾值的右側(cè)。因此,使用該閾值,準(zhǔn)確率為 80%(4/5)。但實際有 6 個數(shù)字 5,分類器只檢測 4 個, 所以召回是 67% (4/6)。現(xiàn)在,如果你 提高閾值(移動到右側(cè)的箭頭),假正例(數(shù)字 6)成為一個真反例,從而提高準(zhǔn)確率(在這種情況下高達 100%),但一個真正例 變成假反例,召回率降低到 50%。相反,降低閾值可提高召回率、降低準(zhǔn)確率。
![圖3-3 決策閾值與準(zhǔn)確度/召回率折衷][../images/chapter_3/chapter3.3.jpeg]
Scikit-Learn 不讓你直接設(shè)置閾值,但是它給你提供了設(shè)置決策分?jǐn)?shù)的方法,這個決策分?jǐn)?shù)可以用來產(chǎn)生預(yù)測。它不是調(diào)用分類器的predict()方法,而是調(diào)用decision_function()方法。這個方法返回每一個樣例的分?jǐn)?shù)值,然后基于這個分?jǐn)?shù)值,使用你想要的任何閾值做出預(yù)測。
>>> y_scores = sgd_clf.decision_function([some_digit]) >>> y_scores array([ 161855.74572176]) >>> threshold = 0 >>> y_some_digit_pred = (y_scores > threshold) array([ True], dtype=bool)
SGDClassifier用了一個等于 0 的閾值,所以前面的代碼返回了跟predict()方法一樣的結(jié)果(都返回了true)。讓我們提高這個閾值:
>>> threshold = 200000 >>> y_some_digit_pred = (y_scores > threshold) >>> y_some_digit_pred array([False], dtype=bool)
這證明了提高閾值會降調(diào)召回率。這個圖片實際就是數(shù)字 5,當(dāng)閾值等于 0 的時候,分類器可以探測到這是一個 5,當(dāng)閾值提高到 20000 的時候,分類器將不能探測到這是數(shù)字 5。
那么,你應(yīng)該如何使用哪個閾值呢?首先,你需要再次使用cross_val_predict()得到每一個樣例的分?jǐn)?shù)值,但是這一次指定返回一個決策分?jǐn)?shù),而不是預(yù)測值。
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method="decision_function")
現(xiàn)在有了這些分?jǐn)?shù)值。對于任何可能的閾值,使用precision_recall_curve(),你都可以計算準(zhǔn)確率和召回率:
from sklearn.metrics import precision_recall_curve precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
最后,你可以使用 Matplotlib 畫出準(zhǔn)確率和召回率(圖 3-4),這里把準(zhǔn)確率和召回率當(dāng)作是閾值的一個函數(shù)。
def plot_precision_recall_vs_threshold(precisions, recalls, thresholds): plt.plot(thresholds, precisions[:-1], "b--", label="Precision") plt.plot(thresholds, recalls[:-1], "g-", label="Recall") plt.xlabel("Threshold") plt.legend(loc="upper left") plt.ylim([0, 1]) plot_precision_recall_vs_threshold(precisions, recalls, thresholds) plt.show()
你也許會好奇為什么準(zhǔn)確率曲線比召回率曲線更加起伏不平。原因是準(zhǔn)確率有時候會降低,盡管當(dāng)你提高閾值的時候,通常來說準(zhǔn)確率會隨之提高。回頭看圖 3-3,留意當(dāng)你從中間箭頭開始然后向右移動一個數(shù)字會發(fā)生什么: 準(zhǔn)確率會由 4/5(80%)降到 3/4(75%)。另一方面,當(dāng)閾值提高時候,召回率只會降低。這也就說明了為什么召回率的曲線更加平滑。
現(xiàn)在你可以選擇適合你任務(wù)的最佳閾值。另一個選出好的準(zhǔn)確率/召回率折衷的方法是直接畫出準(zhǔn)確率對召回率的曲線,如圖 3-5 所示。
可以看到,在召回率在 80% 左右的時候,準(zhǔn)確率急劇下降。你可能會想選擇在急劇下降之前選擇出一個準(zhǔn)確率/召回率折衷點。比如說,在召回率 60% 左右的點。當(dāng)然,這取決于你的項目需求。
我們假設(shè)你決定達到 90% 的準(zhǔn)確率。你查閱第一幅圖(放大一些),在 70000 附近找到一個閾值。為了作出預(yù)測(目前為止只在訓(xùn)練集上預(yù)測),你可以運行以下代碼,而不是運行分類器的predict()方法。
y_train_pred_90 = (y_scores > 70000)
讓我們檢查這些預(yù)測的準(zhǔn)確率和召回率:
>>> precision_score(y_train_5, y_train_pred_90) 0.8998702983138781 >>> recall_score(y_train_5, y_train_pred_90) 0.63991883416343853
很棒!你擁有了一個(近似) 90% 準(zhǔn)確率的分類器。它相當(dāng)容易去創(chuàng)建一個任意準(zhǔn)確率的分類器,只要將閾值設(shè)置得足夠高。但是,一個高準(zhǔn)確率的分類器不是非常有用,如果它的召回率太低!
如果有人說“讓我們達到 99% 的準(zhǔn)確率”,你應(yīng)該問“相應(yīng)的召回率是多少?”
ROC 曲線
受試者工作特征(ROC)曲線是另一個二分類器常用的工具。它非常類似與準(zhǔn)確率/召回率曲線,但不是畫出準(zhǔn)確率對召回率的曲線,ROC 曲線是真正例率(true positive rate,另一個名字叫做召回率)對假反例率(false positive rate, FPR)的曲線。FPR 是反例被錯誤分成正例的比率。它等于 1 減去真反例率(true negative rate, TNR)。TNR是反例被正確分類的比率。TNR也叫做特異性。所以 ROC 曲線畫出召回率對(1 減特異性)的曲線。
為了畫出 ROC 曲線,你首先需要計算各種不同閾值下的 TPR、FPR,使用roc_curve()函數(shù):
from sklearn.metrics import roc_curve fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)
然后你可以使用 matplotlib,畫出 FPR 對 TPR 的曲線。下面的代碼生成圖 3-6.
def plot_roc_curve(fpr, tpr, label=None): plt.plot(fpr, tpr, linewidth=2, label=label) plt.plot([0, 1], [0, 1], 'k--') plt.axis([0, 1, 0, 1]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plot_roc_curve(fpr, tpr) plt.show()
這里同樣存在折衷的問題:召回率(TPR)越高,分類器就會產(chǎn)生越多的假正例(FPR)。圖中的點線是一個完全隨機的分類器生成的 ROC 曲線;一個好的分類器的 ROC 曲線應(yīng)該盡可能遠(yuǎn)離這條線(即向左上角方向靠攏)。
一個比較分類器之間優(yōu)劣的方法是:測量ROC曲線下的面積(AUC)。一個完美的分類器的 ROC AUC 等于 1,而一個純隨機分類器的 ROC AUC 等于 0.5。
Scikit-Learn 提供了一個函數(shù)來計算 ROC AUC:
>>> from sklearn.metrics import roc_auc_score >>> roc_auc_score(y_train_5, y_scores) 0.97061072797174941
因為 ROC 曲線跟準(zhǔn)確率/召回率曲線(或者叫 PR)很類似,你或許會好奇如何決定使用哪一個曲線呢?一個笨拙的規(guī)則是,優(yōu)先使用 PR 曲線當(dāng)正例很少,或者當(dāng)你關(guān)注假正例多于假反例的時候。其他情況使用 ROC 曲線。舉例子,回顧前面的 ROC 曲線和 ROC AUC 數(shù)值,你或許人為這個分類器很棒。但是這幾乎全是因為只有少數(shù)正例(“是 5”),而大部分是反例(“非 5”)。相反,PR 曲線清楚顯示出這個分類器還有很大的改善空間(PR 曲線應(yīng)該盡可能地靠近右上角)。
讓我們訓(xùn)練一個RandomForestClassifier,然后拿它的的ROC曲線和ROC AUC數(shù)值去跟SGDClassifier的比較。首先你需要得到訓(xùn)練集每個樣例的數(shù)值。但是由于隨機森林分類器的工作方式,RandomForestClassifier不提供decision_function()方法。相反,它提供了predict_proba()方法。Skikit-Learn分類器通常二者中的一個。predict_proba()方法返回一個數(shù)組,數(shù)組的每一行代表一個樣例,每一列代表一個類。數(shù)組當(dāng)中的值的意思是:給定一個樣例屬于給定類的概率。比如,70%的概率這幅圖是數(shù)字 5。
from sklearn.ensemble import RandomForestClassifier forest_clf = RandomForestClassifier(random_state=42) y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3, method="predict_proba")
但是要畫 ROC 曲線,你需要的是樣例的分?jǐn)?shù),而不是概率。一個簡單的解決方法是使用正例的概率當(dāng)作樣例的分?jǐn)?shù)。
y_scores_forest = y_probas_forest[:, 1] # score = proba of positive class fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)
現(xiàn)在你即將得到 ROC 曲線。將前面一個分類器的 ROC 曲線一并畫出來是很有用的,可以清楚地進行比較。見圖 3-7。
plt.plot(fpr, tpr, "b:", label="SGD") plot_roc_curve(fpr_forest, tpr_forest, "Random Forest") plt.legend(loc="bottom right") plt.show()
如你所見,RandomForestClassifier的 ROC 曲線比SGDClassifier的好得多:它更靠近左上角。所以,它的 ROC AUC 也會更大。
>>> roc_auc_score(y_train_5, y_scores_forest) 0.99312433660038291
計算一下準(zhǔn)確率和召回率:98.5% 的準(zhǔn)確率,82.8% 的召回率。還不錯。
現(xiàn)在你知道如何訓(xùn)練一個二分類器,選擇合適的標(biāo)準(zhǔn),使用交叉驗證去評估你的分類器,選擇滿足你需要的準(zhǔn)確率/召回率折衷方案,和比較不同模型的 ROC 曲線和 ROC AUC 數(shù)值?,F(xiàn)在讓我們檢測更多的數(shù)字,而不僅僅是一個數(shù)字 5。
-
閾值
+關(guān)注
關(guān)注
0文章
122瀏覽量
18457 -
分類器
+關(guān)注
關(guān)注
0文章
152瀏覽量
13162 -
機器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8349瀏覽量
132315
原文標(biāo)題:【翻譯】Sklearn 與 TensorFlow 機器學(xué)習(xí)實用指南 —— 第3章 分類(中)
文章出處:【微信號:AI_shequ,微信公眾號:人工智能愛好者社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論