爱数课实验 | 第六期-金融反欺诈案例研究

2022-06-27 18:21:47 浏览数 (1)

爱数课:idatacourse.cn

领域:金融

简介:随着银行、支付宝等金融机构提供的移动金融支付方式在生活中越来越普及,涉及的金融诈骗交易事件也层出不出,本实验一是查看金融诈骗交易在交易时间、交易金额等方面的特点,然后通过机器学习的方法来对这些金融诈骗交易进行识别。

数据:

./dataset/data_financial.csv

目录

随着金融科技的发展,移动支付已经在人们的生活中占据越来越重要的地位。大家在生活中肯定会经常用到支付宝、微信等移动支付方式。随着移动金融支付的普及,金融欺诈问题也越来越成为一个严峻的挑战。不同于传统现金支付或者去银行柜台转账,移动金融支付往往只需要输入密码或者指纹,很容易发生客户被诈骗的事件。当前,电话诈骗、短信诈骗、互联网诈骗等层出不穷,给人们的生活带来了极大困扰。金融监管部门正在多渠道对金融诈骗进行打击,对于银行、支付宝、微信等公司来说,有效的识别金融诈骗,尽早阻止诈骗交易也是十分重要的!这不仅有利于维护客户财产安全,增加客户信任,对帮助金融监管部门打击金融诈骗也有积极作用。

该领域对于普通的数据科学研究者来说的难题是,一直以来缺少金融服务尤其是移动金融支付领域的公开数据集,最主要的原因是金融交易具有很强的隐私性。而今天介绍的是Kaggle上一个用于研究金融诈骗的数据集。这个数据集是通过一个叫做PaySim的模拟器模拟出来的,以便应对隐私问题(客户肯定不希望自己的交易信息被其他人知晓)。PaySim使用真实的私人交易数据集来生成模拟数据集,从而完善刻画了一些正常的交易操作,并且加入了一些欺诈交易事件,以便之后评估监测方法的表现。该数据集基于非洲某一个国家某月真实的金融交易记录,该交易记录由一家当前运行在全球14个国家的金融交易服务公司提供,因此该交易数据集具有代表性。本案例使用的数据集是基于真实的交易数据生成的模拟数据集,这个数据集较好地刻画了真实金融交易的状况,但是又不会引发隐私泄露问题。

由于该数据集过于庞大,因此我们只抽取其中一天交易日的数据作为样本。

代码语言:javascript复制
# 载入必要库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt  
%matplotlib inline 
import seaborn as sns

# 设置中文字体
plt.rcParams['font.sans-serif']='SimHei'

# 忽略所有警告
import warnings
warnings.filterwarnings('ignore')

