信用卡风险评估

今天我来带你做一个数据挖掘的项目。在数据挖掘的过程中,我们经常会遇到一些问题,比如:如何选择各种分类器,到底选择哪个分类算法,是 SVM,决策树,还是 KNN?如何优化分类器的参数,以便得到更好的分类准确率?这两个问题,是数据挖掘核心的问题。


当然对于一个新的项目,我们还有其他的问题需要了解,比如掌握数据探索和数据可视化的方式,还需要对数据的完整性和质量做评估。这些内容我在之前的课程中都有讲到过。今天的学习主要围绕下面的三个目标,并通过它们完成信用卡违约率项目的实战


这三个目标分别是:

  • 创建各种分类器,包括已经掌握的 SVM、决策树、KNN 分类器,以及随机森林分类器;
  • 掌握 GridSearchCV 工具,优化算法模型的参数;
  • 使用 Pipeline 管道机制进行流水线作业。因为在做分类之前,我们还需要一些准备过程,比如数据规范化,或者数据降维等。

构建随机森林分类器


在算法篇中,我主要讲了数据挖掘十大经典算法。实际工作中,你也可能会用到随机森林。随机森林的英文是 Random Forest,英文简写是 RF。它实际上是一个包含多个决策树的分类器,每一个子分类器都是一棵 CART 分类回归树。所以随机森林既可以做分类,又可以做回归。


当它做分类的时候,输出结果是每个子分类器的分类结果中最多的那个。你可以理解是每个分类器都做投票,取投票最多的那个结果。当它做回归的时候,输出结果是每棵 CART 树的回归结果的平均值。在 sklearn 中,我们使用 RandomForestClassifier() 构造随机森林模型,函数里有一些常用的构造参数:
352035fe3e92d412d652fd55c77f23f9.png
当我们创建好之后,就可以使用 fit 函数拟合,使用 predict 函数预测。

使用 GridSearchCV 工具对模型参数进行调优


在做分类算法的时候,我们需要经常调节网络参数(对应上面的构造参数),目的是得到更好的分类结果。实际上一个分类算法有很多参数,取值范围也比较广,那么该如何调优呢?


Python 给我们提供了一个很好用的工具 GridSearchCV,它是 Python 的参数自动搜索模块。我们只要告诉它想要调优的参数有哪些以及参数的取值范围,它就会把所有的情况都跑一遍,然后告诉我们哪个参数是最优的,结果如何。


使用 GridSearchCV 模块需要先引用工具包,方法如下:

1
from sklearn.model_selection import GridSearchCV


然后我们使用 GridSearchCV(estimator, param_grid, cv=None, scoring=None) 构造参数的自动搜索模块,这里有一些主要的参数需要说明下:


image.png


构造完 GridSearchCV 之后,我们就可以使用 fit 函数拟合训练,使用 predict 函数预测,这时预测采用的是最优参数情况下的分类器。


这里举一个简单的例子,我们用 sklearn 自带的 IRIS 数据集,采用随机森林对 IRIS 数据分类。假设我们想知道 n_estimators 在 1-10 的范围内取哪个值的分类结果最好,可以编写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# -*- coding: utf-8 -*-
# 使用RandomForest对IRIS数据集进行分类
# 利用GridSearchCV寻找最优参数
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
rf = RandomForestClassifier()
parameters = {"n_estimators": range(1,11)}
iris = load_iris()
# 使用GridSearchCV进行参数调优
clf = GridSearchCV(estimator=rf, param_grid=parameters)
# 对iris数据集进行分类
clf.fit(iris.data, iris.target)
print("最优分数: %.4lf" %clf.best_score_)
print("最优参数:", clf.best_params_)
1
2
3
运行结果如下:
最优分数: 0.9667
最优参数: {'n_estimators': 6}


你能看到当我们采用随机森林作为分类器的时候,最优准确率是 0.9667,当 n_estimators=6 的时候,是最优参数,也就是随机森林一共有 6 个子决策树。

使用 Pipeline 管道机制进行流水线作业


做分类的时候往往都是有步骤的,比如先对数据进行规范化处理,你也可以用 PCA 方法(一种常用的降维方法)对数据降维,最后使用分类器分类。Python 有一种 Pipeline 管道机制。管道机制就是让我们把每一步都按顺序列下来,从而创建 Pipeline 流水线作业。每一步都采用 (‘名称’, 步骤) 的方式来表示。我们需要先采用 StandardScaler 方法对数据规范化,即采用数据规范化为均值为 0,方差为 1 的正态分布,然后采用 PCA 方法对数据进行降维,最后采用随机森林进行分类。具体代码如下:

