Machine Learning基礎(chǔ):模型評價(jià)








1 混淆矩陣(Confusion Matrix)

??對于二分類問題泉粉,預(yù)測模型會(huì)對每一個(gè)樣本預(yù)測一個(gè)得分s或者一個(gè)概率p护盈。 然后灵莲,可以選取一個(gè)閾值t路克,讓得分s>t的樣本預(yù)測為正潮秘,而得分s<t的樣本預(yù)測為負(fù)琼开。 這樣一來,根據(jù)預(yù)測的結(jié)果和實(shí)際的標(biāo)簽可以把樣本分為4類:

--- 正樣本 負(fù)樣本
預(yù)測為正 TP(真陽例) FP(假陽例)
預(yù)測為負(fù) FN(假陰例) TN(真陰例)

??顯然枕荞,混淆矩陣包含四部分的信息:

??True positive(TP)柜候,稱為真陽率,表明實(shí)際是正樣本預(yù)測成正樣本的樣本數(shù)

??False positive(FP)躏精,稱為假陽率渣刷,表明實(shí)際是負(fù)樣本預(yù)測成正樣本的樣本數(shù)

??False negative(FN),稱為假陰率矗烛,表明實(shí)際是正樣本預(yù)測成負(fù)樣本的樣本數(shù)

??True negative(TN)辅柴,稱為真陰率,表明實(shí)際是負(fù)樣本預(yù)測成負(fù)樣本的樣本數(shù)

??對照著混淆矩陣瞭吃,很容易就能把關(guān)系碌嘀、概念理清楚,但是久而久之歪架,也很容易忘記概念股冗。不妨我們按照位置前后分為兩部分記憶,前面的部分是True/False表示真假和蚪,即代表著預(yù)測的正確性止状,后面的部分是positive/negative表示正負(fù)樣本,即代表著預(yù)測的結(jié)果攒霹,所以导俘,混淆矩陣即可表示為正確性-預(yù)測結(jié)果的集合。現(xiàn)在我們再來看上述四個(gè)部分的概念(均代表樣本數(shù)剔蹋,下述省略):

??TN旅薄,預(yù)測是負(fù)樣本,預(yù)測對了

??FP,預(yù)測是正樣本少梁,預(yù)測錯(cuò)了

??FN洛口,預(yù)測是負(fù)樣本,預(yù)測錯(cuò)了

??TP凯沪,預(yù)測是正樣本第焰,預(yù)測對了

??幾乎我所知道的所有評價(jià)指標(biāo),都是建立在混淆矩陣基礎(chǔ)上的妨马,包括準(zhǔn)確率挺举、精準(zhǔn)率、召回率烘跺、F1-score湘纵,當(dāng)然也包括AUC。

1.1 混淆矩陣的評價(jià)指標(biāo)

??AccuracyRate(準(zhǔn)確率): (TP+TN)/(TP+TN+FN+FP)

??ErrorRate(誤分率): (FN+FP)/(TP+TN+FN+FP)

??Recall(召回率滤淳,查全率,擊中概率): TP/(TP+FN), 在所有GroundTruth為正樣本中有多少被識(shí)別為正樣本了;

??Precision(查準(zhǔn)率):TP/(TP+FP),在所有識(shí)別成正樣本中有多少是真正的正樣本梧喷;

??TPR(TruePositive Rate): TP/(TP+FN),實(shí)際就是Recall

??FAR(FalseAcceptance Rate)或FPR(False Positive Rate):FP/(FP+TN), 錯(cuò)誤接收率脖咐,誤報(bào)率铺敌,在所有GroundTruth為負(fù)樣本中有多少被識(shí)別為正樣本了;

??FRR(FalseRejection Rate): FN/(TP+FN),錯(cuò)誤拒絕率屁擅,拒真率偿凭,在所有GroundTruth為正樣本中有多少被識(shí)別為負(fù)樣本了,它等于1-Recall

??調(diào)和平均值F1_score:2PrecisionRecall/(Recall+ Precision)

#準(zhǔn)備數(shù)據(jù)
import numpy as np
from sklearn import datasets

digits = datasets.load_digits()
X = digits['data']
y = digits['target'].copy()

#手動(dòng)讓digits數(shù)據(jù)集9的數(shù)據(jù)偏斜
y[digits['target']==9] = 1
y[digits['target']!=9] = 0

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)

log_reg = LogisticRegression()
log_reg.fit(X_train,y_train)
log_reg.score(X_test,y_test)

y_log_predict = log_reg.predict(X_test)

def TN(y_true,y_predict):
    return np.sum((y_true==0)&(y_predict==0))
TN(y_test,y_log_predict)

def FP(y_true,y_predict):
    return np.sum((y_true==0)&(y_predict==1))
