信用卡欺诈数据分析与建模

数据集是来自kaggle上的信用卡进行交易的数据。此数据集显示两天内发生的交易,其中284,807笔交易中有492笔被盗刷。数据集非常不平衡,被盗刷占所有交易的0.172%。其中数据特征v1,v2....v28是某些特征,银行为了保密,并没有提供具体代表的内容,Class是响应变量,如果发生被盗刷,则取值1,否则为0。Amount为消费金额。

首先来看下具体数据内容:

data = pd.read_csv("/Users/weillschang/Desktop/jupyter notebook/creditcard_fraud/creditcard.csv")
data.head()

输出内容


image

看下class值的分布情况

data['Class'].value_counts()

其中class =0的有284315 class=1也就是属于欺诈类型的有492,两者比例超过500,典型的样本分布不均衡。
对于机器学习常见的分类问题,从训练模型的角度来看,比较理想的情况下是正类和负类样本的数量相差不多,如果某类的样本数量很少,那么自然所能提供的信息就很少,用这些不平衡的数据训练出来的模型,其预测结果偏向训练数据数据比较多的哪一类,比如在正负样本比例为9:1是,当预测精度90%时,即使模型将结果全部划分为90%的那一类,其准确度也有90%,而这是没有意义的,自然我们需要对其进行处理

样本不均衡问题

对样本不均衡的处理通常有以下三种方式,这里有参考这个这篇博客数据不均衡的处理

  • 欠采样
    抛弃数据集中样本数量较多的类别,来缓解不平衡问题,,缺点是会丢失多数类样本中的一些重要信息。
  • 过采样
    对训练集里面过少的样本进行新的数据合成,来达到数据平衡的问题,这里比较经典的算法是SMOTE算法,他会从相近的几个样本中,加入随机噪声,随机扰动一个特征,来生成新的数据实例
  • 权重值的调整
    也就是说调整权重值,将少数样本权重设置为一个较大权重,多数样本设置一个较小权重。

对于信用卡欺诈这个问题,面对不平衡超过500,如果用欠采样的话,会丢弃20多万条数据,这很可能会导致丢失很多重要的信息,而权重调整这里也并不容易找到合适的权重值,这里采用过采样来合成新数据

数据预处理

在合成数据之前我们需要对数据进行常规的预处理,将可能的特征属性进行标准化处理,因为算法都假设所有数据集的所有特征集中在0附近,并且有相同的方差,如果某个特征方差远大于其他特征方差,那么该特征可能在目标函数中占得权重更大,而且差距太大的话,这会对收敛速度产生很大的影响,甚至可能不收敛,这里采用sk-learn自带的StandardScaler来进行处理

from sklearn.preprocessing import StandardScaler

data['Amount'] = StandardScaler().fit_transform(data['Amount'].reshape(-1, 1))
data.drop(['Time'],axis=1)

处理后该列数据会变成均值为0,方差为1的一列数据。

使用SMOTE 算法进行数据合成

SMOTE算法很简单,可以说是K 近邻算法的逆操作,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻后,在从其K 近邻中随机选择N个样本, 在从这些样本与原来的样本之间随机构建生成一个新的数据。
如下:

## 先对数据集进行分割,按30% 划分
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.cross_validation import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report 
X = data.loc[:, data.columns != 'Class']
Y = data.loc[:, data.columns == 'Class']
features_train, features_test, labels_train, labels_test = train_test_split(X,   Y,   test_size=0.2,  random_state=0)
  ## sample 生成
oversampler=SMOTE(random_state=0)
new_features,new_labels=oversampler.fit_sample(features_train,labels_train)

这样数据合成之后,二者便会有相同的样本数了,接下来便可以进行逻辑回归生成模型

模型的生成与调参

