如何用Python和深度神经网络锁定即将流失的客户?

Python深度学习简明实战教程来了。别犹豫了,赶忙从零开始,搭建你本身的第一个深度学习模型吧!html

想不想了解如何用Python快速搭建深度神经网络,完成数据分类任务?本文一步步为你展现这一过程,让你初步领略深度学习模型的强大和易用。python

烦恼

做为一名数据分析师,你来到这家跨国银行工做已经半年了。git

今天上午,老板把你叫到办公室,面色凝重。github

你内心直打鼓,觉得本身捅了什么篓子。幸亏老板的话让你很快打消了顾虑。算法

他发愁,是由于最近欧洲区的客户流失严重,许多客户都跑到了竞争对手那里接受服务了。老板问你该怎么办?数组

你脱口而出“作好客户关系管理啊!”浏览器

老板看了你一眼,缓慢地说“咱们想知道哪些客户最可能在近期流失”。bash

没错,在有鱼的地方钓鱼,才是上策。微信

你明白了本身的任务——经过数据锁定即将流失的客户。这个工做,确实是你这个数据分析师份内的事儿。网络

你很庆幸,这半年作了不少的数据动态采集和整理工做,使得你手头就有一个比较完备的客户数据集。

下面你须要作的,就是如何从数据中“沙里淘金”,找到那些最可能流失的客户。

但是,该怎么作呢?

你拿出欧洲区客户的数据,端详起来。

客户主要分布在法国、德国和西班牙。

你手里掌握的信息,包括他们的年龄、性别、信用、办卡信息等。客户是否已流失的信息在最后一列(Exited)。

怎么用这些数据来判断顾客是否会流失呢?

以你的专业素养,很容易就判断出这是一个分类问题,属于机器学习中的监督式学习。可是,你以前并无作过实际项目,该如何着手呢?

别发愁,我一步步给你演示如何用Python和深度神经网络(或者叫“深度学习”)来完成这个分类任务,帮你锁定那些即将流失的客户。

环境

工欲善其事,必先利其器。咱们先来安装和搭建环境。

首先是安装Python。

请到这个网址下载Anaconda的最新版本。

请选择左侧的Python 3.6版本下载安装。

其次是新建文件夹,起名为demo-customer-churn-ann,而且从这个连接下载数据,放到该文件夹下。

(注:样例数据来自于匿名化处理后的真实数据集,下载自superdatascience官网。)

打开终端(或者命令行工具),进入demo-customer-churn-ann目录,执行如下命令:

jupyter notebook
复制代码

浏览器中会显示以下界面:

点击界面右上方的New按钮,新建一个Python 3 Notebook,起名为customer-churn-ann。

准备工做结束,下面咱们开始清理数据。

清理

首先,读入数据清理最经常使用的pandas和numpy包。

import numpy as np
import pandas as pd
复制代码

customer_churn.csv里读入数据:

df = pd.read_csv('customer_churn.csv')
复制代码

看看读入效果如何:

df.head()
复制代码

这里咱们使用了head()函数,只显示前5行。

能够看到,数据完整无误读入。可是并不是全部的列都对咱们预测用户流失有做用。咱们一一甄别一下:

  • RowNumber:行号,这个确定没用,删除
  • CustomerID:用户编号,这个是顺序发放的,删除
  • Surname:用户姓名,对流失没有影响,删除
  • CreditScore:信用分数,这个很重要,保留
  • Geography:用户所在国家/地区,这个有影响,保留
  • Gender:用户性别,可能有影响,保留
  • Age:年龄,影响很大,年轻人更容易切换银行,保留
  • Tenure:当了本银行多少年用户,很重要,保留
  • Balance:存贷款状况,很重要,保留
  • NumOfProducts:使用产品数量,很重要,保留
  • HasCrCard:是否有本行信用卡,很重要,保留
  • IsActiveMember:是否活跃用户,很重要,保留
  • EstimatedSalary:估计收入,很重要,保留
  • Exited:是否已流失,这将做为咱们的标签数据

