邏輯回歸案例分析——信用卡欺詐
本次案例為信用卡欺詐資料,一共包含31個自變量,其中因變量Class表示使用者在交易中是否發生欺詐行為(1表示欺詐交易,0表示正常交易)。由于資料涉及敏感資訊,其中V1~V28自變量做了标準化處理。本次案例涉及到分類問題中類别比例嚴重失調的情況下應該如何應對,當然主要任務是對0-1樣本即正常與異常樣本的區分。
- 類别比例失調如何處理
- 正負樣本的劃分
首先是庫的導入操作:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
data = pd.read_csv("creditcard.csv")
data.head(5)
Time | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | ... | V21 | V22 | V23 | V24 | V25 | V26 | V27 | V28 | Amount | Class |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0.0 | -1.359807 | -0.072781 | 2.536347 | 1.378155 | -0.338321 | 0.462388 | 0.239599 | 0.098698 | 0.363787 | ... | -0.018307 | 0.277838 | -0.110474 | 0.066928 | 0.128539 | -0.189115 | 0.133558 | -0.021053 | 149.62 | |
1 | 0.0 | 1.191857 | 0.266151 | 0.166480 | 0.448154 | 0.060018 | -0.082361 | -0.078803 | 0.085102 | -0.255425 | ... | -0.225775 | -0.638672 | 0.101288 | -0.339846 | 0.167170 | 0.125895 | -0.008983 | 0.014724 | 2.69 |
2 | 1.0 | -1.358354 | -1.340163 | 1.773209 | 0.379780 | -0.503198 | 1.800499 | 0.791461 | 0.247676 | -1.514654 | ... | 0.247998 | 0.771679 | 0.909412 | -0.689281 | -0.327642 | -0.139097 | -0.055353 | -0.059752 | 378.66 |
3 | 1.0 | -0.966272 | -0.185226 | 1.792993 | -0.863291 | -0.010309 | 1.247203 | 0.237609 | 0.377436 | -1.387024 | ... | -0.108300 | 0.005274 | -0.190321 | -1.175575 | 0.647376 | -0.221929 | 0.062723 | 0.061458 | 123.50 |
4 | 2.0 | -1.158233 | 0.877737 | 1.548718 | 0.403034 | -0.407193 | 0.095921 | 0.592941 | -0.270533 | 0.817739 | ... | -0.009431 | 0.798278 | -0.137458 | 0.141267 | -0.206010 | 0.502292 | 0.219422 | 0.215153 | 69.99 |
5 rows × 31 columns
資料集檢測
在本案例中是的資料集是是否有信用卡欺詐的資料集,結合實際情況,應該是正常類占絕大多數,出現信用卡欺詐的類别占少數的,首先我們需要對我們的資料進行檢驗,看是否滿足這樣的特征
pd.value_counts()對DataFrame的某一列中具有多少重複的值進行統計,并對不同的值進行計數。使用的時候需要對特定的列進行指定,比如下面的使用中指定了Class列,并且進行排序。
#使用注釋掉的代碼可以直接輸出列的情況:
# print(data["Class"].value_counts())
count_classes = pd.value_counts(data["Class"], sort = True).sort_index()
count_classes.plot(kind = "bar")
plt.title("Fruad class histogram")
plt.xlabel("Class")
plt.ylabel("Frequancy")
Text(0, 0.5, 'Frequancy')

