信用卡欺诈检测
1、项目介绍
\quad \quad 本项目通过利用信用卡的历史交易数据,进行机器学习,构建信用卡反欺诈预测模型,提前发现客户信用卡被盗刷的事件。
2、项目背景
\quad \quad 该项目所使用的数据集包含持卡人在两天内使用信用卡的交易情况,共有284807笔交易,其中有492笔交易为盗刷。数据集中的数据是经过了PCA降维,并且出于保密原因,这些特征都进行了脱敏处理,数据以秒为单位记录。通过对这些数据的分析,建模,可以对信用卡盗刷情况进行预测。有利于银行对存在风险的交易采取措施,减小银行和持卡人的损失。并设置合理的阈值,使得银行在减小盗刷损失的前提下,更好的提升使用信用卡的体验。
3、分析项目
\quad \quad 基于信用卡交易记录数据建立分类模型来预测哪些交易记录是异常的哪些是正常的。
4、数据读取与分析
4.1 加载数据
# 导入工具包
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# 魔法指令,在Notebook画图展示使用的
%matplotlib inline
data = pd.read_csv("creditcard.csv")
# 展示数据前前五行,查看数据
data.head()
此图为截取部分数据,原始数据集共有31列,其中数据特征有30列,Amount列表示贷款的金额(其数值变化幅度很大,后续处理时可进行标准化处理),Class列表示分类结果,若Class为0代表该条交易记录正常,若Class为1代表该条交易记录异常。
4.2 查看数据的标签分布
这里看看异常数据和正常数据各自的数量:
count_classes=pd.value_counts(data['Class'],sort=True).sort_index()
print(count_classes)
count_classes.plot(kind='bar')# 柱形图
plt.title("Fraud class histogram")
plt.xlabel("class")
plt.ylabel("Frequency")
out:
0 284315
1 492
Name: Class, dtype: int64
从图中可以看出标签为0的很多,而标签为1的却很少,说明样本的分布情况是非常不均衡的,所以在构建分类器的时候要特别注意一个误区,即使将结果全部预测为0也会出现很好的分类结果,这是在数据处理中需要着重考虑的一点。
5、数据预处理
5.1 特征标准化
1、首先对Amount的值进行标准化处理,从机器学习库Scikit-Learn引入标准化函数即可。
from sklearn.preprocessing import StandardScaler
# 在数据集中加入Amount标准化的一列数据,reshape(-1,1)的含义是将传入数据转换成一列的形式
data['normAmount']=StandardScaler().fit_transform(data['Amount'].values.reshape(-1,1))
# drop操作去掉无用特征
data = data.drop(['Time','Amount'],axis=1)
data.head()
5.2. 使用下采样解决样本数据不均衡
\quad \quad 首先计算异常样本的个数并取其索引,接下来在正常样本中随机选择指定个数样本,最后把所有样本索引拼接在一起即可。
#取出所有属性,不包含class的这一列
X = data.iloc[:, data.columns != 'Class']
#取出class这一列
y = data.iloc[:, data.columns == 'Class']
#计算出class==1(存在欺诈行为)元素有多少个
number_records_fraud = len(data[data.Class == 1])
# 得到所有异常样本的索引
fraud_indices = np.array(data[data.Class == 1].index)
# 得到所有正常样本的索引
normal_indices = data[data.Class == 0].index
# 在正常样本中随机采样出指定个数的样本,并取其索引
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)
# 转换成numpy的格式
random_normal_indices = np.array(random_normal_indices)
# 有了正常和异常样本后把它们的索引都拿到手,将class=0和1的样本的索引拼接在一起
under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])
# 根据索引得到下采样所有样本点
under_sample_data = data.iloc[under_sample_indices,:]
#下采样数据集的数据
X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class']
#下采样数据集的label
y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class']
# 下采样 样本比例
print("正常样本所占整体比例: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
print("异常样本所占整体比例: ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
print("下采样策略总体样本数量: ", len(under_sample_data))
输出结果
正常样本所占整体比例: 0.5
异常样本所占整体比例: 0.5
下采样策略总体样本数量: 984
6、训练数据即划分数据集
疑问? 为什么进行了下采样,还要把原始数据进行切分呢?
这是因为 对数据集的训练是通过下采样的训练集,对数据的测试的是通过原始的数据集的测试集,下采样的测试集可能没有原始部分当中的一些特征,不能充分进行测试。
from sklearn.model_selection import train_test_split
# 整个数据集进行划分
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)
print("原始训练集包含样本数量: ", len(X_train))
print("原始测试集包含样本数量: ", len(X_test))
print("原始样本总数: ", len(X_train)+len(X_test))
# 下采样数据集进行划分
X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample
,y_undersample
,test_size = 0.3
,random_state = 0)
print("")
print("下采样训练集包含样本数量: ", len(X_train_undersample))
print("下采样测试集包含样本数量: ", len(X_test_undersample))
print("下采样样本总数: ", len(X_train_undersample)+len(X_test_undersample))
7、模型建立
7.1 sklearn LR工具包
class
sklearn.linear_model.LogisticRegression(penalty='l2', *, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None,
random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)
7.2 模型调参,初步建立模型
1、正则化惩罚参数调一调
\quad \quad 使用逻辑回归模型构建分类器,通过k折交叉验证寻找最优正则化惩罚参数C。
\quad \quad 由于本文数据的特殊性,分类模型的评估的方法十分钟重要,通常采用的评价指标有准确率、召回率和F值(F-Measure)等。本文采用recall(召回率)作为评估标准。原因是具体举个例子介绍:假设我们在医院中有1000个病人,其中990个为正样本(正常),10个为负样本(癌症),我们的目的是找出其中的10个负样本,假如我们的模型将多有的1000个病人都预测为正样本,虽然精度有99%,但是并没有找到我们所要的10个负样本,所以这个模型是没用的,因为一个癌症病人都找不出来。而recall是对于想找的东西,找到了多少个,而不是所有样本的精度。
\quad \quad 在构造权重参数的时候,为了防止过拟合的现象发生,要引入正则化惩罚项,使这些权重参数处于比较平滑的趋势,具体参数选择在代码中会给出解释。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold,cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report
def printing_Kfold_scores(x_train_data,y_train_data):
#k折交叉验证
fold = KFold(n_splits=5,shuffle=False)
#不同的惩罚参数C的参数集,因为不知道哪一种惩罚参数的力度好,通过验证集结果来选择
c_param_range = [0.01,0.1,1,10,100]
#创建一个5行两列的空的DataFrame框,用于存放数据
results_table = pd.DataFrame(index = range(len(c_param_range),2), columns