數據分析項目之:金融反欺詐(信用卡盜刷)


項目名稱:金融反欺詐(信用卡盜刷)

項目概述:本項目通過利用信用卡的歷史交易數據進行機器學習,構建信用卡反欺詐預測模型,提前發現客戶信用卡被盜刷的事件。

項目背景:數據包含了由歐洲持卡人於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值也逐漸提高,誤判的數量也隨之減少。通過調整模型閾值,控制模型反信用卡欺詐的力度,若想找出更多的信用卡被盜刷就設置較小的閾值,反之,則設置較大的閾值。

   實際業務中,閾值的選擇取決於公司業務邊際利潤和邊際成本的比較;當模型閾值設置較小的值,確實能找出更多的信用卡被盜刷的持卡人,但隨着誤判數量增加,不僅加大了貸后團隊的工作量,也會降低正常情況誤判為信用卡被盜刷客戶的消費體驗,從而導致客戶滿意度下降,如果某個模型閾值能讓業務的邊際利潤和邊際成本達到平衡時,則該模型的閾值為最優值。當然也有例外的情況,發生金融危機,往往伴隨着貸款違約或信用卡被盜刷的幾率會增大,而金融機構會更願意設置小閾值,不惜一切代價守住風險的底線。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM