本次用到的数据来自于Kaggle网站中的Synthetic Financial Datasets For Fraud Detection项目,该项目旨在分析检测金融欺诈,其数据集来自某移动支付公司的交易日志,我们的目的是根据交易记录数据判断该笔交易是否为欺诈行为,数据集地址如下:https://www.kaggle.com/ntnu-testimon/paysim1www.kaggle.com
本项目共包含六百多万条数据,反应了银行每一笔款项的记录。每条数据包含十一个字段,分别为:step(每一步代表1个小时的时间,总共包含744步(模拟30天))
type(交易类型,包含转账、取现等)
amount(交易金额)
nameOrig(转出账户)
oldbalanceOrg/newbalanceOrig(转出账户的前后余额)
nameDest(转入账户)
oldbalanceDest/newbalanceDest(转入账户的前后余额)
isFraud(代表欺诈行为)
isFlaggedFraud(银行系统模型判断的欺诈标签,原数据集解释为只有转账额超过200.000的标注)
通过对数据的预处理和特征分析等步骤,最后使用LogisticRegression算法对数据进行二分类预测,画出ROC曲线并计算AUC值,结果表明对本项目的处理较好。 本文基于对kaggle比赛的初体验,主要是为了加深对Python语法的理解和实际应用,在模型和算法等方面上没有进行深入的研究分析。
1.Import Packages
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
import seaborn as sns
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve,auc
2.Import Dataset
dataset_path="C:\\Users\\KIN\\Desktop\\kaggle dataset\\Fraud Detection.csv"
data=pd.read_csv(dataset_path)
data.head(10)
data.info()
data.describe()
3.Exploratory Data Analysis
data.groupby(['type','isFraud']).size().plot(kind='bar')发现诈骗只发生在转账和取现两种交易方法中
data.loc[data['isFlaggedFraud']==1].type.value_counts()
data.loc[data['isFlaggedFraud']==1]被银行系统标记的诈骗只有16条并且都是transfer形式,并且被标记的诈骗必然是诈骗
考虑到诈骗往往都会有一个金额下限,即小额诈骗的收益过低,因此探究交易金额的影响。同时考虑到诈骗一般会把账户内存款都给骗取干净,诈骗青睐于转出账户中有大额存款的,转入账户为犯罪提现用账户,因此转入账户前余额应该很低甚至为0,基于以上考虑进行下面探究。
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
sns.boxplot(x='isFraud',y='amount',hue='type',data=data_select,ax=axs[0][0])
axs[0][0].set_yscale('log')
sns.scatterplot(x='oldbalanceOrg', y='amount', data=data_select[data_select['isFraud'] ==1], ax=axs[0][1])
sns.boxplot(x='isFraud',y='oldbalanceOrg',hue='type',data=data_select,ax=axs[1][0])
axs[1][0].set(ylim=(0, 5e7))
sns.boxplot(x='isFraud',y='oldbalanceDest',hue='type',data=data_select,ax=axs[1][1])
axs[1][1].set(ylim=(0, 3e7))
plt.show()结果发现诈骗金额确实有集中性,但是考虑到非诈骗转账中金额也有集中性且相近,因此amount字段没有明显的代表性。有趣的是发现转出账户余额低于1e7时,余额被完全诈骗干净。但是随着余额的增加,诈骗金额保持在1e7,这个无法理解。
诈骗集中发生在转出账户余额较低的账户中,这点跟预期不同。诈骗发生时转入账户前余额普遍极低,这点复合诈骗犯避免查封账户,尽量选择空置账户的考虑
sns.scatterplot(x='oldbalanceOrg', y='amount', data=data_select[data_select['isFraud'] ==0])
plt.show()交易金额存在超过1e7数量级的,但诈骗金额不超过1e7
数据集解释账户类型以M开头的是商家账户,那C开头的应该是Customer客户
data_Fraud =data.loc[data.isFraud == 1]
data_NotFraud = data.loc[data.isFraud == 0]
print("转出账户(诈骗)为C类型:",len(data_Fraud.loc[data_Fraud.nameOrig.str.contains('C')]))
print("转出账户(诈骗)为M类型:",len(data_Fraud.loc[data_Fraud.nameOrig.str.contains('M')]))
print("转入账户(诈骗)为C类型:",len(data_Fraud.loc[data_Fraud.nameDest.str.contains('C')]))
print("转入账户(诈骗)为M类型:",len(data_Fraud.loc[data_Fraud.nameDest.str.contains('M')]))
print("转出账户(非诈骗)为C类型:",len(data_NotFraud.loc[data_NotFraud.nameOrig.str.contains('C')]))
print("转出账户(非诈骗)为M类型:",len(data_NotFraud.loc[data_NotFraud.nameOrig.str.contains('M')]))
print("转入账户(非诈骗)为C类型:",len(data_NotFraud.loc[data_NotFraud.nameDest.str.contains('C')]))
print("转入账户(非诈骗)为M类型:",len(data_NotFraud.loc[data_NotFraud.nameDest.str.contains('M')]))所以诈骗只发生在接受账户为C类型的交易中
一般在诈骗后,接受账户应该会把钱转出来,因此查看诈骗中收款人和提现人是否有重复。但结果证明不存在这种账户,step代表的先转账后提现的逻辑也没有体现出来
#因此去掉没用的特征数据列
data_select.drop(['step','nameOrig','nameDest','isFlaggedFraud'],axis=1,inplace=True)
#重新设置索引
data_select.reset_index()
#将type转换成类别数据,即0, 1
data_select.loc[data_select.type=='TRANSFER','type'] = 0
data_select.loc[data_select.type=='CASH_OUT','type'] = 1
4 可视化
data_select['type'].value_counts()
sns.heatmap(data_select.corr())
5 数据建模
data_select['isFraud'].value_counts()发现数据严重不平衡,会造成模型对二者的拟合程度差异
通常我们会参考小众类的数量,在大众类中采样,这称为undersampling.因此在正样本中随机选取8213个样本.
nonfraud_index=data_select.loc[data_select.isFraud==0].index
random_nonfraud_index=np.random.choice(nonfraud_index,size=8213,replace=False)
Fraud_index=data_select.loc[data_select.isFraud==1].index
undersampling_index=np.concatenate([Fraud_index,random_nonfraud_index])
undersampling_data=data_select.loc[data_select.index.isin(undersampling_index)]
x_undersampling=undersampling_data[['type','amount','oldbalanceOrg','newbalanceOrig','oldbalanceDest','newbalanceDest']]
y_undersampling=undersampling_data['isFraud']
x_train, x_test, y_train, y_test=train_test_split(x_undersampling, y_undersampling, test_size=0.25, random_state=0,stratify=x_undersampling.type)
modle=LogisticRegression().fit(x_train,y_train)
pred_score=modle.predict_proba(x_test)
fpr, tpr, thresholds = roc_curve(y_test,pred_score[:, 1])
AUC=auc(fpr,tpr)
plt.plot(fpr,tpr,label='AUC = %0.2f'% AUC)
plt.legend(loc='lower right')
plt.title('Receiver Operating Characteristic')
plt.plot([0,1],[0,1],'r--')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
ROC和AUC值的定义
ROC的全名叫做Receiver Operating Characteristic,其主要分析工具是一个画在二维平面上的曲线——ROC curve。平面的横坐标是false positive rate(FPR),纵坐标是true positive rate(TPR)。AUC(Area Under roc Curve)值为ROC曲线所覆盖的区域面积,通常,AUC的值介于0.5到1.0之间,较大的AUC代表了较好的performance。AUC = 1,是完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。AUC = 0.5,跟随机猜测一样(例:丢铜板),模型没有预测价值。AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
而在本文中,AUC=0.98接近1,说明模型的预测效果较好。
TPR代表在所有正样本中,即实际标签为1的样本中,最终被预测为1的比率;FPR代表在所有负样本中,即实际标签为0的样本中,最终被预测为1的比率;