机器学习案例之_金融反欺诈

给你一囗甜甜゛ 提交于 2020-01-16 20:45:03

项目
本项目通过利用信用卡的历史交易数据,进行机器学习,构建信用卡反欺诈预测模型,提前发现客户信用卡被盗刷的事件。

项目背景
数据集包含由欧洲持卡人于2013年9月使用信用卡进行交的数据。此数据集显示两天内发生的交易,其中284,807笔交易中有492笔被盗刷。数据集非常不平衡, 积极的类(被盗刷)占所有交易的0.172%。

它只包含作为PCA转换结果的数字输入变量。不幸的是,由于保密问题,我们无法提供有关数据的原始功能和更多背景信息。特征V1,V2,… V28是使用PCA 获得的主要组件,没有用PCA转换的唯一特征是“时间”和“量”。特征’时间’包含数据集中每个事务和第一个事务之间经过的秒数。特征“金额”是交易金额,此特 征可用于实例依赖的成本认知学习。特征’类’是响应变量,如果发生被盗刷,则取值1,否则为0。 以上取自Kaggle官网对本数据集部分介绍(谷歌翻译),关于数据集更多介绍请参考《Credit Card Fraud Detection》。

场景解析(算法选择)
首先,我们拿到的数据是持卡人两天内的信用卡交易数据,这份数据包含很多维度,要解决的问题是预测持卡人是否会发生信用卡被盗刷。信用卡持卡人是否会发生被盗刷只有两种可能,发生被盗刷或不发生被盗刷。又因为这份数据是打标好的(字段Class是目标列),也就是说它是一个监督学习的场景。于是,我们判定信用卡持卡人是否会发生被盗刷是一个二元分类问题,意味着可以通过二分类相关的算法来找到具体的解决办法,本项目选用的算法是逻辑斯蒂回归(Logistic Regression)。
分析数据:数据是结构化数据 ,不需要做特征抽象。特征V1至V28是经过PCA处理,而特征Time和Amount的数据规格与其他特征差别较大,需要对其做特征缩放,将特征缩放至同一个规格。在数据质量方面 ,没有出现乱码或空字符的数据,可以确定字段Class为目标列,其他列为特征列。
这份数据是全部打标好的数据,可以通过交叉验证的方法对训练集生成的模型进行评估。70%的数据进行训练,30%的数据进行预测和评估。 &emsp&emsp现对该业务场景进行总结如下:
根据历史记录数据学习并对信用卡持卡人是否会发生被盗刷进行预测,二分类监督学习场景,选择逻辑斯蒂回归(Logistic Regression)算法。
数据为结构化数据,不需要做特征抽象,但需要做特征缩放。
源数据格式:
在这里插入图片描述
步骤

1.数据获取与解析

导包
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
import matplotlib.pyplot as plt
%matplotlib inline

credit = pd.read_csv("./creditcard.csv")
credit.shape

credit.head()

在这里插入图片描述
从上面可以看出,此数据有28万行,31列,数据为结构化数据,不需要抽特征转化,但特征Time和Amount的数据规格和其他特征不一样,需要对其做特征做特征缩放

#查看数据是否有缺失值
credit.isnull().any()
#产看数据类型
credit.info()

由显示结果可以看出,数据类型只有float64和int64,且无缺失值,方便后续处理
在这里插入图片描述
在这里插入图片描述

2.特征工程

c_counts  = credit["Class"].value_counts()
c_counts

在这里插入图片描述

#绘制图形:饼图和柱形图
plt.figure(figsize=(8,8))

ax = plt.subplot(1,2,1)
c_counts.plot(kind = "pie",autopct = "%0.3f%%",ax = ax)

ax = plt.subplot(1,2,2)
c_counts.plot(kind = "bar",ax = ax)

在这里插入图片描述
通过上面的图和数据可知,存在492例盗刷,占总样本的0.17%,由此可知,这是一个明显的数据类别不平衡问题,稍后我们采用过采样(增加数据)的方法对这种问题进行处理。