上述数据列甄别过程,就叫作“特征工程”(Feature Engineering),这是机器学习里面最经常使用的数据预处理方法。若是咱们的数据量足够大,机器学习模型足够复杂,是能够跳过这一步的。可是因为咱们的数据只有10000条,还须要手动筛选特征。

选定了特征以后,咱们来生成特征矩阵X,把刚才咱们决定保留的特征都写进来。

X = df.loc[:,['CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary']]
复制代码

看看特征矩阵的前几行:

X.head()
复制代码

显示结果以下:

2017-11-19_19-2-2_snapshots-01.jpg

特征矩阵构建准确无误,下面咱们构建目标数据y,也就是用户是否流失。

y = df.Exited
复制代码
y.head()
复制代码
0    1
1    0
2    1
3    0
4    0
Name: Exited, dtype: int64
复制代码

此时咱们须要的数据基本上齐全了。可是咱们发现其中有几列数据还不符合咱们的要求。

要作机器学习,只能给机器提供数值,而不能是字符串。但是看看咱们的特征矩阵:

X.head()
复制代码

2017-11-19_19-2-2_snapshots-01.jpg

显然其中的Geography和Gender两项数据都不符合要求。它们都是分类数据。咱们须要作转换,把它们变成数值。

在Scikit-learn工具包里面,专门提供了方便的工具LabelEncoder,让咱们能够方便地将类别信息变成数值。

from sklearn.preprocessing import LabelEncoder, OneHotEncoder
labelencoder1 = LabelEncoder()
X.Geography= labelencoder1.fit_transform(X.Geography)
labelencoder2 = LabelEncoder()
X.Gender = labelencoder2.fit_transform(X.Gender)
复制代码

咱们须要转换两列,因此创建了两个不一样的labelencoder。转换的函数叫作fit_transform

通过转换,此时咱们再来看看特征矩阵的样子:

X.head()
复制代码

2017-11-19_19-2-11_snapshots-03.jpg

显然,Geography和Gender这两列都从原先描述类别的字符串,变成了数字。

这样是否是就完事大吉了呢?

不对,Gender还好说,只有两种取值方式,要么是男,要么是女。咱们能够把“是男性”定义为1,那么女性就取值为0。两种取值只是描述类别不一样,没有歧义。

而Geography就不一样了。由于数据集里面可能的国家地区取值有3种,因此就转换成了0(法国)、1(德国)、2(西班牙)。问题是,这三者之间真的有序列(大小)关系吗?

答案天然是否认的。咱们其实仍是打算用数值描述分类而已。可是取值有数量的序列差别,就会给机器带来歧义。它并不清楚不一样的取值只是某个国家的代码,可能会把这种大小关系带入模型计算,从而产生错误的结果。

解决这个问题,咱们就须要引入OneHotEncoder。它也是Scikit-learn提供的一个类,能够帮助咱们把类别的取值转变为多个变量组合表示。

我们这个数据集里,能够把3个国家分别用3个数字组合来表示。例如法国从原先的0,变成(1, 0, 0),德国从1变成(0, 1, 0),而西班牙从2变成(0, 0, 1)

这样,不再会出现0和1以外的数字来描述类别,从而避免机器产生误会,错把类别数字当成大小来计算了。

特征矩阵里面,咱们只须要转换国别这一列。由于它在第1列的位置(从0开始计数),于是categorical_features只填写它的位置信息。

onehotencoder = OneHotEncoder(categorical_features = [1])
X = onehotencoder.fit_transform(X).toarray()
复制代码

这时候,咱们的特征矩阵数据框就被转换成了一个数组。注意全部被OneHotEncoder转换的列会排在最前面,而后才是那些保持原样的数据列。

咱们只看转换后的第一行:

X[0]
复制代码
array([  1.00000000e+00,   0.00000000e+00,   0.00000000e+00,
         6.19000000e+02,   0.00000000e+00,   4.20000000e+01,
         2.00000000e+00,   0.00000000e+00,   1.00000000e+00,
         1.00000000e+00,   1.00000000e+00,   1.01348880e+05])
复制代码

这样,总算转换完毕了吧?

没有。

由于本例中,OneHotEncoder转换出来的3列数字,其实是不独立的。给定其中两列的信息,你本身均可以计算出其中的第3列取值。

