导入类库

 1 import numpy as np
 2 import pandas as pd
 3 from pandas import Series, DataFrame
 4 import matplotlib.pyplot as plt
 5 from sklearn.preprocessing import StandardScaler
 6 from imblearn.over_sampling import SMOTE
 7 from sklearn.ensemble import GradientBoostingClassifier
 8 from sklearn.model_selection import train_test_split
 9 from sklearn.linear_model import LogisticRegression
10 from sklearn.metrics import confusion_matrix
11 import itertools
12 from sklearn.model_selection import GridSearchCV
13 from sklearn.metrics import auc, roc_curve

作图函数

 1 def plot_confusion_matrix(cm, classes,
 2                           title=\'Confusion matrix\',
 3                           cmap=plt.cm.Blues):
 4     """
 5     This function prints and plots the confusion matrix.
 6     """
 7     plt.imshow(cm, interpolation=\'nearest\', cmap=cmap)
 8     plt.title(title)
 9     plt.colorbar()
10     tick_marks = np.arange(len(classes))
11     plt.xticks(tick_marks, classes, rotation=0)
12     plt.yticks(tick_marks, classes)
13 
14     threshold = cm.max() / 2.
15     for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
16         plt.text(j, i, cm[i, j],
17                  horizontalalignment="center",
18                  color="white" if cm[i, j] > threshold else "black")  # 若对应格子上面的数量不超过阈值则,上面的字体为白色,为了方便查看
19 
20     plt.tight_layout()
21     plt.ylabel(\'True label\')
22     plt.xlabel(\'Predicted label\')
23     plt.show()

数据获取与解析

数据为结构化数据,不需要抽特征转化, 但特征Time和Amount的数据规格和其他特征不一样, 需要对其做特征做特征缩放

1 credit = pd.read_csv(\'./creditcard.csv\')
2 
3 print(\'原始行列 >>>>\', credit.shape)  # (284807行, 31列)
4 # print(credit.head())  # 前5行
5 # print(credit.dtypes)  # 查看特征(列)类型。结果:数据类型只有float64和int64
6 # print(credit.isnull().any())    # 判断是否有缺失值。结果:无缺失值,方便后续处理
7 # print(credit.info())  # 查看数据集详细信息(类型,占用大小,缺失值,行列等)

特征工程

 

 1 # c_counts = credit[\'Class\'].value_counts()
 2 # print(c_counts, type(c_counts))  # 对Class列分类统计,并判断类型
 3 # print(c_counts.index, c_counts.values)  # 提取索引和值
 4 \'\'\'
 5 结果:
 6 0    284315
 7 1       492
 8 Name: Class, dtype: int64
 9 Name: Class, dtype: int64 <class \'pandas.core.series.Series\'>
10 Int64Index([0, 1], dtype=\'int64\') [284315    492]
11 \'\'\'
12 
13 # 对c_counts作图进行分析
14 # plt.figure(figsize=(10, 6))
15 # 饼图:两种作图方式
16 # ax = plt.subplot(121)
17 # c_counts是pandas的Series类型,pandas可以使用plot快速作图
18 # c_counts.plot(kind=\'pie\', autopct=\'%0.3f%%\', ax=ax)
19 # plt.pie(c_counts, autopct=\'%0.3f%%\')
20 
21 # 柱状图:两种作图方式
22 # ax = plt.subplot(122)
23 # c_counts.plot(kind=\'bar\', ax=ax)
24 # plt.bar(c_counts.index, c_counts.values)
25 # plt.show()
26 \'\'\'
27 存在492例盗刷,占总样本的0.17%,
28 存在明显的数据类别不平衡问题,
29 可采用过采样(增加数据)的方法处理该问题
30 \'\'\'

 

特征转换

将时间从单位每秒化为单位每小时 divmod(7201,3600) 结果:(2, 1) 元组,2为商,1为余数

 

1 credit[\'Time\'] = credit[\'Time\'].map(lambda x: divmod(x, 3600)[0])
2 # print(credit[\'Time\'])  # map高级函数:将Time中的每个元素作用于lambda函数

特征选择

 

 1 # Class列中值为0的为True,值为1为False,生成的cond0行数不变
 2 # cond0 = credit[\'Class\'] == 0
 3 # Class列中值为0的为False,值为1为True,生成的cond0行数不变
 4 # cond1 = credit[\'Class\'] == 1
 5 # print(\'cond0 >>>>\', len(cond0))
 6 # print(\'cond1 >>>>\', len(cond1))
 7 
 8 # 作图分析
 9 # credit[\'V1\'][cond0].plot(kind=\'hist\', bins=500)
10 # credit[\'V1\'][cond1].plot(kind=\'hist\', bins=50)
11 # plt.show()
12 
13 # 调试查看用
14 # print("credit[\'V1\'] >>>>", credit[\'V1\'])
15 # print(\'cond0 >>>>\', cond0)
16 # print(\'cond1 >>>>\', cond1)
17 
18 # 筛选出存在于V1列中且在cond0中为True的值(284315)
19 # print("credit[\'V1\'][cond0] >>>>", credit[\'V1\'][cond0])
20 # 筛选出存在于V1列中且在cond0中为True的值(492)
21 # print("credit[\'V1\'][cond1] >>>>", credit[\'V1\'][cond1])
22 
23 \'\'\' 作图分析:将每一个特征根据Class的真假进行划分, 图像中两种图形的重合度越大说明该特征对Class的影响越小, 所以需要剔除掉无用的特征 \'\'\'
24 # cols = [\'V1\', \'V2\', \'V3\', \'V4\', \'V5\', \'V6\', \'V7\', \'V8\', \'V9\', \'V10\',
25 #         \'V11\', \'V12\', \'V13\', \'V14\', \'V15\', \'V16\', \'V17\', \'V18\', \'V19\', \'V20\',
26 #         \'V21\', \'V22\', \'V23\', \'V24\', \'V25\', \'V26\', \'V27\', \'V28\']
27 # 作图:28行,1列,每一行显示一个特征对应的图
28 # plt.figure(figsize=(12, 2800))
29 # for i, col in enumerate(cols):
30 #     ax = plt.subplot(28, 1, i + 1)
31 # density(normed)标准化数据:将过大或过小的数据统一标准化
32 #     credit[col][cond0].plot(kind=\'hist\', bins=500, density=True, ax=ax)
33 #     credit[col][cond1].plot(kind=\'hist\', bins=50, density=True, ax=ax)
34 #
35 #     ax.set_title(col)
36 # plt.show()
37 
38 # 待剔除的列(10列)
39 drops = [\'V13\', \'V15\', \'V20\', \'V22\', \'V23\', \'V24\', \'V25\', \'V26\', \'V27\', \'V28\']
40 # 删除指定列(axis=1按列,axis=0按行)
41 credit2 = credit.drop(labels=drops, axis=1)
42 print(\'人眼剔除无用列后 >>>>\', credit2.shape)
43 \'\'\' 不同变量在信用卡被盗刷和信用卡正常的不同分布情况, 选择在不同信用卡状态下的分布有明显区别的变量。 
因此剔除变量V13 、V15 、V20 、V22、 V23 、V24 、V25 、V26 、V27 和V28变量
\'\'\'

特征缩放

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

 1 # print(\'原Amount数据最大值\', credit2[\'Amount\'].max())
 2 # print(\'原Amount数据最小值\', credit2[\'Amount\'].min())
 3 # print(\'原Time数据最大值\', credit2[\'Time\'].max())
 4 # print(\'原Time数据最小值\', credit2[\'Time\'].min())
 5 
 6 # 创建标准化对象
 7 standScaler = StandardScaler()
 8 cols = [\'Time\', \'Amount\']
 9 # 标准化数据
10 credit2[cols] = standScaler.fit_transform(credit2[cols])
11 # print(\'标准化Amount后最大值 >>>>\', credit2[\'Amount\'].max())
12 # print(\'标准化Amount后最小值 >>>>\', credit2[\'Amount\'].min())
13 # print(\'标准化Time后最大值 >>>>\', credit2[\'Time\'].max())
14 # print(\'标准化Time后最小值 >>>>\', credit2[\'Time\'].min())

特征重要性排序

对特征的重要性进行排序,以进一步减少变量 利用GBDT梯度提升决策树进行特征重要性排序

 1 # 创建GBDT对象
 2 # clf = GradientBoostingClassifier()
 3 # 特征训练集:前20列
 4 # X_train = credit2.iloc[:, :-1]
 5 # print(\'X_train.shape >>>>\', X_train.shape)
 6 # cols = X_train.columns
 7 # print(\'X_train.columns >>>>\', X_train.columns)
 8 # 目标值训练集:Class列
 9 # y_train = credit2[\'Class\']  # y_train = credit2.iloc[:,-1]
10 # print(\'y_train.shape >>>>\', y_train.shape)
11 # 训练数据
12 # clf.fit(X_train, y_train)
13 # 得到特征重要性数据
14 # feature_importances_ = clf.feature_importances_
15 # print(\'feature_importances_ >>>>\', feature_importances_)
16 # 从大到小对特征重要性进行排序,并作图分析
17 # argsort():对数组排序并返回排序后每个元素对应的未排序时自身所在的索引
18 # index = feature_importances_.argsort()[::-1]
19 # print(\'从大到小排列特征重要性,返回每个元素的原索引 >>>>\', index, len(index))
20 
21 # plt.figure(figsize=(12, 9))
22 # 柱状图,第二个参数代表按从大到小排列的特征数据
23 # plt.bar(np.arange(len(index)), feature_importances_[index])
24 # 柱状图x坐标:第二个参数是按特征值从大到小排列后的特征名
25 # plt.xticks(np.arange(len(index)), cols[index])
26 # plt.show()
27 # 根据图像得到要删除的特征列(最小的后9列)
28 drops = [\'V7\', \'V21\', \'V8\', \'V5\', \'V4\', \'V11\', \'V19\', \'V1\', \'Amount\']
29 credit3 = credit2.drop(labels=drops, axis=1)
30 print(\'通过GBDT分析剔除无用列后 >>>>\', credit3.shape)
31 # print(\'credit3.columns >>>>\', credit3.columns)

模型训练

 

处理样本不平衡问题
目标变量“Class”正常和被盗刷两种类别的数量差别较大,会对模型学习造成困扰。
举例来说,假如有100个样本,其中只有1个是被盗刷样本,
其余99个全为正常样本,那么学习器只要制定一个简单的方法:
即判别所有样本均为正常样本,就能轻松达到99%的准确率。
而这个分类器的决策对我们的风险控制毫无意义。
因此,在将数据代入模型训练之前,我们必须先解决样本不平衡的问题。
现对该业务场景进行总结如下:
过采样(oversampling):
增加正样本使得正、负样本数目接近,然后再进行学习。
欠采样(undersampling):
去除一些负样本使得正、负样本数目接近,然后再进行学习。 
本次处理样本不平衡采用的方法是过采样,
具体操作使用SMOTE(Synthetic Minority Oversampling Technique),
SMOET的基本原理是:
采样最邻近算法,计算出每个少数类样本的K个近邻,
从K个近邻中随机挑选N个样本进行随机线性插值,
构造新的少数样本,同时将新样本与原数据合成,产生新的训练集。

  

 1 # SMOTE 过采样
 2 X = credit3.iloc[:, :-1]
 3 y = credit3.Class
 4 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
 5 X_train,y_train 作为训练数据 训练时,保证样本均衡,将X_train和y_train样本过采样处理 测试时候,可以样本不均衡
 6 # print(\'未均衡的y训练集分类统计(Class) >>>>\', y_train.value_counts())
 7 
 8 smote = SMOTE()
 9 # ndarray
10 X_train_new, y_train_new = smote.fit_sample(X_train, y_train)
11 # print(\'均衡后的x训练集 >>>>\', X_train_new, type(X_train_new))
12 # print(\'均衡后的y训练集(Class) >>>>\', y_train_new, type(y_train_new), len(y_train_new))
13 # y_train_new类型为numpy.ndarray,需转化为pandas.Series类型才可分类统计
14 # print(\'均衡后的y训练集分类统计(Class) >>>>\', Series(y_train_new).value_counts())

求召回率

单独的逻辑回归求得查全率Recall rate,Recall也叫召回率

 

 1 # 创建逻辑回归对象
 2 # logistic = LogisticRegression()
 3 # print(logistic)
 4 \'\'\'
 5 LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
 6           intercept_scaling=1, max_iter=100, multi_class=\'ovr\', n_jobs=1,
 7           penalty=\'l2\', random_state=None, solver=\'liblinear\', tol=0.0001,
 8           verbose=0, warm_start=False)
 9 \'\'\'
10 # 训练均衡后的数据
11 # logistic.fit(X_train_new, y_train_new)
12 # 预测
13 # y_ = logistic.predict(X_test)
14 # print(\'y_test >>>>\', y_test)
15 # print(\'预测的y_ >>>>\', y_)
16 # 交叉表
17 # print(\'交叉表 >>>>\', pd.crosstab(y_test, y_, margins=True))
18 
19 # 混合矩阵
20 # cm = confusion_matrix(y_test, y_)
21 # print(\'混合矩阵 >>>>\', cm, type(cm))
22 # Recall------“正确被检索的正样本item(TP)"占所有"应该检索到的item(TP+FN)"的比例
23 # plot_confusion_matrix(cm, [0, 1], title=\'Recall:%0.3f\' % (cm[1, 1] / (cm[1, 0] + cm[1, 1])))

交叉验证与调优

 

 1 logistic = LogisticRegression()
 2 clf = GridSearchCV(logistic, param_grid={\'tol\': [1e-3, 1e-4, 1e-5], \'C\': [1, 0.1, 10, 100]}, cv=10, iid=False, n_jobs=1)
 3 print(clf.fit(X_train_new, y_train_new))
 4 # print(\'best_score_ >>>>\', clf.best_score_)
 5 # print(\'best_params_ >>>>\', clf.best_params_)
 6 # print(\'best_index_ >>>>\', clf.best_index_)
 7 # print(\'best_estimator_ >>>>\', clf.best_estimator_)
 8 
 9 # 预测
10 # y3_ = clf.best_estimator_.predict(X_test)
11 # print(\'y3_预测(best_estimator_) >>>>\', confusion_matrix(y_test, y3_))
12 
13 # y2_ = clf.predict(X_test)
14 # print(\'y2_预测 >>>>\', confusion_matrix(y_test, y2_))
15 
16 # cm2 = confusion_matrix(y_test, y2_)
17 
18 # 可视化,对比逻辑斯蒂回归和GridSearchCV结果
19 # plot_confusion_matrix(cm, [0, 1], title=\'Logistic Recall:%0.3f\' % (cm[1, 1] / (cm[1, 0] + cm[1, 1])))
20 # plot_confusion_matrix(cm2, [0, 1], title=\'GridSearchCV Recall:%0.3f\' % (cm2[1, 1] / (cm2[1, 0] + cm2[1, 1])))

模型评估

解决不同的问题,通常需要不同的指标来度量模型的性能。
例如我们希望用算法来预测癌症是否是恶性的,
假设100个病人中有5个病人的癌症是恶性, 
对于医生来说,尽可能提高模型的查全率(recall)比提高查准率(precision)更为重要,
因为站在病人的角度,发生漏发现癌症为恶性比发生误 判为癌症是恶性更为严重
由此可见就上面的两个算法而言,明显lgb过拟合了,
考虑到样本不均衡问题,
故应该选用简单一点的算法(逻辑回归)来减少陷入过拟合的陷阱

  

1 y_proba = clf.predict_proba(X_test)
2 # 预测被盗刷的概率
3 print(y_proba)

 

版权声明:本文为siplips原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/siplips/p/9810354.html