一、概念:
准確率(Accuracy), 精確率(Precision), 召回率(Recall)和F1-Measure
機器學習(ML), 自然語言處理(NLP), 信息檢索(IR)等領域, 評估(Evaluation)是一個必要的工作, 而其評價指標往往有如下幾點: 准確率(Accuracy), 精確率(Precision), 召回率(Recall) 和 F1-Measure.(注:相對來說,IR 的 ground truth 很多時候是一個 Ordered List, 而不是一個 Bool 類型的 Unordered Collection,在都找到的情況下,排在第三名還是第四名損失並不是很大,而排在第一名和第一百名,雖然都是“找到了”,但是意義是不一樣的,因此更多可能適用於 MAP 之類評估指標.)
本文將簡單介紹其中幾個概念. 中文中這幾個評價指標翻譯各有不同, 所以一般情況下推薦使用英文.
現在我先假定一個具體場景作為例子.
假如某個班級有男生 80 人, 女生20人, 共計 100 人. 目標是找出所有女生. 現在某人挑選出 50 個人, 其中 20 人是女生, 另外還錯誤的把 30 個男生也當作女生挑選出來了. 作為評估者的你需要來評估(evaluation)下他的工作
首先我們可以計算准確率(accuracy), 其定義是: 對於給定的測試數據集,分類器正確分類的樣本數與總樣本數之比. 也就是損失函數是0-1損失時測試數據集上的准確率[1].
這樣說聽起來有點抽象,簡單說就是,前面的場景中,實際情況是那個班級有男的和女的兩類,某人(也就是定義中所說的分類器)他又把班級中的人分為男女兩類. accuracy 需要得到的是此君分正確的人占總人數的比例. 很容易,我們可以得到:他把其中70(20女+50男)人判定正確了, 而總人數是100人,所以它的 accuracy 就是70 %(70 / 100).
由准確率,我們的確可以在一些場合,從某種意義上得到一個分類器是否有效,但它並不總是能有效的評價一個分類器的工作. 舉個例子, google 抓取了 argcv 100個頁面,而它索引中共有10,000,000個頁面, 隨機抽一個頁面,分類下, 這是不是 argcv 的頁面呢?如果以 accuracy 來判斷我的工作,那我會把所有的頁面都判斷為"不是 argcv 的頁面", 因為我這樣效率非常高(return false, 一句話), 而 accuracy 已經到了99.999%(9,999,900/10,000,000), 完爆其它很多分類器辛辛苦苦算的值, 而我這個算法顯然不是需求期待的, 那怎么解決呢?這就是 precision, recall 和 f1-measure 出場的時間了.
再說 precision, recall 和 f1-measure 之前, 我們需要先需要定義 TP, FN, FP, TN 四種分類情況.
按照前面例子, 我們需要從一個班級中的人中尋找所有女生, 如果把這個任務當成一個分類器的話, 那么女生就是我們需要的, 而男生不是, 所以我們稱女生為"正類", 而男生為"負類".
相關(Relevant), 正類 | 無關(NonRelevant), 負類 | |
---|---|---|
被檢索到(Retrieved) | true positives (TP 正類判定為正類, 例子中就是正確的判定"這位是女生") | false positives (FP 負類判定為正類,"存偽", 例子中就是分明是男生卻判斷為女生, 當下偽娘橫行, 這個錯常有人犯) |
未被檢索到(Not Retrieved) | false negatives (FN 正類判定為負類,"去真", 例子中就是, 分明是女生, 這哥們卻判斷為男生--梁山伯同學犯的錯就是這個) | true negatives (TN 負類判定為負類, 也就是一個男生被判斷為男生, 像我這樣的純爺們一准兒就會在此處) |
或者
可以很容易看出, 所謂 TRUE/FALSE 表示從結果是否分對了, Positive/Negative 表示我們認為的是"是"還是"不是".
通過這張表, 我們可以很容易得到這幾個值:
- TP=20
- FP=30
- FN=0
- TN=50
精確率(precision)的公式是P=TPTP+FPP = \frac{TP}{TP+FP}P=TP+FPTP, 它計算的是所有"正確被檢索的結果(TP)"占所有"實際被檢索到的(TP+FP)"的比例.
在例子中就是希望知道此君得到的所有人中, 正確的人(也就是女生)占有的比例. 所以其 precision 也就是40%(20女生/(20女生+30誤判為女生的男生)).
召回率(recall)的公式是R=TPTP+FNR = \frac{TP}{TP+FN}R=TP+FNTP, 它計算的是所有"正確被檢索的結果(TP)"占所有"應該檢索到的結果(TP+FN)"的比例.
在例子中就是希望知道此君得到的女生占本班中所有女生的比例, 所以其 recall 也就是100%(20女生/(20女生+ 0 誤判為男生的女生))
F1值就是精確值和召回率的調和均值, 也就是
2F1=1P+1R\frac{2}{F_1} = \frac{1}{P} + \frac{1}{R} F12=P1+R1
調整下也就是
F1=2PRP+R=2TP2TP+FP+FNF_1 = \frac{2PR}{P+R} = \frac{2TP}{2TP + FP + FN} F1=P+R2PR=2TP+FP+FN2TP
例子中 F1-measure 也就是約為 57.143%(2∗0.4∗10.4+1\frac{2 * 0.4 * 1}{0.4 + 1}0.4+12∗0.4∗1).
需要說明的是, 有人[3]列了這樣個公式, 對非負實數β\betaβ
Fβ=(β2+1)∗PRβ2∗P+RF_\beta = (\beta^2 + 1) * \frac{PR}{\beta^2*P+R}Fβ=(β2+1)∗β2∗P+RPR
將 F-measure 一般化.
F1-measure 認為精確率和召回率的權重是一樣的, 但有些場景下, 我們可能認為精確率會更加重要, 調整參數 β\betaβ , 使用 Fβ_\betaβ-measure 可以幫助我們更好的 evaluate 結果.
注意:召回率就是tpr(TP/(TP+FN)) (命中率),fpr(FP/(FP+TN)):特異性(specificity),TNR(TN/(FP+TN)):敏感性
參考鏈接:http://mlwiki.org/index.php/ROC_Analysis#ROC_Analysis
https://blog.argcv.com/articles/1036.c
http://www.cnblogs.com/pinard/p/5993450.html
1、簡單(源碼):
print(__doc__)
import numpy as np
import matplotlib.pyplot as plt
from itertools import cycle
from sklearn import svm, datasets
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from scipy import interp
# Import some data to play with
iris = datasets.load_iris()
X = iris.data
y = iris.target
# Binarize the output
y = label_binarize(y, classes=[0, 1, 2])
n_classes = y.shape[1]
# Add noisy features to make the problem harder
random_state = np.random.RandomState(0)
n_samples, n_features = X.shape
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)]
# shuffle and split training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5,
random_state=0)
# Learn to predict each class against the other
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True,
random_state=random_state))
y_score = classifier.fit(X_train, y_train).decision_function(X_test)
# Compute ROC curve and ROC area for each class
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(n_classes):
fpr[i], tpr[i], _ = roc_curve(y_test[:, 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_test.ravel(), y_score.ravel())
roc_auc["micro"] = auc(fpr["micro"], tpr["micro"])
#Plot of a ROC curve for a specific class
plt.figure()
lw = 2
plt.plot(fpr[2], tpr[2], color='darkorange',
lw=lw, label='ROC curve (area = %0.2f)' % roc_auc[2])
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
2、復雜(源碼):
print(__doc__) import numpy as np from scipy import interp import matplotlib.pyplot as plt from itertools import cycle from sklearn import svm, datasets from sklearn.metrics import roc_curve, auc from sklearn.model_selection import StratifiedKFold # ############################################################################# # Data IO and generation # Import some data to play with iris = datasets.load_iris() X = iris.data y = iris.target X, y = X[y != 2], y[y != 2] n_samples, n_features = X.shape # Add noisy features random_state = np.random.RandomState(0) X = np.c_[X, random_state.randn(n_samples, 200 * n_features)] # ############################################################################# # Classification and ROC analysis # Run classifier with cross-validation and plot ROC curves cv = StratifiedKFold(n_splits=6) classifier = svm.SVC(kernel='linear', probability=True, random_state=random_state) tprs = [] aucs = [] mean_fpr = np.linspace(0, 1, 100) i = 0 for train, test in cv.split(X, y): probas_ = classifier.fit(X[train], y[train]).predict_proba(X[test]) # Compute ROC curve and area the curve fpr, tpr, thresholds = roc_curve(y[test], probas_[:, 1]) tprs.append(interp(mean_fpr, fpr, tpr)) tprs[-1][0] = 0.0 roc_auc = auc(fpr, tpr) aucs.append(roc_auc) plt.plot(fpr, tpr, lw=1, alpha=0.3, label='ROC fold %d (AUC = %0.2f)' % (i, roc_auc)) i += 1 plt.plot([0, 1], [0, 1], linestyle='--', lw=2, color='r', label='Chance', alpha=.8) mean_tpr = np.mean(tprs, axis=0) mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) std_auc = np.std(aucs) plt.plot(mean_fpr, mean_tpr, color='b', label=r'Mean ROC (AUC = %0.2f $\pm$ %0.2f)' % (mean_auc, std_auc), lw=2, alpha=.8) std_tpr = np.std(tprs, axis=0) tprs_upper = np.minimum(mean_tpr + std_tpr, 1) tprs_lower = np.maximum(mean_tpr - std_tpr, 0) plt.fill_between(mean_fpr, tprs_lower, tprs_upper, color='grey', alpha=.2, label=r'$\pm$ 1 std. dev.') plt.xlim([-0.05, 1.05]) plt.ylim([-0.05, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver operating characteristic example') plt.legend(loc="lower right") plt.show()
Best threshold:
- we know the slope of the accuracy line: it's 1
- the best classifier for this slope is the 6th one
- threshold value θ
- so we take the score obtained on the 6th record
- and use it as the threshold value θ
- i.e. predict positive if θ⩾0.54
- if we check, we see that indeed we have accuracy = 0.7