比如说,某一行的前两列数字是(0, 0),那么第三列确定是1。由于这是转换规则决定的。3列里只能有1个是1,其他都是0。

若是你作过多元线性回归,应该知道这种状况下,咱们是须要去掉其中一列,才能继续分析的。否则会落入“虚拟变量陷阱”(dummy variable trap)。

咱们删掉第0列,避免掉进坑里。

X = np.delete(X, [0], 1)
复制代码

再次打印第一行:

X[0]
复制代码
array([  0.00000000e+00,   0.00000000e+00,   6.19000000e+02,
         0.00000000e+00,   4.20000000e+01,   2.00000000e+00,
         0.00000000e+00,   1.00000000e+00,   1.00000000e+00,
         1.00000000e+00,   1.01348880e+05])
复制代码

检查完毕,如今我们的特征矩阵处理基本完成。

可是监督式学习,最重要的是有标签(label)数据。本例中的标签就是用户是否流失。咱们目前的标签数据框,是这个样子的。

y.head()
复制代码
0    1
1    0
2    1
3    0
4    0
Name: Exited, dtype: int64
复制代码

它是一个行向量,咱们须要把它先转换成为列向量。你能够想象成把它“竖过来”。

y = y[:, np.newaxis]
y
复制代码
array([[1],
       [0],
       [1],
       ...,
       [1],
       [1],
       [0]])
复制代码

这样在后面训练的时候,他就能够和前面的特征矩阵一一对应来操做计算了。

既然标签表明了类别,咱们也把它用OneHotEncoder转换,这样方便咱们后面作分类学习。

onehotencoder = OneHotEncoder()
y = onehotencoder.fit_transform(y).toarray()
复制代码

此时的标签变成两列数据,一列表明顾客存留,一列表明顾客流失。

y
复制代码
array([[ 0.,  1.],
       [ 1.,  0.],
       [ 0.,  1.],
       ...,
       [ 0.,  1.],
       [ 0.,  1.],
       [ 1.,  0.]])
复制代码

整体的数据已经齐全了。可是咱们不能把它们都用来训练。

这就好像老师不该该把考试题目拿来给学生作做业和练习同样。只有考学生没见过的题,才能区分学生是掌握了正确的解题方法,仍是死记硬背了做业答案。

咱们拿出20%的数据,放在一边,等着用来作测试。其他8000条数据用来训练机器学习模型。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
复制代码

咱们看看训练集的长度:

len(X_train)
复制代码
8000
复制代码

再看看测试集的长度:

len(X_test)
复制代码
2000
复制代码

确认无误。

是否是能够开始机器学习了?

能够,可是下面这一步也很关键。咱们须要把数据进行标准化处理。由于原先每一列数字的取值范围都各不相同,所以有的列方差要远远大于其余列。这样对机器来讲,也是很困扰的。数据的标准化处理,能够在保持列内数据多样性的同时,尽可能减小不一样类别之间差别的影响,可让机器公平对待所有特征。

咱们调用Scikit-learn的StandardScaler类来完成这一过程。

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
复制代码

注意,咱们只对特征矩阵作标准化,标签是不能动的。另外训练集和测试集须要按照统一的标准变化。因此你看,训练集上,咱们用了fit_transform函数,先拟合后转换;而在测试集上,咱们直接用训练集拟合的结果,只作转换。

X_train
复制代码
array([[-0.5698444 ,  1.74309049,  0.16958176, ...,  0.64259497,
        -1.03227043,  1.10643166],
       [ 1.75486502, -0.57369368, -2.30455945, ...,  0.64259497,
         0.9687384 , -0.74866447],
       [-0.5698444 , -0.57369368, -1.19119591, ...,  0.64259497,
        -1.03227043,  1.48533467],
       ...,
       [-0.5698444 , -0.57369368,  0.9015152 , ...,  0.64259497,
        -1.03227043,  1.41231994],
       [-0.5698444 ,  1.74309049, -0.62420521, ...,  0.64259497,
         0.9687384 ,  0.84432121],
       [ 1.75486502, -0.57369368, -0.28401079, ...,  0.64259497,
        -1.03227043,  0.32472465]])
