python分析信用卡反欺詐(上)——邏輯回歸、隨機森林、SVM三種方法建模比較

前言

  • 本文研究的是大數據量(284807條數據)下模型選擇的問題,也參考了一些文獻,但大多不夠清洗,因此吐血整理本文,希望對大家有幫助;
  • 本文試著從數據分析師的角度,設想「拿到數據該如何尋找規律、選那種模型來構建反欺詐模型?」的角度來分析,以業務導向為主,不深究演算法原理;
  • 後面會有一篇文章(python分析信用卡反欺詐(下)),來說明數據結構極度不平衡的情況下,該如何修正數據集、如何調整參數;
  • 閱讀本文大約需要15分鐘,如有錯誤歡迎留言指正,謝謝????

一,數據來源及項目概況

數據是從kaggle上看到的項目,具體鏈接如下:

Credit Card Fraud Detection?

www.kaggle.com

獲取本例數據的,可在上述項目詳情鏈接中下載數據。

數據集包含歐洲持卡人於2013年9月通過信用卡進行的交易。該數據集提供兩天內發生的交易,其中在284,807筆交易中有492起欺詐行為。數據集非常不平衡,負面類別(欺詐)占所有交易的0.172%。

它只包含數值輸入變數,這是PCA變換的結果。不幸的是,由於保密問題,我們無法提供有關數據的原始特徵和更多背景信息。特徵V1,V2,... V28是用PCA獲得的主要組件,唯一沒有用PCA轉換的特徵是Time和Amount。

  • 「時間」包含每個事務與數據集中第一個事務之間經過的秒數。
  • 金額是交易金額,該特徵可以用於依賴於例子的成本敏感性學習。
  • 「Class」是響應變數,在欺詐的情況下其值為1,否則為0。

二,準備並初步查看數據集

# 導入包import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport matplotlib.gridspec as gridspecimport seaborn as sns; plt.style.use(ggplot)import sklearnfrom sklearn.preprocessing import StandardScalerfrom sklearn.model_selection import train_test_splitfrom sklearn.utils import shufflefrom sklearn.metrics import confusion_matrixfrom sklearn.manifold import TSNEpass# 倒入並查看數據crecreditcard_data=pd.read_csv(./creditcard.csv)crecreditcard_data.shape,crecreditcard_data.info()<class pandas.core.frame.DataFrame>RangeIndex: 284807 entries, 0 to 284806Data columns (total 31 columns):Time 284807 non-null float64V1 284807 non-null float64V2 284807 non-null float64V3 284807 non-null float64V4 284807 non-null float64V5 284807 non-null float64V6 284807 non-null float64V7 284807 non-null float64V8 284807 non-null float64V9 284807 non-null float64V10 284807 non-null float64V11 284807 non-null float64V12 284807 non-null float64V13 284807 non-null float64V14 284807 non-null float64V15 284807 non-null float64V16 284807 non-null float64V17 284807 non-null float64V18 284807 non-null float64V19 284807 non-null float64V20 284807 non-null float64V21 284807 non-null float64V22 284807 non-null float64V23 284807 non-null float64V24 284807 non-null float64V25 284807 non-null float64V26 284807 non-null float64V27 284807 non-null float64V28 284807 non-null float64Amount 284807 non-null float64Class 284807 non-null int64dtypes: float64(30), int64(1)memory usage: 67.4 MB((284807, 31), None)crecreditcard_data.describe()passcrecreditcard_data.head()pass# 看看欺詐與非欺詐的比例如何count_classes=pd.value_counts(crecreditcard_data[Class],sort=True).sort_index()# 統計下具體數據count_classes.value_counts()# 也可以用count_classes[0],count_classes[1]看分別數據284315 1492 1Name: Class, dtype: int64count_classes.plot(kind=bar)plt.show()

  • 0代表正常,1代表欺詐,二者數量嚴重失衡,極度不平衡,根本不在一個數量級上;

三,欺詐與時間序列分布關係

# 查看二者的描述性統計,與時間的序列分布關係print(Normal)print(crecreditcard_data. Time[crecreditcard_data.Class == 0].describe())print(-*25)print(Fraud)print(crecreditcard_data. Time[crecreditcard_data.Class == 1].describe())Normalcount 284315.000000mean 94838.202258std 47484.015786min 0.00000025% 54230.00000050% 84711.00000075% 139333.000000max 172792.000000Name: Time, dtype: float64-------------------------Fraudcount 492.000000mean 80746.806911std 47835.365138min 406.00000025% 41241.50000050% 75568.50000075% 128483.000000max 170348.000000Name: Time, dtype: float64f,(ax1,ax2)=plt.subplots(2,1,sharex=True,figsize=(12,6))bins=50ax1.hist(crecreditcard_data.Time[crecreditcard_data.Class == 1],bins=bins)ax1.set_title(欺詐(Fraud)),fontsize=22)ax1.set_ylabel(交易量,fontsize=15)ax2.hist(crecreditcard_data.Time[crecreditcard_data.Class == 0],bins=bins)ax2.set_title(正常(Normal,fontsize=22)plt.xlabel(時間(單位:秒),fontsize=15)plt.xticks(fontsize=15)plt.ylabel(交易量,fontsize=15)# plt.yticks(fontsize=22)plt.show()

  • 欺詐與時間並沒有必然聯繫,不存在周期性;
  • 正常交易有明顯的周期性,有類似雙峰這樣的趨勢。

