項目名稱:金融反欺詐(信用卡盜刷)
項目概述:本項目通過利用信用卡的歷史交易數據進行機器學習,構建信用卡反欺詐預測模型,提前發現客戶信用卡被盜刷的事件。
項目背景:數據包含了由歐洲持卡人於2013年9月使用信用卡進行交易的數據。此數據集顯示兩天內發生的交易,其中284807筆交易中有492筆被盜刷。
數據集非常不均衡,積極的類(被盜刷)占所有交易的0.172%。
它只包含作為PCA轉換結果的數字輸入變量,不幸的是,由於保密問題,我們無法提供有關數據的原始功能和更多背景信息。特征V1,
V2,...V28是使用PCA獲得的主要組件,沒有用PCA轉換的特征是“時間”和“金額”。特征“時間”包含數據集中每個事務和第一個事務
之間經過的秒數。特征“金額”是交易金額,此特征可用於實例依賴的成本認知學習。特征“類”是響應變量(目標值),如果發生被盜刷,
則取值1,否則為0。
建模思路:

數據鏈接:鏈接:https://pan.baidu.com/s/1iDUderFhwM8YMJSgp2DrVg 密碼:cglg
場景解析(算法選擇)

0. 導包
1 import numpy as np 2 import pandas as pd 3 import matplotlib.pyplot as plt 4 import seaborn as sns 5 sns.set_style('whitegrid') 6 %matplotlib inline 7 8 import missingno as miss 9 from sklearn.linear_model import LogisticRegression 10 from sklearn.ensemble import GradientBoostingClassifier # 梯度提升樹(用於特征重要性排序) 11 from sklearn.preprocessing import StandardScaler # 數據標准化 12 from imblearn.over_sampling import SMOTE # 過采樣(解決樣本不均衡問題) 13 from sklearn.metrics import roc_curve,auc # ROC-AUC曲線 14 from sklearn.metrics import confusion_matrix # 混淆矩陣 15 from sklearn.metrics import recall_score 16 from sklearn.metrics import accuracy_score 17 from sklearn.model_selection import GridSearchCV # 模型優化 18 from sklearn.model_selection import train_test_split 19 import itertools 20 21 import warnings 22 warnings.filterwarnings('ignore') 23 24 from pylab import mpl # 用於顯示中文 25 mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體 26 mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
1. 數據載入
1 data = pd.read_csv('./creditcard.csv') 2 3 data1 = data.copy()
2. 查看數據概況
1 display(data1.shape,data1.tail(),data1.info(),data1.describe())

1 miss.matrix(data1) # 查看數據缺失情況

可以看到數據很干凈,很完整。
總結如下:
1. 數據為格式化數據,無需做特征抽象;
2. 沒有空值和異常值;
3. Time 和 Amount 兩個特征需要做特征縮放。
4. 特征工程
4.1目標變量可視化
1 # 目標變量可視化 2 fig,ax=plt.subplots(1,2,figsize=(12,8)) 3 sns.set(style='darkgrid') 4 5 sns.countplot(x='Class',data=data1,ax=ax[0]) 6 ax[0].set_title("目標變量中每類的頻數分布直方圖") 7 # autopct='%1.2f%%' --- 長度為1,保留百分號前面的2個小數點 8 data1['Class'].value_counts().plot(kind='pie',ax=ax[1],fontsize=23,autopct='%1.2f%%') 9 ax[1].set_title("目標變量中的每類頻率分布餅圖")

data1['Class'].value_counts() ''' 0 284315 1 492 Name: Class, dtype: int64 '''
通過上面的圖和數據可知,存在492例盜刷,占總樣本的0.17%,由此可知,這是一個明顯的數據類別不平衡問題,稍后采用過采樣(增加數據) 的方法
對這種問題進行處理。
4.2 特征轉換(特征縮放)
1 # Time 和 Amount兩個特征做特征縮放 2 std = StandardScaler() 3 4 # Amount 標准化 5 data1['Amount'] = std.fit_transform(data1[['Amount']]) 6 7 # Time 將單位變為小時 8 data1['Time'] = data1['Time'].map(lambda x : x//3600) 9 # Time 標准化 10 data1['Time'] = std.fit_transform(data1[['Time']]) 11 12 data1.tail()


4.3 特征選擇
1 # 篩選條件 2 cond_0 = data1['Class'] == 0 3 cond_1 = data1['Class'] == 1 4 5 # 根據條件繪制各特征的直方圖(可以觀察類別區分是否明顯,重合度較大的特征可以選擇刪除) 6 plt.figure(figsize=(9,28*6)) 7 8 for i in range(1,29): 9 ax = plt.subplot(28,1,i) 10 11 data['V%d'%i][cond_0].plot(kind='hist',bins=500,density=True,ax=ax) 12 data['V%d'%i][cond_1].plot(kind='hist',bins=50,density=True,ax=ax) 13 14 ax.set_title('V%d'%i)