复制代码

你会发现,许多列的方差比原先小得多。机器学习起来,会更加方便。

数据清理和转换工做至此完成。

决策树

若是读过个人《贷仍是不贷:如何用Python和机器学习帮你决策?》一文,你应该有一种感受——这个问题和贷款审批决策很像啊!既然在该文中,决策树很好使,咱们继续用决策树不就行了?

好的,咱们先测试一下经典机器学习算法表现如何。

从Scikit-learn中,读入决策树工具。而后拟合训练集数据。

from sklearn import tree
clf = tree.DecisionTreeClassifier()
clf = clf.fit(X_train, y_train)
复制代码

而后,利用咱们创建的决策树模型作出预测。

y_pred = clf.predict(X_test)
复制代码

打印预测结果:

y_pred
复制代码
array([[ 1.,  0.],
       [ 0.,  1.],
       [ 1.,  0.],
       ...,
       [ 1.,  0.],
       [ 1.,  0.],
       [ 0.,  1.]])
复制代码

这样看不出来什么。让咱们调用Scikit-learn的classification_report模块,生成分析报告。

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
复制代码
precision    recall  f1-score   support
复制代码
0       0.89      0.86      0.87      1595
          1       0.51      0.58      0.54       405
复制代码
avg / total       0.81      0.80      0.81      2000
复制代码

经检测,决策树在我们的数据集上,表现得仍是不错的。整体的准确率为0.81,召回率为0.80,f1分数为0.81,已经很高了。对10个客户作流失可能性判断,它有8次都能判断正确。

可是,这样是否足够?

咱们或许能够调整决策树的参数作优化,尝试改进预测结果。

或者咱们能够采用深度学习

深度

深度学习的使用场景,每每是由于原有的模型经典机器学习模型过于简单,没法把握复杂数据特性。

我不许备给你讲一堆数学公式,我们动手作个实验。

请你打开这个网址

你会看到以下图所示的深度学习游乐场:

右侧的图形,里面是蓝色数据,外圈是黄色数据。你的任务就是要用模型分类两种不一样数据。

你说那还不容易?我一眼就看出来了。

你看出来没有用。经过你的设置,让机器也能正确区分,才算数。

图中你看到许多加减号。我们就经过操纵它们来玩儿一玩儿模型。

首先,点图中部上方的"2 HIDDEN LAYERS"左侧减号,把中间隐藏层数下降为1。

而后,点击"2 neurons"上面的减号,把神经元数量减小为1。

把页面上方的Activation函数下拉框打开,选择“Sigmoid”。

如今的模型,其实就是经典的逻辑回归(Logistic Regression)。

点击左上方的运行按钮,咱们看看执行效果。

因为模型过于简单,因此机器绞尽脑汁,试图用一条直线切分二维平面上的两类节点。

损失(loss)居高不下。训练集和测试集损失都在0.4左右,显然不符合咱们的分类需求。

下面咱们试试增长层数和神经元数量。此次点击加号,把隐藏层数加回到2,两层神经元数量都取2。

再次点击运行。

通过一段时间,结果稳定了下来,你发现此次电脑用了两条线,把平面切分红了3部分。

测试集损失降低到了0.25左右,而训练集损失更是下降到了0.2如下。

模型复杂了,效果彷佛更好一些。

再接再砺,咱们把第一个隐藏层的神经元数量增长为4看看。

点击运行,不一下子有趣的事情就发生了。

机器用一条近乎完美的曲线把平面分红了内外两个部分。测试集和训练集损失都极速降低,训练集损失甚至接近于0。

这告诉咱们,许多时候模型过于简单带来的问题,能够经过加深隐藏层次、增长神经元的方法提高模型复杂度,加以改进。

目前流行的划分方法,是用隐藏层的数量多少来区分是否“深度”。当神经网络中隐藏层数量达到3层以上时,就被称为“深度神经网络”,或者“深度学习”。

久闻大名的深度学习,原来就是这么简单。

若是有时间的话,建议你本身在这个游乐场里多动手玩儿一玩儿。你会很快对神经网络和深度学习有个感性认识。

