項目目的
通過信用卡的歷史交易數(shù)據(jù)绘趋,利用機(jī)器學(xué)習(xí)算法構(gòu)建信用卡反欺詐預(yù)測模型,提前發(fā)現(xiàn)客戶信用卡被盜刷的事件颗管。
項目介紹
該數(shù)據(jù)集包含由歐洲持卡人于2013年9月使用信用卡進(jìn)行交易的數(shù)據(jù)陷遮。此數(shù)據(jù)集顯示兩天內(nèi)發(fā)生的交易記錄,其中284,807筆交易中有492筆被盜刷垦江。數(shù)據(jù)集非常不平衡帽馋,積極的類(被盜刷)占所有交易的0.172%。因為其中涉及到了隱私的內(nèi)容比吭,數(shù)據(jù)集已經(jīng)進(jìn)行了PCA的處理绽族,將特征數(shù)據(jù)提取出來了,特征V1衩藤,V2吧慢,... V28是使用PCA獲得的主要特征變量。
模型選擇:該項目要解決的問題是預(yù)測持卡人是否會發(fā)生信用卡被盜刷赏表。信用卡持卡人是否會發(fā)生被盜刷只有兩種可能检诗,所以這是一個二分類問題,意味著可以通過二分類相關(guān)的算法來找到具體的解決辦法瓢剿,本項目選用的算法模型是邏輯回歸模型(Logistic Regression)逢慌。
-
數(shù)據(jù)處理:
- 數(shù)據(jù)是結(jié)構(gòu)化數(shù)據(jù) ,不需要做特征抽象间狂。特征V1至V28是經(jīng)過PCA處理攻泼,而特征Time和Amount的數(shù)據(jù)規(guī)格與其他特征差別較大,需要對其做特征縮放前标,將特征縮放至同一個規(guī)格坠韩。在數(shù)據(jù)質(zhì)量方面 距潘,沒有出現(xiàn)亂碼或空字符的數(shù)據(jù)炼列,可以確定字段Class為目標(biāo)列,其他列為特征列音比。
- 該數(shù)據(jù)集非常不平衡俭尖,被盜刷占所有交易的0.172%,需要進(jìn)行平衡樣本處理。
分析思路:
- 一:數(shù)據(jù)預(yù)處理
1:導(dǎo)入數(shù)據(jù)
2:標(biāo)準(zhǔn)化處理
3:樣本不均衡處理- 二:模型訓(xùn)練
1:欠采樣平衡樣本
1.1 數(shù)據(jù)拆分:訓(xùn)練集稽犁、驗證集與測試集
1.2 交叉驗證
1.3 模型評估方法
1.4 正則化懲罰項
1.5 構(gòu)建模型
1.5.1K折交叉驗證-尋找較優(yōu)參數(shù)*
1.5.2使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練焰望,下采樣數(shù)據(jù)進(jìn)行測試*
1.5.3不同閾值對結(jié)果的影響*
1.5.4使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練,原始數(shù)據(jù)進(jìn)行測試*
1.5.5使用原始數(shù)據(jù)進(jìn)行訓(xùn)練與測試*
2:過采樣平衡樣本
2.1 SMOTE算法
2.2 生成過采樣數(shù)據(jù)
2.3 K折交叉驗證獲取較優(yōu)參數(shù)
2.5 使用過采樣數(shù)據(jù)訓(xùn)練和測試模型- 三:總結(jié):過采樣和欠采樣模型對比
項目實現(xiàn)步驟
一:數(shù)據(jù)預(yù)處理
1:導(dǎo)入數(shù)據(jù)
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# 導(dǎo)入數(shù)據(jù)
data = pd.read_csv("creditcard.csv")
data.head()
2:標(biāo)準(zhǔn)化處理
Amount這一列的數(shù)據(jù)已亥,沒有進(jìn)行特征縮放熊赖,所以接下來對其進(jìn)行標(biāo)準(zhǔn)化處理
from sklearn.preprocessing import StandardScaler
data['normAmount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1))
data = data.drop(['Time','Amount'],axis=1)
data.head()
3:樣本不平衡
count_classes = pd.value_counts(data['Class'], sort = True).sort_index() # 查看Class字段值得類別
count_classes.plot(kind = 'bar',rot=0)
plt.xlabel("Class")
plt.ylabel("頻數(shù)")
for a, b in zip(count_classes.index, count_classes.values):
plt.text(a, b,b,ha='center', va='bottom', fontsize=10)
響應(yīng)變量Class的取值中,0是指正常交易虑椎,1是指被盜刷震鹉。通過上面的圖和數(shù)據(jù)可知,存在492例盜刷,這是一個明顯樣本不平衡問題捆姜。
如何平衡樣本传趾?
在進(jìn)行不平衡樣本的數(shù)據(jù)預(yù)處理以前,先來談?wù)劮瞧胶鈽颖镜挠绊懸约俺S玫囊恍┢胶鈽颖镜姆椒嗉迹斑m用場景浆兰。
-
不平衡樣本
不平衡的樣本會影響模型的評估效果,嚴(yán)重的會帶來過擬合或欠擬合的結(jié)果珊豹。所以我們需要讓正負(fù)樣本在訓(xùn)練過程中擁有相同話語權(quán)或權(quán)重簸呈。在這里,稱數(shù)據(jù)集中樣本較多的一類稱為“大眾類”,樣本較少的一類稱為“小眾類”。 -
處理方法
1:下采樣(Undersampling,欠采樣):以小眾類的樣本數(shù)為標(biāo)準(zhǔn)叉寂,在大眾類的樣本中取得樣本數(shù)和小眾類的樣本數(shù)一樣(如讓0和1兩個樣本同樣少)尊剔。
2:上采樣(Oversampling,過采樣):以大眾類的樣本數(shù)為標(biāo)準(zhǔn)叹哭,生成一些樣本使得小眾類的樣本數(shù)和大眾類的樣本數(shù)一樣多(對1號樣本進(jìn)行生成,讓 0 和 1 這兩個樣本一樣多)。
但是這樣簡單處理也會有相應(yīng)的弊端出現(xiàn)吃溅。因為上采樣是復(fù)制多份小眾類,所以上采樣中小眾類會反復(fù)出現(xiàn)一些樣本鸯檬,這會導(dǎo)致過擬合决侈;下采樣是選取部分大眾類,下采樣會由于丟失信息而導(dǎo)致欠擬合喧务。
-
改進(jìn)方法
1:下采樣:下采樣信息丟失的問題赖歌,有EasyEnsemble,與BalanceCascade兩種改進(jìn)方法功茴。
(1)EasyEnsemble:多次下采樣選擇不同的數(shù)據(jù)集庐冯,訓(xùn)練不同的分類器。
(2)BalanceCascade:先通過一次下采樣產(chǎn)生數(shù)據(jù)集坎穿,訓(xùn)練一個分類器展父,然后將那些分類正確的大眾樣本不放回返劲,然后對剩余的大眾樣本采樣產(chǎn)生數(shù)據(jù)集,訓(xùn)練多個分類器栖茉。
2:上采樣:
(1)數(shù)據(jù)合成:SMOTE是最為常見過數(shù)據(jù)合成方法來基于已有的數(shù)據(jù)生成更多的樣本
(2)數(shù)據(jù)加權(quán):其難點(diǎn)在于如何合理設(shè)置權(quán)重篮绿。
3:異常值檢測:
可以換一種角度,對于正負(fù)樣本極不平衡的情況下吕漂,我們也可以視其為異常值檢測或一分類問題亲配。 -
方法選擇
所以解決不平衡樣本的問題有很多種方法,那如何選擇惶凝?
1:在正負(fù)樣本都非常之少的情況下, 采用數(shù)據(jù)合成的方式:
2:在負(fù)樣本足夠多弃榨,正樣本非常之少且比例及其懸殊的情況下, 考慮一分類方法
3:在正負(fù)樣本都足夠多且比例不是特別懸殊的情況下, 應(yīng)該考慮采樣或者加權(quán)的方法
4:同時也有適用于不平衡樣本的模型比如XGBoost 。
二:模型訓(xùn)練
針對樣本不平衡本項目將依次采用欠采樣和過采樣兩種方法進(jìn)行數(shù)據(jù)處理梨睁,并進(jìn)行模型訓(xùn)練鲸睛。
1:欠采樣平衡樣本
X = data.loc[:, data.columns != 'Class'] # 特征變量數(shù)據(jù)集
y = data.loc[:, data.columns == 'Class'] # 響應(yīng)變量數(shù)據(jù)集
number_records_fraud = len(data[data.Class == 1]) # 欺詐記錄的數(shù)量
fraud_indices = np.array(data[data.Class == 1].index) #欺詐記錄的索引
normal_indices = data[data.Class == 0].index # 正常記錄的索引
# 從正常記錄中隨機(jī)抽取與欺詐記錄相同數(shù)量的數(shù)據(jù)
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)
random_normal_indices = np.array(random_normal_indices)
under_sample_indices = np.concatenate([fraud_indices,random_normal_indices]) # 將正常記錄索引和欺詐記錄的索引合并
under_sample_data = data.iloc[under_sample_indices,:] # 下采樣得到的的樣本數(shù)據(jù)
X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class'] # 下采樣的特征變量數(shù)據(jù)集
y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class'] # 下采樣的相應(yīng)變量數(shù)據(jù)集
print("正常交易的數(shù)量占比: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
print("欺詐交易的數(shù)量占比 ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
print("下采樣的交易數(shù)據(jù)數(shù)量: ", len(under_sample_data))
正常交易的數(shù)量占比: 0.5
欺詐交易的數(shù)量占比 0.5
下采樣的交易數(shù)據(jù)數(shù)量: 984
(1)數(shù)據(jù)拆分:訓(xùn)練集、驗證集與測試集
a)訓(xùn)練集:直接參與了模型調(diào)參的過程坡贺,顯然不能用來反映模型真實的能力(防止課本死記硬背的學(xué)生擁有最好的成績官辈,即防止過擬合)
b)驗證集:參與了人工調(diào)參(超參數(shù))的過程,也不能用來最終評判一個模型(刷題庫的學(xué)生不能算是學(xué)習(xí)好的學(xué)生)
c)測試集:所以要通過最終的測試來考察一個模型真正的效果
這里僅僅將數(shù)據(jù)分成了訓(xùn)練集和測試集遍坟,之后介紹交叉驗證拳亿,會把訓(xùn)練集再次分成訓(xùn)練集與測試集。
from sklearn.model_selection import train_test_split
# 將整個樣本數(shù)據(jù)集劃分:訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)
## test_size=0.3愿伴,表示測試集占總樣本數(shù)據(jù)集30%
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)
print("訓(xùn)練數(shù)據(jù)集長度: ", len(X_train))
print("測試數(shù)據(jù)集長度: ", len(X_test))
print("整個數(shù)據(jù)集長度: ", len(X_train)+len(X_test))
# 下采樣數(shù)據(jù)集劃分:訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)
X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample
,y_undersample
,test_size = 0.3
,random_state = 0)
print("")
print("下采樣訓(xùn)練數(shù)據(jù)集長度: ", len(X_train_undersample))
print("下采樣測試數(shù)據(jù)集長度: ", len(X_test_undersample))
print("整個下采樣數(shù)據(jù)集長度: ", len(X_train_undersample)+len(X_test_undersample))
> 訓(xùn)練數(shù)據(jù)集長度: 199364
測試數(shù)據(jù)集長度: 85443
整個數(shù)據(jù)集長度: 284807
>下采樣訓(xùn)練數(shù)據(jù)集長度: 688
下采樣測試數(shù)據(jù)集長度: 296
整個下采樣數(shù)據(jù)集長度: 984
說明:為什么進(jìn)行了下采樣肺魁,還要把原始數(shù)據(jù)進(jìn)行切分呢?對數(shù)據(jù)集的訓(xùn)練是通過下采樣的訓(xùn)練集隔节,對數(shù)據(jù)的測試的是通過原始的數(shù)據(jù)集的測試集鹅经,下采樣的測試集可能沒有原始部分當(dāng)中的一些特征,不能充分進(jìn)行測試怎诫。
(2)交叉驗證
交叉驗證是在機(jī)器學(xué)習(xí)建立模型和驗證模型參數(shù)時常用的辦法瘾晃,就是重復(fù)的使用數(shù)據(jù),把得到的樣本數(shù)據(jù)進(jìn)行切分幻妓,組合為不同的訓(xùn)練集和測試集蹦误,用訓(xùn)練集來訓(xùn)練模型,用測試集來評估模型預(yù)測的好壞肉津。在此基礎(chǔ)上可以得到多組不同的訓(xùn)練集和測試集强胰,某次訓(xùn)練集中的某樣本在下次可能成為測試集中的樣本,即所謂“交叉”妹沙。
交叉驗證方法
1:簡單交叉驗證:首先隨機(jī)將已給數(shù)據(jù)分成兩份偶洋,一份作為訓(xùn)練集,另一份作為測試集(比如: 70%的訓(xùn)練集初烘,30%的測試集)涡真。然后用訓(xùn)練集來訓(xùn)練模型,在測試集上驗證模型及參數(shù)肾筐。接著哆料,我們再把樣本打亂,重新選擇訓(xùn)練集和測試集吗铐,繼續(xù)訓(xùn)練數(shù)據(jù)和檢驗?zāi)P投唷W詈笪覀冞x擇損失函數(shù)評估最優(yōu)的模型和參數(shù)。
2:K折交叉驗證:會把樣本數(shù)據(jù)隨機(jī)的分成S份唬渗,每次隨機(jī)的選擇S-1份作為訓(xùn)練集典阵,剩下的1份做測試集。當(dāng)這一輪完成后镊逝,重新隨機(jī)選擇S-1份來訓(xùn)練數(shù)據(jù)壮啊。若干輪(小于S)之后,選擇損失函數(shù)評估最優(yōu)的模型和參數(shù)撑蒜。
3:留一交叉驗證:它是第二種情況的特例歹啼,此時S等于樣本數(shù)N,這樣對于N個樣本座菠,每次選擇N-1個樣本來訓(xùn)練數(shù)據(jù)狸眼,留一個樣本來驗證模型預(yù)測的好壞。此方法主要用于樣本量非常少的情況浴滴,比如對于普通適中問題拓萌,N小于50時,一般采用留一交叉驗證升略。
對于普通適中問題微王,如果數(shù)據(jù)樣本量小于一萬條,我們就會采用交叉驗證來訓(xùn)練優(yōu)化選擇模型品嚣。 如果樣本大于一萬條的話骂远,我們一般隨機(jī)的把數(shù)據(jù)分成三份,一份為訓(xùn)練集腰根,一份為驗證集激才,最后一份為測試集。用訓(xùn)練集來訓(xùn)練模型额嘿,用驗證集來評估模型預(yù)測的好壞和選擇模型及其對應(yīng)的參數(shù)瘸恼。把最終得到的模型再用于測試集,最終決定使用哪個模型以及對應(yīng)參數(shù)册养。
(3)模型評估方法
混淆矩陣:以分類模型中最簡單的二分類為例东帅,對于這種問題,我們的模型最終需要判斷樣本的結(jié)果是0還是1球拦,或者說是negative還是positive靠闭。
準(zhǔn)確率: 所有預(yù)測結(jié)果中帐我,預(yù)測正確的樣本數(shù)量占比。
1)公式:Acc=(TN+TF) / S愧膀。
2)由于樣本不平衡的問題拦键,導(dǎo)致了得到的高準(zhǔn)確率結(jié)果含有很大的水分。即如果樣本不平衡檩淋,準(zhǔn)確率就會失效芬为。正因為如此,也就衍生出了其它兩種指標(biāo):精準(zhǔn)率和召回率
精確率: 預(yù)測為正中蟀悦,實際為正占比媚朦。
1)公式:PRE=TP / Mt
2)精準(zhǔn)率代表對正樣本結(jié)果中的預(yù)測準(zhǔn)確程度,而準(zhǔn)確率則代表整體的預(yù)測準(zhǔn)確程度日戈,既包括正樣本询张,也包括負(fù)樣本。
召回率: 實際為正中浙炼,預(yù)測為正的占比瑞侮。
1)公式:Recall=TP / Nt
2)召回率越高,代表實際壞用戶被預(yù)測出來的概率越高鼓拧,它的含義類似:寧可錯殺一千半火,絕不放過一個。
漏檢率: 實際為正中季俩,預(yù)測為反的占比钮糖,漏檢率與召回率相反。
1)公式:MR=1-Recall
誤檢率: 預(yù)測為真中實際為負(fù)酌住,占所有負(fù)樣本比例店归。
1)公式:FPR=FP / Nf
F1-score
1)公式:F1-score=2 * 精準(zhǔn)率*召回率/ (精準(zhǔn)率+召回率)
2)我們希望精準(zhǔn)率和召回率同時都非常高,但是精準(zhǔn)率和召回率是兩個向矛盾的指標(biāo)酪我,如果其中一個非常高消痛,另一個肯定會非常低,如當(dāng)召回率很高時都哭,精準(zhǔn)率往往很低秩伞。這需要綜合考慮他們情況,F(xiàn)1-score同時考慮了兩個指標(biāo)欺矫,讓二者同時達(dá)到最高纱新,取一個平衡,當(dāng)F1-score較高時說明模型有效穆趴。
ROC曲線
1)縱坐標(biāo):召回率TPR/Recall
2)橫坐標(biāo):誤檢率FPR
3)ROC曲線也是通過遍歷所有閾值來繪制整條曲線的脸爱。如果不斷的遍歷所有閾值,預(yù)測的正樣本和負(fù)樣本是在不斷變化的未妹,相應(yīng)的精準(zhǔn)率和召回率在ROC曲線圖中也會沿著曲線滑動簿废,但是曲線本身是不會變的空入。判斷一個模型的ROC曲線是好的呢?我們當(dāng)然是希望精準(zhǔn)率越高族檬,誤檢率越低(即ROC曲線越陡峭)那么模型的性能越好歪赢。
AUC:ROC曲線下的面積
1:ROC曲線越陡越好,所以理想值就是1导梆,一個正方形,而最差的隨機(jī)判斷都有0.5迂烁,所以一般AUC的值是介于0.5到1之間的看尼。
2:AUC的一般判斷標(biāo)準(zhǔn)
0.5 - 0.7:效果較低
0.7 - 0.85:效果一般
0.85 - 0.95:效果很好
0.95 - 1:效果非常好,但一般不太可能
(4)正則化懲罰項
正則化是結(jié)構(gòu)風(fēng)險最小化策略的實現(xiàn)盟步,是在經(jīng)驗風(fēng)險上加上一個正則化項或懲罰項藏斩。正則化項一般是負(fù)責(zé)模型復(fù)雜度的單調(diào)遞增函數(shù),模型越復(fù)雜却盘,正則化值就越大狰域。正則化的作用是選擇經(jīng)驗風(fēng)險與模型復(fù)雜度同時較小的模型。正則化符合奧卡姆剃刀原理黄橘,在所有可能選擇的模型中兆览,能夠很好地解釋數(shù)據(jù)并且十分簡單的才是最好的模型。
-
下面損失函數(shù)中塞关,第一項是經(jīng)驗風(fēng)險抬探,第二項為正則化項。正則化項可以取不同的形式帆赢,可以是參數(shù)向量的L2范數(shù)小压,也可以是L1范數(shù)。
L1正則化和L2正則化對比
- L1正則化中可以對|w|求累加和椰于,但是只直接計算絕對值求累加和的話怠益,[1,0,0,0,0]和[0.25,0.25,0.25,0.25,0.25]的結(jié)果是相同的,無法做出區(qū)分瘾婿。
- L2正則化懲罰力度更大蜻牢,對權(quán)重參數(shù)求平方和,目的是讓大的更大偏陪,相對懲罰更多孩饼。
- λ系數(shù),表示正則化懲罰力度竹挡。λ值越大镀娶,表示懲罰力度越大,對模型影響越大揪罕。如果λ值較小梯码,意味著懲罰力度較小宝泵,不會對結(jié)果產(chǎn)生太大影響。
選擇L1正則化的模型還是L2正則化轩娶?
- 如果數(shù)據(jù)集中有很多特征儿奶,而這些特征中并不是每一個都對結(jié)果有重要影響,那么就應(yīng)該選擇L1正則化的模型鳄抒。
- 但如果數(shù)據(jù)集中特征本來就不多闯捎,而且每一個都有很重要的作用,那么就應(yīng)該選擇L2正則化的模型许溅。
(5)構(gòu)建模型
K折交叉驗證——使用下采樣訓(xùn)練數(shù)據(jù)集瓤鼻,訓(xùn)練和測試模型,尋找較優(yōu)模型參數(shù)
- 模型正則化L1參數(shù)分別設(shè)置為[0.01,0.1,1,10,100]贤重;使用K折交叉驗證將下采樣訓(xùn)練數(shù)據(jù)分拆為5份茬祷,逐一遍歷數(shù)據(jù)訓(xùn)練模型,尋找較優(yōu)的L1參數(shù)并蝗。
- 在異常檢測問題中祭犯,精準(zhǔn)率不是衡量模型好壞的指標(biāo),因為我們的目的是找出可能的異常值滚停,所以用召回率衡量模型的好壞沃粗。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report
def printing_Kfold_scores(x_train_data,y_train_data):
#k折交叉驗證
# 會得到一個可迭代對象(可以用 for 循環(huán)遍歷取出),可以遍歷5次键畴,每次遍歷出來的會是一個2值列表陪每,
# 存放每一次的訓(xùn)練集和驗證集的索引
fold = KFold(n_splits=5,shuffle=False)
#不同的C參數(shù)
c_param_range = [0.01,0.1,1,10,100]
results_table = pd.DataFrame(index = range(len(c_param_range)), columns = ['C_parameter','Mean recall score'])
results_table['C_parameter'] = c_param_range
#k折操作將會給出兩個列表:train_indices = indices[0], test_indices = indices[1]
j = 0
for c_param in c_param_range:
print('-------------------------------------------')
print('C parameter: ', c_param)
print('-------------------------------------------')
print('')
recall_accs = [] # 存放召回率
# enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列镰吵,同時列出數(shù)據(jù)和數(shù)據(jù)下標(biāo)檩禾,一般用在 for 循環(huán)當(dāng)中。
for iteration,indices in enumerate(fold.split(x_train_data)):
#把c_param_range代入到邏輯回歸模型中疤祭,并使用了l1正則化
lr = LogisticRegression(C = c_param,penalty = 'l1',solver='liblinear')
#使用indices[0]的數(shù)據(jù)進(jìn)行擬合曲線盼产,
lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel()) #ravel()將多維數(shù)組轉(zhuǎn)換為一維數(shù)組
#在indices[1]數(shù)據(jù)上預(yù)測值
y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
#根據(jù)不同的c_parameter計算召回率
recall_acc = recall_score(y_train_data.iloc[indices[1],:].values,y_pred_undersample)
recall_accs .append(recall_acc)
print('Iteration ', iteration,': recall score = ', recall_acc)
#求出我們想要的召回平均值
results_table.loc[j,'Mean recall score'] = np.mean(recall_accs)
j += 1
print('')
print('Mean recall score ', np.mean(recall_accs))
print('')
best_c = results_table.loc[results_table['Mean recall score'].values.argmax()]['C_parameter']
#最后選擇最好的 C parameter
print('*********************************************************************************')
print('Best model to choose from cross validation is with C parameter = ', best_c)
print('*********************************************************************************')
return best_c
# 下采樣-訓(xùn)練數(shù)據(jù)集-召回率
best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample)
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 0 : recall score = 0.9315068493150684
Iteration 1 : recall score = 0.9178082191780822
Iteration 2 : recall score = 1.0
Iteration 3 : recall score = 0.9864864864864865
Iteration 4 : recall score = 0.9696969696969697
Mean recall score 0.9610997049353213
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 0 : recall score = 0.8493150684931506
Iteration 1 : recall score = 0.863013698630137
Iteration 2 : recall score = 0.9322033898305084
Iteration 3 : recall score = 0.9459459459459459
Iteration 4 : recall score = 0.8939393939393939
Mean recall score 0.8968834993678272
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 0 : recall score = 0.863013698630137
Iteration 1 : recall score = 0.8904109589041096
Iteration 2 : recall score = 0.9830508474576272
Iteration 3 : recall score = 0.9459459459459459
Iteration 4 : recall score = 0.9090909090909091
Mean recall score 0.9183024720057457
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 0 : recall score = 0.863013698630137
Iteration 1 : recall score = 0.8904109589041096
Iteration 2 : recall score = 0.9830508474576272
Iteration 3 : recall score = 0.9459459459459459
Iteration 4 : recall score = 0.9242424242424242
Mean recall score 0.9213327750360488
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 0 : recall score = 0.863013698630137
Iteration 1 : recall score = 0.8904109589041096
Iteration 2 : recall score = 0.9830508474576272
Iteration 3 : recall score = 0.9459459459459459
Iteration 4 : recall score = 0.9242424242424242
Mean recall score 0.9213327750360488
*********************************************************************************
Best model to choose from cross validation is with C parameter = 0.01
*********************************************************************************
- 上述結(jié)果可以得知:當(dāng)參數(shù)C為0.01時,模型的平均召回率最高勺馆,即模型的預(yù)測效果最好戏售。同時,我們可以發(fā)現(xiàn)草穆,對于相同的參數(shù)值灌灾,用交叉驗證每次迭代的召回率相差較大,這個現(xiàn)象說明交叉驗證是非常必要的悲柱,交叉驗證可以盡量減少訓(xùn)練數(shù)據(jù)對結(jié)果的影響锋喜。
繪制混淆矩陣
import itertools
def plot_confusion_matrix(cm,classes,title='Confusion matrix',cmap=plt.cm.Blues):
'''這個方法用來輸出和畫出混淆矩陣的'''
#cm為混淆矩陣數(shù)據(jù),interpolation='nearest'使用最近鄰插值,cmap顏色圖譜(colormap), 默認(rèn)繪制為RGB(A)顏色空間
plt.imshow(cm,interpolation='nearest',cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
#xticks(刻度下標(biāo)嘿般,刻度標(biāo)簽)
plt.xticks(tick_marks, classes, rotation=0)
plt.yticks(classes)
plt.ylim([1.5,-0.5])
#text()命令可以在任意的位置添加文字
thresh = cm.max() / 2
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, cm[i, j],
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.tight_layout() #自動緊湊布局
plt.ylabel('True label')
plt.xlabel('Predicted label')
計算模型指標(biāo)
# 計算模型指標(biāo)
from sklearn.metrics import f1_score,precision_score,recall_score,roc_auc_score,accuracy_score,roc_curve
def metrics_score(y_test,y_pred):
Recall=recall_score(y_test,y_pred) # 召回率
Pre=precision_score(y_test,y_pred) # 精準(zhǔn)率
F1_score=f1_score(y_test,y_pred) # F1_score
AUC=roc_auc_score(y_test,y_pred) # roc面積
return Recall,Pre,F1_score,AUC
使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練段标,下采樣數(shù)據(jù)進(jìn)行測試
lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred_undersample = lr.predict(X_test_undersample.values)
# 計算混淆矩陣:使用下采樣測試數(shù)據(jù)
cnf_matrix = confusion_matrix(y_test_undersample,y_pred_undersample)
np.set_printoptions(precision=2) #輸出精度為小數(shù)點(diǎn)后兩位
#畫出非標(biāo)準(zhǔn)化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix,classes=class_names,title='Confusion matrix')
plt.show()
# 計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(y_test_undersample,y_pred_undersample)
print(' 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# ROC曲線圖
y_pred_proba=lr.predict_proba(X_test_undersample.values)
lr_fpr0,lr_tpr0,lr_threasholds0=roc_curve(y_test_undersample,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
召回率:93.20% | 精準(zhǔn)率:92.57% | F1_score:0.93 | AUC:0.93
- 使用下采樣數(shù)據(jù)訓(xùn)練和測試模型,得到的模型召回率為93.19%,精確率為91.33%炉奴,看起來效果好像非常好逼庞;但這只是對下采樣數(shù)據(jù)的測試集的預(yù)測效果,我們應(yīng)該用原始數(shù)據(jù)的測試集來衡量這個模型的優(yōu)劣瞻赶。
不同閾值對結(jié)果的影響
- 使用下采樣數(shù)據(jù)來訓(xùn)練模型并作出不同閾值條件下下采樣數(shù)據(jù)的測試集的混淆矩陣赛糟,以便觀察閾值對模型的影響,從而選擇合適的閾值獲得最優(yōu)模型.
lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred_undersample_proba = lr.predict_proba(X_test_undersample.values)
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
plt.figure(figsize=(10,10))
j = 1
for i in thresholds:
y_test_predictions_high_recall = y_pred_undersample_proba[:,1] > i
plt.subplot(3,3,j)
j += 1
#計算混淆矩陣
cnf_matrix = confusion_matrix(y_test_undersample,y_test_predictions_high_recall)
#輸出精度為小數(shù)點(diǎn)后兩位
np.set_printoptions(precision=2)
#畫出非標(biāo)準(zhǔn)化的混淆矩陣
class_names = [0,1]
plot_confusion_matrix(cnf_matrix,classes=class_names,title='Threshold >= %s'%i)
# 計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(y_test_undersample,y_test_predictions_high_recall)
print('閾值:{} | 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} AUC{:.2f}'.format(i,Recall,Pre,F1_score,AUC))
閾值:0.1 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.2 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.3 | 召回率:100.00% | 精準(zhǔn)率:49.66% | F1_score:0.66 | AUC:0.50
閾值:0.4 | 召回率:96.60% | 精準(zhǔn)率:61.74% | F1_score:0.75 | AUC:0.69
閾值:0.5 | 召回率:93.20% | 精準(zhǔn)率:92.57% | F1_score:0.93 | AUC:0.93
閾值:0.6 | 召回率:87.07% | 精準(zhǔn)率:97.71% | F1_score:0.92 | AUC:0.93
閾值:0.7 | 召回率:81.63% | 精準(zhǔn)率:99.17% | F1_score:0.90 | AUC:0.90
閾值:0.8 | 召回率:76.19% | 精準(zhǔn)率:100.00% | F1_score:0.86 | AUC:0.88
閾值:0.9 | 召回率:57.82% | 精準(zhǔn)率:100.00% | F1_score:0.73 | AUC:0.79
- 由上可知砸逊,閾值的設(shè)置極大的影響了模型的泛化效果璧南,隨著閾值的改變,模型的召回率和精確率都會隨之變化痹兜;當(dāng)閾值設(shè)置為0.5和0.6時穆咐,得到的AUC相同颤诀,但綜合考慮召回率和F1_score指標(biāo)字旭,應(yīng)該選擇閾值為0.5。
使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練崖叫,原始數(shù)據(jù)進(jìn)行測試
lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred = lr.predict(X_test.values)
#計算混淆矩陣
cnf_matrix = confusion_matrix(y_test,y_pred)
#輸出精度為小數(shù)點(diǎn)后兩位
np.set_printoptions(precision=2)
np.set_printoptions(precision=2)
Recall=cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]) # 召回率
Pre=cnf_matrix[1,1]/(cnf_matrix[0,1]+cnf_matrix[1,1]) #精準(zhǔn)率
F1_score=2*Recall*Pre/(Recall+Pre) # F1_score
print('召回率:{:.2%}'.format(cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1])), '|',
'精確率:{:.2%}'.format(cnf_matrix[1,1]/(cnf_matrix[0,1]+cnf_matrix[1,1])),'|',
'F1_score:{:.2%}'.format(F1_score))
#畫出非標(biāo)準(zhǔn)化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix,classes=class_names,title='Confusion matrix')
plt.show()
召回率:91.16% | 精準(zhǔn)率:1.41% | F1_score:0.03 | AUC:0.90
- 使用下采樣數(shù)據(jù)進(jìn)行訓(xùn)練遗淳,原始數(shù)據(jù)進(jìn)行測試,得到的召回率:92.52% ,精確率:1.40%心傀。雖然召回率很高屈暗,但模型出現(xiàn)了錯將9573個正常值誤判為異常值的情況,這說明該模型具有較大的誤殺率脂男,導(dǎo)致精確率很低养叛,這是下采樣方法的缺陷。
使用原始數(shù)據(jù)進(jìn)行訓(xùn)練與測試
- 對原始數(shù)據(jù)進(jìn)行K折交叉驗證
best_c2 = printing_Kfold_scores(X_train,y_train)
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 0 : recall score = 0.4925373134328358
Iteration 1 : recall score = 0.6027397260273972
Iteration 2 : recall score = 0.6833333333333333
Iteration 3 : recall score = 0.5692307692307692
Iteration 4 : recall score = 0.45
Mean recall score 0.5595682284048672
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 0 : recall score = 0.5671641791044776
Iteration 1 : recall score = 0.6164383561643836
Iteration 2 : recall score = 0.6833333333333333
Iteration 3 : recall score = 0.5846153846153846
Iteration 4 : recall score = 0.525
Mean recall score 0.5953102506435158
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 0 : recall score = 0.5522388059701493
Iteration 1 : recall score = 0.6164383561643836
Iteration 2 : recall score = 0.7166666666666667
Iteration 3 : recall score = 0.6153846153846154
Iteration 4 : recall score = 0.5625
Mean recall score 0.612645688837163
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 0 : recall score = 0.5522388059701493
Iteration 1 : recall score = 0.6164383561643836
Iteration 2 : recall score = 0.7333333333333333
Iteration 3 : recall score = 0.6153846153846154
Iteration 4 : recall score = 0.575
Mean recall score 0.6184790221704963
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 0 : recall score = 0.5522388059701493
Iteration 1 : recall score = 0.6164383561643836
Iteration 2 : recall score = 0.7333333333333333
Iteration 3 : recall score = 0.6153846153846154
Iteration 4 : recall score = 0.575
Mean recall score 0.6184790221704963
*********************************************************************************
Best model to choose from cross validation is with C parameter = 10.0
*********************************************************************************
- 構(gòu)建模型
lr = LogisticRegression(C = best_c2, penalty = 'l1',solver='liblinear')
lr.fit(X_train,y_train.values.ravel())
y_pred_undersample = lr.predict(X_test.values)
- 畫混淆矩陣圖
#計算混淆矩陣
cnf_matrix = confusion_matrix(y_test,y_pred_undersample)
np.set_printoptions(precision=2) #輸出精度為小數(shù)點(diǎn)后兩位
# 畫混淆矩陣圖
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
#計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(y_test,y_pred_undersample)
print(' 召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# 畫ROC曲線
y_pred_proba=lr.predict_proba(X_test.values)
lr_fpr,lr_tpr,lr_threasholds=roc_curve(y_test,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
召回率:61.90% | 精準(zhǔn)率:88.35% | F1_score:0.73 | AUC:0.81
- 單純使用未處理過的原始數(shù)據(jù)訓(xùn)練和測試模型宰翅,得到的模型召回率僅為61.90%,精確率為88.35%弃甥,并沒有下采樣模型效果好。
2:過采樣平衡樣本
SMOTE算法
- 由于隨機(jī)采樣采取簡單素質(zhì)樣本的策略來增加少數(shù)類樣本汁讼,這樣容易產(chǎn)生模型過擬合的問題淆攻。
- SMOTE算法它是基于采樣算法的一種改進(jìn)方案。用SMOTE算法生成小眾類樣本嘿架,使得大眾類樣本和小眾類樣本數(shù)量相同瓶珊,用生成后的數(shù)據(jù)來做模型訓(xùn)練和測試。
算法流程:
-
設(shè)一個少數(shù)類樣本數(shù)為T耸彪,那么SMOTE算法將為這個少數(shù)類合成NT個新樣本伞芹。這里要求N必須是正整數(shù),如果給定的N<1蝉娜,那么算法認(rèn)為少數(shù)類的樣本數(shù)T=NT丑瞧,并將強(qiáng)制N=1柑土。考慮該少數(shù)類的一個樣本i绊汹,其特征向量為Xi,i∈{1,…,T}
- Step1:首先從該少數(shù)類的全部T個樣本中找到樣本Xi的k個近鄰(例如歐式距離)稽屏,記為:xi(near),near∈{1,…,k}。
- Step2:然后從這k個近鄰中隨機(jī)選擇一個樣本Xi(nn)西乖,再生成一個0到1之間的隨機(jī)數(shù)random狐榔,從而合成一個新樣本Xi1:Xi1=Xi+random*(Xi(nn)-Xi);
- Step3:將步驟2重復(fù)進(jìn)行N次获雕,從而可以合成N個新樣本: Xinew,new∈{1,…,k}薄腻。
-
那么,對全部的T個少數(shù)類樣本進(jìn)行上述操作届案,便可為該少數(shù)類合成NT個新樣本庵楷。如果樣本的特征維數(shù)是2維,那么每個樣本都可以用二維平面的一個點(diǎn)來表示楣颠。SMOTE算法所合成出的一個新樣本xi1相當(dāng)于是表示樣本xi的點(diǎn)和表示樣本xi(nn)的點(diǎn)之間所連線段上的一個點(diǎn)尽纽,所以說該算法是基于“差值”來合成新樣本。
# 導(dǎo)入相關(guān)庫
import pandas as pd
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier # 隨機(jī)森林分類器
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
SMOTE算法生成過采樣數(shù)據(jù)
#導(dǎo)入數(shù)據(jù)
credit_cards=pd.read_csv('creditcard.csv')
columns=credit_cards.columns
features_columns=columns.delete(len(columns)-1) # 為了獲得特征列童漩,移除最后一列標(biāo)簽列
features = credit_cards[features_columns] # 特征數(shù)據(jù)
labels=credit_cards['Class'] #響應(yīng)變量
# 劃分訓(xùn)練集合測試集
features_train, features_test, labels_train, labels_test = train_test_split(features, labels, test_size=0.2, random_state=0)
# 過采樣
oversampler = SMOTE(random_state=0)
os_features,os_labels = oversampler.fit_sample(features_train,labels_train)
print('過采樣后 1 的樣本的個數(shù)為:',len(os_labels[os_labels==1]))
過采樣后 1 的樣本的個數(shù)為: 227454
K折交叉驗證獲取較優(yōu)參數(shù)L1
os_features = pd.DataFrame(os_features) # 特征數(shù)據(jù)
os_labels = pd.DataFrame(os_labels) # 響應(yīng)變量
best_c = printing_Kfold_scores(os_features,os_labels)
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 0 : recall score = 0.8903225806451613
Iteration 1 : recall score = 0.8947368421052632
Iteration 2 : recall score = 0.9687506916011951
Iteration 3 : recall score = 0.9555181851155736
Iteration 4 : recall score = 0.9584308811729921
Mean recall score 0.9335518361280369
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 0 : recall score = 0.8903225806451613
Iteration 1 : recall score = 0.8947368421052632
Iteration 2 : recall score = 0.9704105344694036
Iteration 3 : recall score = 0.9559688286565327
Iteration 4 : recall score = 0.9528912630109583
Mean recall score 0.9328660097774637
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 0 : recall score = 0.8903225806451613
Iteration 1 : recall score = 0.8947368421052632
Iteration 2 : recall score = 0.9698351222750913
Iteration 3 : recall score = 0.9598707422428859
Iteration 4 : recall score = 0.9605082379837548
Mean recall score 0.9350547050504312
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 0 : recall score = 0.8903225806451613
Iteration 1 : recall score = 0.8947368421052632
Iteration 2 : recall score = 0.9705433218988603
Iteration 3 : recall score = 0.9597718204899924
Iteration 4 : recall score = 0.9608819423835746
Mean recall score 0.9352513015045704
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 0 : recall score = 0.8903225806451613
Iteration 1 : recall score = 0.8947368421052632
Iteration 2 : recall score = 0.9705433218988603
Iteration 3 : recall score = 0.9603543597014761
Iteration 4 : recall score = 0.9602884118662138
Mean recall score 0.9352491032433949
*********************************************************************************
Best model to choose from cross validation is with C parameter = 10.0
*********************************************************************************
使用過采樣數(shù)據(jù)訓(xùn)練和測試模型
lr = LogisticRegression(C = best_c, penalty = 'l1',solver='liblinear')
lr.fit(os_features,os_labels.values.ravel())
y_pred = lr.predict(features_test.values)
# 計算混淆矩陣
cnf_matrix = confusion_matrix(labels_test,y_pred)
np.set_printoptions(precision=2)
# 畫出非規(guī)范化的混淆矩陣
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
#計算指標(biāo)得分
Recall,Pre,F1_score,AUC=metrics_score(labels_test,y_pred)
print('召回率:{:.2%} | 精準(zhǔn)率:{:.2%} | F1_score:{:.2f} | AUC:{:.2f}'.format(Recall,Pre,F1_score,AUC))
# 畫出ROC曲線圖
y_pred_proba=lr.predict_proba(features_test.values)
lr_fpr,lr_tpr,lr_threasholds=roc_curve(labels_test,y_pred_proba[:,1]) # 計算ROC的值,lr_threasholds為閾值
plt.title("ROC曲線(AUC={:.2f})".format(AUC))
plt.xlabel('誤檢率')
plt.ylabel('召回率')
plt.plot(lr_fpr,lr_tpr)
plt.show()
召回率:91.09% | 精準(zhǔn)率:14.63% | F1_score:0.25 | AUC:0.95
總結(jié):過采樣和欠采樣模型對比
- 使用欠采樣數(shù)據(jù)訓(xùn)練出的模型弄贿,召回率:91.16% ,精準(zhǔn)率:1.41% 矫膨,誤殺數(shù):9369差凹,F(xiàn)1_score:0.03 , AUC:0.90
使用過采樣數(shù)據(jù)訓(xùn)練出的模型侧馅,召回率:91.09% 危尿,精準(zhǔn)率:14.63% ,誤殺數(shù):537馁痴,F(xiàn)1_score:0.25 谊娇,AUC:0.95 - 通過對比兩個模型得分,在召回率相差不大的情況下弥搞,使用過采樣數(shù)據(jù)訓(xùn)練出的模型邮绿,誤殺數(shù)量明顯更少,模型精準(zhǔn)率更高攀例,所以使用過采樣處理數(shù)據(jù)的方法更好船逮。
- 另外不同的懲罰力度c和不同的閾值會對模型的泛化能力有很大影響漠畜,所以需要反復(fù)調(diào)試以得到最合適的參數(shù)匕累,從而獲得最優(yōu)模型。