FP(y_test,y_log_predict)

def FN(y_true,y_predict):
    return np.sum((y_true==1)&(y_predict==0))
FN(y_test,y_log_predict)

def TP(y_true,y_predict):
    return np.sum((y_true==1)&(y_predict==1))
TP(y_test,y_log_predict)

#構(gòu)建混淆矩陣
def confusion_matrix(y_true,y_predict):
    return np.array([
        [TN(y_true,y_predict),FP(y_true,y_predict)],
        [FN(y_true,y_predict),TP(y_true,y_predict)]
    ])
confusion_matrix(y_test,y_log_predict)

#精準(zhǔn)率
def precision_score(y_true,y_predict):
    tp = TP(y_true,y_predict)
    fp = FP(y_true,y_predict)
    try:
        return tp/(tp+fp)
    except:
        return 0.0
precision_score(y_test,y_log_predict)

#召回率
def recall_score(y_true,y_predict):
    tp = TP(y_true,y_predict)
    fn = FN(y_true,y_predict)
    try:
        return tp/(tp+fn)
    except:
        return 0.0
recall_score(y_test,y_log_predict)

#scikitlearn中的精準(zhǔn)率和召回率

#構(gòu)建混淆矩陣
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test,y_log_predict)

#精準(zhǔn)率
from sklearn.metrics import precision_score
precision_score(y_test,y_log_predict)

#f1-score
f1_score(y_test,y_log_predict)

1.2 Precision-Recall的平衡

??一般來說派歌,決策邊界為theta.T*x_b=0弯囊,即計(jì)算出p>0.5時(shí)分類為1,如果我們手動(dòng)改變這個(gè)threshold硝皂,就可以平移這個(gè)決策邊界,改變精準(zhǔn)率和召回率

#該函數(shù)可以得到log_reg的預(yù)測分?jǐn)?shù)作谭,未帶入sigmoid
decsion_scores = log_reg.decision_function(X_test)

#將threshold由默認(rèn)的0調(diào)為5
y_predict2 = decsion_scores>=5.0
precision_score(y_test,y_predict2)
# 0.96
recall_score(y_test,y_predict2)
# 0.5333333333333333

y_predict2 = decsion_scores>=-5.0
precision_score(y_test,y_predict2)
# 0.7272727272727273
recall_score(y_test,y_predict2)
# 0.8888888888888888

1.3 精準(zhǔn)率和召回率曲線

??可以用precisions-recalls曲線與坐標(biāo)軸圍成的面積衡量模型的好壞

from sklearn.metrics import precision_score
from sklearn.metrics import recall_score

thresholds = np.arange(np.min(decsion_scores),np.max(decsion_scores))
precisions = []
recalls = []

for threshold in thresholds:
    y_predict = decsion_scores>=threshold
    precisions.append(precision_score(y_test,y_predict))
    recalls.append(recall_score(y_test,y_predict))
import matplotlib.pyplot as plt

plt.plot(thresholds,precisions)
plt.plot(thresholds,recalls)
plt.show()
plt.plot(precisions,recalls)
plt.show()