四,欺詐與金額Amount的關係和分布情況

print(欺詐)print(crecreditcard_data.Amount[crecreditcard_data.Class ==1].describe())print(-*25)print(正常交易)print(crecreditcard_data.Amount[crecreditcard_data.Class==0].describe())欺詐count 492.000000mean 122.211321std 256.683288min 0.00000025% 1.00000050% 9.25000075% 105.890000max 2125.870000Name: Amount, dtype: float64-------------------------正常交易count 284315.000000mean 88.291022std 250.105092min 0.00000025% 5.65000050% 22.00000075% 77.050000max 25691.160000Name: Amount, dtype: float64f,(ax1,ax2)=plt.subplots(2,1,sharex=True,figsize=(12,6))bins=30ax1.hist(crecreditcard_data.Amount[crecreditcard_data.Class == 1],bins=bins)ax1.set_title(欺詐(Fraud),fontsize=22)ax1.set_ylabel(交易量,fontsize=15)ax2.hist(crecreditcard_data.Amount[crecreditcard_data.Class == 0],bins=bins)ax2.set_title(正常(Normal),fontsize=22)plt.xlabel(金額($),fontsize=15)plt.xticks(fontsize=15)plt.ylabel(交易量,fontsize=15)plt.yscale(log)plt.show()

  • 金額普遍較低,可見金額這一列的數據對分析的參考價值不大。

五,查看各個自變數(V1 ~V29)與因變數的關係

看看各個變數與正常、欺詐之間是否存在聯繫,為了更直觀展示,通過distplot圖來逐個判斷,如下:

features=[x for x in crecreditcard_data.columns if x not in [Time,Amount,Class]]plt.figure(figsize=(12,28*4))gs =gridspec.GridSpec(28,1)import warningswarnings.filterwarnings(ignore)for i,cn in enumerate(crecreditcard_data[v_features]): ax=plt.subplot(gs[i]) sns.distplot(crecreditcard_data[cn][crecreditcard_data.Class==1],bins=50,color=red) sns.distplot(crecreditcard_data[cn][crecreditcard_data.Class==0],bins=50,color=green) ax.set_xlabel() ax.set_title(直方圖:+str(cn))plt.savefig(各個變數與class的關係.png,transparent=False,bbox_inches=tight)plt.show()

  • 紅色表示欺詐,綠色表示正常
  • 兩個分布的交叉面積越大,欺詐與正常的區分度最小,如V15;
  • 兩個分布的交叉面積越小,則該變數對因變數的影響越大,如V14;

下面我們看看各個單變數與class的相關性分析,為更直觀展示,直接作圖,如下:

# 各個變數的矩陣分布crecreditcard_data.hist(figsize=(15,15),bins=50)plt.show()


六,三種方法建模並分析

本部分將應用邏輯回歸、隨機森林、支持向量SVM三種方法建模分析,分別展開如下:

  • 準備數據

# 先把數據分為欺詐組和正常組,然後按比例生產訓練和測試數據集# 分組Fraud=crecreditcard_data[crecreditcard_data.Class == 1]Normal=crecreditcard_data[crecreditcard_data.Class == 0]# 訓練特徵集x_train=Fraud.sample(frac=0.7)x_train=pd.concat([x_train,Normal.sample(frac=0.7)],axis=0)# 測試特徵集x_test=crecreditcard_data.loc[~crecreditcard_data.index.isin(x_train.index)]# 標籤集y_train=x_train.Classy_test=x_test.Class# 去掉特徵集里的標籤和時間列x_train=x_train.drop([Class,Time],axis=1)x_test=x_test.drop([Class,Time],axis=1)# 查看數據結構print(x_train.shape,y_train.shape,
,x_test.shape,y_test.shape)(199364, 29) (199364,) (85443, 29) (85443,)

6.1 邏輯回歸方法