首先,简单介绍一下该数据集各字段的含义( 数据集链接:http://www.idatascience.cn/dataset-detail?table_id=101002 ):

列名

含义说明

step

时间范围,1表示凌晨12:00-1:00

type

交易类型:共有CASH-IN, CASH-OUT, DEBIT, PAYMENT and TRANSFER五种

amount

交易金额(以当地货币单位计价)

nameOrig

交易者

oldbalanceOrg

交易之前的账户余额

newbalanceOrig

交易之后的账户余额

nameDest

交易的接受方

oldbalanceDest

交易之前交易接受方的账户余额,如果是商家,则无信息

newbalanceDest

交易之后交易接受方的账户余额,如果是商家,则无信息

isFraud

如果是欺诈交易,取值为1,否则为0

1. 数据导入与描述性统计

1.1 数据导入

我们将数据导入,并查看一下数据的基本信息(数据量,每一列的数据类型,以及是否存在空值):

代码语言:javascript复制
# 读取数据
data = pd.read_csv('./dataset/data_financial.csv')
# 查看数据基本信息
data.info()

可以看到,该数据集有574255个样本,每一列都没有缺失数据,这意味着我们不需要进行缺失值处理 。另外,还可以看到每一列的数据类型。

1.2 描述性统计

接下来,对数据进行描述性统计:使用DataFrame对象的describe()方法可以查看各个列的基本统计信息,统计并生成数据集中各个字段的样本数、均值、标准差、最小值、四分位数等基本信息。

代码语言:javascript复制
# 查看数值和离散型数据的基本统计信息 
data.describe(include='all')

数据类型为object的字段,比如type,众数top是CASH_OUT,频数freq是204397,即现金支出的交易笔数为204397。数值型的字段,比如isFraud这一列的均值mean是0.000472,代表其中有0.047%的交易是欺诈交易。总体来看,数据不存在极端值。

2. 金融诈骗相关问题分析

2.1 金融交易时间分布状况分析

从上面的信息可以看到,大概有0.047%的交易是欺诈交易,也就是说每一万笔交易中大概有五笔交易是欺诈交易,而支付宝每天的交易规模都在上亿笔,如果金融欺诈交易发生概率是0.047%,那么金融欺诈交易的数量会是一个非常可怕的数字。

接下来,我们逐列分析一下交易数据,来发现数字背后隐藏的规律。

首先,看一下 step 列(这一列的数据分布应该是从1-24,表示一天的24个小时):

代码语言:javascript复制
# 查看'step'这一列有哪些值,并且按照从小到大的顺序排列
np.sort(data['step']______)

结果表明,数据是符合我们的要求的,没有异常数据。

接下来,我们看一下客户的交易是否存在时间倾向,也就是每天各个时间段交易的笔数是否存在显著差异呢?

代码语言:javascript复制
# 依据“step”列进行分组,再统计每组的数据量(即每个时间段的交易笔数)
series_1 = data['step'].value_counts(sort=False) # sort默认为TRUE,会对结果进行排序,False就是不进行排序
series_1

然后,对金融交易时间分布状况进行可视化展示:

代码语言:javascript复制
# 设置画布的大小和分辨率
plt.figure(figsize=(8,6), dpi=100)

# 绘制折线图
plt.plot(data['step']_________(sort=False), )

# 设置标题
plt.title('金融交易时间分布状况',fontsize=13)

# 设置横坐标
plt.xlabel('时间段')

# 设置纵坐标
plt.ylabel('交易笔数')

Text(0, 0.5, '交易笔数')

可以看到,凌晨和夜晚交易的笔数相对较少,交易数量从上午7点左右开始迅速增多,一直到20点开始快速下降,这是符合常理的。交易的峰值出现在晚上18点-19点,有超过5万笔交易数据,因为此时人们通常已经下班,相对有更加充足的时间进行网上购物、支付转账等交易。

2.2 金融诈骗时间分布状况分析

我们再来看一下金融诈骗通常发生在什么时候呢?为了实现这个目标,需要对isFraud列进行分组求和:

代码语言:javascript复制
# 依据时间段,对isFraud列进行分组求和
series_2=data__________(by=['step'])['isFraud'].sum() 
series_2

同样地,再进行可视化展示:

代码语言:javascript复制
# 设置画布的大小和分辨率
plt.figure(figsize=(8,6), dpi=100)

# 绘制折线图
sns.lineplot(data=series_2, )

# 设置标题
plt.title('金融诈骗交易时间分布状况',fontsize=13)

# 设置横坐标
plt.xlabel('时间段')

# 设置纵坐标
plt.ylabel('交易笔数')

Text(0, 0.5, '交易笔数')

相比于金融交易,金融诈骗交易的时间段分布并不是那么有规律可循,而是类似于呈现波峰波谷的交替分布,表明金融诈骗在一天中的各个时间段都有可能发生。

如果考虑到凌晨12点到早晨七点相对较少的金融交易量,那么夜晚是否会是金融诈骗的高发期呢?为了验证这个想法,我们再看一下一天各个时间段的金融诈骗发生率,这需要生成一个新的列作为各个时间段金融诈骗率的衡量,我们用每天各个时间段的金融交易诈骗数量除以该时段的金融交易总数:

代码语言:javascript复制
# 创建新的数据框,列名是'rate_zhapian',各时段诈骗率=各时段金融诈骗交易笔数/各时段金融交易总笔数
df=pd.DataFrame(columns=['rate_zhapian'])
df['rate_zhapian']=series_2/series_1
df['rate_zhapian']

对一天各个时段的金融交易诈骗率进行可视化展示:

代码语言:javascript复制
# 设置画布的大小和分辨率
plt.figure(figsize=(8,6), dpi=100)

# 画折线图,可视化一天各个时段的金融诈骗发生率
___________(data=df['rate_zhapian'],)  

# 设置标题
plt.title('各时段金融交易诈骗率',fontsize=13)

# 设置横坐标
plt.xlabel('时间段')

# 设置纵坐标
plt.ylabel('金融交易诈骗率')

Text(0, 0.5, '金融交易诈骗率')

我们可以发现,金融诈骗确实主要发生在夜间!也就是用户熟睡的时候,因为这个时候用户基本上没有什么防范能力。联想到之前发生的不法分子趁用户熟睡时进行金融诈骗的案例—— 《吓人!凌晨手机信号从4G变2G,然后钱就被转空了… 》,表明该数据是和现实情况一致的,这也提醒银行、支付宝等金融服务部门和金融监管部门在该时间段对此类问题给予更多的关注。

2.3 金融交易类型及金融诈骗金额分析

接下来,我们看一下交易数据的类型,也就是五种交易类型各占多少。我们使用groupby()函数,以type为分类,求每一类交易的总数量:

代码语言:javascript复制
# 查看金融交易各个类型的交易总数:依据type进行分组,然后计算各个type的数量
data.groupby(by=['type'])['step'].count()
代码语言:javascript复制
# 绘制出金融交易类别分布条形图

# 设置画布的大小和分辨率
plt.figure(figsize=(8,6), dpi=100)

# 绘制条形图,并设置标题
data.groupby(by=['type'])['step'].count().plot(kind='bar', rot=360, title='交易类别数量分布条形图') # rot: 轴标签的旋转度数

# 设置横坐标
plt.xlabel('交易类型')

# 设置纵坐标
plt.ylabel('交易数量')

Text(0, 0.5, '交易数量')

可以看出,现金流出CASH_OUT占交易比重最大,其次是支付PAYMENT

接下来,再看一下哪种交易类型最容易发生金融诈骗事件(实际上答案非常简单,但是我们还是通过数据来验证一下):

代码语言:javascript复制
# 依据'type'进行分组,然后计算各个交易类别的金融诈骗交易事件(isFraud=1)的总数
data.groupby(by=['type'])['isFraud'].sum()

可以发现,金融诈骗只出现在两种交易中,一种是现金流出CASH_OUT,另外一种是转账TRANSFER。因为诈骗一定涉及到财产的减少,也就是财产流出,所以诈骗交易只出现在现金流出CASH_OUT和转账TRANSFER两列,是符合实际的。

那么每次金融诈骗发生的时候,会转走用户多少钱呢?是会选择全部转走吗?这里,我们要查看涉及诈骗交易时,交易后用户账户余额newbalanceOrig列为0的账户个数。

首先我们找到所有涉及诈骗交易的账户,然后我们查看诈骗交易账户中交易后余额为0的账户数量。

代码语言:javascript复制
# 筛选出金融诈骗交易
df_zhapian=data[data["isFraud"]==1]  

# 查看所有金融诈骗交易中,交易后原账户余额为0的账号数量:
df_zhapian[df_zhapian["newbalanceOrig"]==0]["isFraud"].count() 

267

代码语言:javascript复制
# 也可以使用下面的代码,查看所有金融诈骗交易中,交易后原账户余额为0的账号数量:
df_zhapian[df_zhapian["newbalanceOrig"]==0]["isFraud"].value_counts() # DataFrame

1 267

Name: isFraud, dtype: int64

可以发现,一共只有271单诈骗交易,而选择全部将客户的钱转走占据了267单!这警示我们一旦诈骗得手,造成的会是非常严重的损失——有很大的概率转移走全部账户余额!

那么金融诈骗的平均额度是多少呢?相比于总的平均交易规模如何呢?我们首先对金融诈骗账户的amount列求均值,然后对所有涉及现金流出CASH_OUT和转账TRANSFER业务的正常交易账户的amount列求均值,最后对这两个值进行比较。

代码语言:javascript复制
# 计算金融诈骗交易的平均金额
data[data["isFraud"]==1]['amount']________

779202.3153505538

代码语言:javascript复制
# 计算非金融诈骗交易时,涉及现金流出CASH_OUT和转账TRANSFER的正常金融交易业务的平均金额
data[(data["isFraud"]==0)&(data["type"]______(["CASH_OUT","TRANSFER"]))]['amount'].mean()

272962.3373505693

可以发现,金融诈骗的平均交易金额远远大于正常的(现金流出CASH_OUT和转账TRANSFER业务)平均交易金额,因此金融监管部门在遇到超过正常平均交易金额很多的交易的时候,需要加以注意,判断其是否是金融诈骗。

2.4 金融诈骗相关问题分析小结

通过数据清洗和数据描述性统计等方法研究了何时为金融诈骗交易高发时段、金融诈骗交易诈骗金额以及相对于正常金融交易金额的大小。研究发现,凌晨12点至早上7点是金融诈骗交易多发时段,因为此时用户防范意识最小;金融诈骗交易一旦得手,往往会全部转移走客户财产,造成巨额损失;金融诈骗交易平均金额相对于正常金融交易金额来说非常大,这给我们识别移动支付中的金融风险提供了借鉴意义。想更加准确地识别金融欺诈交易,则需要更加复杂的机器学习算法。

3. 机器学习方法识别金融交易欺诈

3.1 数据预处理

该数据集不需要处理缺失值,首先删除无关的列,并对类别型特征进行数值编码。

代码语言:javascript复制
# 将'nameOrig','nameDest'这两列与分类问题无关的列删除,得到用于分类的新数据集
data_for_classification = data.drop(['nameOrig','nameDest'], axis=1)
data_for_classification.head()
代码语言:javascript复制
# 从LabelEncoder类中,调用函数fit_transform,对分类型特征type进行编码,映射到0~4
from sklearn.preprocessing import LabelEncoder
type_encoder = LabelEncoder() 
data_for_classification['type'] = type_encoder.__________(data_for_classification['type'])

data_for_classification.head()

3.2 数据集划分和分层采样

在Sklearn中的model_selection模块,存在train_test_split()函数,用作训练集和测试集划分,函数语法为:train_test_split(x,y,test_size = None,random_state = None,stratify = y),其中:

  • x,y: 分别为预测所需的所有特征,以及需要预测的特征
  • test_size: 测试集比例,例如test_size=0.2则表示划分20%的数据作为测试集
  • random_state: 随机种子,因为划分过程是随机的,为了进行可重复的训练,需要固定一个random_state,结果重现
  • stratify: 使用分层采样,保证训练集和测试集中类别分布一致
代码语言:javascript复制
# 划分训练集测试集,测试集比例设置为20%,保证在训练集和测试集中,正常金融交易和金融诈骗交易样本数量比例一致
from sklearn import model_selection

x = data_for_classification.drop(['isFraud'], axis=1)
y = data_for_classification['isFraud']

# 函数最终将返回四个变量,分别为`x`的训练集和测试集,以及`y`的训练集和测试集。
x_train,x_test,y_train,y_test = model_selection.train_test_split(x, y, 
                                         random_state=10, # 随机种子
                                         test_size=0.2, # 测试集比例
                                         stratify=data_for_classification["isFraud"]) #保持比例

3.3 构建随机森林模型

在Python中使用sklearn.ensembleRandomForestClassifier构建分类模型,其主要参数包括:

  • n_estimators : 训练分类器的数量(默认为10)
  • class_weight : 类别权重(默认为None),可设置{dict}或者 ‘balanced‘(自动平衡正负样本的权重)
  • random_state : 随机种子
代码语言:javascript复制
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators = 20,                # 基学习器个数
                            random_state=5,                   # 随机种子
                            #class_weight={0:1, 1:1000})
                            class_weight=______ )          # 类别权重
