项目实战-信用卡检测任务
基于信用卡交易记录数据建立分类模型来预测那些交易是异常的哪些是正常的。
任务流程:
- 加载数据,观察问题(缺失值、异常值)
- 针对问题给出解决方案
- 数据集切分
- 评估方法对比
- 逻辑归回模型
- 建模结果对比
- 方案效果对比
主要解决问题
(1)。。。
数据分析
对于列每一个样本属性进行了脱敏处理,最后一列class是标签,标记正常异常。
实战代码
导包
import pandas as pd
import matplotlib.pyplot as plt
import numpy as ny
%matplotlib inline
# 将图嵌入
读文件
这里要先在jupyter首页把文件upload
data = pd.read_csv("creditcard.csv")
data.head()
提出问题:这两列数据大小差别很大;可能会导致cita权重不起作用,我们希望使用cita来表示样本特征的重要程度,而不是样本特征的数值大小
数据标签分布
count_classes = pd.value_counts(data['Class'],sort = True).sort_index()
# 对于Class这一列统计0和1的个数,指定排序,将多的排在前面
count_classes.plot(kind='bar')
plt.title("Fraud class histogram")
plt.xlabel("Class")
plt.ylabel("Frequency")
存在问题:正负样本数量差别太大,预测时极易容易预测成多的样本类型。
解决问题:1. over sample 过采样,希望正负样本一样多,对少的样本造数据。
2. under sample 下采样,希望正负样本一样少,取多的里面跟少的一样的样本数
数据标准化处理
使两列取值范围差别大的映射到相同的取值范围
第一步、(x-μ)/(y-μ),数据有左上变右下
第二步、(x-μ)/cigama (是对某一列单独做的。÷这一列的方差,取值范围大的方差也大,从而除数也大,缩小了范围;反之亦然,扩大了范围)
fit 看作求μ和cigama(等参数),transform进行转换(可以查看sklearn的API文档)values作用是转化成array,reshape使文档要求二维
from sklearn.preprocessing import StandardScaler
data['normAmount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1,1))
data = data.drop(['Time','Amount'],axis=1)
data.head()
可以看到normAmount这一列已经跟前面取值范围类似了(实际情况应该对每一列都进行处理,不过前几列Vi已经是标准的了)
1. 下采样方案
从正常样本(多的样本)提取跟异常样本(少的样本)相同数目的数据
# X特征,Y标签 “:”就是取所有特征
X=data.iloc[:,data.columns!='Class']
Y=data.iloc[:,data.columns=='Class']
# 从多的里面选择跟少的一样的数目
# 得到所有异常样本的索引
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)
random_normal_indices=np.array(random_normal_indices)
# 将正常和异常的样本索引拼在一起
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']
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))
交叉验证
划分成三部分:训练,检测和最终测试集。训练后使用val验证训练结果,如果训练结果不好再返回训练,直到验证结果不错在使用test数据集测试;但是有可能最后的两个样本偏简单或者难,预测结果会出现误差。
所以切分和建立模型过程如下图,建立四次,最后求验证结果的和并求均值。
数据集划分
在理想情况下,下采样测试集的正常异常样本比例应该是1:1的;但是如果是这样计算相当于透题,准确率会偏高;但是实际情况下因该是极度不均衡的;应该使用下采样的训练集训练,使用原始样本进行测试(因为原始样本是真实的)
from sklearn.cross_validation import train_test_plit
# 整个数据集划分,random_state=0保证每次数据集划分是一样的,避免建立不同模型时难以区分是模型起作用还是数据集影响
X_train,X_test,y_train,y_test=train_test_plit(X,y,test_size=0.3,random_state=0)
print("原始训练集包含样本数量:",len(X_tarin))
print("原始测试集包含样本数量:",len(X_test))
print("原始样本数量:",len(X_tarin)+len(X_test))
# 对下采样数据集切分
X_train_undersample,X_test_undersample,y_train_undersample,y_test_undersample=train_test_plit(X_undersample,
y_undersample,
test_size=0.3,
random_state=0)
print(" ")
print("下采样训练集包含样本数量:",len(X_tarin_undersample))
print("下采样测试集包含样本数量:",len(X_test_undersample))
print("下采样样本数量:",len(X_tarin)+len(X_test_undersample)+len(X_test_undersample))
模型评估方法
不应该只看准确率,也应该看召回率,这里我们是想要得到的异常的,所以应该以异常的为分母,查看有多少异常的被预测出来,这就是召回率,我们应该用召回率评估模型
首先清楚TP,FP,T,FN的概念
训练逻辑回归模型
导包
# Recall=TP/(TP+FN)
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
from sklearn.model_selection import cross_val_predict
进行交差验证。评估惩罚力度里的不同参数的结果,外层循环遍历不同的参数,对每一个参数进行交叉验证,选定一个参数进行多次建模求平均值,求得每一个惩罚力度的recall。
def printing_Kfold_scores(x_train_data,y_train_data):
fold=KFold(5,shuffle=False)
# 定义不同惩罚力度
c_param_range=[0.01,0.1,1,10,100]
# 展示结果所用表格
results_table=pd.DataFrame(index=range(len(c_param_range),2),columns=['C_parameter','Mean recall score'])
results_table['C_parameter']=c_param_range
# k_fold 表示K折的交叉验证,这里会得到两个索引集合,训练集=indices[0] 测试集=inices[1]
j=0
# 外层循环遍历参数
for c_param in c_param_range:
print('------------------------------')
print('正则化惩罚力度:',c_param)
print('-----------------------------')
print(' ')
recall_accs=[]
# 进行交叉验证
# 第一个参数是i,第二个参数是索引标签,0表示训练集,1表示测试集;执行五次
for train_index,test_index in fold.split(x_train_data):
# 指定算法模型,并且给定参数;通过查询API文档可知C这个参数值是取反的,给的值越小惩罚力度越大
lr=LogisticRegression(C=c_param,penalty='l1',solver='liblinear')
# 训练模型 通过索引train_index取出的是训练集属性和标签,values取值不带列名,ravel使数据变成一维
lr.fit(x_train_data.iloc[train_index,:],y_train_data.iloc[train_index,:].values.ravel())
# lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel())
# 建立好模型后,预测模型结果,通过索引1取出验证集;y_pred_undersample就是预测结果
y_pred_undersample=lr.predict(x_train_data.iloc[test_index,:].values)
# y_pred_undersample=lr.predict(x_train_data.iloc[indices[1],:].values)
# 有了预测结果,进行评估 recall_score传入预测值和真实值
recall_acc=recall_score(y_train_data.iloc[test_index,:].values,y_pred_undersample)
# 保存起来做一步计算
recall_accs.append(recall_acc)
print('召回率=',recall_acc)
# 当执行完所有交叉验证后,计算平均结果
results_table.loc[j,'Mean recall score']=np.mean(recall_accs)
j+=1
print(' ')
print('平均召回率',np.mean(recall_accs))
print(' ')
# 找到最好的参数,哪一个recall高,就是最好的
best_c=results_table.loc[results_table['Mean recall score'].astype('float32').idxmax()]['C_parameter']
# 打印最好的结果
print('**************************************')
print('效果最好的模型所选参数=',best_c)
print('**************************************')
return best_c
调用函数
best_c=printing_Kfold_scores(X_train_undersample,y_train_undersample)
结果截图。可以看到对于不同的惩罚力度差距还是比较大的。