Sklearn(v3)——SVM理论(4)
二分類SVC中的樣本不均衡問題:重要參數(shù)class_weight
對(duì)于分類問題,永遠(yuǎn)都逃不過的一個(gè)痛點(diǎn)就是樣本不均衡問題。樣本不均衡是指在一組數(shù)據(jù)集中,標(biāo)簽的一類天生?占有很大的比例,但我們有著捕捉出某種特定的分類的需求的狀況。比如,我們現(xiàn)在要對(duì)潛在犯罪者和普通人進(jìn)行分類,潛在犯罪者占總?cè)丝诘谋壤窍?/span>當(dāng)?shù)偷?/span>,也許只有2%左右,98%的人都是普通人,而我們的目標(biāo)是要捕獲出潛在犯罪者。這樣的標(biāo)簽分布會(huì)帶來許多問題。
首先,分類模型天生會(huì)傾向于多數(shù)的類,讓多數(shù)類更容易被判斷正確,少數(shù)類被犧牲掉。因?yàn)閷?duì)于模型而言,樣本量越大的標(biāo)簽可以學(xué)習(xí)的信息越多,算法就會(huì)更加依賴于從多數(shù)類中學(xué)到的信息來進(jìn)行判斷。如果我們希望捕獲少數(shù)類,模型就會(huì)失敗。??其次,模型評(píng)估指標(biāo)會(huì)失去意義。這種分類狀況下,即便模型什么也不做,全把所有人都當(dāng)成不會(huì)犯罪的人,準(zhǔn)確率也能非常高,這使得模型評(píng)估指標(biāo)accuracy變得毫無意義,根本無法達(dá)到我們的“要識(shí)別出會(huì)犯罪的人”的建模目的。
所以現(xiàn)在,我們首先要讓算法意識(shí)到數(shù)據(jù)的標(biāo)簽是不均衡的,通過施加一些懲罰或者改變樣本本身,來讓模型向著捕獲少數(shù)類的方向建模。然后,我們要改進(jìn)我們的模型評(píng)估指標(biāo),使用更加針對(duì)于少數(shù)類的指標(biāo)來優(yōu)化模型。
要解決第一個(gè)問題,我們?cè)谶壿嫽貧w中已經(jīng)介紹了一些基本方法,比如上采樣下采樣。但這些采樣方法會(huì)增加樣本的總數(shù),對(duì)于支持向量機(jī)這個(gè)樣本總是對(duì)計(jì)算速度影響巨大的算法來說,我們完全不想輕易地增加樣本數(shù)量。況且,支持向量機(jī)中地決策僅僅決策邊界的影響,而決策邊界又僅僅受到參數(shù)C和支持向量的影響,單純地增加樣本數(shù)量不僅會(huì)增加計(jì)算時(shí)間,可能還會(huì)增加無數(shù)對(duì)決策邊界無影響的樣本點(diǎn)。因此在支持向量機(jī)中,我們要大力依賴我們調(diào)節(jié)樣本均衡的參數(shù):SVC類中的class_weight和接口?t中可以設(shè)定的sample_weight。
在邏輯回歸中,參數(shù)class_weight默認(rèn)None,此模式表示假設(shè)數(shù)據(jù)集中的所有標(biāo)簽是均衡的,即自動(dòng)認(rèn)為標(biāo)簽的比例是1:1。所以當(dāng)樣本不均衡的時(shí)候,我們可以使用形如{"標(biāo)簽的值1":權(quán)重1,"標(biāo)簽的值2":權(quán)重2}的字典來輸入真實(shí)的樣本標(biāo)簽比例,來讓算法意識(shí)到樣本是不平衡的。或者使用”balanced“模式,直接使用n_samples/(n_classes?* np.bincount(y))作為權(quán)重,可以比較好地修正我們的樣本不均衡情況。
但在SVM中,我們的分類判斷是基于決策邊界的,而最終決定究竟使用怎樣的支持向量和決策邊界的參數(shù)是參數(shù)C,所以所有的樣本均衡都是通過參數(shù)C來調(diào)整的。
SVC的參數(shù):class_weight
可輸入字典或者"balanced”,可不填,默認(rèn)None對(duì)SVC,將類i的參數(shù)C設(shè)置為class_weight?[i]?* C。如果沒有給出具體的class_weight,則所有類都被假設(shè)為占有相同的權(quán)重1,模型會(huì)根據(jù)數(shù)據(jù)原本的狀況去訓(xùn)練。如果希望改善樣本不均衡狀況,請(qǐng)輸入形如{"標(biāo)簽的值1":權(quán)重1,"標(biāo)簽的值2":權(quán)重2}的字典,則參數(shù)C將會(huì)自動(dòng)被設(shè)為:標(biāo)簽的值1的C:權(quán)重1?* C,標(biāo)簽的值2的C:權(quán)重2*C。
或者,可以使用“balanced”模式,這個(gè)模式使用y的值自動(dòng)調(diào)整與輸入數(shù)據(jù)中的類頻率成反比的權(quán)重為n_samples/(n_classes?* np.bincount(y))
SVC的接口?t的參數(shù):sample_weight
數(shù)組,結(jié)構(gòu)為?(n_samples,?),必須對(duì)應(yīng)輸入?t中的特征矩陣的每個(gè)樣本
每個(gè)樣本在?t時(shí)的權(quán)重,讓權(quán)重?*?每個(gè)樣本對(duì)應(yīng)的C值來迫使分類器強(qiáng)調(diào)設(shè)定的權(quán)重更大的樣本。通常,較大的權(quán)重加在少數(shù)類的樣本上,以迫使模型向著少數(shù)類的方向建模
通常來說,這兩個(gè)參數(shù)我們只選取一個(gè)來設(shè)置。如果我們同時(shí)設(shè)置了兩個(gè)參數(shù),則C會(huì)同時(shí)受到兩個(gè)參數(shù)的影響,?即?class_weight中設(shè)定的權(quán)重?*?sample_weight中設(shè)定的權(quán)重?*?C。
我們接下來就來看看如何使用這個(gè)參數(shù)。
首先,我們來自建一組樣本不平衡的數(shù)據(jù)集。我們?cè)谶@組數(shù)據(jù)集上建兩個(gè)SVC模型,一個(gè)設(shè)置有class_weight參數(shù),一個(gè)不設(shè)置class_weight參數(shù)。我們對(duì)兩個(gè)模型分別進(jìn)行評(píng)估并畫出他們的決策邊界,以此來觀察class_weight帶來的效果。
import numpy as np import matplotlib.pyplot as plt from sklearn import svm from sklearn.datasets import make_blobsclass_1 = 500 #類別1有500個(gè)樣本 class_2 = 50 #類別2只有50個(gè) centers = [[0.0, 0.0], [2.0, 2.0]] #設(shè)定兩個(gè)類別的中心 clusters_std = [1.5, 0.5] #設(shè)定兩個(gè)類別的方差,通常來說,樣本量比較大的類別會(huì)更加松散 X, y = make_blobs(n_samples=[class_1, class_2], centers=centers, cluster_std=clusters_std, random_state=0, shuffle=False)#看看數(shù)據(jù)集長什么樣 plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10) #其中紅色點(diǎn)是少數(shù)類,紫色點(diǎn)是多數(shù)類 #不設(shè)定class_weight clf = svm.SVC(kernel='linear', C=1.0) clf.fit(X, y)#設(shè)定class_weight wclf = svm.SVC(kernel='linear', class_weight={1: 10}) wclf.fit(X, y)#給兩個(gè)模型分別打分看看,這個(gè)分?jǐn)?shù)是accuracy準(zhǔn)確度 print(clf.score(X,y)) wclf.score(X,y)結(jié)果:?
0.9418181818181818 0.9127272727272727?繪制兩個(gè)模型下數(shù)據(jù)的決策邊界
#首先要有數(shù)據(jù)分布 plt.figure(figsize=(6,5)) plt.scatter(X[:,0], X[:,1], c=y, cmap="rainbow",s=10) ax = plt.gca() #獲取當(dāng)前的子圖,如果不存在,則創(chuàng)建新的子圖 #繪制決策邊界的第一步:要有網(wǎng)格xlim = ax.get_xlim() ylim = ax.get_ylim()xx = np.linspace(xlim[0], xlim[1], 30) yy = np.linspace(ylim[0], ylim[1], 30) YY, XX = np.meshgrid(yy, xx) xy = np.vstack([XX.ravel(), YY.ravel()]).T#第二步:找出我們的樣本點(diǎn)到?jīng)Q策邊界的距離 Z_clf = clf.decision_function(xy).reshape(XX.shape) a = ax.contour(XX, YY, Z_clf, colors='black', levels=[0], alpha=0.5, linestyles=['-'])Z_wclf = wclf.decision_function(xy).reshape(XX.shape) b = ax.contour(XX, YY, Z_wclf, colors='red', levels=[0], alpha=0.5, linestyles=['-'])#第三步:畫圖例 plt.legend([a.collections[0], b.collections[0]], ["non weighted" , "weighted"],loc="upper right") plt.show()可以看出,從準(zhǔn)確率的角度來看,不做樣本平衡的時(shí)候準(zhǔn)確率反而更高,做了樣本平衡準(zhǔn)確率反而變低了,這是因?為做了樣本平衡后,為了要更有效地捕捉出少數(shù)類,模型誤傷了許多多數(shù)類樣本,而多數(shù)類被分錯(cuò)的樣本數(shù)量?>?少數(shù)類被分類正確的樣本數(shù)量,使得模型整體的精確性下降。現(xiàn)在,如果我們的目的是模型整體的準(zhǔn)確率,那我們就要拒絕樣本平衡,使用class_weight被設(shè)置之前的模型。
然而在現(xiàn)實(shí)中,我們往往都在追求捕捉少數(shù)類,因?yàn)樵诤芏嗲闆r下,將少數(shù)類判斷錯(cuò)的代價(jià)是巨大的。比如我們之前提到的,判斷潛在犯罪者和普通人的例子,如果我們沒有能夠識(shí)別出潛在犯罪者,那么這些人就可能去危害社會(huì),造成惡劣影響,但如果我們把普通人錯(cuò)認(rèn)為是潛在犯罪者,我們也許只是需要增加一些監(jiān)控和人為甄別的成本。所以對(duì)我們來說,我們寧愿把普通人判錯(cuò),也不想放過任何一個(gè)潛在犯罪者。我們希望不惜一切代價(jià)來捕獲少數(shù)類,或者希望捕捉出盡量多的少數(shù)類,那我們就必須使用class_weight設(shè)置后的模型。
SVC的模型評(píng)估指標(biāo)
從上一節(jié)的例子中可以看出,如果我們的目標(biāo)是希望盡量捕獲少數(shù)類,那準(zhǔn)確率這個(gè)模型評(píng)估逐漸失效,所以我們需要新的模型評(píng)估指標(biāo)來幫助我們。如果簡(jiǎn)單來看,其實(shí)我們只需要查看模型在少數(shù)類上的準(zhǔn)確率就好了,只要能夠?qū)⑸?/span>數(shù)類盡量捕捉出來,就能夠達(dá)到我們的目的。
但此時(shí),新問題又出現(xiàn)了,我們對(duì)多數(shù)類判斷錯(cuò)誤后,會(huì)需要人工甄別或者更多的業(yè)務(wù)上的措施來一一排除我們判斷錯(cuò)誤的多數(shù)類,這種行為往往伴隨著很高的成本。比如銀行在判斷”一個(gè)申請(qǐng)信用卡的客戶是否會(huì)出現(xiàn)違約行為“的時(shí)候,如果一個(gè)客戶被判斷為”會(huì)違約“,這個(gè)客戶的信用卡申請(qǐng)就會(huì)被駁回,如果為了捕捉出”會(huì)違約“的人,大量地將”不會(huì)違約“的客戶判斷為”會(huì)違約“的客戶,就會(huì)有許多無辜的客戶的申請(qǐng)被駁回。信用卡對(duì)銀行來說意味著利息收入,而拒絕了許多本來不會(huì)違約的客戶,對(duì)銀行來說就是巨大的損失。同理,大眾在召回不符合歐盟標(biāo)準(zhǔn)的汽車時(shí),如果為了找到所有不符合標(biāo)準(zhǔn)的汽車,而將一堆本來符合標(biāo)準(zhǔn)了的汽車召回,這個(gè)成本是不可估量的。
也就是說,單純地追求捕捉出少數(shù)類,就會(huì)成本太高,而不顧及少數(shù)類,又會(huì)無法達(dá)成模型的效果。所以在現(xiàn)實(shí)中,我們往往在尋找捕獲少數(shù)類的能力和將多數(shù)類判錯(cuò)后需要付出的成本的平衡。如果一個(gè)模型在能夠盡量捕獲少?數(shù)類的情況下,還能夠盡量對(duì)多數(shù)類判斷正確,則這個(gè)模型就非常優(yōu)秀了。為了評(píng)估這樣的能力,我們將引入新的模型評(píng)估指標(biāo):混淆矩陣和ROC曲線來幫助我們。
混淆矩陣(Confusion?Matrix)
混淆矩陣是二分類問題的多維衡量指標(biāo)體系,在樣本不平衡時(shí)極其有用。在混淆矩陣中,我們將少數(shù)類認(rèn)為是正例,多數(shù)類認(rèn)為是負(fù)例。在決策樹,隨機(jī)森林這些普通的分類算法里,即是說少數(shù)類是1,多數(shù)類是0。在SVM里,就是說少數(shù)類是1,多數(shù)類是-1。普通的混淆矩陣,一般使用{0,1}來表示。混淆矩陣陣如其名,十分容易讓人混淆,在許多教材中,混淆矩陣中各種各樣的名稱和定義讓大家難以理解難以記憶。我為大家找出了一種簡(jiǎn)化的方式來顯示標(biāo)準(zhǔn)二分類的混淆矩陣,如圖所示:?
混淆矩陣中,永遠(yuǎn)是真實(shí)值在前,預(yù)測(cè)值在后。其實(shí)可以很容易看出,11和00的對(duì)角線就是全部預(yù)測(cè)正確的,01和10的對(duì)角線就是全部預(yù)測(cè)錯(cuò)誤的。基于混淆矩陣,我們有六個(gè)不同的模型評(píng)估指標(biāo),這些評(píng)估指標(biāo)的范圍都在[0,1]之間,所有以11和00為分子的指標(biāo)都是越接近1越好,所以以01和10為分子的指標(biāo)都是越接近0越好。對(duì)于所有的指標(biāo),我們用橙色表示分母,用綠色表示分子,則我們有:?
準(zhǔn)確率
#對(duì)于沒有class_weight,沒有做樣本平衡的灰色決策邊界來說: print((y[y == clf.predict(X)] == 1).sum()/(clf.predict(X) == 1).sum()) #對(duì)于有class_weight,做了樣本平衡的紅色決策邊界來說: (y[y == wclf.predict(X)] == 1).sum()/(wclf.predict(X) == 1).sum()結(jié)果:
0.7142857142857143 0.5102040816326531精確度也叫查準(zhǔn)率,也就是決策邊界上方的所有點(diǎn)中紅色點(diǎn)所占的比例
可以看出,做了樣本平衡之后,精確度是下降的。因?yàn)楹苊黠@,樣本平衡之后,有更多的多數(shù)類紫色點(diǎn)被我們誤傷了。精確度可以幫助我們判斷,是否每一次對(duì)少數(shù)類的預(yù)測(cè)都精確,所以又被稱為”查準(zhǔn)率“。在現(xiàn)實(shí)的樣本不平衡
例子中,當(dāng)每一次將多數(shù)類判斷錯(cuò)誤的成本非常高昂的時(shí)候(比如大眾召回車輛的例子),我們會(huì)追求高精確度。 精確度越低,我們對(duì)多數(shù)類的判斷就會(huì)越錯(cuò)誤。當(dāng)然了,如果我們的目標(biāo)是不計(jì)一切代價(jià)捕獲少數(shù)類,那我們并不在意精確度。(在意多數(shù)類是否判斷正確)召回率
召回率Recall,又被稱為敏感度(sensitivity),真正率,查全率,表示所有真實(shí)為1的樣本中,被我們預(yù)測(cè)正確的樣本所占的比例。在支持向量機(jī)中,召回率可以被表示為,決策邊界上方的所有紅色點(diǎn)占全部樣本中的紅色點(diǎn)的比例。召回率越高,代表我們盡量捕捉出了越多的少數(shù)類,召回率越低,代表我們沒有捕捉出足夠的少數(shù)類。 #所有predict為1的點(diǎn) / 全部為1的點(diǎn)的比例 #對(duì)于沒有class_weight,沒有做樣本平衡的灰色決策邊界來說: print((y[y == clf.predict(X)] == 1).sum()/(y == 1).sum())#對(duì)于有class_weight,做了樣本平衡的紅色決策邊界來說: (y[y == wclf.predict(X)] == 1).sum()/(y == 1).sum() 0.6 1.0如果我們希望不計(jì)一切代價(jià),找出少數(shù)類(比如找出潛在犯罪者的例子),那我們就會(huì)追求高召回率,相反如果我們的目標(biāo)不是盡量捕獲少數(shù)類,那我們就不需要在意召回率。
注意召回率和精確度的分子是相同的(都是11),只是分母不同。而召回率和精確度是此消彼長的,兩者之間的平?衡代表了捕捉少數(shù)類的需求和盡量不要誤傷多數(shù)類的需求的平衡。究竟要偏向于哪一方,取決于我們的業(yè)務(wù)需求:究竟是誤傷多數(shù)類的成本更高,還是無法捕捉少數(shù)類的代價(jià)更高。
F1分?jǐn)?shù)
為了同時(shí)兼顧精確度和召回率,我們創(chuàng)造了兩者的調(diào)和平均數(shù)作為考量?jī)烧咂胶獾木C合性指標(biāo),稱之為F1?measure。兩個(gè)數(shù)之間的調(diào)和平均傾向于靠近兩個(gè)數(shù)中比較小的那一個(gè)數(shù),因此我們追求盡量高的F1?measure,?能夠保證我們的精確度和召回率都比較高。F1?measure在[0,1]之間分布,越接近1越好。
假負(fù)率(False Negative Rate),它等于 1 - Recall。
特異度和假正率
#所有被正確預(yù)測(cè)為0的樣本 / 所有的0樣本 #對(duì)于沒有class_weight,沒有做樣本平衡的灰色決策邊界來說: print((y[y == clf.predict(X)] == 0).sum()/(y == 0).sum())#對(duì)于有class_weight,做了樣本平衡的紅色決策邊界來說: (y[y == wclf.predict(X)] == 0).sum()/(y == 0).sum()結(jié)果:
0.976 0.904混淆矩陣
sklearn當(dāng)中提供了大量的類來幫助我們了解和使用混淆矩陣。
基于混淆矩陣,我們學(xué)習(xí)了總共六個(gè)指標(biāo):準(zhǔn)確率Accuracy,精確度Precision,召回率Recall,精確度和召回度的平衡指標(biāo)F?measure,特異度Speci?city,以及假正率FPR。其中,假正率有一個(gè)非常重要的應(yīng)用:我們?cè)谧非筝^高的Recall的時(shí)候,Precision會(huì)下降,就是說隨著更多的少數(shù)類被捕捉出來,會(huì)有更多的多數(shù)類被判斷錯(cuò)誤,但我們很好奇,隨著Recall的逐漸增加,模型將多數(shù)類判斷錯(cuò)誤的能力如何變化呢?
我們希望理解,我每判斷正確一個(gè)少數(shù)類,就有多少個(gè)多數(shù)類會(huì)被判斷錯(cuò)誤。假正率正好可以幫助我們衡量這個(gè)能力的變化。相對(duì)的,Precision無法判斷這些判斷錯(cuò)誤的多數(shù)類在全部多數(shù)類中究竟占多大的比例,所以無法在提升Recall的過程中也顧及到模型整體的Accuracy。因此,我們可以使用Recall和FPR之間的平衡,來替代Recall和Precision之間的平衡,讓我們衡量模型在盡量捕捉少數(shù)類的時(shí)候,誤傷多數(shù)類的情況如何變化,這就是我們的ROC曲線衡量的平衡。
ROC曲線,全稱The?Receiver Operating?Characteristic Curve,譯為受試者操作特性曲線。這是一條以不同閾值下的假正率FPR為橫坐標(biāo),不同閾值下的召回率Recall為縱坐標(biāo)的曲線。讓我們先從概率和閾值開始講起。
class_1_ = 7 class_2_ = 4 centers_ = [[0.0, 0.0], [1,1]] clusters_std = [0.5, 1] X_, y_ = make_blobs(n_samples=[class_1_, class_2_], centers=centers_, cluster_std=clusters_std , random_state=0, shuffle=False) plt.scatter(X_[:, 0], X_[:, 1], c=y_, cmap="rainbow",s=30)?
from sklearn.linear_model import LogisticRegression as LogiR clf_lo = LogiR().fit(X_,y_)prob = clf_lo.predict_proba(X_)#將樣本和概率放到一個(gè)DataFrame中 import pandas as pd prob = pd.DataFrame(prob) prob.columns = ["0","1"]prob #手動(dòng)調(diào)節(jié)閾值,來改變我們的模型效果 for i in range(prob.shape[0]):if prob.loc[i,"1"] > 0.5:prob.loc[i,"pred"] = 1else:prob.loc[i,"pred"] = 0 prob["y_true"] = y_ prob = prob.sort_values(by="1",ascending=False) prob from sklearn.metrics import confusion_matrix as CM, precision_score as P, recall_score as Rprint(CM(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) #1是少數(shù)類(寫前面)0是多數(shù)類 #試試看手動(dòng)計(jì)算Precision和Recall? print(P(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) R(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0]) [[2 2][0 7]] 1.0 0.5 for i in range(prob.shape[0]):if prob.loc[i,"1"] > 0.4:prob.loc[i,"pred"] = 1 else:prob.loc[i,"pred"] = 0print(CM(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) print(P(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) print(R(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) [[2 2][1 6]] 0.6666666666666666 0.5可見,在不同閾值下,我們的模型評(píng)估指標(biāo)會(huì)發(fā)生變化,我們正利用這一點(diǎn)來觀察Recall和FPR之間如何互相影響。??但是注意,并不是升高閾值,就一定能夠增加或者減少Recall,一切要根據(jù)數(shù)據(jù)的實(shí)際分布來進(jìn)行判斷。而要體現(xiàn)閾值的影響,首先必須的得到分類器在少數(shù)類下的預(yù)測(cè)概率。對(duì)于邏輯回歸這樣天生生成似然的算法和樸素貝葉斯這樣就是在計(jì)算概率的算法,自然非常容易得到概率,但對(duì)于一些其他的分類算法,比如決策樹,比如SVM,?他們的分類方式和概率并不相關(guān)。那在他們身上,我們就無法畫ROC曲線了嗎?并非如此。
決策樹有葉子節(jié)點(diǎn),一個(gè)葉子節(jié)點(diǎn)上可能包含著不同類的樣本。假設(shè)一個(gè)樣本被包含在葉子節(jié)點(diǎn)a中,節(jié)點(diǎn)a包含10?個(gè)樣本,其中6個(gè)為1,4個(gè)為0,則1這個(gè)正類在這個(gè)葉子節(jié)點(diǎn)中的出現(xiàn)概率就是60%,類別0在這個(gè)葉子節(jié)點(diǎn)中的出現(xiàn)概率就是40%。對(duì)于所有在這個(gè)葉子節(jié)點(diǎn)中的樣本而言,節(jié)點(diǎn)上的1和0出現(xiàn)的概率,就是這個(gè)樣本對(duì)應(yīng)的取到1和0的概率,大家可以去自己驗(yàn)證一下。但是思考一個(gè)問題,由于決策樹可以被畫得很深,在足夠深的情況下,決策樹的每個(gè)葉子節(jié)點(diǎn)上可能都不包含多個(gè)類別的標(biāo)簽了,可能一片葉子中只有唯一的一個(gè)標(biāo)簽,即葉子節(jié)點(diǎn)的不純度為0,此時(shí)此刻,對(duì)于每個(gè)樣本而言,他們所對(duì)應(yīng)的“概率”就是0或者1了。這個(gè)時(shí)候,我們就無法調(diào)節(jié)閾值來調(diào)???節(jié)我們的Recall和FPR了。對(duì)于隨機(jī)森林,也是如此。
所以,如果我們有概率需求,我們還是會(huì)優(yōu)先追求邏輯回歸或者樸素貝葉斯。不過其實(shí),SVM也可以生成概率,我們一起來看看,它是怎么做的。
SVM概率預(yù)測(cè):重要參數(shù)probability,接口predict_proba以及decision_function
我們?cè)诋嫷雀呔€,也就是決策邊界的時(shí)候曾經(jīng)使用SVC的接口decision_function,它返回我們輸入的特征矩陣中每個(gè)樣本到劃分?jǐn)?shù)據(jù)集的超平面的距離。我們?cè)?/span>SVM中利用超平面來判斷我們的樣本,本質(zhì)上來說,當(dāng)兩個(gè)點(diǎn)的距離是相同的符號(hào)的時(shí)候,越遠(yuǎn)離超平面的樣本點(diǎn)歸屬于某個(gè)標(biāo)簽類的概率就很大。比如說,一個(gè)距離超平面0.1的點(diǎn),和一個(gè)距離超平面100的點(diǎn),明顯是距離為0.1的點(diǎn)更有可能是負(fù)類別的點(diǎn)混入了邊界。同理,一個(gè)距離超平面距離為-0.1的點(diǎn),和一個(gè)離超平面距離為-100的點(diǎn),明顯是-100的點(diǎn)的標(biāo)簽更有可能是負(fù)類。所以,到超平面的距離一定程度上反應(yīng)了樣本歸屬于某個(gè)標(biāo)簽類的可能性。??接口decision_function返回的值也因此被我們認(rèn)為是SVM?中的置信度(con?dence)。
值得注意的是,在二分類過程中,??decision_function只會(huì)生成一列距離,樣本的類別由距離的符號(hào)來判斷,但是?predict_proba會(huì)生成兩個(gè)類別分別對(duì)應(yīng)的概率。SVM也可以生成概率,所以我們可以使用和邏輯回歸同樣的方式來在SVM上設(shè)定和調(diào)節(jié)我們的閾值。
毋庸置疑,Platt縮放中涉及的交叉驗(yàn)證對(duì)于大型數(shù)據(jù)集來說非常昂貴,計(jì)算會(huì)非常緩慢。另外,由于Platt縮放的理論原因,在二分類過程中,有可能出現(xiàn)predict_proba返回的概率小于0.5,但樣本依舊被標(biāo)記為正類的情況出現(xiàn),?畢竟支持向量機(jī)本身并不依賴于概率來完成自己的分類。如果我們的確需要置信度分?jǐn)?shù),但不一定非要是概率形式的話,那建議可以將probability設(shè)置為False,使用decision_function這個(gè)接口而不是predict_proba。
繪制SVM的ROC曲線?
#開始繪圖 recall = [] FPR = []probrange = np.linspace(clf_proba.predict_proba(X)[:,1].min(),clf_proba.predict_proba(X)[:,1].max(),num=50,endpoint=False)from sklearn.metrics import confusion_matrix as CM, recall_score as R import matplotlib.pyplot as plot for i in probrange:y_predict = []for j in range(X.shape[0]):if clf_proba.predict_proba(X)[j,1] > i:y_predict.append(1)else:y_predict.append(0)cm = CM(y,y_predict,labels=[1,0])recall.append(cm[0,0]/cm[0,:].sum())FPR.append(cm[1,0]/cm[1,:].sum()) recall.sort() FPR.sort()plt.plot(FPR,recall,c="red") plt.plot(probrange+0.05,probrange+0.05,c="black",linestyle="--") plt.show()現(xiàn)在我們就畫出了ROC曲線了,那我們?nèi)绾卫斫膺@條曲線呢?先來回憶一下,我們建立ROC曲線的根本目的是找尋Recall和FPR之間的平衡,讓我們能夠衡量模型在盡量捕捉少數(shù)類的時(shí)候,誤傷多數(shù)類的情況會(huì)如何變化。橫坐標(biāo)是FPR,代表著模型將多數(shù)類判斷錯(cuò)誤的能力,縱坐標(biāo)Recall,代表著模型捕捉少數(shù)類的能力,所以ROC曲線代表著,隨著Recall的不斷增加,FPR如何增加。我們希望隨著Recall的不斷提升,FPR增加得越慢越好,這說明我們可 以盡量高效地捕捉出少數(shù)類,而不會(huì)將很多地多數(shù)類判斷錯(cuò)誤。所以,我們希望看到的圖像是,縱坐標(biāo)急速上升,?橫坐標(biāo)緩慢增長,也就是在整個(gè)圖像左上方的一條弧線。這代表模型的效果很不錯(cuò),擁有較好的捕獲少數(shù)類的能力。
中間的虛線代表著,當(dāng)recall增加1%,我們的FPR也增加1%,也就是說,我們每捕捉出一個(gè)少數(shù)類,就會(huì)有一個(gè)多數(shù)類被判錯(cuò),這種情況下,模型的效果就不好,這種模型捕獲少數(shù)類的結(jié)果,會(huì)讓許多多數(shù)類被誤傷,從而增加我們的成本。ROC曲線通常都是凸型的。對(duì)于一條凸型ROC曲線來說,曲線越靠近左上角越好,越往下越糟糕,曲線如果在虛線的下方,則證明模型完全無法使用。但是它也有可能是一條凹形的ROC曲線。對(duì)于一條凹型ROC曲線來說,應(yīng)該越靠近右下角越好,凹形曲線代表模型的預(yù)測(cè)結(jié)果與真實(shí)情況完全相反,那也不算非常糟糕,只要我們手動(dòng)將模型的結(jié)果逆轉(zhuǎn),就可以得到一條左上方的弧線了。最糟糕的就是,無論曲線是凹形還是凸型,曲線位于圖像中間,和虛線非常靠近,那我們拿它無能為力。
好了,現(xiàn)在我們有了這條曲線,我們的確知道模型的效果還算是不錯(cuò)了。但依然非常摸棱兩可,有沒有具體的數(shù)字來幫助我們理解ROC曲線和模型的效果呢?的確存在,這個(gè)數(shù)字就叫做AUC面積,它代表了ROC曲線下方的面積,這個(gè)面積越大,代表ROC曲線越接近左上角,模型就越好。AUC面積的計(jì)算比較繁瑣,因此,我們使用sklearn來幫助我們。接下來我們來看看,在sklearn當(dāng)中,如何繪制我們的ROC曲線,找出我們的的AUC面積。
利用ROC曲線找出最佳閾值
在sklearn中,我們有幫助我們計(jì)算ROC曲線的橫坐標(biāo)假正率FPR,縱坐標(biāo)Recall和對(duì)應(yīng)的閾值的類sklearn.metrics.roc_curve。同時(shí)有幫助我們計(jì)算AUC面積的類sklearn.metrics.roc_auc_score。在一些比較老舊的sklearn版本中,我們使用sklearn.metrics.auc這個(gè)類來計(jì)算AUC面積,但這個(gè)類即將在0.22版本中被放棄,因此建議大家都使用roc_auc_score,來看看我們的這兩個(gè)類:
置信度,概率作為y_score都可?
from sklearn.metrics import roc_curveFPR, recall, thresholds = roc_curve(y,clf_proba.decision_function(X), pos_label=1)print(FPR) print(recall) print(thresholds)結(jié)果:
[0. 0. 0.006 0.006 0.008 0.008 0.01 0.01 0.014 0.014 0.018 0.0180.022 0.022 0.024 0.024 0.028 0.028 0.03 0.03 0.032 0.032 0.036 0.0360.04 0.04 0.042 0.042 0.044 0.044 0.05 0.05 0.054 0.054 0.058 0.0580.066 0.066 0.072 0.072 0.074 0.074 0.086 0.086 1. ][0. 0.02 0.02 0.06 0.06 0.16 0.16 0.2 0.2 0.22 0.22 0.36 0.36 0.420.42 0.6 0.6 0.62 0.62 0.64 0.64 0.68 0.68 0.7 0.7 0.74 0.74 0.760.76 0.82 0.82 0.84 0.84 0.86 0.86 0.88 0.88 0.92 0.92 0.94 0.94 0.960.96 1. 1. ][ 3.18236076 2.18236076 1.48676267 1.35964325 1.339208171.14038015 1.13383091 1.00003406 0.85085628 0.844764390.78571364 0.60568093 0.5389064 0.46718521 0.443960460.03907036 -0.07011269 -0.10668727 -0.1258212 -0.13845693-0.14034183 -0.16790648 -0.2040958 -0.22137683 -0.24381463-0.26762451 -0.34446784 -0.3467975 -0.39182241 -0.40676459-0.4589064 -0.46310299 -0.49195707 -0.5088941 -0.53560561-0.55152081 -0.62628865 -0.67580418 -0.78127198 -0.79874442-0.88438995 -0.91257798 -1.01417607 -1.08601917 -10.31959605] from sklearn.metrics import roc_auc_score as AUC area = AUC(y,clf_proba.decision_function(X)) area結(jié)果:
0.9696400000000001 plt.figure() plt.plot(FPR, recall, color='red',label='ROC curve (area = %0.2f)' % area) plt.plot([0, 1], [0, 1], color='black', linestyle='--') plt.xlim([-0.05, 1.05]) plt.ylim([-0.05, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('Recall') plt.title('Receiver operating characteristic example') plt.legend(loc="lower right") plt.show()?結(jié)果:
現(xiàn)在,有了ROC曲線,了解了模型的分類效力,以及面對(duì)樣本不均衡問題時(shí)的效力,那我們?nèi)绾吻蠼馕覀冏罴训拈?/span>值呢?我們想要了解,什么樣的狀況下我們的模型的效果才是最好的。回到我們對(duì)ROC曲線的理解來:ROC曲線反應(yīng)的是recall增加的時(shí)候FPR如何變化,也就是當(dāng)模型捕獲少數(shù)類的能力變強(qiáng)的時(shí)候,會(huì)誤傷多數(shù)類的情況是否嚴(yán)重。我們的希望是,模型在捕獲少數(shù)類的能力變強(qiáng)的時(shí)候,盡量不誤傷多數(shù)類,也就是說,隨著recall的變大,FPR?的大小越小越好。所以我們希望找到的最有點(diǎn),其實(shí)是Recall和FPR差距最大的點(diǎn)。這個(gè)點(diǎn),又叫做約登指數(shù)。
maxindex = (recall - FPR).tolist().index(max(recall - FPR)) print(thresholds[maxindex])#我們可以在圖像上來看看這個(gè)點(diǎn)在哪里 plt.scatter(FPR[maxindex],recall[maxindex],c="black",s=30)#把上述代碼放入這段代碼中: plt.figure() plt.plot(FPR, recall, color='red',label='ROC curve (area = %0.2f)' % area) plt.plot([0, 1], [0, 1], color='black', linestyle='--') plt.scatter(FPR[maxindex],recall[maxindex],c="black",s=30) plt.xlim([-0.05, 1.05]) plt.ylim([-0.05, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('Recall') plt.title('Receiver operating characteristic example') plt.legend(loc="lower right")結(jié)果:
最佳閾值就這樣選取出來了,由于現(xiàn)在我們是使用decision_function來畫ROC曲線,所以我們選擇出來的最佳閾值其實(shí)是最佳距離。如果我們使用的是概率,我們選取的最佳閾值就會(huì)使一個(gè)概率值了。只要我們讓這個(gè)距離/概率以上的點(diǎn),都為正類,讓這個(gè)距離/概率以下的點(diǎn)都為負(fù)類,模型就是最好的:即能夠捕捉出少數(shù)類,又能夠盡量不誤傷多數(shù)類,整體的精確性和對(duì)少數(shù)類的捕捉都得到了保證。
而從找出的最優(yōu)閾值點(diǎn)來看,這個(gè)點(diǎn),其實(shí)是圖像上離左上角最近的點(diǎn),離中間的虛線最遠(yuǎn)的點(diǎn),也是ROC曲線的轉(zhuǎn)折點(diǎn)。如果沒有時(shí)間進(jìn)行計(jì)算,或者橫坐標(biāo)比較清晰的時(shí)候,我們就可以觀察轉(zhuǎn)折點(diǎn)來找到我們的最佳閾值。
到這里為止,SVC的模型評(píng)估指標(biāo)就介紹完畢了。但是,SVC的樣本不均衡問題還可以有很多的探索。另外,我們還可以使用KS曲線,或者收益曲線(pro?t?chart)來選擇我們的閾值,都是和ROC曲線類似的用法。大家若有余力,可以自己深入研究一下。模型評(píng)估指標(biāo),還有很多深?yuàn)W的地方。
總結(jié)
以上是生活随笔為你收集整理的Sklearn(v3)——SVM理论(4)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sklearn(v3)——SVM理论(3
- 下一篇: Sklearn(v3)——朴素贝叶斯(1