from sklearn import metricsimport scipy.optimize as opfrom sklearn.linear_model import LogisticRegressionfrom sklearn.cross_validation import KFold,cross_val_scorefrom sklearn.metrics import (precision_recall_curve, auc,roc_auc_score, roc_curve,recall_score, classification_report)lrmodel = LogisticRegression(penalty=l2)lrmodel.fit(x_train, y_train)#查看模型print(lrmodel)print(lrmodel)lrmodelLogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class=ovr, n_jobs=1, penalty=l2, random_state=None, solver=liblinear, tol=0.0001, verbose=0, warm_start=False)#查看混淆矩陣ypred_lr=lrmodel.predict(x_test)print(confusion_matrix)print(metrics.confusion_matrix(y_test,ypred_lr))confusion_matrix[[85284 11] [ 56 92]]#查看分類報告print(classification_report)print(metrics.classification_report(y_test,ypred_lr))classification_report precision recall f1-score support 0 1.00 1.00 1.00 85295 1 0.89 0.62 0.73 148avg / total 1.00 1.00 1.00 85443#查看預測精度與決策覆蓋面print(Accuracy:%f%(metrics.accuracy_score(y_test,ypred_lr)))print(Area under the curve:%f%(metrics.roc_auc_score(y_test,ypred_lr)))Accuracy:0.999216Area under the curve:0.810746

6.2 隨機森林模型

from sklearn.ensemble import RandomForestClassifierrfmodel=RandomForestClassifier()rfmodel.fit(x_train,y_train)#查看模型print(rfmodel)rfmodelrfmodelRandomForestClassifier(bootstrap=True, class_weight=None, criterion=gini, max_depth=None, max_features=auto, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False)#查看混淆矩陣ypred_rf=rfmodel.predict(x_test)print(confusion_matrix)print(metrics.confusion_matrix(y_test,ypred_rf))confusion_matrix[[85291 4] [ 34 114]]#查看分類報告print(classification_report)print(metrics.classification_report(y_test,ypred_rf))classification_report precision recall f1-score support 0 1.00 1.00 1.00 85295 1 0.97 0.77 0.86 148avg / total 1.00 1.00 1.00 85443#查看預測精度與決策覆蓋面print(Accuracy:%f%(metrics.accuracy_score(y_test,ypred_rf)))print(Area under the curve:%f%(metrics.roc_auc_score(y_test,ypred_rf)))Accuracy:0.999625Area under the curve:0.902009

6.3 支持向量機SVM

# SVM分類from sklearn.svm import SVCsvcmodel=SVC(kernel=sigmoid)svcmodel.fit(x_train,y_train)#查看模型print(svcmodel)svcmodelSVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape=ovr, degree=3, gamma=auto, kernel=sigmoid, max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)#查看混淆矩陣ypred_svc=svcmodel.predict(x_test)print(confusion_matrix)print(metrics.confusion_matrix(y_test,ypred_svc))confusion_matrix[[85197 98] [ 142 6]]#查看分類報告print(classification_report)print(metrics.classification_report(y_test,ypred_svc))classification_report precision recall f1-score support 0 1.00 1.00 1.00 85295 1 0.06 0.04 0.05 148avg / total 1.00 1.00 1.00 85443#查看預測精度與決策覆蓋面print(Accuracy:%f%(metrics.accuracy_score(y_test,ypred_svc)))print(Area under the curve:%f%(metrics.roc_auc_score(y_test,ypred_svc)))Accuracy:0.997191Area under the curve:0.519696


小結

  • 通過三種模型的表現可知,隨機森林的誤殺率最低;
  • 不應只盯著精度,有時候模型的精度高並不能說明模型就好,特別是像本項目中這樣的數據嚴重不平衡的。舉個例子,我們拿到有1000條病人的數據集,其中990人為健康,10個有癌症,我們要通過建模找出這10個癌症病人,如果一個模型預測到了全部健康的990人,而10個病人一個都沒找到,此時其正確率仍然有99%,但這個模型是無用的,並沒有達到我們尋找病人的目的;
  • 建模分析時,遇到像本例這樣的極度不平衡數據集,因採取下採樣、過採樣等辦法,使數據平衡,這樣的預測才有意義,下一篇文章將針對這個問題進行改進。
  • 模型、演算法並沒有高低、好壞之分,只是在不同的情況下有不同的發揮罷了,這點應正確的看待。

以上就是本文的全部,謝謝你查看本文,後面還有一篇「python分析信用卡反欺詐(下)」。

(人氣稀薄????,急需關愛????。如果您竟然看到了這裡還沒走開,請幫忙多多點贊、收藏哈,謝謝啦朋友們~~)

推薦閱讀:

草根學Python(十一)枚舉類
sanic如何使用,是否有詳細教程?如何與uvloop配合使用?
Python中為什麼沒有switch語法結構,有什麼代替方案嗎?
可以用 Python 寫只暴露一個 function 的模塊嗎?

TAG:數據分析 | Python | 互聯網金融 |