分類模型評價指標說明
分類涉及到的指標特別容易搞混,不是這個率就是那個率,最后都分不清誰是誰,這份文檔就是為此給大家梳理一下。
混淆矩陣
混淆矩陣很重要,很多指標都是源於混淆矩陣,這個務必要弄懂。
例子
為了解釋混淆矩陣,先來看看下面這個二分類的例子。
例:有20個病人來醫院檢查,是否患病的預測值和真實值如下表所示。
病號 | 預測值 | 真實值 | 病號 | 預測值 | 真實值 |
---|---|---|---|---|---|
1 | 1 | 1 | 11 | 0 | 0 |
2 | 0 | 0 | 12 | 0 | 0 |
3 | 1 | 1 | 13 | 0 | 0 |
4 | 0 | 0 | 14 | 1 | 1 |
5 | 0 | 0 | 15 | 0 | 0 |
6 | 1 | 1 | 16 | 1 | 0 |
7 | 0 | 0 | 17 | 1 | 1 |
8 | 0 | 0 | 18 | 0 | 0 |
9 | 0 | 1 | 19 | 0 | 0 |
10 | 0 | 0 | 20 | 0 | 1 |
其中,1表示患病,0表示不患病。
本文檔默認用0和1來作為二分類符號。
你也可以用其他符號來表示,如1表示患病,-1表示不患病。只要能區分就行。
這樣就出現4種結果:
- 預測為1,實際也為1,包括病號1,3,6,14,17,一共5個樣本;
- 預測為1,實際為0,包括病號16,只有1個樣本;
- 預測為0,實際為1,包括病號9,20,只有2個樣本;
- 預測為0,實際也為0,包括病號2,4,5,7,8,10,11,12,13,15,18,19,一共12個樣本。
我們把各個結果的數量填到下面這個表格中
這就是病患例子的混淆矩陣。
混淆矩陣定義
二分類混淆矩陣的一般定義只是將1和0叫做正例和負例,把4種結果的樣本數量用符號來表示,用什么符號呢?
如果我們用P(Positive)代表1,用N(Negative)代表0,那這四種結果分別是PP,PN,NP,NN,但這樣表示有點問題,譬如,PN的意思是預測為1實際為0還是預測為0實際為1?需要規定好了,還得記住,好麻煩。
干脆再引入符號T(True)代表預測正確,F(False)表示預測錯誤,那么之前的P和N代表預測是1還是0,T和F表示預測是否正確。
四種情況可以分別表示為
- TP:預測為1,預測正確,即實際也為1;
- FP:預測為1,預測錯誤,即實際為0;
- FN:預測為0,預測錯誤,即實際為1;
- TN:預測為0,預測正確,即實際也為0。
混淆矩陣的定義如下:
混淆矩陣代碼
采用sklearn.metrics中的confusion_matrix函數計算混淆矩陣,數據用的還是之前那個病患檢查的樣本。
from sklearn.metrics import confusion_matrix
# 真實值
y_true = [1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1]
# 預測值
y_pred = [1,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,1,0,0,0]
c_matrix = confusion_matrix(y_true, y_pred)
print(c_matrix)
代碼輸出
[[12 1]
[ 2 5]]
有了混淆矩陣,就可以定義一些指標了。
正確率
准確率(Accuracy)的定義很簡單,就是猜對的樣本占總樣本的比例,公式如下:
正樣本是實際為正例的樣本,負樣本是實際為負例的樣本。
計算正確率可以調用sklearn.metrics的accuracy_score函數,代碼如下:
from sklearn.metrics import accuracy_score
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 預測值
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0]
mc = accuracy_score(y_true, y_pred)
print('Accuracy: %.2f'%mc)
結果為
Accuracy: 0.85
正確率作為評價指標有一個很致命的缺點,就是樣本不平衡時正確率無法反映模型結果的好壞。
舉個例子,預估某個網站上某一天廣告的點擊率,假如一天有1000個人瀏覽,實際有50個人點擊廣告,假如分類器預測沒有人會點擊,那么這個模型結果的正確率是多少呢?
我們算一下:分類器預測正確的有950個樣本,一共有1000個樣本,根據定義\(\text{Accuracy} = \frac{950}{1000} = 0.95\),正確率為95%!!!
一個點擊的人都沒有預測對,正確率都能有95%,那這個指標對模型的評價不合理。
那樣本不平衡的時候怎么辦呢?
細心想想,樣本不平衡的問題是正負樣本在數量上有很大差距,數量少的那方被重視程度低,比較吃虧,要解決這個問題,把正負樣本分開評價不就好啦,大家河水不犯井水。
按照這個思路,引入下面兩個概念:真陽率和假陽率。
真陽率和假陽率
真陽率
真陽率(True Positive Rate, TPR)的定義是:正樣本中猜對的比例。公式如下
假陽率
假陽率(False Positive Rate, FPR)的定義是:負樣本中猜錯的比例。公式如下
真陽率和假陽率的公式比較
TPR公式的分母是正樣本數量,FPR公式的分母是負樣本數量,這就遵循了正負樣本分開評價的思路。
TPR公式的分子是TP,說明這個指標關注正確率;FPR公式的分子是FP,說明這個指標關注錯誤率。
通常,這兩個指標不單獨使用,那要怎么用呢?
那就不得不介紹ROC/AUC的概念了。
ROC/AUC
例子
還是那個病患事例,不同在於預測值不是0和1的離散值,而是一個0到1的連續值,叫做置信度(confidence score),可以理解為”概率“,越接近1,結果越可能為1;越接近0,結果越可能為0。
病號 | 置信度 | 真實值 | 病號 | 置信度 | 真實值 |
---|---|---|---|---|---|
1 | 0.8 | 1 | 11 | 0.8 | 0 |
2 | 0.2 | 0 | 12 | 0.1 | 0 |
3 | 0.4 | 1 | 13 | 0.2 | 0 |
4 | 0.1 | 0 | 14 | 0.9 | 1 |
5 | 0.4 | 0 | 15 | 0.3 | 0 |
6 | 0.8 | 1 | 16 | 0.6 | 0 |
7 | 0.3 | 0 | 17 | 0.8 | 1 |
8 | 0.2 | 0 | 18 | 0.2 | 0 |
9 | 0.6 | 1 | 19 | 0.2 | 0 |
10 | 0.5 | 0 | 20 | 0.4 | 1 |
預測值是置信度的話,要怎么算TPR和FPR呢?
很簡單,給個閾值就行,不小於這個閾值就設為1,小於設為0。
注意,在實際的做法中,一般不用卡閾值的方法,而是按照置信度排序,然后取前N條樣本,其實效果等同取閾值。
但閾值設多大好呢?
這就很關鍵了,因為閾值的大小會影響TPR和FPR。
閾值對TPR和FPR的影響
假如病患例子的閾值設為0.9,閾值判決后的預測結果如下表。
病號 | 預測值 | 真實值 | 病號 | 預測值 | 真實值 |
---|---|---|---|---|---|
1 | 0 | 1 | 11 | 0 | 0 |
2 | 0 | 0 | 12 | 0 | 0 |
3 | 0 | 1 | 13 | 0 | 0 |
4 | 0 | 0 | 14 | 1 | 1 |
5 | 0 | 0 | 15 | 0 | 0 |
6 | 0 | 1 | 16 | 0 | 0 |
7 | 0 | 0 | 17 | 0 | 1 |
8 | 0 | 0 | 18 | 0 | 0 |
9 | 0 | 1 | 19 | 0 | 0 |
10 | 0 | 0 | 20 | 0 | 1 |
可以算出TP=1,TN=13,FP=0,FN=6,那么
這結果TPR和FPR都很低,FPR低是好事,說明負樣本的預測錯誤率低,但TPR也低就不好了,因為正樣本的預測正確率不高。
那換個閾值再試試,閾值設為0.1,就是全部猜作正例,不列詳細計算過程了,直接給出結果
這結果剛好相反,TPR和FPR都很高,正樣本的預測正確率上來了,負樣本的預測錯誤率也變大了。
通過上面的比較,能看出來:閾值設得越高,TPR和FPR越低;閾值設得越低,TPR和FPR越高。
ROC曲線
上一節我們知道了TPR和FPR會隨閾值變化而變化,你要是把所有閾值對應的TPR和FPR求出來,畫個直角坐標系,以FPR為橫軸,TPR為縱軸,把不同閾值下的(FPR,TPR)坐標點標上並連起來,你就能看到TPR和FPR的整個變化曲線,而這條曲線就稱為ROC(Receiver Operating Characteristic)曲線。
Receiver Operating Characteristic這名字挺奇怪的,可能是因為最早出現在雷達信號檢測領域,用於評價接收器(Receiver)偵測敵機的能力。
嘗試畫出病患事例的ROC曲線,先求不同閾值下的FPR和TPR,置信度從大到小(重復的不算)排列為[0.9, 0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1],一共有8個閾值,如果手算TPR和FPR那太費勁了,幸好sklearn.metrics模塊有現成的roc_curve函數來算,代碼如下:
import pandas as pd
from sklearn.metrics import roc_curve
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 計算TPR和FPR
fpr, tpr, thresholds = roc_curve(y_true, y_score)
# 把fpr,tpr,thresholds用DataFrame表格保存,方便顯示
result = pd.DataFrame([thresholds,tpr,fpr], index=
['threshold','TPR','FPR'])
print(result)
結果如下
0 1 2 3 4 5 6 \
threshold 1.9 0.900000 0.800000 0.600000 0.500000 0.400000 0.300000
TPR 0.0 0.142857 0.571429 0.714286 0.714286 1.000000 1.000000
FPR 0.0 0.000000 0.076923 0.153846 0.230769 0.307692 0.461538
7 8
threshold 0.200000 0.1
TPR 1.000000 1.0
FPR 0.846154 1.0
上面結果有兩點需要注意:
- roc_curve函數結果的第一列沒有什么實際意義,只是畫ROC曲線圖一般都會有原點(0,0),它直接幫用戶給加上了。
- 關於第一列的threshold為什么是1.9?根據官方API的解釋,它是用
max(y_score) + 1
算的,為什么要這么算?官方API沒有說明,所以我也不知道這腦洞是怎么來的。
接下來,就是根據FPR和TPR結果畫ROC曲線,畫出來如下圖。
畫圖代碼如下:
import matplotlib.pyplot as plt
plt.figure()
# 畫散點圖,標出點的位置
plt.scatter(fpr, tpr)
# 畫ROC曲線圖
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve')
plt.xlim([-0.05, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
如果樣本多了之后,畫出來的ROC曲線會平滑得多。
ROC曲線的用處
當你需要評價多個分類模型結果時,ROC曲線能幫你看出這些模型的優劣。
下面給出了A和B兩個分類模型的ROC曲線圖,哪一個模型的結果比較好呢?
很顯然是模型A,為什么呢?
因為模型A的ROC曲線要比模型B的往左上凸,這樣的話,如果固定FPR,模型A的TPR大於模型B;如果固定TPR,模型A的FPR要小於模型B。怎么樣都是模型A比模型B強。
模型C是有特殊意義的,如果拋硬幣來做二分類預測(取任一類的概率是0.5),最后畫出來的ROC曲線圖就跟C很接近。
可以做個實驗:用概率為0.5取0或1來預測真實值,看看算出來的TPR和FPR的結果。
先構造一個1000樣本的真實值列表。
from sklearn.metrics import confusion_matrix import random # 構造真實值,正例有100個,負例有900個,用shuffle隨機打亂順序 y_true = [1]*100+[0]*900 random.shuffle(y_true)
用概率為0.5取0或1做預測,並計算TPR和FPR。
import numpy as np # 隨機生成1000個0和1的預測值 y_pred = np.random.randint(0,2,size=1000) # 計算TPR和FPR tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel() print('FPR: %.2f'%(fp/(tn+fp))) print('TPR: %.2f'%(tp/(tp+fn)))
結果如下
FPR: 0.50 TPR: 0.53
由於預測值是隨機的,每次出來結果會有不同,但基本都圍繞在點(FPR,TRP)=(0.5,0.5)附近,也就是說,按概率為0.5取0或1的方式做預測,勢必經過(0.5,0.5),其ROC曲線就會表現為一條往右上的對角線。
某個模型全面碾壓的情況不太多,大多數情況會如下圖所示,兩個模型的ROC曲線是相交的。
那哪個模型的結果比較好呢?
需要分情況。比如,如果限定FPR要小於相交點,無疑模型A好於模型B。
AUC
如果沒有特定的限制,那怎么選模型呢?有一招,直接算ROC曲線下的面積,稱為AUC(Area Under Curve)。
AUC越大,模型結果越好,下面算算醫患事例的AUC,用sklearn.metrics的roc_auc_score函數。
from sklearn.metrics import roc_auc_score
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 計算AUC
auc = roc_auc_score(y_true, y_score)
print('AUC: %.2f'%auc)
結果是
AUC: 0.89
AUC能評價二分類模型結果,其實是有概率解釋的,AUC的概率含義是:隨機從樣本集中取一對正負樣本,正樣本得分(置信度)大於負樣本的概率。實際上可以理解為,模型把正樣本(按照置信度)排在負樣本前面的概率。
具體的解釋參考下面鏈接:
https://tracholar.github.io/machine-learning/2018/01/26/auc.html#auc對正負樣本比例不敏感
精准率和召回率
在信息檢索、Web搜索領域,時常會關心“檢索的信息有多少是用戶感興趣的”“用戶感興趣的信息有多少被檢索出來”,為滿足這樣的評價需求,有了精准率和召回率這兩個指標。
精准率
精准率(Precision)的定義是:預測為正的樣本中猜對的比例。公式如下
這個指標反映的是你預測正樣本預測有多准,關鍵在准,因此Precision也被稱為查准率。
召回率
召回率(Recall)的定義是:實際為正的樣本中被猜對的比例。公式如下
看定義,召回率是有點不好理解,舉個例子吧。
假如患病的為正樣本,不患病的為負樣本,100個人里面有10個病患,醫生檢查出了病患中的8個,那這個結果的召回率是多少?
按照定義,先看實際為正的樣本,也就是患病的人,共有10個,這里面醫生猜對的有8個,那么\(\text{Recall} = \frac{8}{10} = 0.8\)。由此可知,召回率關注的是病患(正樣本)是不是都找全了,關鍵在全,因此Recall也被稱為查全率。
兩者公式比較
Precision和Recall公式的分子都是TP,這表示兩者都關心有多少猜對的正樣本。
差別在於分母:Precision的是TP+FP,即預測為1(Positive)的樣本;Recall的是TP+FN,即實際為1的樣本(FN表示預測為0但沒猜對,實際是1)。
它們的關注點都是跟1有關的樣本,根本沒有考慮TN(預測為0,實際為0)。
精准率和召回率的關系
還是用那個病患事例(預測值是置信度的情況)說明。
病號 | 置信度 | 真實值 | 病號 | 置信度 | 真實值 |
---|---|---|---|---|---|
1 | 0.8 | 1 | 11 | 0.8 | 0 |
2 | 0.2 | 0 | 12 | 0.1 | 0 |
3 | 0.4 | 1 | 13 | 0.2 | 0 |
4 | 0.1 | 0 | 14 | 0.9 | 1 |
5 | 0.4 | 0 | 15 | 0.3 | 0 |
6 | 0.8 | 1 | 16 | 0.6 | 0 |
7 | 0.3 | 0 | 17 | 0.8 | 1 |
8 | 0.2 | 0 | 18 | 0.2 | 0 |
9 | 0.6 | 1 | 19 | 0.2 | 0 |
10 | 0.5 | 0 | 20 | 0.4 | 1 |
和TPR/FPR一樣,需要對置信度卡閾值判定0和1后,才能計算Precision和Recall。
下面先看看閾值的大小對Precision和Recall的影響。
閾值對精准率和召回率的影響
閾值設為0.9,講TPR/FPR的時候算過,為TP=1,TN=13,FP=0,FN=6,那么
這結果Precision很高,Recall很低,說明猜正樣本猜得很准,預測為正樣本的都猜對了,只是猜得不全,還有好多正樣本沒猜到。
如果閾值為0.1,就是全部猜作正樣本,不列詳細計算過程了,直接給出結果
這結果剛好相反,Precision很低,Recall很高,說明正樣本都找全了,就是猜得不怎么准。
通過上面的比較,能看出來閾值對Precision和Recall的影響:
-
把閾值設得高,預測正樣本的把握確實要大,但會漏掉好多正樣本;
-
把閾值設得低,正樣本都能找到,但是預測正樣本的准度就不怎么樣了。
舉個現實的例子:
- 沒有99.99%的概率(閾值設得高)會賺錢就不投資,當然投了基本都賺,但會失去很多賺大錢的機會;
- 熱點項目不管能不能賺錢(閾值設得低)都投,當然很可能把大魚(如初創的google,facebook)都逮到,但會有好多投資的項目是賠錢的。
P-R曲線
Precision和Recall是一對矛盾體,一方大了另一方就小,隨着閾值的變動此起彼伏。
和ROC曲線一樣,算出不同閾值下的Precision和Recall,以Recall為橫軸,以Precision為縱軸,也可以畫出一條曲線圖,稱為P-R(精確率-召回率)曲線。
嘗試把病患事例的PR曲線圖畫出來,先算不同閾值下的Precision和Recall,調用sklearn.metrics中的precision_score和recall_score函數來算,代碼如下:
# 閾值划分函數
def binary_by_thres(x,t):
if x >= t:
return 1
else:
return 0
from sklearn.metrics import precision_score,recall_score
# 閾值列表
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.8,0.9]
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
precision = []
recall = []
for t in thresholds:
# 根據閾值t把預測值划分為0和1
y_thres = list( map(binary_by_thres, y_score, [t]*len(y_score)) )
precision.append( precision_score(y_thres, y_true) )
recall.append( recall_score(y_thres, y_true) )
result = pd.DataFrame([thresholds,precision,recall], index=
['threshold','precision','recall'])
print(result)
結果如下:
0 1 2 3 4 5 6 \
threshold 0.10 0.200000 0.300000 0.400000 0.500000 0.600000 0.800000
precision 0.35 0.388889 0.538462 0.636364 0.625000 0.714286 0.800000
recall 1.00 1.000000 1.000000 1.000000 0.714286 0.714286 0.571429
7
threshold 0.900000
precision 1.000000
recall 0.142857
畫個直角坐標系,以Recall為橫軸,Precision為縱軸,看看不同閾值下的(Recall,Precision)坐標點的P-R曲線變化,畫出的圖如下:
上圖有兩點需要注意:
-
最后一個(Recall,Precision)坐標點規定是(0,1),跟閾值無關。官方API解釋說是為了畫圖從縱軸開始。
-
畫圖代碼計算Precision和Recall用的是sklearn.metrics中的precision_recall_curve函數,它的計算結果如下,和之前用precision_score和recall_score函數計算的結果不同,少了閾值為0.1,0.2,0.3的情況。
0 1 2 3 4 5 threshold 0.400000 0.500000 0.600000 0.800000 0.900000 NaN precision 0.636364 0.625000 0.714286 0.800000 1.000000 1.0 recall 1.000000 0.714286 0.714286 0.571429 0.142857 0.0
對這個的解釋:首先這三種情況的Recall都是1,都在Recall=1的直線上,畫階梯圖時考不考慮這三個點對最終的圖沒有影響,所以precision_recall_curve函數就懶得輸出了吧。
畫圖所用代碼如下:
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 計算precision和recall
precision, recall, thresholds = precision_recall_curve(y_true, y_score)
# 規定畫布的大小
plt.figure(figsize=(12,8))
# 畫填充圖
plt.fill_between(recall, precision, alpha=0.2, color='b', step='post')
# 畫散點圖,凸顯坐標點位置
plt.scatter(recall, precision, alpha=0.8, color='r')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.05])
plt.show()
如果樣本多了之后,畫出來的P-R曲線會平滑得多。
P-R曲線的用處
和ROC曲線一樣,P-R曲線能評價多個模型結果的優劣。
下面給出了兩個分類模型的P-R曲線圖,哪一個模型的結果比較好呢?
顯然是紫色的模型B,為什么呢?
因為模型B的PR曲線要比模型A的往右上凸。
如果固定Recall,模型B的Precision大於模型A;如果固定Precision,模型B的Recall還是大於模型A。怎么樣都是模型B比模型A強。
當然,絕大多數情況如下圖所示,兩個模型的PR曲線是相交的。
那哪個模型的結果比較好呢?跟ROC曲線一樣,需要分情況討論。
如,Recall大於交點時,模型A比模型C好。
實際工作中,如果是做搜索,在保證召回率的情況下要盡量提升准確率,那就更願意選模型A;如果做疾病監測、反垃圾郵件等,則是保精確率的條件下提升召回率,那更更傾向於選模型C。
那P-R曲線有沒有像ROC曲線中的AUC那樣的評價指標呢?
有的,P-R曲線下的面積其實是AP(Average Precision)。
AP
原始計算方式
AP(Average Precision)英文的意思就是平均精准度,為什么P-R曲線下的面積就是平均精准度呢?
先來看P-R曲線下面積的計算公式
其中,\(t\)是閾值,\(P(t)\)是對應閾值\(t\)的Precision,\(R(t)\)是對應閾值\(t\)的Recall,\(\Delta R(t)=R(t)-R(t-1)\)。
把公式變一下形式
而且\(\sum_t \Delta R(t) = 1\)。
上面公式就是加權平均值的計算形式,每個\(P(t)\)對應的權值是Recall的變化量\(\Delta R(t)\)。
根據上面的說明,我們可以求病患事例的AP,先列出之前算出的threshold,precision,recall。
0 1 2 3 4 5
threshold 0.400000 0.500000 0.600000 0.800000 0.900000 NaN
precision 0.636364 0.625000 0.714286 0.800000 1.000000 1.0
recall 1.000000 0.714286 0.714286 0.571429 0.142857 0.0
用代碼來驗證一下結果:
from sklearn.metrics import average_precision_score
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 計算average precision
ap = average_precision_score(y_true, y_score)
print('Average Precision:%.6f' % ap)
輸出為
Average Precision:0.769573
其他計算方式
實際上,原始的AP計算方式用的不多,常用的是PASCAL VOC CHALLENGE的計算方法,它有兩種計算方式:
-
11-point Interpolated Average Precision
給Recall設定一組閾值,[0, 0.1, 0.2, … , 1],對於Recall大於等於每一個閾值,都有一個對應的最大precision,這樣我們就計算出了11個precision,11-point Interpolated Average Precision即為這11個precision的平均值。
還是算算病患事例的11-point Interpolated Average Precision吧。
之前通過precision_score和recall_score算得的病患事例Precision和Recall如下:
0 1 2 3 4 5 6 \ threshold 0.10 0.200000 0.300000 0.400000 0.500000 0.600000 0.800000 precision 0.35 0.388889 0.538462 0.636364 0.625000 0.714286 0.800000 recall 1.00 1.000000 1.000000 1.000000 0.714286 0.714286 0.571429 7 threshold 0.900000 precision 1.000000 recall 0.142857
先算閾值0的情況。
按照定義,Recall大於等於閾值0對應的Precision都算,有哪些呢?全部。然后從中挑選一個最大的,那肯定就是1了。
這就算完了閾值為0的情況,其他的閾值也是依葫蘆畫瓢,不詳細去算了,直接給吧。
Recall大於等於的閾值 最大Precision 0 1 0.1 1 0.2 0.8 0.3 0.8 0.4 0.8 0.5 0.8 0.6 0.714286 0.7 0.714286 0.8 0.636364 0.9 0.636364 1 0.636364 把上面11個最大Precision求平均,就得到11-point Interpolated Average Precision,為0.776151。
-
PASCAL VOC CHALLENGE 2010版計算方法
PASCAL VOC CHALLENGE自2010年后就換了另一種計算方法,跟11-point Interpolated Average Precision的區別不是特別大,差異在於給Recall設定閾值改為\([\frac{1}{M},\frac{2}{M},\cdots,\frac{M-1}{M},1]\),M為正樣本的個數。
老規矩,拿病患事例來說明,病患事例數據有7個正樣本,M=7。
那閾值就確定了,為\([\frac{1}{7},\frac{2}{7},\cdots,\frac{6}{7},1]\)。
跟11-point Interpolated Average Precision一樣算閾值的最大Precision,結果如下:
Recall大於等於的閾值 最大Precision \(\frac{1}{7}\) 1 \(\frac{2}{7}\) 1 \(\frac{3}{7}\) 1 \(\frac{4}{7}\) 0.8 \(\frac{5}{7}\) 0.714286 \(\frac{6}{7}\) 0.636364 1 0.636364 對最大Precision求平均,得0.826716。
AP的參考鏈接:
https://www.bbsmax.com/A/MAzAOw159p/
http://blog.sina.com.cn/s/blog_9db078090102whzw.html
F1分數
如果Precision和Recall兩個指標都要求高,可以用F1分數來評價模型。
F1分數(F1 score)的計算公式
F1分數采用的是調和平均數(Harmonic Average)。
什么是調和平均數?其實就是倒數的平均數,看下面公式
F1分數公式變換一下形式就能看出來是調和平均數
那為什么要用調和平均數?直接求算術平均數不行嗎?
那我們舉個極端的例子,\(Precision=0,Recall=1\),現實中絕對不會出現這種情況,這只是為了凸顯算術平均數和調和平均數之間的差異。
那么有
這說明:調和平均比算術平均更關注值較小的數,就像馬雲的財富和你的財富算術平均一下,你也是億萬富翁,如果是用調和平均,那馬雲的財富水平也會和你的相當。
所以,F1分數如果比較大,那\(Precision\)和\(Recall\)都不會小,這樣就能平衡地看待兩者。
計算病患例子的F1分數,代碼如下:
# 閾值划分函數
def binary_by_thres(x,t):
if x >= t:
return 1
else:
return 0
import pandas as pd
from sklearn.metrics import f1_score
# 閾值列表
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.8,0.9]
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,
0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
fscore = []
for t in thresholds:
# 根據閾值t把預測值划分為0和1
y_thres = list( map(binary_by_thres, y_score, [t]*len(y_score)) )
# 計算F1分數
fscore.append( f1_score(y_true, y_thres) )
result = pd.DataFrame([thresholds,fscore], index=['threshold','f1_score'])
print(result)
輸出為
0 1 2 3 4 5 6 7
threshold 0.100000 0.20 0.3 0.400000 0.500000 0.600000 0.800000 0.90
f1_score 0.518519 0.56 0.7 0.777778 0.666667 0.714286 0.666667 0.25
根據均值不等式(高中知識):\(\frac{2}{\frac{1}{a}+\frac{1}{b}} \le \sqrt{ab}\),當且僅當\(a=b\)時取等號。
那么,\(F1 = \frac{2}{\frac{1}{Precision}+\frac{1}{Recall}} \le \sqrt{Precision \cdot Recall}\),當且僅當\(Precision = Recall\)取等號。
所以,只有當\(Precision = Recall\)時F1分數取最大值。
在PR曲線圖中,往右上的對角線與PR曲線的交點就是F1分數的最大點。
Matthews相關系數
Matthews相關性系數(Matthews Correlation Coefficient, MCC)跟之前的指標關系不大,所以放最后講。
它的公式如下
乍看起來讓人有點蒙,不知道怎么來的,下面我就來解釋一下。
Matthews相關系數既然叫相關系數,我們第一聯想到的會是什么呢?
就是概率論里面學的Pearson相關系數,先回歸一下Pearson相關系數的知識。
Pearson相關系數計算公式如下:
其中,\(X\)和\(Y\)是兩個變量,\(E(\cdot)\)和\(Var(\cdot)\)分別代表均值和方差。
Pearson相關系數的用處在於衡量\(X\)和\(Y\)之間的線性關系,取值范圍是[-1,1]。
- \(\rho\)越接近1,越正相關,\(X\)和\(Y\)越趨近於同向變化;
- \(\rho\)越接近-1,越負相關,\(X\)和\(Y\)越趨近於反向變化;
- \(\rho\)越接近0,相關性越弱,\(X\)和\(Y\)之間的線性關系越小。
那Pearson相關系數和Matthews相關系數之間有什么關系呢?
其實Matthews相關系數就是特殊的Pearson相關系數,Matthews相關系數針對的是\(X\)和\(Y\)都是0-1分布的情況。
在實際中,只知道\(X\)和\(Y\)的樣本,只能通過樣本來求均值和方差。
現在假定\(X\)是二分類的預測值,\(Y\)是二分類的真實值,兩者的取值都是0和1。
先復習一下樣本均值和樣本方差的知識。
設\(X_1,X_2,\cdots,X_N\)是來自總體\(X\)的一個樣本,\(x_1,x_2,\cdots,x_N\)是對應的觀察值。
樣本均值的觀察值計算公式:
\[\bar{x} = \frac{1}{N} \sum_{i=1}^N x_i \]樣本方差的觀察值計算公式:
\[s^2 = \frac{1}{N} (\sum_{i=1}^N x_i^2 - N\bar{x}^2) \]概率論的書上計算樣本方差時除以的是\(N-1\),從無偏性來考慮書上確實是對的,但實際應用中\(N\)都比較大,所以常常直接除以\(N\),其實對結果影響不大。
對於符合0-1分布的樣本X,假設樣本觀察值取1的數量為\(N_1\),樣本總量為\(N\)。
那么X的樣本均值和樣本方差的觀察值分別是
\[\bar{x} = \frac{N_1}{N} \]\[s^2 = \frac{1}{N} (\sum_{i=1}^N x_i^2 - N\bar{x}^2) = \frac{1}{N} [N_1 - N(\frac{N_1}{N})^2] = \frac{N_1}{N} (1-\frac{N_1}{N}) \]
\(X\)和\(Y\)有關的樣本均值和樣本方差如下
其中\(N=TP+FP+TN+FN\)。
把這些結果代入Pearson相關系數
這就推出了Matthews相關系數。
從Pearson相關系數的含義可以很方便地去理解Matthews相關系數的意義:
Matthews相關系數的作用是衡量都服從0-1分布的\(X\)和\(Y\)的線性關系。
- \(MCC=1\),表示\(X\)預測是1,真實值\(Y\)確實是1,\(X\)預測是0,真實值\(Y\)也確實是0,兩者同向變化,說明預測全是對的;
- \(MCC=-1\),表示\(X\)預測是1,真實值\(Y\)卻是0,\(X\)預測是0,真實值\(Y\)卻是1,兩者反向變化,說明預測全是錯的;
- \(MCC=0\),表示\(X\)和\(Y\)沒有線性關系,預測有時候是對的,有時候是錯的,而且對的和錯的一樣多,相當於你拋硬幣瞎猜。
計算Matthews相關系數可以調用sklearn.metrics的matthews_corrcoef函數,代碼如下:
from sklearn.metrics import matthews_corrcoef
# 真實值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 預測值
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0]
# 計算Matthews相關系數
mc = matthews_corrcoef(y_true, y_pred)
print('Matthews Correlation Coefficient: %.2f' % mc)
結果為
Matthews Correlation Coefficient: 0.66
對Matthews相關系數的理解啟發於https://stats.stackexchange.com/questions/59343/relationship-between-the-phi-matthews-and-pearson-correlation-coefficients