rf.fit(x_train, y_train)
y_pred_rf = rf.predict(x_test)

在评价模型好坏时,我们使用函数confusion_matrix()classification_report()用于输出模型的混淆矩阵和分类报告。

代码语言:javascript复制
# 输出混淆矩阵
from sklearn.metrics import classification_report,confusion_matrix

confusion_matrix = confusion_matrix(y_test, y_pred_rf)
print(confusion_matrix)

# 绘制混淆矩阵热力图

# 创建总画布窗口
plt.figure(figsize=(8,6))

# 绘制热力图,设置图像参数
# annot=True:热力图的每个单元上显示数值;annot_kws:设置单元格中数值标签的其他属性;
# fmt:指定单元格中数据的显示格式;cmap:用于热力图的填充色,'YlGnBu_r'代表数字越大,颜色越浅
sns.heatmap(confusion_matrix,annot=True,annot_kws={'size':15}, fmt='d',cmap = 'YlGnBu_r')

# 设置横纵坐标与标题
plt.ylabel('真实值')
plt.xlabel('预测值')
plt.title('随机森林混淆矩阵热力图')
代码语言:javascript复制
[[114795      2]
 [    28     26]]
代码语言:javascript复制
# 输出模型的分类报告,查看评价指标
print(classification_report(y_test, y_pred_rf))