框架

游乐场背后使用的引擎,就是Google的深度学习框架Tensorflow。

所谓框架,就是别人帮你构造好的基础软件应用。你能够经过调用它们,避免本身重复发明轮子,大幅度节省时间,提高效率。

支持Python语言的深度学习的框架有不少,除了Tensorflow外,还有PyTorch, Theano和MXNet等。

我给你的建议是,找到一个你喜欢的软件包,深刻学习使用,不断实践来提高本身的技能。千万不要跟别人争论哪一个深度学习框架更好。一来萝卜白菜各有所爱,每一个人都有本身的偏好;二来深度学习的江湖水很深,言多有失。说错了话,别的门派可能会不高兴哟。

我比较喜欢Tensorflow。可是Tensorflow自己是个底层库。虽然随着版本的更迭,界面愈来愈易用。可是对初学者来讲,许多细节依然有些过于琐碎,不容易掌握。

初学者的耐心有限,挫折过多容易放弃。

幸亏,还有几个高度抽象框架,是创建在Tensorflow之上的。若是你的任务是应用现成的深度学习模型,那么这些框架会给你带来很是大的便利。

这些框架包括Keras, TensorLayer等。我们今天将要使用的,叫作TFlearn。

它的特色,就是长得很像Scikit-learn。这样若是你熟悉经典机器学习模型,学起来会特别轻松省力。

实战

闲话就说这么多,下面我们继续写代码吧。

写代码以前,请回到终端下,运行如下命令,安装几个软件包:

pip install tensorflow
pip install tflearn
复制代码

执行完毕后,回到Notebook里。

咱们呼叫tflearn框架。

import tflearn
复制代码

而后,咱们开始搭积木同样,搭神经网络层。

首先是输入层。

net = tflearn.input_data(shape=[None, 11])
复制代码

注意这里的写法,由于咱们输入的数据,是特征矩阵。而通过咱们处理后,特征矩阵如今有11列,所以shape的第二项写11。

shape的第一项,None,指的是咱们要输入的特征矩阵行数。由于咱们如今是搭建模型,后面特征矩阵有可能一次输入,有可能分红组块输入,长度可大可小,没法事先肯定。因此这里填None。tflearn会在咱们实际执行训练的时候,本身读入特征矩阵的尺寸,来处理这个数值。

下面咱们搭建隐藏层。这里咱们要使用深度学习,搭建3层。

net = tflearn.fully_connected(net, 6, activation='relu')
net = tflearn.fully_connected(net, 6, activation='relu')
net = tflearn.fully_connected(net, 6, activation='relu')
复制代码

activation刚才在深度学习游乐场里面咱们遇到过,表明激活函数。若是没有它,全部的输入输出都是线性关系。

Relu函数是激活函数的一种。它大概长这个样子。

若是你想了解激活函数的更多知识,请参考后文的学习资源部分。

隐藏层里,每一层咱们都设置了6个神经元。其实至今为之,也不存在最优神经元数量的计算公式。工程界的一种作法,是把输入层的神经元数量,加上输出层神经元数量,除以2取整。我们这里就是用的这种方法,得出6个。

搭好了3个中间隐藏层,下面咱们来搭建输出层。

net = tflearn.fully_connected(net, 2, activation='softmax')
net = tflearn.regression(net)
复制代码

这里咱们用两个神经元作输出,而且说明使用回归方法。输出层选用的激活函数为softmax。处理分类任务的时候,softmax比较合适。它会告诉咱们每一类的可能性,其中数值最高的,能够做为咱们的分类结果。

积木搭完了,下面咱们告诉TFlearn,以刚刚搭建的结构,生成模型。

model = tflearn.DNN(net)
复制代码

有了模型,咱们就可使用拟合功能了。你看是否是跟Scikit-learn的使用方法很类似呢?

model.fit(X_train, y_train, n_epoch=30, batch_size=32, show_metric=True)
复制代码

注意这里多了几个参数,咱们来解释一下。

  • n_epoch:数据训练几个轮次。
  • batch_size:每一次输入给模型的数据行数。
  • show_metric:训练过程当中要不要打印结果。