3、特征转换:将时间单位每秒化为单位每小时

>>> credit["Time"].max()
172792.0
>>> credit["Time"].min()
0.0
>>> divmod(7200, 3600)[0]
2
>>> credit["Time"] = credit["Time"].map(lambda x: divmod(x, 3600)[0])
>>> credit["Time"].max()
47.0

4.特征选择

#31个特征, 非常重要的
credit["V1"]

#28万多
cond0 = credit["Class"] == 0
#492
cond1  = credit["Class"] == 1

credit["V1"][cond0].plot(kind = "hist", bins = 500, normed = True)
credit["V1"][cond1].plot(kind = "hist", bins = 50, normed = True)
#目测,数据分的越开,就证明分类效果越好

在这里插入图片描述

credit.columns
cols = ['V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10',
       'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20',
       'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28']
cond_0 = credit["Class"] == 0
cond_1  =credit["Class"] == 1
plt.figure(figsize=(12, 28*6))
for i, col in enumerate(cols):
    ax = plt.subplot(28, 1, i+1)
    credit[col][cond_0].plot(kind = "hist", bins = 500,normed = True,ax= ax)
    credit[col][cond_1].plot(kind = "hist", bins = 50,normed = True,ax= ax)
    ax.set_title(col)

在这里插入图片描述
上图是不同变量在信用卡被盗刷和信用卡正常的不同分布情况,我们将选择在不同信用卡状态下的分布有明显区别的变量。因此剔除变量V8、V13 、V15 、V20 、V21 、V22、 V23 、V24 、V25 、V26 、V27 和V28变量。

>>> credit.shape
(284807, 31)
>>> drops = ["V8","V13","V15",'V20',
...        'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28']
>>> credit2 = credit.drop(labels=drops, axis = 1)
>>> credit2.shape
(284807, 19)

5.特征缩放

Amount变量和Time变量的取值范围与其他变量相差较大,所以要对其进行特征缩放
sklearn.preprocessing.StandarScaler

credit2.head()
from sklearn.preprocessing import StandardScaler

credit2["Amount"].min()
credit2["Amount"].max()

#标准化
standarscaler = StandardScaler()
#对Time,Amount 进行数据的预处理
cols = ["Time", "Amount"]
credit2[cols] = standarscaler.fit_transform(credit2[cols])

credit2["Amount"].max()
credit2["Time"].max()

6.特征重要性排序

对特征的重要性进行排序,以进一步减少变量
利用GBDT梯度提升决策树进行特征重要性排序(GradientBoostingClassifier,这个是一个算法,和决策树, 逻辑斯蒂回归是一样的)
注意:gbdt和随机森林区别

导包
from sklearn.ensemble import GradientBoostingClassifier
#GradientBoostingClassifier,这个是一个算法, 和决策树, 逻辑斯蒂回归是一样的,
#只不过在这个地方使用这个算法进行特征重要性的排序而已
#https://blog.csdn.net/w28971023/article/details/8240756
clf = GradientBoostingClassifier()
X_train  = credit2.iloc[:,:-1]
y_train = credit2["Class"]
clf.fit(X_train,y_train)

#这个值就是属性重要性的量化
feature_importance = clf.feature_importances_
feature_importance

cols = X_train.columns
cols

#从大到小进行排序,就可以看出来那个属性重要了
index = feature_importance.argsort()[::-1]
index

len(index)

plt.figure(figsize=(12,9))
plt.bar(np.arange(len(index)), feature_importance[index])

_ = plt.xticks(np.arange(len(index)), cols[index])

在这里插入图片描述

drops = ["V5","V8","V19","V1","Amount"]
credit3 = credit2.drop(labels = drops, axis = 1)

credit3.shape

7.模型训练(处理样本不平衡问题)