可以看到,随机森林模型对金融诈骗交易(少数类)的召回率较低,接下来可以尝试构建其他模型。

3.4 构建XGBoost模型

XGBoost模型是基于Boosting思想的集成学习方法,在Python中使用xgboost.sklearn XGBClassifier构建分类模型,其主要参数包括:

  • n_estimators : 训练分类器的数量(默认为100)
  • learning_rate : 学习率(默认为0.3)
  • objective : 目标函数,默认是reg:linear,最常用的值有binary:logistic,二分类的逻辑回归,返回预测的概率
  • scale_pos_weight :默认为1。在二分类模型中,如果两个分类的样本比例失衡,可以设置该参数,模型效果会更好
  • random_state : 随机种子
代码语言:javascript复制
# 构建XGBoost模型
from xgboost.sklearn import XGBClassifier
xgbt = XGBClassifier(n_estimators=100,                 # 基学习器
                          learning_rate=0.3,           # 学习率
                          random_state=5,              # 随机种子
                          objective='binary:logistic', # 目标函数
                          ____________=100)        # 正样本权重 
xgbt.fit(x_train, y_train)
y_pred_xgbt = xgbt.predict(x_test)
代码语言:javascript复制
# 输出混淆矩阵
from sklearn.metrics import classification_report,confusion_matrix