1
2
3
4
5
6
7

from sklearn.model_selection import GridSearchCV
pipeline = Pipeline([
('scaler', StandardScaler()),
('pca', PCA()),
('randomforestclassifier', RandomForestClassifier())
])

那么我们现在采用 Pipeline 管道机制,用随机森林对 IRIS 数据集做一下分类。先用 StandardScaler 方法对数据规范化,然后再用随机森林分类,编写代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- coding: utf-8 -*-
# 使用RandomForest对IRIS数据集进行分类
# 利用GridSearchCV寻找最优参数,使用Pipeline进行流水作业
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
rf = RandomForestClassifier()
parameters = {"randomforestclassifier__n_estimators": range(1,11)}
iris = load_iris()
pipeline = Pipeline([
('scaler', StandardScaler()),
('randomforestclassifier', rf)
])
# 使用GridSearchCV进行参数调优
clf = GridSearchCV(estimator=pipeline, param_grid=parameters)
# 对iris数据集进行分类
clf.fit(iris.data, iris.target)
print("最优分数: %.4lf" %clf.best_score_)
print("最优参数:", clf.best_params_)
1
2
3
运行结果:
最优分数: 0.9667
最优参数: {'randomforestclassifier__n_estimators': 9}


你能看到是否采用数据规范化对结果还是有一些影响的,有了 GridSearchCV 和 Pipeline 这两个工具之后,我们在使用分类器的时候就会方便很多。

对信用卡违约率进行分析


我们现在来做一个信用卡违约率的项目,这个数据集你可以从 GitHub 上下载:https://github.com/cystanford/credit_default


这个数据集是台湾某银行 2005 年 4 月到 9 月的信用卡数据,数据集一共包括 25 个字段,具体含义如下:
1730fb3a809c99950739e7f50e1a6988.jpg
现在我们的目标是要针对这个数据集构建一个分析信用卡违约率的分类器。具体选择哪个分类器,以及分类器的参数如何优化,我们可以用 GridSearchCV 这个工具跑一遍。先梳理下整个项目的流程:


929c96584cbc25972f63ef39101c96a5.jpg

  1. 加载数据;
  2. 准备阶段:探索数据,采用数据可视化方式可以让我们对数据有更直观的了解,比如我们想要了解信用卡违约率和不违约率的人数。因为数据集没有专门的测试集,我们还需要使用 train_test_split 划分数据集。
  3. 分类阶段:之所以把数据规范化放到这个阶段,是因为我们可以使用 Pipeline 管道机制,将数据规范化设置为第一步,分类为第二步。因为我们不知道采用哪个分类器效果好,所以我们需要多用几个分类器,比如 SVM、决策树、随机森林和 KNN。然后通过 GridSearchCV 工具,找到每个分类器的最优参数和最优分数,最终找到最适合这个项目的分类器和该分类器的参数。


基于上面的流程,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import matplotlib

import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split, GridSearchCV

# 构造各种分类器
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier

classifiers = [
SVC(random_state = 1, kernel = 'rbf'),
DecisionTreeClassifier(random_state = 1, criterion = 'gini'),
RandomForestClassifier(random_state = 1, criterion = 'gini'),
KNeighborsClassifier(metric = 'minkowski'),
]
# 分类器名称
classifier_names = [
'svc',
'decisiontreeclassifier',
'randomforestclassifier',
'kneighborsclassifier',
]
# 分类器参数
classifier_param_grid = [
{'svc__C':[1], 'svc__gamma':[0.01]},
{'decisiontreeclassifier__max_depth':[6,9,11]},
{'randomforestclassifier__n_estimators':[3,5,6]} ,
{'kneighborsclassifier__n_neighbors':[4,6,8]},
]

# 对具体的分类器进行GridSearchCV参数调优
def GridSearchCV_work(pipeline, train_x, train_y, test_x, test_y, param_grid, score = 'accuracy'):
response = {}
gridsearch = GridSearchCV(estimator = pipeline, param_grid = param_grid, scoring = score)
# 寻找最优的参数 和最优的准确率分数
search = gridsearch.fit(train_x, train_y)
print("GridSearch最优参数:", search.best_params_)
print("GridSearch最优分数: %0.4lf" %search.best_score_)
predict_y = gridsearch.predict(test_x)
print("准确率 %0.4lf" %accuracy_score(test_y, predict_y))
response['predict_y'] = predict_y
response['accuracy_score'] = accuracy_score(test_y,predict_y)
return response


