一. ROC曲線
1、roc曲線:接收者操作特征(receiveroperating characteristic),roc曲線上每個點反映着對同一信號刺激的感受性。
橫軸:負正類率(false postive rate FPR)特異度,划分實例中所有負例占所有負例的比例;(1-Specificity)
縱軸:真正類率(true postive rate TPR)靈敏度,Sensitivity(正類覆蓋率)
2、針對一個二分類問題,將實例分成正類(postive)或者負類(negative)。但是實際中分類時,會出現四種情況.
(1)若一個實例是正類並且被預測為正類,即為真正類(True Postive TP)
(2)若一個實例是正類,但是被預測成為負類,即為假負類(False Negative FN)
(3)若一個實例是負類,但是被預測成為正類,即為假正類(False Postive FP)
(4)若一個實例是負類,但是被預測成為負類,即為真負類(True Negative TN)
TP:正確的肯定數目
FN:漏報,沒有找到正確匹配的數目
FP:誤報,沒有的匹配不正確
TN:正確拒絕的非匹配數目
列聯表如下,1代表正類,0代表負類:
由上表可得出橫,縱軸的計算公式:
(1)真正類率(True Postive Rate)TPR: TP/(TP+FN),代表分類器預測的正類中實際正實例占所有正實例的比例。
(2)負正類率(False Postive Rate)FPR: FP/(FP+TN),代表分類器預測的正類中實際負實例占所有負實例的比例。
(3)真負類率(True Negative Rate)TNR: TN/(FP+TN),代表分類器預測的負類中實際負實例占所有負實例的比例,TNR=1-FPR。
假設采用邏輯回歸分類器,其給出針對每個實例為正類的概率,那么通過設定一個閾值如0.6,概率大於等於0.6的為正類,小於0.6的為負類。對應的就可以算出一組(FPR,TPR),在平面中得到對應坐標點。隨着閾值的逐漸減小,越來越多的實例被划分為正類,但是這些正類中同樣也摻雜着真正的負實例,即TPR和FPR會同時增大。閾值最大時,對應坐標點為(0,0),閾值最小時,對應坐標點(1,1)。
如下面這幅圖,(a)圖中實線為ROC曲線,線上每個點對應一個閾值。
橫軸FPR:1-TNR,1-Specificity,FPR越大,預測正類中實際負類越多。
縱軸TPR:Sensitivity(正類覆蓋率),TPR越大,預測正類中實際正類越多。
二. 如何畫roc曲線
假設已經得出一系列樣本被划分為正類的概率,然后按照大小排序,下圖是一個示例,圖中共有20個測試樣本,“Class”一欄表示每個測試樣本真正的標簽(p表示正樣本,n表示負樣本),“Score”表示每個測試樣本屬於正樣本的概率。
接下來,我們從高到低,依次將“Score”值作為閾值threshold,當測試樣本屬於正樣本的概率大於或等於這個threshold時,我們認為它為正樣本,否則為負樣本。舉例來說,對於圖中的第4個樣本,其“Score”值為0.6,那么樣本1,2,3,4都被認為是正樣本,因為它們的“Score”值都大於等於0.6,而其他樣本則都認為是負樣本。每次選取一個不同的threshold,我們就可以得到一組FPR和TPR,即ROC曲線上的一點。這樣一來,我們一共得到了20組FPR和TPR的值,將它們畫在ROC曲線的結果如下圖:
上圖ROC曲線一些點怎么得到的分析
具體分析如下:
可以看出實際類別為P的有10個,實際類別為N的也有10個
- Score = 0.9時,樣本1被認為(預測)是正類(P)。此時根據混淆矩陣有: TP = 1, FN = 9, FP = 0,TN = 10。
這是因為樣本1為正類(P)被正確預測為正類(P),因此TP = 1; 其他實際正類類別(class為P的)樣本有9個,它們被預測為負類(N)了,因此FN = 9; 樣本實際為負類(N)的,被預測為正類(P)的沒有一個,因此FP = 0, 其他實際負類類別(class為N的)樣本有10個,它們全部被預測為負類(N)了,因此TN = 10 。故TPR = 1/(1+9) = 0.1- Score = 0.8時, 同理有: TP = 2, FN = 8,FP = 0, TN = 10。 故TPR = 2/(2+8) = 0.2
- Score = 0.7時,有: TP = 2, FN = 8,FP = 1, TN = 9。 故TPR = 2/(2+8) = 0.2
- Score = 0.6時,有:TP = 3, FN = 7,FP = 1, TN = 9。 故TPR = 3/(3+7) = 0.3
以此類推
AUC(Area under Curve):Roc曲線下的面積,介於0.1和1之間。Auc作為數值可以直觀的評價分類器的好壞,值越大越好。
首先AUC值是一個概率值,當你隨機挑選一個正樣本以及負樣本,當前的分類算法根據計算得到的Score值將這個正樣本排在負樣本前面的概率就是AUC值,AUC值越大,當前分類算法越有可能將正樣本排在負樣本前面,從而能夠更好地分類。
三 為什么使用Roc和Auc評價分類器
既然已經這么多標准,為什么還要使用ROC和AUC呢?因為ROC曲線有個很好的特性:當測試集中的正負樣本的分布變換的時候,ROC曲線能夠保持不變。在實際的數據集中經常會出現樣本類不平衡,即正負樣本比例差距較大,而且測試數據中的正負樣本也可能隨着時間變化。下圖是ROC曲線和Presision-Recall曲線的對比:
在上圖中,(a)和(c)為Roc曲線,(b)和(d)為Precision-Recall曲線。
(a)和(b)展示的是分類其在原始測試集(正負樣本分布平衡)的結果,(c)(d)是將測試集中負樣本的數量增加到原來的10倍后,分類器的結果,可以明顯的看出,ROC曲線基本保持原貌,而Precision-Recall曲線變化較大。
繪制ROC曲線計算AUC值
輸入的數據集可以參考svm預測結果
- case1: 只去前面36行樣本數據,txt文件名為:
evaluate_result2
'''
這里的.txt文件格式如:http://kubicode.me/img/AUC-Calculation-by-Python/evaluate_result.txt
'''
#繪制二分類ROC曲線
import matplotlib.pyplot as plt
from math import log, exp, sqrt
evaluate_result = './evaluate_result2.txt'
db = [ ] #[score,nonclk,clk]
pos, neg = 0, 0
with open(evaluate_result, 'r') as fs:
for line in fs:
nonclk, clk, score = line.strip().split('\t') #變成列表list形式
nonclk = int(nonclk)
clk = int(clk)
db.append([score,nonclk,clk])
score = float(score)
neg = neg + nonclk
#print("neg數量====", neg, end = '')
pos = pos + clk
#print(", pos數量====", pos)
#print("db= ",db)
db = sorted(db, key=lambda x:x[0], reverse=True) #對ad的score進行降序排序
#print("db_reverse= ",db)
#計算ROC坐標點
xy_arr = [ ]
tp, fp = 0., 0.
for i in range(len(db)):
fp += db[i][1]
#print("fp ===", fp, end = '')
tp += db[i][2]
#print(", tp ===",tp)
xy_arr.append([fp/neg, tp/pos]) #fp除以negative數目, tp除以positive數目。作為坐標點
#print(xy_arr) #[[0.06666666666666667, 0.0], [0.06666666666666667, 0.029411764705882353],...]
# 計算曲線下面積
auc = 0.
prev_x = 0
for x, y in xy_arr: # x= fp/neg, y = tp/pos
if x != prev_x:
auc += (x - prev_x) * y #矩形面積累加
prev_x = x
print("The auc is %s" %auc)
x = [ v[0] for v in xy_arr]
y = [ v[1] for v in xy_arr]
plt.title("ROC curve of %s (AUC = %.4f)" % ('svm',auc))
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.plot(x, y)
plt.show()
輸出:
The auc is 0.43137254901960786
- case2: 取全部數據,txt文件名為:
evaluate_result
'''
這里的.txt文件格式如:http://kubicode.me/img/AUC-Calculation-by-Python/evaluate_result.txt
'''
#繪制二分類ROC曲線
import matplotlib.pyplot as plt
from math import log, exp, sqrt
evaluate_result = './evaluate_result.txt'
db = [ ] #[score,nonclk,clk]
pos, neg = 0, 0
with open(evaluate_result, 'r') as fs:
for line in fs:
nonclk, clk, score = line.strip().split('\t') #變成列表list形式
nonclk = int(nonclk)
clk = int(clk)
db.append([score,nonclk,clk])
score = float(score)
neg = neg + nonclk
#print("neg數量====", neg, end = '')
pos = pos + clk
#print(", pos數量====", pos)
#print("db= ",db)
db = sorted(db, key=lambda x:x[0], reverse=True) #對ad的score進行降序排序
#print("db_reverse= ",db)
#計算ROC坐標點
xy_arr = [ ]
tp, fp = 0., 0.
for i in range(len(db)):
fp += db[i][1]
#print("fp ===", fp, end = '')
tp += db[i][2]
#print(", tp ===",tp)
xy_arr.append([fp/neg, tp/pos]) #fp除以negative數目, tp除以positive數目。作為坐標點
#print(xy_arr) #[[0.06666666666666667, 0.0], [0.06666666666666667, 0.029411764705882353],...]
# 計算曲線下面積
auc = 0.
prev_x = 0
for x, y in xy_arr: # x= fp/neg, y = tp/pos
if x != prev_x:
auc += (x - prev_x) * y #矩形面積累加
prev_x = x
print("The auc is %s" %auc)
x = [ v[0] for v in xy_arr]
y = [ v[1] for v in xy_arr]
plt.title("ROC curve of %s (AUC = %.4f)" % ('svm',auc))
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.plot(x, y)
plt.show()
輸出:
The auc is 0.5788039199235478