confusion_matrix = confusion_matrix(y_test, y_pred_xgbt)
print(confusion_matrix)

# 绘制混淆矩阵热力图

# 创建总画布窗口
plt.figure(figsize=(8,6))

# 绘制热力图,设置图像参数
# annot=True:热力图的每个单元上显示数值;annot_kws:设置单元格中数值标签的其他属性;
# fmt:指定单元格中数据的显示格式;cmap:于热力图的填充色,'YlGnBu_r'代表数字越大,颜色越浅
sns.heatmap(confusion_matrix,annot=True,annot_kws={'size':15}, fmt='d',cmap = 'YlGnBu_r')

# 设置横纵坐标与标题
plt.ylabel('真实值')
plt.xlabel('预测值')
plt.title('XGBoost混淆矩阵热力图')
代码语言:javascript复制
print(classification_report(y_test, y_pred_xgbt))

可以看到,XGBoost模型效果不错,对金融诈骗交易(少数类)的召回率达到了78%

代码语言:javascript复制
# 通过网格搜索选择最优参数
from sklearn.model_selection import GridSearchCV
param_grid = [{
    'n_estimators':[10,20,30,50],
    'scale_pos_weight':[50,100,200,500,1000,2000]
}]
grid_search = GridSearchCV(xgbt, param_grid, scoring = 'f1')
代码语言:javascript复制
# 输出最佳参数组合以及分数
grid_search.fit(x, y)

print("best params:", grid_search.best_params_)

print("best score:", grid_search.best_score_)
代码语言:javascript复制
# 输出应用最佳参数组合时,模型的评价指标结果
from sklearn.metrics import recall_score, precision_score,f1_score

best_estimator = grid_search.best_estimator_

print("test precision:{:.3f}".format(precision_score(y_test, best_estimator.predict(x_test))))
print("test recall:{:.3f}".format(recall_score(y_test, best_estimator.predict(x_test))))
print("test f1-score:{:.3f}".format(f1_score(y_test, best_estimator.predict(x_test))))

可以看到,选择参数'n_estimators': 10, 'scale_pos_weight': 50,模型表现良好,对金融诈骗交易(少数类)的召回率达到了90.7%

4. 总结

本案例首先通过描述性统计和可视化的方法初步分析了金融诈骗交易在交易时间、交易类型和交易金额方面的分布特征。然后通过构建随机森林与XGBoost,用以识别金融诈骗交易,在该案例中XGBoost模型表现较优。本案例的处理方法能够在一定程度上对金融诈骗交易的识别问题提供参考。

爱数课(iDataCourse)是一个面向院校的大数据和人工智能课程和资源平台。平台提供权威的课程资源、数据资源、案例实验资源,助力院校大数据和人工智能专业建设,课程建设和师资能力建设。

0 人点赞