def get_data():
data = pd.read_csv('UCI_Credit_Card.csv')
# 数据条数和字段数量
print(data.shape)
# 数据探索
print(data.describe)
return data


def show_data(data):
# 查看下一个月的违约率
next_month = data['default.payment.next.month'].value_counts()

print(next_month)
# 统计违约率结果
df = pd.DataFrame({'default.payment.next.month': next_month.index, 'values': next_month.values})
print(df)

# 正常显示中文标签
# plt.rcParams['font.sans-serif'] = ['FangSong'] # 用来正常显示中文标签
plt.rcParams["font.family"] = 'Arial Unicode MS'
plt.figure(figsize=(6, 6))

plt.title('信用卡违约率客户 (违约:1,守约:0)')
sns.set_color_codes('pastel')
sns.barplot(x='default.payment.next.month', y="values", data=df)
locs, labels = plt.xticks()
plt.show()


def get_feature(data):
# 特征选择,去掉ID字段、最后一个结果字段即可
data.drop(['ID'], inplace=True, axis=1) # ID这个字段没有用
target = data['default.payment.next.month'].values
columns = data.columns.tolist()
columns.remove('default.payment.next.month')
features = data[columns].values
return features, target


def main():
data = get_data()
show_data(data)
features, target = get_feature(data)
# 30%作为测试集,其余作为训练集
train_x, test_x, train_y, test_y = train_test_split(features, target, test_size=0.30, stratify = target, random_state = 1)

for model, model_name, model_param_grid in zip(classifiers, classifier_names, classifier_param_grid):
pipeline = Pipeline([
('scaler', StandardScaler()),
(model_name, model)
])
result = GridSearchCV_work(pipeline, train_x, train_y, test_x, test_y, model_param_grid, score='accuracy')

if __name__ == '__main__':
main()

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(30000, 25)
<bound method NDFrame.describe of ID LIMIT_BAL SEX ... PAY_AMT5 PAY_AMT6 default.payment.next.month
0 1 20000.0 2 ... 0.0 0.0 1
1 2 120000.0 2 ... 0.0 2000.0 1
2 3 90000.0 2 ... 1000.0 5000.0 0
3 4 50000.0 2 ... 1069.0 1000.0 0
4 5 50000.0 1 ... 689.0 679.0 0
... ... ... ... ... ... ...
29995 29996 220000.0 1 ... 5000.0 1000.0 0
29996 29997 150000.0 1 ... 0.0 0.0 0
29997 29998 30000.0 1 ... 2000.0 3100.0 1
29998 29999 80000.0 1 ... 52964.0 1804.0 1
29999 30000 50000.0 1 ... 1000.0 1000.0 1
[30000 rows x 25 columns]>
0 23364
1 6636
Name: default.payment.next.month, dtype: int64
default.payment.next.month values
0 0 23364
1 1 6636
GridSearch最优参数: {'svc__C': 1, 'svc__gamma': 0.01}
GridSearch最优分数: 0.8186
准确率 0.8172
GridSearch最优参数: {'decisiontreeclassifier__max_depth': 6}
GridSearch最优分数: 0.8208
准确率 0.8113
GridSearch最优参数: {'randomforestclassifier__n_estimators': 6}
GridSearch最优分数: 0.8004
准确率 0.7994
GridSearch最优参数: {'kneighborsclassifier__n_neighbors': 8}
GridSearch最优分数: 0.8040
准确率 0.8036

187d0233d4fb5f07a9653e5ae4754372.png


从结果中,我们能看到 SVM 分类器的准确率最高,测试准确率为 0.8172。在决策树分类中,我设置了 3 种最大深度,当最大深度 =6 时结果最优,测试准确率为 0.8113;在随机森林分类中,我设置了 3 个决策树个数的取值,取值为 6 时结果最优,测试准确率为 0.7994;在 KNN 分类中,我设置了 3 个 n 的取值,取值为 8 时结果最优,测试准确率为 0.8036。

总结


今天我给你讲了随机森林的概念及工具的使用,另外针对数据挖掘算法中经常采用的参数调优,也介绍了 GridSearchCV 工具这个利器。并将这两者结合起来,在信用卡违约分析这个项目中进行了使用。


很多时候,我们不知道该采用哪种分类算法更适合。即便是对于一种分类算法,也有很多参数可以调优,每个参数都有一定的取值范围。我们可以把想要采用的分类器,以及这些参数的取值范围都设置到数组里,然后使用 GridSearchCV 工具进行调优。


14f9cddc17d6cceb0b8cbc4381c65216.png