如下就是电脑输出的最终训练结果。其实中间运行过程看着更激动人心,你本身试一下就知道了。

Training Step: 7499  | total loss: 0.39757 | time: 0.656s
| Adam | epoch: 030 | loss: 0.39757 - acc: 0.8493 -- iter: 7968/8000
Training Step: 7500  | total loss: 0.40385 | time: 0.659s
| Adam | epoch: 030 | loss: 0.40385 - acc: 0.8487 -- iter: 8000/8000
--
复制代码

咱们看到训练集的损失(loss)大概为0.4左右。

打开终端,咱们输入

tensorboard --logdir=/tmp/tflearn_logs/
复制代码

而后在浏览器里输入http://localhost:6006/

能够看到以下界面:

这是模型训练过程的可视化图形,能够看到准确度的攀升和损失下降的曲线。

打开GRAPHS标签页,咱们能够查看神经网络的结构图形。

咱们搭积木的过程,在此处一目了然。

评估

训练好了模型,咱们来尝试作个预测吧。

看看测试集的特征矩阵第一行。

X_test[0]
复制代码
array([ 1.75486502, -0.57369368, -0.55204276, -1.09168714, -0.36890377,
        1.04473698,  0.8793029 , -0.92159124,  0.64259497,  0.9687384 ,
        1.61085707])
复制代码

咱们就用它来预测一下分类结果。

y_pred = model.predict(X_test)
复制代码

打印出来看看:

y_pred[0]
复制代码
array([ 0.70956731,  0.29043278], dtype=float32)
复制代码

模型判断该客户不流失的可能性为0.70956731。

咱们看看实际标签数据:

y_test[0]
复制代码
array([ 1.,  0.])
复制代码

客户果真没有流失。这个预测是对的。

可是一个数据的预测正确与否,是没法说明问题的。咱们下面跑整个测试集,而且使用evaluate函数评价模型。

score = model.evaluate(X_test, y_test)
print('Test accuarcy: %0.4f%%' % (score[0] * 100))
复制代码
Test accuarcy: 84.1500%
复制代码

在测试集上,准确性达到84.15%,好样的!

但愿在你的努力下,机器作出的准确判断能够帮助银行有效锁定可能流失的客户,下降客户的流失率,继续日进斗金。

说明

你可能以为,深度学习也没有什么厉害的嘛。原先的决策树算法,那么简单就能实现,也能够达到80%以上的准确度。写了这么多语句,深度学习结果也无非只提高了几个百分点而已。

首先,准确度达到某种高度后,提高是不容易的。这就好像学生考试,从不及格到及格,付出的努力并不须要很高;从95分提高到100,倒是许多人一生也没有完成的目标。

其次,在某些领域里,1%的提高意味着以百万美圆计的利润,或者几千我的的生命所以获得拯救。

第三,深度学习的崛起,是由于大数据的环境。在许多状况下,数据越多,深度学习的优点就越明显。本例中只有10000条记录,与“大数据”的规模还相去甚远。

学习资源

若是你对深度学习感兴趣,推荐如下学习资源。

首先是教材。

第一本是Deep Learning,绝对的经典。

第二本是Hands-On Machine Learning with Scikit-Learn and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems,深刻浅出,通俗易懂。

其次是MOOC。

推荐吴恩达(Andrew Ng)教授在Coursera上的两门课程。

一门是机器学习。这课推出有年头了,可是很是有趣和实用。具体的介绍请参考拙做《机器学习哪里有这么玄?》以及《如何用MOOC组合掌握机器学习?》。

一门是深度学习。这是个系列课程,包括5门子课程。今年推出的新课,自成体系,可是最好有前面那门课程做为基础。

讨论

你对深度学习感兴趣吗?以前有没有作过深度学习项目?你掌握了哪些深度学习框架?有没有什么建议给初学者?欢迎留言,把心得分享给你们,咱们一块儿交流讨论。

喜欢请点赞。还能够微信关注和置顶个人公众号“玉树芝兰”(nkwangshuyi)

若是你对数据科学感兴趣,不妨阅读个人系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

相关文章
相关标签/搜索