上圖是不同特征在信用卡正常和信用卡被盜刷的不同分布情況,我們選擇在不同信用卡狀態下的分布有明顯區別的特征,重合度較高的特征選擇剔除。
1 # 觀察上圖發現特征V5,V6,V13,V15,V19,V20,V22,V23,V24,V25,V26,V27,V28類別重合度較高,剔除這幾個特征 2 droplabels = ['V5','V6','V13','V15','V19','V20','V22','V23','V24','V25','V26','V27','V28'] 3 4 data1.drop(droplabels,axis=1,inplace=True)
4.4 對特征重要性進行排序,進一步減少特征(這里用的是梯度提升樹作為基模型)
1 gdbt = GradientBoostingClassifier() 2 3 # 數據 4 X = data1.iloc[:,:-1] 5 # 目標值 6 y = data1['Class'] 7 8 # 訓練 9 gdbt.fit(X,y) 10 11 12 argsort = gdbt.feature_importances_.argsort()[::-1] 13 argsort
# 繪圖查看特征重要性排名 plt.figure(figsize=(9,6)) plt.bar(np.arange(17),gdbt.feature_importances_[argsort]) _ = plt.xticks(np.arange(17),X.columns[argsort])

計算特征兩兩之間的相關性
1 # Compute pairwise correlation of columns 計算特征兩兩之間的相關性 2 corr = data1.corr().loc[['Class']] # loc[['Class']] --- 將 series 轉換為 dataframe 3 corr

結合上圖以及特征之間的相關性,剔除 Time,V8,V21,Amount幾個特征
1 droplabels = ['Time','V8','V21','Amount'] 2 3 data1.drop(droplabels,axis=1,inplace=True) 4 5 data1.tail()

5. 模型訓練
處理樣本不均衡問題:
目標變量“Class”正常和被盜刷兩種類別的數量差別較大,會對模型學習造成困擾。舉例來說,假如有100個樣本,其中只有1個是被盜刷樣本, 其余99個全為正常樣本,那么模型只要制定一個簡單的方法,即判斷所有樣本均為正常樣本,就能輕松達到99%的准確率。而這個分類器的 決策對我們的風險控制毫無意義。因此,在將數據帶入模型訓練之前,我們必須先解決樣本不均衡的問題。
現對該業務場景總結如下:
1. 過采樣(Oversampling):增加正樣本使得正負樣本數量接近,然后再進行學習; 2. 欠采樣(Undersampling):去除一些負樣本使得正負樣本數量接近,然后再進行學習。 本次采用過采樣,具體操作:使用SMOTE(Synthetic Minority Oversampling Technique)。
5.1 解決樣本不均衡問題(SMOTE過采樣)
1 smote = SMOTE() 2 3 X_smote,y_smote = smote.fit_resample(X,y) 4 5 display(X.shape,X_smote.shape) 6 7 ''' 8 (284807, 17) 9 (568630, 17) 10 '''
1 (y_smote == 0).sum() 2 3 ''' 4 284315 5 ''' 6 7 (y_smote == 1).sum() 8 9 ''' 10 284315 11 '''
1 fig,ax=plt.subplots(1,2,figsize=(12,8)) 2 sns.set(style="darkgrid") 3 4 data1["Class"].value_counts().plot(kind="pie",ax=ax[0],fontsize=23,autopct='%1.2f%%') 5 ax[0].set_title("SMOTE采樣之前的頻率分布餅圖") 6 pd.Series(y_smote).value_counts().plot(kind="pie",ax=ax[1],fontsize=23,autopct='%1.2f%%')#長度為1,保留百分號前面的2個小數點 7 ax[1].set_title("SMOTE采樣之后的頻率分布餅圖") 8 ax[1].set_ylabel("Class") 9 plt.savefig("./smote.jpg")
5.2 自定義可視化函數
1 # for 循環 2 import itertools 3 # 畫圖方法 4 # 繪制真實值和預測值對比情況 5 def plot_confusion_matrix(cm, classes, 6 title='Confusion matrix', 7 cmap=plt.cm.Blues): 8 """ 9 This function prints and plots the confusion matrix. 10 """ 11 plt.imshow(cm, interpolation='nearest', cmap=cmap) 12 plt.title(title) 13 # plt.colorbar(shrink = 0.5) 14 tick_marks = np.arange(len(classes)) 15 plt.xticks(tick_marks, classes, rotation=0) 16 plt.yticks(tick_marks, classes) 17 18 threshold = cm.max() / 2. 19 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): 20 plt.text(j, i, cm[i, j], 21 horizontalalignment="center", 22 color="white" if cm[i, j] > threshold else "black")#若對應格子上面的數量不超過閾值則,上面的字體為白色,為了方便查看 23 24 plt.tight_layout() 25 plt.ylabel('True label') 26 plt.xlabel('Predicted label')
5.3 建模訓練預測
1 X_train,X_test,y_train,y_test = train_test_split(X_smote,y_smote,test_size=0.2) # 數據分割 2 3 lg = LogisticRegression() # 建模(邏輯回歸) 4 5 lg.fit(X_train,y_train) # 訓練 6 7 lg.score(X_test,y_test) # 預測
0.9412535392082725
lg.predict_proba(X_test) # 二分類則得到[(px1,px2)] 分別表示預測為0的概率和預測為1的概率

