1、什么是AUROC?
接受者操作特征曲線下面積(area under the receiver operating characteristic,AUROC)?是一個用來衡量分類器性能的指標。其通過接受者操作特征曲線(receiver operating?characteristic curve)與坐標軸之間的面積大小來反應分類器的性能,其意義可以理解為均勻抽取的隨機陽性樣本(正樣本)排名在均勻抽取的隨機陰性樣本(負樣本)之前的期望。AUROC是一個介于0到1之間的數值,當AUROC值接近于1是,表示分類器可以較好的分類正負樣本。
2、二分類問題中ROC曲線的繪制
ROC曲線也稱為感受性曲線(sensitivity curve),該曲線反應的是同一信號刺激在不同的判定標準下的不同判定結果。在介紹ROC曲線前,首先介紹一些基本的概念。
2.1一些基本概念
ROC曲線也稱為感受性曲線(sensitivity curve),該曲線反應的是同一信號刺激在不同的判定標準下的不同判定結果。在介紹ROC曲線前,首先介紹一些二分類問題中的基本的表示。
P (Positive) :正樣本,一般用1來表示。
N (Negative) :負樣本,一般用0來表示。
TP(True?Positive): 真陽,預測為1,真實為1
FP(False Positive):假陽,預測為1,真實為0。
TN(Ture Negative):真陰,預測為0,真實為0。
FN(False Negative): 假陰:預測為0,真實為1。
TPR (True Positive Rate):真陽率,所有真實(標簽)為1的樣本中,預測為1的樣本的比例。該值越高越好。
FPR(False Positive Rate): 假陽率:所有真實(標簽)為0的樣本中,預測為1的樣本的比例。該值越低越好。
2.2 ROC曲線繪制
在二分類問題中,我們可以得到一個樣本為1的概率值,該概率值是一個0到1之間的數值,同時,我們也可以設定一個閾值,當概率值大于閾值時,則認為該樣本為正樣本,反之,則認為該樣本為負樣本。
我們可以設置多個閾值,每一個閾值下,都可以得到測試樣本的測試結果,以及該閾值下的TPR與FPR,對于一個樣本數為N的測試集,我們可以設置N+2個閾值,得到N+2組TPR與FPR值,將TPR作為x軸,FPR作為y軸,便可以將N個點放在坐標系中,將所有的點連接起來,便可以得到ROC曲線。
2.3 一個示例
示例:有2個正樣本A與B,其預測為正樣本的概率分別為0.4,0.9,有兩個負樣本C與D,其預測為正樣本的概率分別為0.2,0.5。
由于我們有四個樣本,所以我們可以設置5個閾值,0,0.2,0.4,0.5,0.9,置信度大于閾值,則視為正樣本
當閾值為0時,A,B,C,D 均預測為正樣本,此時TPR為2/2 = 1,FPR為2/2 = 1;
當閾值為0.2時,A,B,D預測為正樣本,C預測為負樣本,TPR為2/2 = 1,FPR為1/2 = 0.5;
當閾值為0.4時,B,D預測為正樣本,A,C預測為負樣本,TPR為1/2 = 0.5,FPR為1/2 = 0.5;
當閾值為0.5時,B 預測為正樣本,A,C,D預測為負樣本,TPR為1/2 = 0.5,FPR為0/2 = 0;
當閾值為0.9時,A,B,C,D均預測為負樣本,TPR為0/2 = 0,FPR為0/2 = 0;
我們將5組TPR與FPR畫在坐標軸,就可以得到ROC曲線了,重復的可以看作一個點。
2.4 程序實現
根據上述實例,我們可以發現一些規律。
(1)閾值可以用置信度的排序來選取。
(2)隨著閾值的增加,TPR與FPR的分母是不變的,只有分子在變化。
(3)隨著閾值的增加,分子的變化是單調遞減的。
因此,只要知道了隨著閾值的改變,分子是怎么變的,就可以得到ROC曲線了。
換言之,閾值每改變一次,只需要知道是TPR的分子減1還是FPR的分子減1就好了。
下面詳細介紹ROC的繪制函數。
def?compute_roc(pred_p,?pred_n,?labels):
函數的輸入為兩個變量,
pred_p:正樣本經過Softmax得到的分類為正樣本的置信度?N*1,N為樣本數,
pred_q: 負樣本經過Softmax得到分類為負樣本的置信度 M*1,M為樣本數
可以看到,正樣本的總數為N,負樣本的總數為M,因此,TPR與FPR的分母分別為N與M。
????predict?=?np.concatenate((pred_p,?pred_q),?axis=0)
將所有的置信度拼接起來。
? ? TPR_target?=?np.concatenate((np.ones(len(x1)),?np.zeros(len(x2))),?axis=0)
? ? FPR_target?=?np.concatenate((np.zeros(len(x1)),?np.ones(len(x2))),?axis=0)
創建兩個矩陣,分別記錄樣本的屬性,
在TPR_target 中,正樣本為1,負樣本為0(TPR更關注正樣本的個數,即有多少個正樣本被正確分類)
在FPR_target 中,正樣本為0,負樣本為1(FPR更關注負樣本的個數,即有多少個負樣本被錯誤分類)
????idx?=?predict.argsort()
????s_TPR_target?=?TPR_target[idx]
????s_FPR_target?=?FPR_target[idx]
? ? n?=?len(predict) #樣本總個數,N+M
將拼接后的置信度根據大小進行排序,得到索引,樣例中,[0.4,0.9,0.2,0.5]的排序結果為[0.2,0.4,0.5,0.9],所以索引為[2,0,3,1 ]
即原始的predict中predict[2] 是最小的,predict[0]是第二小的,依次類推。排序后,標簽也要根據排序結果重新排列,
s_?TPR?_target [0] =?TPR?_target[2],s_?TPR?_target [1] =?TPR?_target[0],依次類推。這個過程可以簡寫為? s_?TPR?_target?=??TPR?_target[idx]。
? ? TPR?=?[0?for?x?in?range(n+1)]???
? ? FPR?=?[0?for?x?in?range(n+1)]?
創建兩個空數組,用于存放不同閾值下的TPR值與FPR值
下面開始計算不同的閾值下,TP的個數與FP的個數
首先,不考慮閾值為0,因為閾值為0時,所有樣本都是正樣本,此時TPR與FPR均為1 。當閾值為最小值0.2時,0.2對應的樣本被分為負樣本,其他被分為正樣本,此時TP個數為s_?TPR?_target 中[1:]的和,時TP個數為s_ FPR?_target 中[2:]的和。根據這個方法,可得如下程序
????for?k?in?range(n-1):
?# k表示第k個閾值(不包含0和1)
???????TP =?s_?TPR?_targe[k+1:].sum()
????????FP?=??s_ FPR?_targe[k:].sum()
????????TPR?[k]?=?TP??/?float(len(x1))
????????FPR [k]?=?FP??/?float(len(x2))
?# 補充閾值為0的情況
????CCR[n]?=?0.0
????FPR[n]?=?0.0
3、開集問題中ROC曲線的繪制
開集識別是一個特殊的二分類問題,這里把測試集中的已知類看作正樣本,未知類看作負樣本。認為某一閾值下,低于閾值的樣本為開集。
開集識別與普通二分類問題的區別在于,開集識別不僅僅要把已知類識別為已知類,還要把已知類正確分類,才認為分類正確。所以有人提出了OSCR這一指標來對AUROC進行補充。
開集識別中,需要對?TPR?_targe進行糾正,將即使識別為已知類,也不能正確分類的樣本的值定義為0。即TPR不會受到此樣本的影響。
4、OSCR計算代碼
def?compute_oscr(pred_k,?pred_u,?labels):
????#?pred_k:已知類別經過Softmax得到的置信度?N*C,N為樣本數,C為已知類的類別數
????#?pred_u:未知類別經過Softmax得到的置信度?M*C,M為樣本數,C為已知類的類別數
????#?labels:已知類別標簽?N*1,N為樣本數
????#?x1,x2?最大置信度?N*1,M*1
????x1,?x2?=?np.max(pred_k,?axis=1),?np.max(pred_u,?axis=1)
????#?pred_k中最大置信度對應的類別
????pred?=?np.argmax(pred_k,?axis=1)
????#?pred_k中預測正確的樣本
????correct?=?(pred?==?labels)
????m_x1?=?np.zeros(len(x1))
????m_x1[pred?==?labels]?=?1
? ? #注意,不同點在于這里
????k_target?=?np.concatenate((m_x1,?np.zeros(len(x2))),?axis=0)
????u_target?=?np.concatenate((np.zeros(len(x1)),?np.ones(len(x2))),?axis=0)
????predict?=?np.concatenate((x1,?x2),?axis=0)
????n?=?len(predict)
????#?Cutoffs?are?of?prediction?values
????CCR?=?[0?for?x?in?range(n+1)]
????FPR?=?[0?for?x?in?range(n+1)]?
????idx?=?predict.argsort()
????s_k_target?=?k_target[idx]
????s_u_target?=?u_target[idx]
????for?k?in?range(n-1):
????????CC?=?s_k_target[k+1:].sum()
????????FP?=?s_u_target[k:].sum()
????????#?True??Positive?Rate
????????CCR[k]?=?float(CC)?/?float(len(x1))
????????#?False?Positive?Rate
????????FPR[k]?=?float(FP)?/?float(len(x2))
????CCR[n]?=?0.0
????FPR[n]?=?0.0
????#?Positions?of?ROC?curve?(FPR,?TPR)
????ROC?=?sorted(zip(FPR,?CCR),?reverse=True)
????OSCR?=?0
????#?Compute?AUROC?Using?Trapezoidal?Rule
????for?j?in?range(n):
????????h?=???ROC[j][0]?-?ROC[j+1][0]
????????w?=??(ROC[j][1]?+?ROC[j+1][1])?/?2.0
????????OSCR?=?OSCR?+?h*w
????return?OSCR