![https://upload-images.jianshu.io/upload_images/5420272-a58ca7b5ef330aa6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/457]

??使用scikit-learn繪制Precision-Recall曲線

from sklearn.metrics import precision_recall_curve
precisions,recalls,thresholds = precision_recall_curve(y_test,decsion_scores)

#由于precisions和recalls中比thresholds多了一個(gè)元素稽物,因此要繪制曲線,先去掉這個(gè)元素
plt.plot(thresholds,precisions[:-1])
plt.plot(thresholds,recalls[:-1])
plt.show()


2 ROC曲線

??對于某個(gè)二分類分類器來說折欠,輸出結(jié)果標(biāo)簽(0還是1)往往取決于輸出的概率以及預(yù)定的概率閾值贝或,比如常見的閾值就是0.5,大于0.5的認(rèn)為是正樣本锐秦,小于0.5的認(rèn)為是負(fù)樣本咪奖。如果增大這個(gè)閾值,預(yù)測錯(cuò)誤(針對正樣本而言酱床,即指預(yù)測是正樣本但是預(yù)測錯(cuò)誤羊赵,下同)的概率就會(huì)降低但是隨之而來的就是預(yù)測正確的概率也降低;如果減小這個(gè)閾值,那么預(yù)測正確的概率會(huì)升高但是同時(shí)預(yù)測錯(cuò)誤的概率也會(huì)升高昧捷。實(shí)際上闲昭,這種閾值的選取也一定程度上反映了分類器的分類能力。我們當(dāng)然希望無論選取多大的閾值靡挥,分類都能盡可能地正確序矩,也就是希望該分類器的分類能力越強(qiáng)越好,一定程度上可以理解成一種魯棒能力吧跋破。

??為了形象地衡量這種分類能力簸淀,ROC曲線橫空出世!如下圖所示毒返,即為一條ROC曲線(該曲線的原始數(shù)據(jù)第三部分會(huì)介紹)∽饽唬現(xiàn)在關(guān)心的是:

??橫軸:False Positive Rate(假陽率,F(xiàn)PR)

??縱軸:True Positive Rate(真陽率饿悬,TPR)

??假陽率令蛉,簡單通俗來理解就是預(yù)測為正樣本但是預(yù)測錯(cuò)了的可能性,顯然狡恬,我們不希望該指標(biāo)太高珠叔。

??真陽率,則是代表預(yù)測為正樣本但是預(yù)測對了的可能性弟劲,當(dāng)然祷安,我們希望真陽率越高越好。

??顯然兔乞,ROC曲線的橫縱坐標(biāo)都在[0,1]之間汇鞭,自然ROC曲線的面積不大于1。現(xiàn)在我們來分析幾個(gè)特殊情況庸追,從而更好地掌握ROC曲線的性質(zhì):

??(0,0):假陽率和真陽率都為0霍骄,即分類器全部預(yù)測成負(fù)樣本

??(0,1):假陽率為0,真陽率為1淡溯,全部完美預(yù)測正確读整,happy

??(1,0):假陽率為1,真陽率為0咱娶,全部完美預(yù)測錯(cuò)誤米间,悲劇

??(1,1):假陽率和真陽率都為1,即分類器全部預(yù)測成正樣本

??TPR=FPR膘侮,斜對角線屈糊,預(yù)測為正樣本的結(jié)果一半是對的,一半是錯(cuò)的琼了,代表隨機(jī)分類器的預(yù)測效果逻锐。

??于是,我們可以得到基本的結(jié)論:ROC曲線在斜對角線以下,則表示該分類器效果差于隨機(jī)分類器谦去,反之慷丽,效果好于隨機(jī)分類器,當(dāng)然鳄哭,我們希望ROC曲線盡量位于斜對角線以上要糊,也就是向左上角(0,1)凸。

3 AUC(Area under the ROC curve)曲線

??ROC曲線一定程度上可以反映分類器的分類效果妆丘,但是不夠直觀锄俄,我們希望有這么一個(gè)指標(biāo),如果這個(gè)指標(biāo)越大越好勺拣,越小越差奶赠,于是,就有了AUC药有。AUC實(shí)際上就是ROC曲線下的面積毅戈。AUC直觀地反映了ROC曲線表達(dá)的分類能力。

??AUC = 1愤惰,代表完美分類器

??0.5 < AUC < 1苇经,優(yōu)于隨機(jī)分類器

??0 < AUC < 0.5,差于隨機(jī)分類器

3.1 AUC的用處

??AUC最大的應(yīng)用應(yīng)該就是點(diǎn)擊率預(yù)估(CTR)的離線評估宦言。CTR的離線評估在公司的技術(shù)流程中占有很重要的地位扇单,一般來說,ABTest和轉(zhuǎn)全觀察的資源成本比較大奠旺,所以蜘澜,一個(gè)合適的離線評價(jià)可以節(jié)省很多時(shí)間、人力响疚、資源成本鄙信。那么,為什么AUC可以用來評價(jià)CTR呢忿晕?我們首先要清楚兩個(gè)事情:

??(1)CTR是把分類器輸出的概率當(dāng)做是點(diǎn)擊率的預(yù)估值装诡,如業(yè)界常用的LR模型,利用sigmoid函數(shù)將特征輸入與概率輸出聯(lián)系起來杏糙,這個(gè)輸出的概率就是點(diǎn)擊率的預(yù)估值慎王。內(nèi)容的召回往往是根據(jù)CTR的排序而決定的蚓土。

??(2)AUC量化了ROC曲線表達(dá)的分類能力宏侍。這種分類能力是與概率、閾值緊密相關(guān)的蜀漆,分類能力越好(AUC越大)谅河,那么輸出概率越合理,排序的結(jié)果越合理。

??我們不僅希望分類器給出是否點(diǎn)擊的分類信息绷耍,更需要分類器給出準(zhǔn)確的概率值吐限,作為排序的依據(jù)。所以褂始,這里的AUC就直觀地反映了CTR的準(zhǔn)確性(也就是CTR的排序能力).

3.2 AUC求解

??步驟如下:

??(1)得到結(jié)果數(shù)據(jù)诸典,數(shù)據(jù)結(jié)構(gòu)為:(輸出概率,標(biāo)簽真值)

??(2)對結(jié)果數(shù)據(jù)按輸出概率進(jìn)行分組崎苗,得到(輸出概率狐粱,該輸出概率下真實(shí)正樣本數(shù),該輸出概率下真實(shí)負(fù)樣本數(shù))胆数。這樣做的好處是方便后面的分組統(tǒng)計(jì)肌蜻、閾值劃分統(tǒng)計(jì)等

??(3)對結(jié)果數(shù)據(jù)按輸出概率進(jìn)行從大到小排序

??(4)從大到小,把每一個(gè)輸出概率作為分類閾值必尼,統(tǒng)計(jì)該分類閾值下的TPR和FPR

??(5)微元法計(jì)算ROC曲線面積蒋搜、繪制ROC曲線

??代碼如下所示:

import pylab as pl
from math import log,exp,sqrt
import itertools
import operator

def read_file(file_path, accuracy=2):
    db = []  #(score,nonclk,clk)
    pos, neg = 0, 0 #正負(fù)樣本數(shù)量
    #讀取數(shù)據(jù)
    with open(file_path,'r') as fs:
        for line in fs:
            temp = eval(line)
            #精度可控
            #score = '%.1f' % float(temp[0])
            score = float(temp[0])
            trueLabel = int(temp[1])
            sample = [score, 0, 1] if trueLabel == 1 else [score, 1, 0]
            score,nonclk,clk = sample
            pos += clk #正樣本
            neg += nonclk #負(fù)樣本
            db.append(sample)
    return db, pos, neg

def get_roc(db, pos, neg):
    #按照輸出概率,從大到小排序
    db = sorted(db, key=lambda x:x[0], reverse=True)
    file=open('data.txt','w')
    file.write(str(db))
    file.close()
    #計(jì)算ROC坐標(biāo)點(diǎn)
    xy_arr = []
    tp, fp = 0., 0.
    for i in range(len(db)):
        tp += db[i][2]
        fp += db[i][1]
        xy_arr.append([fp/neg,tp/pos])
    return xy_arr

def get_AUC(xy_arr):
    #計(jì)算曲線下面積
    auc = 0.
    prev_x = 0
    for x,y in xy_arr:
        if x != prev_x:
            auc += (x - prev_x) * y
            prev_x = x
    return auc

def draw_ROC(xy_arr):
    x = [_v[0] for _v in xy_arr]
    y = [_v[1] for _v in xy_arr]
    pl.title("ROC curve of %s (AUC = %.4f)" % ('clk',auc))
    pl.xlabel("False Positive Rate")
    pl.ylabel("True Positive Rate")
    pl.plot(x, y)# use pylab to plot x and y
    pl.show()# show the plot on the screen

??數(shù)據(jù):提供的數(shù)據(jù)為每一個(gè)樣本的(預(yù)測概率判莉,真實(shí)標(biāo)簽)tuple

??數(shù)據(jù)鏈接:https://pan.baidu.com/s/1c1FUzVM豆挽,密碼1ax8

??計(jì)算結(jié)果:AUC=0.747925810016,與Spark MLLib中的roc_AUC計(jì)算值基本吻合

??當(dāng)然骂租,選擇的概率精度越低祷杈,AUC計(jì)算的偏差就越大。








Reference:

  1. 模型評價(jià)(一) AUC大法

  2. Python3入門機(jī)器學(xué)習(xí) - 混淆矩陣渗饮、精準(zhǔn)率但汞、召回率

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市互站,隨后出現(xiàn)的幾起案子私蕾,更是在濱河造成了極大的恐慌,老刑警劉巖胡桃,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踩叭,死亡現(xiàn)場離奇詭異,居然都是意外死亡翠胰,警方通過查閱死者的電腦和手機(jī)容贝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來之景,“玉大人斤富,你說我怎么就攤上這事《凸罚” “怎么了满力?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵焕参,是天一觀的道長。 經(jīng)常有香客問我油额,道長叠纷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任潦嘶,我火速辦了婚禮涩嚣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掂僵。我一直安慰自己缓艳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布看峻。 她就那樣靜靜地躺著阶淘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪互妓。 梳的紋絲不亂的頭發(fā)上溪窒,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音冯勉,去河邊找鬼澈蚌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灼狰,可吹牛的內(nèi)容都是我干的宛瞄。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼交胚,長吁一口氣:“原來是場噩夢啊……” “哼份汗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蝴簇,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤杯活,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后熬词,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旁钧,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年互拾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歪今。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颜矿,死狀恐怖寄猩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情或衡,我是刑警寧澤焦影,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站封断,受9級特大地震影響斯辰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坡疼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一彬呻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柄瑰,春花似錦闸氮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至授翻,卻和暖如春或悲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背堪唐。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工巡语, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淮菠。 一個(gè)月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓男公,卻偏偏與公主長得像,于是被迫代替她去往敵國和親合陵。 傳聞我的和親對象是個(gè)殘疾皇子枢赔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容