樣本數量均衡化處理
可以看出正常樣本的數量時明顯多于異常樣本的,即樣本的資料是極度不均衡的,需要進行處理。
面對資料不均衡時我們往往采取兩種解決方法:
- 過采樣:對少數樣本進行資料生成,使少數的樣本變得與多數樣本數量相當
- 下采樣:在多數的樣本中取出和少數樣本規模相同的子樣本作為分類的資料對象,這樣使得兩個樣本同樣的少
後面會針對兩種不同的方法分别進行分析
樣本特征的歸一化處理
此外,在上面的資料中我們也可以發現Amount列的資料的大小浮動是比較大的,有的是幾百,有的資料是個位數,amount和前面的V1-V28這些特征在未說明的情況下對結果産生的影響是相當的,如果不進行處理,機器學習算法的結果可能對較大的數予以較大的權重指派,進入誤區。是以在機器學習中我們要保證特征之間的分布差異處于一個相當的範圍内。
比如我們可以看到前面的V1-V28這些特征,他們大概分布在-1-1這樣的區間内,是以我們最好也要對amount的數值進行歸一化處理。
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()
V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | ... | V21 | V22 | V23 | V24 | V25 | V26 | V27 | V28 | Class | normAmount |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
-1.359807 | -0.072781 | 2.536347 | 1.378155 | -0.338321 | 0.462388 | 0.239599 | 0.098698 | 0.363787 | 0.090794 | ... | -0.018307 | 0.277838 | -0.110474 | 0.066928 | 0.128539 | -0.189115 | 0.133558 | -0.021053 | 0.244964 | |
1 | 1.191857 | 0.266151 | 0.166480 | 0.448154 | 0.060018 | -0.082361 | -0.078803 | 0.085102 | -0.255425 | -0.166974 | ... | -0.225775 | -0.638672 | 0.101288 | -0.339846 | 0.167170 | 0.125895 | -0.008983 | 0.014724 | -0.342475 |
2 | -1.358354 | -1.340163 | 1.773209 | 0.379780 | -0.503198 | 1.800499 | 0.791461 | 0.247676 | -1.514654 | 0.207643 | ... | 0.247998 | 0.771679 | 0.909412 | -0.689281 | -0.327642 | -0.139097 | -0.055353 | -0.059752 | 1.160686 |
3 | -0.966272 | -0.185226 | 1.792993 | -0.863291 | -0.010309 | 1.247203 | 0.237609 | 0.377436 | -1.387024 | -0.054952 | ... | -0.108300 | 0.005274 | -0.190321 | -1.175575 | 0.647376 | -0.221929 | 0.062723 | 0.061458 | 0.140534 |
4 | -1.158233 | 0.877737 | 1.548718 | 0.403034 | -0.407193 | 0.095921 | 0.592941 | -0.270533 | 0.817739 | 0.753074 | ... | -0.009431 | 0.798278 | -0.137458 | 0.141267 | -0.206010 | 0.502292 | 0.219422 | 0.215153 | -0.073403 |
5 rows × 30 columns
下采樣處理
減少多數的樣本以和少數的樣本的數量一樣多
#取出特征列
X = data.iloc[:,data.columns != "Class"]
#取出标簽列
y = data.iloc[:,data.columns == "Class"]
#Number of data points in the minority class
# 得到不正常樣本的索引值和數量
number_records_fraud = len(data[data.Class == 1])
fraud_indices =np.array(data[data.Class == 1].index)
#在正常樣本中進行随機選擇
normal_indices = data[data.Class == 0].index
#基于我們上一步找出來的那些樣本的下标,在這些下标中随機選擇出number_records_fraud數量的下标,并轉換成ndarray對象
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)
# 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,:]
#選取最終的特征列和标簽列
X_undersample = under_sample_data.iloc[:,under_sample_data.columns != "Class"]
y_undersample = under_sample_data.iloc[:,under_sample_data.columns == "Class"]
# 展示下采樣樣本比例
print("正常樣本占下采樣樣本的比例:",len(under_sample_data[under_sample_data.Class == 0]) / len(under_sample_data))
print("異常樣本占下采樣樣本的比例:",len(under_sample_data[under_sample_data.Class == 1]) / len(under_sample_data))
print("下采樣樣本總數:",len(under_sample_data))
正常樣本占下采樣樣本的比例: 0.5
異常樣本占下采樣樣本的比例: 0.5
下采樣樣本總數: 984
訓練集和測試集的劃分以及交叉驗證
訓練集和測試集劃分
經過上面的操作我們已經得到了需要處理的一系列的資料,下面我們要做的就是資料集和測試集的劃分的過程了。
所謂的劃分就是要把樣本分為資料集和測試集兩個部分
- 資料集:用來建立我們的回歸模型
- 測試集:用來驗證建立模型的準确性
注意的是資料集和測試集的選擇要随機且采用同一選擇的算法。
交叉驗證
交叉驗證是指我們要将資料集進行進一步的劃分,這樣進一步的劃分内進行互相組合,記錄結果以更好地選擇模型中的參數。
具體的操作見下面的過程:
原始測試集進行測試
from sklearn.model_selection import train_test_split
#導入原始資料集進行一個切分的操作,這種切分是經過洗牌的切分的
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state = 0)
print("原始訓練集包含樣本數量:", len(X_train))
print("原始測試集包含樣本數量:", len(X_test))
print("原始樣本總數:",len(X_train)+len(X_test))
#導入下采樣資料集進行切分操作
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("下采樣訓練集包含樣本數量:", len(X_train_undersample))
print("下采樣測試集包含樣本數量:", len(X_test_undersample))
print("下采樣樣本總數:",len(X_train_undersample)+len(X_test_undersample))
原始訓練集包含樣本數量: 199364
原始測試集包含樣本數量: 85443
原始樣本總數: 284807
下采樣訓練集包含樣本數量: 688
下采樣測試集包含樣本數量: 296
下采樣樣本總數: 984
模型的評估
召回率:recall
如何評價我們建立模型的好壞,直接的想法是使用精度來判斷。下面我們舉一個例子說明精度判斷的局限性,
有上面的例子可以知道精度評估具有一定的欺騙性,是以這裡我們引入“召回率”:recall的概念,來對我們模型的好壞進行評估
R e c a l l = T P ( T P + F N ) Recall =\frac{ TP}{ (TP + FN)} Recall=(TP+FN)TP
正則化懲罰
假如我們得到了模型A( θ 1 , θ 2 , θ 3 , . . . , θ 10 \theta_1,\theta_2,\theta_3,...,\theta_{10} θ1,θ2,θ3,...,θ10)和模型B( θ 1 , θ 2 , θ 3 , . . . , θ 10 \theta_1,\theta_2,\theta_3,...,\theta_{10} θ1,θ2,θ3,...,θ10)兩個模型,兩個模型經過召回率的評估結果是一樣的,那麼我們在選擇的時候是不是在A和B兩個模型中随意選擇一個就可以了呢?
這裡我們還要考慮A和B得到的參數( θ 1 , θ 2 , θ 3 , . . . , θ 10 \theta_1,\theta_2,\theta_3,...,\theta_{10} θ1,θ2,θ3,...,θ10)的正則化問題,要考慮這組參數的波動程度。一般情況下我們認為波動程度更小的參數組合的模型更好,因為這樣的模型泛化程度更高,能過減小過拟合的現象。
所謂的過拟合是指我們的模型在訓練集上的發揮是很好的,但是在測試集上的效果不是很好,過拟合現象在機器學習的算法中經常出現,需要避免。
對于正則化懲罰的方法這裡介紹一種 L 2 L_2 L2懲罰法。在未引入這個概念之前我們我們對于模型的建立的過程是要經過梯度下降的過程,使損失函數loss達到最小值,而現在我們引入了正交懲罰這個概念,在損失函數loss的基礎上添加了 1 2 ω 2 \frac{1}{2}\omega^2 21ω2,即添加了正則系數。我們現在的目标不是僅僅使損失函數 l o s s loss loss達到最小值了,我們的目标是要經過梯度下降使 l o s s + 1 2 ω 2 loss+\frac{1}{2}\omega^2 loss+21ω2達到最小值,這就是正則化懲罰的過程。
此外還有一種 L 1 L_1 L1正則化懲罰的方式,是加上 ∣ ω ∣ |\omega| ∣ω∣
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折的交叉驗證
fold = KFold(5, shuffle = False)
#懲罰項的不同的權重
c_param_range = [0.01, 0.1, 1, 10, 100]
#展示結果所用的表格,5行2列,來存儲不同的參數進行交叉驗證的召回率
result_table = pd.DataFrame(index = range(len(c_param_range), 2), columns = ["C_parameter","Mean recall score"])
result_table["C_parameter"] = c_param_range
j = 0
# 外層循環:尋找最佳的懲罰項的權重:
for c_param in c_param_range:
print("-------------------------------------------")
print("C parameter:", c_param)
print("-------------------------------------------")
print(" ")
recall_accs = []
#内層循環:進行交叉驗證
for iteration, indices in enumerate(fold.split(x_train_data),start=1):
#iteration:i值,第i次交叉驗證;indices:兩個索引集合,訓練集 = indices[0],驗證集 = indices[1]
#enumerate():用于将一個可比案例的資料對象組合為一個索引序列,将fold和下标組合成一個索引序列
#【建立邏輯回歸模型】,傳入懲罰項權重和懲罰方式,這裡選擇L1懲罰
lr = LogisticRegression(C = c_param, penalty = "l1", solver="liblinear")
#使用訓練集(索引是0)【訓練模型】
lr.fit(x_train_data.iloc[indices[0],:], y_train_data.iloc[indices[0],:].values.ravel())
#建立好模型後用測試集去預測模型結果
y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
#評估分類結果,計算召回率
recall_acc = recall_score(y_train_data.iloc[indices[1],:].values, y_pred_undersample)#計算一次召回率
recall_accs.append(recall_acc)#把五次的召回率的結果和在一起,以便後續求平均值
print("Iteration ", iteration,":召回率=", recall_acc)
#當執行完所有的交叉驗證後,計算每個參數c對應的平均召回率并列印
result_table.loc[j,"Mean recall score"] = np.mean(recall_accs)
j +=1
print("")
print("平均召回率:",np.mean(recall_accs))
print("")
best_c = result_table.loc[result_table["Mean recall score"].astype(float).idxmax()]["C_parameter"]
#列印出最好的結果:
print("*********************************************************************************")
print("效果最好的模型所選的懲罰參數C是:", best_c)
print("*********************************************************************************")
return best_c
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 1 :召回率= 0.9315068493150684
Iteration 2 :召回率= 0.9178082191780822
Iteration 3 :召回率= 1.0
Iteration 4 :召回率= 0.9594594594594594
Iteration 5 :召回率= 0.9545454545454546
平均召回率: 0.9526639964996129
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 1 :召回率= 0.8493150684931506
Iteration 2 :召回率= 0.863013698630137
Iteration 3 :召回率= 0.9491525423728814
Iteration 4 :召回率= 0.9324324324324325
Iteration 5 :召回率= 0.8939393939393939
平均召回率: 0.897570627173599
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 1 :召回率= 0.863013698630137
Iteration 2 :召回率= 0.9041095890410958
Iteration 3 :召回率= 0.9491525423728814
Iteration 4 :召回率= 0.9459459459459459
Iteration 5 :召回率= 0.9090909090909091
平均召回率: 0.9142625370161939
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 1 :召回率= 0.863013698630137
Iteration 2 :召回率= 0.9041095890410958
Iteration 3 :召回率= 0.9661016949152542
Iteration 4 :召回率= 0.9459459459459459
Iteration 5 :召回率= 0.9090909090909091
平均召回率: 0.9176523675246685
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 1 :召回率= 0.863013698630137
Iteration 2 :召回率= 0.9041095890410958
Iteration 3 :召回率= 0.9661016949152542
Iteration 4 :召回率= 0.9459459459459459
Iteration 5 :召回率= 0.9090909090909091
平均召回率: 0.9176523675246685
*********************************************************************************
效果最好的模型所選的懲罰參數C是: 0.01
*********************************************************************************
模型的測試
繪制混淆矩陣
#繪制混淆矩陣函數(cm:計算出的混淆矩陣的值,classes:标簽分類, title:标題,cmp:繪圖樣式)
def plot_confusion_matrix(cm, classes,
title='Confusion matrix',
cmap=plt.cm.Blues):
plt.imshow(cm,cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes)
plt.yticks(tick_marks, classes)
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')
使用下采樣資料集測試下采樣方案
上述的探索已經得出了最佳的懲罰權重值,best_c,這裡直接使用best_c作為懲罰項的權重系數,進行回歸預測,并用混淆矩陣進行結果展示
import itertools
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)
cnf_matrix = confusion_matrix(y_test_undersample, y_pred_undersample)#計算混淆矩陣所需值
np.set_printoptions(precision=2)#設定精度為小數點後兩位
#使用公式進行召回率的計算
print("測試集中的召回率為:",cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
#繪圖
class_names = [0,1]
plt.figure()#畫圖前先設定好畫布
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
測試集中的召回率為: 0.9387755102040817
上面的情況是對下采樣樣本的測試集進行測試的結果,是理想情況下的判斷結果。而實際我們應該是用整個樣本的測試集對模型進行測試,下面我們進行用整個模型的測試集測試,檢驗一下下采樣模型的可靠性:
用原始測試集測試下采樣
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)
# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test,y_pred)
np.set_printoptions(precision=2)
print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
# Plot non-normalized confusion matrix
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
Recall metric in the testing dataset: 0.9115646258503401
根據上述的結果,可以看出在下采樣的處理方法下,在測試集資料量增大的情況下,召回率的結果并沒有太大的變化,但是可以看到正常資料被誤判成異常資料的比例較高,即假陽性太多,需要做出改進。
下采樣方法的改進
①不進行采樣,直接使用原資料集進行模型的建立與預測
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 1 :召回率= 0.4925373134328358
Iteration 2 :召回率= 0.6027397260273972
Iteration 3 :召回率= 0.6833333333333333
Iteration 4 :召回率= 0.5692307692307692
Iteration 5 :召回率= 0.45
平均召回率: 0.5595682284048672
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 1 :召回率= 0.5671641791044776
Iteration 2 :召回率= 0.6164383561643836
Iteration 3 :召回率= 0.6833333333333333
Iteration 4 :召回率= 0.5846153846153846
Iteration 5 :召回率= 0.525
平均召回率: 0.5953102506435158
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 1 :召回率= 0.5522388059701493
Iteration 2 :召回率= 0.6164383561643836
Iteration 3 :召回率= 0.7166666666666667
Iteration 4 :召回率= 0.6153846153846154
Iteration 5 :召回率= 0.5625
平均召回率: 0.612645688837163
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 1 :召回率= 0.5522388059701493
Iteration 2 :召回率= 0.6164383561643836
Iteration 3 :召回率= 0.7333333333333333
Iteration 4 :召回率= 0.6153846153846154
Iteration 5 :召回率= 0.575
平均召回率: 0.6184790221704963
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 1 :召回率= 0.5522388059701493
Iteration 2 :召回率= 0.6164383561643836
Iteration 3 :召回率= 0.7333333333333333
Iteration 4 :召回率= 0.6153846153846154
Iteration 5 :召回率= 0.575
平均召回率: 0.6184790221704963
*********************************************************************************
效果最好的模型所選的懲罰參數C是: 10.0
*********************************************************************************
可以看出由于原始資料嚴重的不平衡,是以召回率較低,由此,對于資料進行下采樣處理,進行平衡化是很有必要的。
下面我們通過混淆矩陣來觀察直接用原資料集處理的效果。
lr = LogisticRegression(C = best_c, penalty = 'l1',solver = "liblinear")
lr.fit(X_train,y_train.values.ravel())
y_pred = lr.predict(X_test.values)
# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test,y_pred)
np.set_printoptions(precision=2)
print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
# Plot non-normalized confusion matrix
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
Recall metric in the testing dataset: 0.6190476190476191
由上述結果可以看出,直接使用原資料集進行模型的訓練,測試結果中的假陽性變少,但是對應的召回率較低,其實很多的異常資料沒有找到,模型仍需改進。
下面使用過采樣的方式對模型進行訓練分析。
法②改變判斷的門檻值的方式
在模型的預設參數中,我們認為機率大于0.5為A類,機率小于0.5為B類。這個0.5被稱為門檻值,但是他是可以手動進行調整的,是以下面我們對模型的門檻值thresh進行人為地設定,再分别進行模型的召回率計算和檢驗
#模組化
lr = LogisticRegression(C = 0.01, 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)#利用predict_prob得到預測結果的機率值
#之前的predict()函數是會直接得到0-1的類别的,現在我們得到的是一個機率值,用這個得到的機率與thresholds作比較
#設定不同的門檻值
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#将機率值轉化,判斷(以0.5為例),大于0.5是異常,小于0.5是正常
plt.subplot(3,3,j) #3*3的子圖, j表示第幾個圖,每張圖對應一個門檻值
j +=1
#計算混淆矩陣所需值
cnf_matrix = confusion_matrix(y_test_undersample,y_test_predictions_high_recall)
np.set_printoptions(precision=2)
print("測試集的召回率:",cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
#繪圖
class_names = [0,1]
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Thresholds >= %s'%i)
測試集的召回率: 1.0
測試集的召回率: 1.0
測試集的召回率: 1.0
測試集的召回率: 0.9727891156462585
測試集的召回率: 0.9387755102040817
測試集的召回率: 0.8775510204081632
測試集的召回率: 0.8163265306122449
測試集的召回率: 0.7755102040816326
測試集的召回率: 0.5918367346938775
從上面的結果可以看出,随着門檻值的增大,因為判斷異常的标準升高,召回率降低。當門檻值為0.1-0.3時,召回率為1,但是将所有的樣本都當成了異常樣本,這樣的模型失去了意義。而當門檻值為0.4時,召回率高但是假陽性太高,不适合。當門檻值為0.6時,對比0.5時,檢漏變多,但是假陽性變少,具體使用哪個門檻值要看需求。當模型的門檻值大于等于0.7時,召回率過低,模型不适合。
下采樣模型具有假陽性高的缺點,下面使用過采樣方案。
過采樣方案
過采樣方案主要是要生成更多的異常樣本,使異常樣本變得和正常樣本一樣多,即将異常樣本規模擴大n倍。
異常樣本的生成主要是使用SMOTE算法:
首先我們要選擇出少數類樣本,選擇出其中的一個樣本,對其他的樣本點進行歐式距離計算,并且從小到大排列,隻取出最小的1/n*樣本數個,然後使用一定的随機數進行填充,其他的樣本也進行,直至完成擴大n倍數。
import pandas as pd
#引入SOMTE算法子產品
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
讀取資料,劃分特征值和标簽
credit_cards=pd.read_csv("creditcard.csv")
columns=credit_cards.columns
features_columns=columns.delete(len(columns)-1)#删除最後一列:Class列
features=credit_cards[features_columns]
labels=credit_cards['Class']
資料集切分,将資料集切分為訓練集和測試集
features_train, features_test, labels_train, labels_test = train_test_split(features,
labels,
test_size=0.2,
random_state=0)
基于SMOTE算法進行樣本的生成,創造新的資料集,這樣正負樣本的數量就一緻了。
oversampler=SMOTE(random_state=0)
#獲得一個對象
os_features,os_labels=oversampler.fit_sample(features_train,labels_train)
#隻對train集進行資料的生成,測試集一定是不動的,會使兩個label的自動平衡
此時我們檢視訓練集的樣本資料,可以發現是1:1的了
count_classes = pd.value_counts(os_labels, sort = True).sort_index()
count_classes.plot(kind = "bar")
plt.title("Fruad class histogram")
plt.xlabel("Class")
plt.ylabel("Frequancy")
Text(0, 0.5, 'Frequancy')
#進行懲罰參數權重的選擇
os_features = pd.DataFrame(os_features)
os_labels = pd.DataFrame(os_labels)
best_c = printing_Kfold_scores(os_features,os_labels)
-------------------------------------------
C parameter: 0.01
-------------------------------------------
Iteration 1 :召回率= 0.8903225806451613
Iteration 2 :召回率= 0.8947368421052632
Iteration 3 :召回率= 0.968861347792409
Iteration 4 :召回率= 0.9578593332673855
Iteration 5 :召回率= 0.958408898561238
平均召回率: 0.9340378004742915
-------------------------------------------
C parameter: 0.1
-------------------------------------------
Iteration 1 :召回率= 0.8903225806451613
Iteration 2 :召回率= 0.8947368421052632
Iteration 3 :召回率= 0.9704769281841319
Iteration 4 :召回率= 0.9599256987722712
Iteration 5 :召回率= 0.9603323770897221
平均召回率: 0.93515888535931
-------------------------------------------
C parameter: 1
-------------------------------------------
Iteration 1 :召回率= 0.8903225806451613
Iteration 2 :召回率= 0.8947368421052632
Iteration 3 :召回率= 0.9705433218988603
Iteration 4 :召回率= 0.960321385783845
Iteration 5 :召回率= 0.954517976280762
平均召回率: 0.9340884213427783
-------------------------------------------
C parameter: 10
-------------------------------------------
Iteration 1 :召回率= 0.8903225806451613
Iteration 2 :召回率= 0.8947368421052632
Iteration 3 :召回率= 0.9705211906606175
Iteration 4 :召回率= 0.957529594091074
Iteration 5 :召回率= 0.9605082379837548
平均召回率: 0.9347236890971743
-------------------------------------------
C parameter: 100
-------------------------------------------
Iteration 1 :召回率= 0.8903225806451613
Iteration 2 :召回率= 0.8947368421052632
Iteration 3 :召回率= 0.9700121721810335
Iteration 4 :召回率= 0.9603433683955991
Iteration 5 :召回率= 0.960761038018927
平均召回率: 0.9352352002691969
*********************************************************************************
效果最好的模型所選的懲罰參數C是: 100.0
*********************************************************************************
#s使用最佳的懲罰系數進行模型的訓練和驗證
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)
print("測試集中的召回率: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
測試集中的召回率: 0.9108910891089109
觀察混淆矩陣可知,在召回率較高的情況下,此時我們的誤判數由原來的8000+降低到了500+,誤判的數量大大降低了,即假陽性降低,故采用過采樣的方案效果更好。