对于有监督学习算法,过拟合比欠拟合有时更难处理,尤其是过多的特征与过少的数据,最会容易导致过拟合问题
解决过拟合问题,通常有增加数据集,和减少模型复杂度(比如减少学习特征,让某个特征不被模型学习到),而正则化则是减少模型复杂度的一种方法
正则化中我们将保留所有的特征变量,但是会减小特征变量的数量级(参数数值的大小θ(j))。
sk_learn 逻辑回归算法,提供了c值也就是正则化系数倒数供我们选择,c值越小,对应越强的正则化,越强的正则化越能得到一个更简单的假设曲线,也就越能减少过拟合的风险(可以,这很“奥体姆剃刀”),下面使用不同的c值进行准确率的计算

from sklearn.linear_model import LogisticRegression
fold = KFold(len(os_labels),5,shuffle=False) 
c_param_range = [0.01,0.1,1,10,100]
for c_param in c_param_range:
    print('C parameter: ', c_param)
    listacc=[]
    for iteration, indices in enumerate(fold,start=1):
       
        lr = LogisticRegression(C = c_param, penalty = 'l1')
        lr.fit(new_features.iloc[indices[0],:],new_labels.iloc[indices[0],:].values.ravel())
        predicted_data=lr.predict(new_features.iloc[indices[1],:].values)
        recall_acc = recall_score(new_labels.iloc[indices[1],:].values,predicted_data)
        listacc.append(recall_acc)
        print" recall score = {}".format(recall_acc)
    print "mean_acc:{}".format(float(sum(listacc))/len(listacc))

penalty参数选择l1正则化范式,比较适合模型的特征非常多,同时希望将一些不重要的特征系数归零,从而让模型系数更加稀疏,权重值更低
这里衡量精度采用recall_score,也就是召回率,而不是准确率和正确率。

召回率=TP/(TP+FN)
也就是真正为正例的样本中,正确预测为正例的比例
这里以欺诈为正例。

也就是说假设真的欺诈数目有10个,我们正确预测到了其中9个,其召回率也就是0.9。
如上图最终输出结果为

.....
('C parameter: ', 0.01)
 recall score = 0.922580645161
 recall score = 0.901315789474
 recall score = 0.931415292686
 recall score = 0.922632197931
 recall score = 0.921346215144
mean_acc:0.919858028079
('C parameter: ', 0.1)
 recall score = 0.922580645161
 recall score = 0.907894736842
 recall score = 0.932123492309
 recall score = 0.924105032919
 recall score = 0.922500302261
mean_acc:0.921840841899
('C parameter: ', 1)
 recall score = 0.916129032258
 recall score = 0.907894736842
 recall score = 0.932167754786
 recall score = 0.924324859037
 recall score = 0.922687154461
mean_acc:0.920640707477
.......

这里选取recall平均值最大的c值,即为0.1
接下来,把预测结果的结果精度显示在一个混淆矩阵里面,看下当前预测结果,(绘制混淆矩阵代码,参考自网上)

from sklearn.cross_validation import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report 
import itertools
from sklearn.linear_model import LogisticRegression
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)

    thresh = 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] > thresh else "black")

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

单单看召回率还是不错,可是右上角的还有1045个的误杀(即将正常的判断为欺诈),接下来考虑设置不同的判断阈值来做更严格的划分并画出混淆矩阵,默认的是0.5 即将将结果划分为超过50%一侧的值。

lr = LogisticRegression(C = 0.1, penalty = 'l1')
lr.fit(new_features,new_labels.values.ravel())
y_pred_proba = lr.predict_proba(features_test.values)
from __future__ import division 
thresholds = [0.6,0.7,0.8,0.9]

plt.figure(figsize=(10,10))

j = 1
for i in thresholds:
    y_test_predictions_high_recall = y_pred_proba[:,1] > i
    
    plt.subplot(3,3,j)
    j += 1
    
    cnf_matrix = confusion_matrix(labels_test,y_test_predictions_high_recall)
    np.set_printoptions(precision=2)

    print "Recall:{}".format(cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
   
    class_names = [0,1]
    plot_confusion_matrix(cnf_matrix
                          , classes=class_names
                          , title='Threshold >= %s'%i) 

结果如下


image

很明显 随着判断阈值越大(即判断的要求更严格),召回率是下降趋势的,但是误杀的概率则明显下降,综合来看0.8附近是个相对比较合理的值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容