目标变量“Class”正常和被盗刷两种类别的数量差别较大,会对模型学习造成困扰。举例来说,假如有100个样本,其中只有1个是被盗刷样本,其余99个全为正常样本,那么学习器只要制定一个简单的方法:即判别所有样本均为正常样本,就能轻松达到99%的准确率。而这个分类器的决策对我们的风险控制毫无意义。因此,在将数据代入模型训练之前,我们必须先解决样本不平衡的问题。 现对该业务场景进行总结如下:

过采样(oversampling),增加正样本使得正、负样本数目接近,然后再进行学习。
欠采样(undersampling),去除一些负样本使得正、负样本数目接近,然后再进行学习。 本次处理样本不平衡采用的方法是过采样,具体操作使用SMOTE(Synthetic Minority Oversampling Technique),SMOET的基本原理是:采样最邻近算法,计算出每个少数类样本的K个近邻,从K个近邻中随机挑选N个样本进行随机线性插值,构造新的少数样本,同时将新样本与原数据合成,产生新的训练集。更详细说明参考CMU关于SMOTE: Synthetic Minority Over-sampling Technique的介绍。

SMOTE过采样
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
#imblearn 这个包需要下载, pip install imblearn 就可以了

X = credit3.iloc[:,:-1]
y = credit3['Class']

X_train, X_test, y_train,y_test = train_test_split(X, y, test_size = 0.3)
#咱们要对X_train进行过采样, 因为要训练 X_train 
#X_test
y_train.value_counts()

smote = SMOTE()
X_train_new, y_train_new = smote.fit_sample(X_train, y_train)
Series(y_train_new).value_counts()
#到目前为止,样本均衡了

8、自定义可视化函数

import itertools
#画图的方法,用这个图画真实值和预测值的对比情况, 交叉矩阵
def plot_confusion_matrix(cm, classes,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=0)
    plt.yticks(tick_marks, classes)

    threshold = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > threshold else "black")
                 #若对应格子上面的数量不超过阈值则,上面的字体为白色,为了方便查看

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')


9.单独的逻辑回归求得查全率(Recall rate)

注意:查全率也叫召回率

from sklearn.linear_model import LogisticRegression

logic = LogisticRegression()
logic.fit(X_train_new, y_train_new)

y_ = logic.predict(X_test)
logic.score(X_test, y_test)

#交叉表
pd.crosstab(y_test, y_)
#本来是0 (没被盗刷的)的  预测成0  83246    本来是0  预测成1了    2053个   误判
#本来是1  ()预测成了1      131    本来是1 的预测成了0(没被盗刷)    13个 漏判

#混合矩阵(又称作交叉矩阵)
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, y_)
cm

cm[1,1]

#混合矩阵可以画一个图
#Recall:正确被检索到的正样本占所有应该被检索到的值的比例
plot_confusion_matrix(cm, [0,1], title= "Recall:%0.3f"%(cm[1,1]/ (cm[1,0]+ cm[1,1])))

在这里插入图片描述

10.交叉验证和参数调优

利用GridSearchCV进行交叉验证和模型参数自动调优

from sklearn.model_selection import GridSearchCV
logic = LogisticRegression()
clf = GridSearchCV(logic, param_grid={"C":[1,0.1,10,100], "tol":[1e-3, 1e-4,1e-5]})
clf.fit(X_train_new, y_train_new)

clf.best_params_
clf.best_score_

11.预测

y2_ = clf.predict(X_test)
confusion_matrix(y_test, y2_)
y3_ = clf.best_estimator_.predict(X_test)
confusion_matrix(y_test, y3_)
cm2 = confusion_matrix(y_test, y3_)

12.结果可视化

对比逻辑斯蒂回归和GridSearchCV结果

#目前是一样  Recall是一样的
plot_confusion_matrix(cm, [0,1], "recall:%0.3f"%(cm[1,1]/(cm[1,0]+cm[1,1])))

在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!