1 y_ = lg.predict(X_test) 2 y_ 3 4 5 array([1, 0, 0, ..., 0, 1, 0])
求得查全率 Recall rate
1 # 混淆矩陣,使用自定義繪圖函數繪圖 2 cm = confusion_matrix(y_test,y_) # 生成混淆矩陣 3 np.set_printoptions(precision=2)#精確到兩位小數點 4 5 # 繪圖 6 class_names = [0,1] 7 plt.figure(figsize=(6,4)) 8 plt.subplot(1,1,1) 9 plot_confusion_matrix(cm,classes=class_names, 10 title='logit_Confusion matrix,recall is {:.4f}'.format(cm[1,1]/cm[1].sum())) 11 12 plt.savefig('./邏輯回歸.jpg',dpi=600)

6. 模型優化與評估
6.1.1 利用GridSearchCV進行交叉驗證和模型參數自動調優
1 lg = LogisticRegression() 2 3 clf = GridSearchCV(lg,param_grid={'C':[0.1,0.2,0.4,0.6,1.0,10,100]}) 4 5 clf.fit(X_train,y_train) 6 7 display(clf.best_score_,clf.best_params_) 8 9 10 ''' 11 0.9401236290922194 12 {'C': 100} 13 '''
1 prob_ = clf.predict_proba(X_test)[:,-1]
6.1.2 結果可視化,對比邏輯回歸和GridSearchCV結果
1 # GridSearchCV 2 y_clf = clf.predict(X_test) 3 cm_clf = confusion_matrix(y_test,y_clf) 4 plot_confusion_matrix(cm_clf,classes=[0,1])

1 # 邏輯回歸 2 cm = confusion_matrix(y_test,y_) 3 plot_confusion_matrix(cm,classes=[0,1])

6.2 模型評估
解決不同的問題,通常需要不同的指標來度量模型的性能。例如我們希望用算法來預測癌症是否是惡性的,假設100個病人中有5個病人的癌症是惡性, 對於醫生來說,盡可能提高模型的查全率(recall)比提高查准率(precision)更為重要,因為站在病人的角度,發生漏發現癌症為惡性比發生誤 判為癌症是惡性更為嚴重。
考慮設置閾值,來調整預測被盜刷的概率,依次來調整模型的查全率(Recall)
1 # 設置閾值 2 threshold = np.arange(0.05,1.05,0.05) 3 threshold 4 5 6 ''' 7 array([0.05, 0.1 , 0.15, 0.2 , 0.25, 0.3 , 0.35, 0.4 , 0.45, 0.5 , 0.55, 8 0.6 , 0.65, 0.7 , 0.75, 0.8 , 0.85, 0.9 , 0.95, 1. ]) 9 '''
1 plt.figure(figsize=(12,20)) 2 3 recall = [] 4 5 accuracy = [] 6 7 for i,t in enumerate(threshold): 8 9 y_ = (prob_ >= t).astype(np.int8) 10 11 # y_test 12 cm = confusion_matrix(y_test,y_) 13 print('threshold:%0.4f,score:%0.4f,Recall:%0.4f'%(t,(cm[0,0] + cm[1,1])/cm.sum(),cm[1,1]/cm[1].sum())) 14 15 recall.append(cm[1,1]/cm[1].sum()) 16 accuracy.append((cm[0,0] + cm[1,1])/cm.sum()) 17 plt.subplot(5,5,i+1) 18 plot_confusion_matrix(cm,classes=[0,1])



7. 趨勢圖
1 plt.plot(threshold,recall,color = 'green') 2 3 plt.plot(threshold,accuracy,color = 'red') 4 5 plt.legend()

由上圖所見,隨着閾值逐漸變大,Recall rate逐漸變小,AUC值先增后減
8. 找出模型最優的閾值
precision和recall是一組矛盾的變量。從上面混淆矩陣和PRC曲線可以看到,閾值越小,recall值越大,模型能找出信用卡被盜刷的數量也就更多,但換來的代價是誤判的數量也較大。隨着閾值的提高,recall值逐漸降低,precision值也逐漸提高,誤判的數量也隨之減少。通過調整模型閾值,控制模型反信用卡欺詐的力度,若想找出更多的信用卡被盜刷就設置較小的閾值,反之,則設置較大的閾值。
實際業務中,閾值的選擇取決於公司業務邊際利潤和邊際成本的比較;當模型閾值設置較小的值,確實能找出更多的信用卡被盜刷的持卡人,但隨着誤判數量增加,不僅加大了貸后團隊的工作量,也會降低正常情況誤判為信用卡被盜刷客戶的消費體驗,從而導致客戶滿意度下降,如果某個模型閾值能讓業務的邊際利潤和邊際成本達到平衡時,則該模型的閾值為最優值。當然也有例外的情況,發生金融危機,往往伴隨着貸款違約或信用卡被盜刷的幾率會增大,而金融機構會更願意設置小閾值,不惜一切代價守住風險的底線。
