機器學習篇-指標:AUC
AUC是什麼東西?
AUC是一個模型評價指標,只能夠用於二分類模型的評價,對於二分類模型來說還有很多其他的評價指標:
比如:logloss,accuracy,precision
在上述的評價指標當中,數據挖掘類比賽中,AUC和logloss是比較常見的模型評價指標
那麼問題來了||ヽ(* ̄▽ ̄*)ノミ|Ю為啥是AUC和logloss?
因為很多機器學習的模型對分類問題的預測結果都是概率,如果要計算accuracy的話,需要先將概率轉換成類別,這就需要手動設置一個閾值,如果對一個樣本的預測概率高於這個預測,就把這個樣本放進一個類別當中,如果低於這個閾值,就放在另一個類別當中,閾值在很大程度上影響了accuracy的計算
使用AUC或者logloss的好處就是可以避免將預測概率轉換成類別
AUC: Area under curve
字面理解:某條曲線下面區域的面積
問題來了,到底是哪一條曲線?
曲線的名字叫做:ROC曲線
ROC曲線講解
(該內容來自於維基百科)
ROC分析的是二元分類模型,也就是輸出結果只有兩種類別的模型(垃圾郵件/非垃圾郵件)
當觀測量的結果是一個連續值的時候,類與類的邊界必須用一個閾值threshold來界定
舉例來說,用血壓值來檢測一個人是否有高血壓,測出的血壓值是連續的實數(從0~200都有可能),以收縮壓140/舒張壓90為閾值,閾值以上便診斷為有高血壓,閾值未滿者診斷為無高血壓。二元分類模型的個案預測有四種結局:P:positive N:negative
- 真陽性(TP):診斷為有,實際上也有高血壓。
- 偽陽性(FP):診斷為有,實際卻沒有高血壓。
- 真陰性(TN):診斷為沒有,實際上也沒有高血壓。
- 偽陰性(FN):診斷為沒有,實際卻有高血壓。
這四種結局可以畫成2 × 2的混淆矩陣:
ROC空間將偽陽性率FPR定義為X軸,將真陽性率TPR定義為Y軸
TPR:在所有實際為陽性的樣本中,被正確地判斷為陽性之比率。
FPR:在所有實際為陰性的樣本中,被錯誤地判斷為陽性之比率。
給定一個二元分類模型和它的閾值,就能從所有樣本的(陽性/陰性)真實值和預測值計算出一個 (X=FPR, Y=TPR) 座標點。
從 (0, 0) 到 (1,1) 的對角線將ROC空間劃分為左上/右下兩個區域,在這條線的以上的點代表了一個好的分類結果(勝過隨機分類),而在這條線以下的點代表了差的分類結果(劣於隨機分類)。
完美的預測是一個在左上角的點,在ROC空間座標 (0,1)點,X=0 代表著沒有偽陽性,Y=1 代表著沒有偽陰性(所有的陽性都是真陽性);也就是說,不管分類器輸出結果是陽性或陰性,都是100%正確。一個隨機的預測會得到位於從 (0, 0) 到 (1, 1) 對角線(也叫無識別率線)上的一個點;最直觀的隨機預測的例子就是拋硬幣。
讓我們來看在實際有100個陽性和100個陰性的案例時,四種預測方法(可能是四種分類器,或是同一分類器的四種閾值設定)的結果差異:
將這4種結果畫在ROC空間里:
離左上角越近的點預測(診斷)準確率越高。離右下角越近的點,預測越不準。
在A、B、C三者當中,最好的結果是A方法。
B方法的結果位於隨機猜測線(對角線)上,在例子中我們可以看到B的準確度是50%。
C雖然預測準確度最差,甚至劣於隨機分類,也就是低於0.5(低於對角線)。然而,當將C以 (0.5, 0.5) 為中點作一個鏡像後,C的結果甚至要比A還要好。這個作鏡像的方法,簡單說,不管C(或任何ROC點低於對角線的情況)預測了什麼,就做相反的結論。
上述ROC空間里的單點,是給定分類模型且給定閾值後得出的。但同一個二元分類模型的閾值可能設定為高或低,每種閾值的設定會得出不同的FPR和TPR。
將同一模型每個閾值 的 (FPR, TPR) 座標都畫在ROC空間里,就成為特定模型的ROC曲線。
在同一個分類器之內,閾值的不同設定對ROC曲線的影響,仍有一些規律可循:
- 當閾值設定為最高時,亦即所有樣本都被預測為陰性,沒有樣本被預測為陽性,此時在偽陽性率 FPR = FP / ( FP + TN ) 算式中的 FP = 0,所以 FPR = 0%。同時在真陽性率(TPR)算式中, TPR = TP / ( TP + FN ) 算式中的 TP = 0,所以 TPR = 0%
→ 當閾值設定為最高時,必得出ROC座標系左下角的點 (0, 0)。
- 當閾值設定為最低時,亦即所有樣本都被預測為陽性,沒有樣本被預測為陰性,此時在偽陽性率FPR = FP / ( FP + TN ) 算式中的 TN = 0,所以 FPR = 100%。同時在真陽性率 TPR = TP / ( TP + FN ) 算式中的 FN = 0,所以 TPR=100%
→ 當閾值設定為最低時,必得出ROC座標系右上角的點 (1, 1)。
因為TP、FP、TN、FN都是累積次數,TN和FN隨著閾值調低而減少(或持平),TP和FP隨著閾值調低而增加(或持平),所以FPR和TPR皆必隨著閾值調低而增加(或持平)。
→ 隨著閾值調低,ROC點 往右上(或右/或上)移動,或不動;但絕不會往左下(或左/或下)移動。
接下來就是我們本節的內容了:(~ ̄▽ ̄)~
曲線下面積(AUC)
在比較不同的分類模型時,可以將每個模型的ROC曲線都畫出來,比較曲線下面積做為模型優劣的指標。
意義
ROC曲線下方的面積(英語:Area under the Curve of ROC (AUC ROC)),其意義是:
因為是在1x1的方格里求面積,AUC必在0~1之間。
假設閾值以上是陽性,以下是陰性;
若隨機抽取一個陽性樣本和一個陰性樣本,分類器正確判斷陽性樣本的值高於陰性樣本之機率=AUC
簡單說:AUC值越大的分類器,正確率越高。
從AUC判斷分類器(預測模型)優劣的標準:
AUC = 1,是完美分類器,採用這個預測模型時,存在至少一個閾值能得出完美預測。絕大多數預測的場合,不存在完美分類器。
0.5 < AUC < 1,優於隨機猜測。這個分類器(模型)妥善設定閾值的話,能有預測價值。
AUC = 0.5,跟隨機猜測一樣(例:丟銅板),模型沒有預測價值。
AUC < 0.5,比隨機猜測還差;但只要總是反預測而行,就優於隨機猜測。
計算
AUC的計算有兩種方式,都是以逼近法求近似值。
AUC為什麼可以衡量分類的效果?
- AUC就是從所有1樣本中隨機選取一個樣本,從所有0樣本中隨機選取一個樣本,然後根據你的分類器對兩個隨機樣本進行預測,把1樣本預測為1的概率為p1,把0樣本預測為1的概率為p2,p1>p2的概率就是AUC。所以AUC應該反映的是分類器對樣本的排序能力,另外,AUC對樣本類別是否均衡並不敏感,這也是不均衡樣本通常採用AUC評價分類性能的原因
根據上面的推斷,那麼隨機抽取一個樣本,對應每一潛在可能值X都有對應一個判定正樣本的概率P。
對一批已知正負的樣本集合進行分類,按照預測概率從高到低進行排序,
對於正樣本中概率最高的,排序為rank_1,
比它概率小的有M-1個正樣本(M為正樣本個數),(ranl_1-M)個負樣本。
正樣本中概率第二高的,排序為rank_2,
比它概率小的有M-2個正樣本,(rank_2-(M-1))個負樣本,
以此類推,正樣本中概率最小的,排序為rank_M,
比它概率小的有0個正樣本,(rank_M-1)個負樣本。
總共有M*N個正負樣本對,把所有比較中正樣本概率大於負樣本概率的例子都算上,
得到公式:
就是正樣本概率大於負樣本概率的可能性,將上述結果化簡之後:
上述結果就是,AUC公式
AUC是現在分類模型中,特別是二分類模型使用的主要離線評測指標之一,相比於準確率,召回率,AUC有一個獨特的優勢,就是不管具體的得分,只關注於排序結果,這使得它特別適用於排序問題的效果評估
根據上面的公式求解AUC
首先對score從大到小排序,然後令最大score對應的sample的rank值為n,第二大score對應sample的rank值為n-1,以此類推從n到1。然後把所有的正類樣本的rank相加,再減去正類樣本的score為最小的那M個值的情況。得到的結果就是有多少對正類樣本的score值大於負類樣本的score值,最後再除以M×N即可。值得注意的是,當存在score相等的時候,對於score相等的樣本,需要賦予相同的rank值(無論這個相等的score是出現在同類樣本還是不同類的樣本之間,都需要這樣處理)。具體操作就是再把所有這些score相等的樣本 的rank取平均。然後再使用上述公式。此公式描述如下:
def naive_auc(labels,preds): """ 最簡單粗暴的方法 先排序,然後統計有多少正負樣本對滿足:正樣本預測值>負樣本預測值, 再除以總的正負樣本對個數。複雜度 O(NlogN), N為樣本數 """ n_pos = sum(labels) n_neg = len(labels) - n_pos total_pair = n_pos * n_neg labels_preds = zip(labels,preds) labels_preds = sorted(labels_preds,key=lambda x:x[1])//從小到大,倒序計算 accumulated_neg = 0 satisfied_pair = 0 for i in range(len(labels_preds)): if labels_preds[i][0] == 1: satisfied_pair += accumulated_neg else: accumulated_neg += 1 return satisfied_pair / float(total_pair)
接下來還可以採用進一步加速的方法:
使用近似方式,將預測值分桶,對正負樣本分別構建直方圖,再統計滿足條件的正負樣本對
關於AUC還有一個很有趣的性質,它和Wilcoxon-Mann-Witney Test類似(可以去google搜一下),而Wilcoxon-Mann-Witney Test就是測試任意給一個正類樣本和一個負類樣本,正類樣本的score有多大的概率大於負類樣本的score。有了這個定義,就可以得到了另外一中計算AUC的方法:計算出這個概率值。我們知道,在有限樣本中我們常用的得到概率的辦法就是通過頻率來估計之。這種估計隨著樣本規模的擴大而逐漸逼近真實值。樣本數越多,計算的AUC越準確類似,也和計算積分的時候,小區間劃分的越細,計算的越準確是同樣的道理。具體來說就是: 統計一下所有的 M×N(M為正類樣本的數目,N為負類樣本的數目)個正負樣本對中,有多少個組中的正樣本的score大於負樣本的score。當二元組中正負樣本的 score相等的時候,按照0.5計算。然後除以MN。實現這個方法的複雜度為O(n^2 )。n為樣本數(即n=M+N),公式表示如下:
def approximate_auc(labels,preds,n_bins=100): """ 近似方法,將預測值分桶(n_bins),對正負樣本分別構建直方圖,再統計滿足條件的正負樣本對 複雜度 O(N) 這種方法有什麼缺點?怎麼分桶? """ n_pos = sum(labels) n_neg = len(labels) - n_pos total_pair = n_pos * n_neg pos_histogram = [0 for _ in range(n_bins)] neg_histogram = [0 for _ in range(n_bins)] bin_width = 1.0 / n_bins for i in range(len(labels)): nth_bin = int(preds[i]/bin_width) if labels[i]==1: pos_histogram[nth_bin] += 1 else: neg_histogram[nth_bin] += 1 accumulated_neg = 0 satisfied_pair = 0 for i in range(n_bins): satisfied_pair += (pos_histogram[i]*accumulated_neg + pos_histogram[i]*neg_histogram[i]*0.5) accumulated_neg統計當前bins之前有多少負樣本桶,將其*pos_histogram[i] 等價於當前正樣本桶有大於負樣本桶 pos_histogram[i]*neg_histogram[i]*0.5,有可能正樣本桶和負樣本桶落在同一個區間,當前區間無法計算,近似 accumulated_neg += neg_histogram[i] return satisfied_pair / float(total_pair)
但是上述方法的問題在於:
預測值大多不是均勻分布在0-1之間的,分布特別不平衡的話,均勻分出來的桶就不太好,
採用頻率進行劃分桶,(均勻劃分,等頻劃分)
進一步的嘗試MapReduce如何計算AUC
MAP階段:統計直方圖
Reduce階段:計算AUC的結果
上述完整代碼:
# coding=utf-8# auc值的大小可以理解為: 隨機抽一個正樣本和一個負樣本,正樣本預測值比負樣本大的概率# 根據這個定義,我們可以自己實現計算aucimport randomimport timedef timeit(func): """ 裝飾器,計算函數執行時間 """ def wrapper(*args, **kwargs): time_start = time.time() result = func(*args, **kwargs) time_end = time.time() exec_time = time_end - time_start print "{function} exec time: {time}s".format(function=func.__name__,time=exec_time) return result return wrapperdef gen_label_pred(n_sample): """ 隨機生成n個樣本的標籤和預測值 """ labels = [random.randint(0,1) for _ in range(n_sample)] preds = [random.random() for _ in range(n_sample)] return labels,preds@timeitdef naive_auc(labels,preds): """ 最簡單粗暴的方法 先排序,然後統計有多少正負樣本對滿足:正樣本預測值>負樣本預測值, 再除以總的正負樣本對個數 複雜度 O(NlogN), N為樣本數 """ n_pos = sum(labels) n_neg = len(labels) - n_pos total_pair = n_pos * n_neg labels_preds = zip(labels,preds) labels_preds = sorted(labels_preds,key=lambda x:x[1]) accumulated_neg = 0 satisfied_pair = 0 for i in range(len(labels_preds)): if labels_preds[i][0] == 1: satisfied_pair += accumulated_neg else: accumulated_neg += 1 return satisfied_pair / float(total_pair)@timeitdef approximate_auc(labels,preds,n_bins=100): """ 近似方法,將預測值分桶(n_bins),對正負樣本分別構建直方圖,再統計滿足條件的正負樣本對 複雜度 O(N) 這種方法有什麼缺點?怎麼分桶? """ n_pos = sum(labels) n_neg = len(labels) - n_pos total_pair = n_pos * n_neg pos_histogram = [0 for _ in range(n_bins)] neg_histogram = [0 for _ in range(n_bins)] bin_width = 1.0 / n_bins for i in range(len(labels)): nth_bin = int(preds[i]/bin_width) if labels[i]==1: pos_histogram[nth_bin] += 1 else: neg_histogram[nth_bin] += 1 accumulated_neg = 0 satisfied_pair = 0 for i in range(n_bins): satisfied_pair += (pos_histogram[i]*accumulated_neg + pos_histogram[i]*neg_histogram[i]*0.5) accumulated_neg += neg_histogram[i] return satisfied_pair / float(total_pair)# 思考:mapreduce版本的auc該怎麼寫if __name__ == "__main__": labels,preds = gen_label_pred(10000000) naive_auc_rst = naive_auc(labels,preds) approximate_auc_rst = approximate_auc(labels,preds) print "naive auc result:{},approximate auc result:{}".format(naive_auc_rst,approximate_auc_rst) """ naive_auc exec time: 31.7306630611s approximate_auc exec time: 2.32403683662s naive auc result:0.500267265728,approximate auc result:0.50026516844 """
參考:
分類之性能評估指標https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_testhttps://gist.github.com/wepe/02e3842cc5224016b50d7e403c117bbaAUC理解與實現 - CSDN博客天池錄播機器學習和統計裡面的auc怎麼理解?
推薦閱讀:
※如何六個月內學會深度學習
※機器學習基礎與實踐(二)----數據轉換
※機器學習篇-評估機器學習的模型
※關鍵詞提取Part1(A Quick Review)
TAG:機器學習 |