Python中用PyTorch机器学习神经网络分类预测银行客户流失模型

分类问题属于机器学习问题的类别,其中给定一组特征,任务是预测离散值。

由Kaizong Ye,Coin Ge撰写

分类问题的一些常见示例是,预测肿瘤是否为癌症,或者学生是否可能通过考试

在本文中,鉴于银行客户的某些特征,我们将预测客户在6个月后是否可能离开银行。客户离开组织的现象也称为客户流失。因此,我们的任务是根据各种客户特征预测客户流失。

数据集

让我们将所需的库和数据集导入到我们的Python应用程序中:

×


PyTorch更有利于研究人员、爱好者、小规模项目等快速搞出原型。而TensorFlow更适合大规模部署,特别是需要跨平台和嵌入式部署时。

然后咱们一项一项分着说。



上手时间

赢家:PyTorch

PyTorch本质上是Numpy的替代者,而且支持GPU、带有高级功能,可以用来搭建和训练深度神经网络。如果你熟悉Numpy、Python以及常见的深度学习概念(卷积层、循环层、SGD等),会非常容易上手PyTorch。

而TensorFlow可以看成是一个嵌入Python的编程语言。你写的TensorFlow代码会被Python编译成一张图,然后由TensorFlow执行引擎运行。我见过好多新手,因为这个增加的间接层而困扰。也正是因为同样的原因,TensorFlow有一些额外的概念需要学习,例如会话、图、变量作用域(variable scoping)、占位符等。

另外还需要更多的样板代码才能让一个基本的模型运行。所以TensorFlow的上手时间,肯定要比PyTorch长。

图创建和调试

赢家:PyTorch

创建和运行计算图可能是两个框架最不同的地方。在PyTorch中,图结构是动态的,这意味着图在运行时构建。而在TensorFlow中,图结构是静态的,这意味着图先被“编译”然后再运行。

举一个简单的例子,在PyTorch中你可以用标准的Python语法编写一个for循环结构

此处T可以在每次执行代码时改变。而TensorFlow中,这需要使用“控制流操作”来构建图,例如tf.while_loop。TensorFlow确实提供了dynamic_rnn用于常见结构,但是创建自定义动态计算真的更加困难。

PyTorch中简单的图结构更容易理解,更重要的是,还更容易调试。调试PyTorch代码就像调试Python代码一样。你可以使用pdb并在任何地方设置断点。调试TensorFlow代码可不容易。要么得从会话请求要检查的变量,要么学会使用TensorFlow的调试器(tfdbg)。



全面性

赢家:TensorFlow

随着PyTorch逐渐成熟,我预计这部分的差距会趋近于零。但目前,TensorFlow还是有一些PyTorch不支持的功能。它们是:

  • 沿维翻转张量(np.flip, np.flipud, np.fliplr)

  • 检查无穷与非数值张量(np.is_nan, np.is_inf)

  • 快速傅里叶变换(np.fft)

这些TensorFlow都支持。另外,TensorFlow的contrib软件包中,有更多PyTorch没有的高级功能和模型。

序列化

赢家:TensorFlow

两种框架下保存和加载模型都很简单。PyTorch有一个特别简单的API,可以保存模型的所有权重或pickle整个类。TensorFlow的Saver对象也很易用,而且为检查提供了更多的选项。

TensorFlow序列化的主要优点是可以将整个图保存为protocol buffer。包括参数和操作。然而图还能被加载进其他支持的语言(C++、Java)。这对于部署堆栈至关重要。理论上,当你想改动模型源代码但仍希望运行旧模型时非常有用。

部署

赢家:TensorFlow

对于小规模的服务器端部署(例如一个Flask web server),两个框架都很简单。

对于移动端和嵌入式部署,TensorFlow更好。不只是比PyTorch好,比大多数深度学习框架都要要。使用TensorFlow,部署在Android或iOS平台时只需要很小的工作量,至少不必用Java或者C++重写模型的推断部分。

对于高性能服务器端的部署,还有TensorFlow Serving能用。除了性能之外,TensorFlow Serving一个显著的优点是可以轻松的热插拔模型,而不会使服务失效。

文档

赢家:平手

对于两个框架,我都在文档中找到所需的一切。Python API被很好的记录,以及有足够的案例和教程来学习框架。

一个特例是,PyTorch的C库大多数没有文档。不过,这只有在你编写一个定制化的C扩展时才有影响。

数据加载

赢家:PyTorch

PyTorch中用于加载数据的API设计的很棒。接口由一个数据集、一个取样器和一个数据加载器构成。数据加载器根据取样器的计划,基于数据集产生一个迭代器。并行化数据加载简单的就像把num_workers参数传递给数据加载器一样简单。

我在TensorFlow中没有发现特别有用的数据加载工具。很多时候,并不总能直接把准备并行运行的预处理代码加入TensorFlow图。以及API本身冗长难学。

设备管理

赢家:TensorFlow

TensorFlow的设备管理非常好用。通常你不需要进行调整,因为默认的设置就很好。例如,TensorFlow会假设你想运行在GPU上(如果有的话)。而在PyTorch中,即使启用了CUDA,你也需要明确把一切移入设备。

TensorFlow设备管理唯一的缺点是,默认情况下,它会占用所有的GPU显存。简单的解决办法是指定CUDA_VISIBLE_DEVICES。有时候大家会忘了这一点,所以GPU在空闲的时候,也会显得很忙。

在PyTorch中,我发现代码需要更频繁的检查CUDA是否可用,以及更明确的设备管理。在编写能够同时在CPU和GPU上运行的代码时尤其如此。以及得把GPU上的PyTorch变量转换为Numpy数组,这就显得有点冗长。

自定义扩展

赢家:PyTorch

两个框架都可以构建和绑定用C、C++、CUDA编写的自定义扩展。TensorFlow仍然需要更多的样板代码,尽管这对于支持多类型和设备可能更好。在PyTorch中,你只需为每个CPU和GPU编写一个接口和相应的实现。两个框架中编译扩展也是直接记性,并不需要在pip安装的内容之外下载任何头文件或者源代码。


我们可以使用pandas库的read_csv()方法来导入包含我们的数据集的CSV文件。

让我们输出数据集 :

输出:

输出显示该数据集具有1万条记录和14列。我们可以使用head()数据框的方法来输出数据集的前五行。

输出:

您可以在我们的数据集中看到14列。根据前13列,我们的任务是预测第14列的值,即Exited

探索性数据分析

让我们对数据集进行一些探索性数据分析。我们将首先预测6个月后实际离开银行并使用饼图进行可视化的客户比例。让我们首先增加图形的默认绘图大小:

以下脚本绘制该Exited列的饼图。

输出:

输出显示,在我们的数据集中,有20%的客户离开了银行。这里1代表客户离开银行的情况,0代表客户没有离开银行的情况。让我们绘制数据集中所有地理位置的客户数量:

输出显示,几乎一半的客户来自法国,而西班牙和德国的客户比例分别为25%。

现在,让我们绘制来自每个唯一地理位置的客户数量以及客户流失信息。我们可以使用库中的countplot()函数seaborn来执行此操作。

输出显示,尽管法国客户总数是西班牙和德国客户总数的两倍,但法国和德国客户离开银行的客户比例是相同的。同样,德国和西班牙客户的总数相同,但是离开银行的德国客户数量是西班牙客户的两倍,这表明德国客户在6个月后离开银行的可能性更大。

数据预处理

在训练PyTorch模型之前,我们需要预处理数据。如果查看数据集,您将看到它具有两种类型的列:数值列和分类列。数字列包含数字信息。CreditScoreBalanceAge等。类似地,GeographyGender是分类列,因为它们含有分类信息,如客户的位置和性别。有几列可以视为数字列和类别列。例如,该HasCrCard列的值可以为1或0。但是,那HasCrCard列包含有关客户是否拥有信用卡的信息。

让我们再次输出数据集中的所有列,并找出哪些列可以视为数字列,哪些列应该视为类别列。columns数据框的属性显示所有列名称:


用R语言实现神经网络预测股票实例

阅读文章


从我们的数据列,我们将不使用的RowNumberCustomerId以及Surname列,因为这些列的值是完全随机的,并与输出无关。例如,客户的姓氏对客户是否离开银行没有影响。其中列的其余部分,GeographyGenderHasCrCard,和IsActiveMember列可以被视为类别列。让我们创建这些列的列表: 除该列外,其余所有列均可视为数字列。

最后,输出(Exited列中的值)存储在outputs变量中。

我们已经创建了分类,数字和输出列的列表。但是,目前,分类列的类型不是分类的。您可以使用以下脚本检查数据集中所有列的类型:
输出:

您可以看到GeographyGender列的类型是object,HasCrCardIsActive列的类型是int64。我们需要将分类列的类型转换为category。我们可以使用astype()函数来做到这一点,


随时关注您喜欢的主题


现在,如果再次绘制数据集中各列的类型,您将看到以下结果:

输出量

现在让我们查看Geography列中的所有类别:

当您将列的数据类型更改为类别时,该列中的每个类别都会分配一个唯一的代码。例如,让我们绘制列的前五行,Geography并输出前五行的代码值:

输出:

以下脚本在该列的前五行中绘制了值的代码Geography

输出:

输出显示法国已编码为0,西班牙已编码为2。

将分类列与数字列分开的基本目的是,可以将数字列中的值直接输入到神经网络中。但是,必须首先将类别列的值转换为数字类型。分类列中的值的编码部分地解决了分类列的数值转换的任务。

由于我们将使用PyTorch进行模型训练,因此需要将分类列和数值列转换为张量。首先让我们将分类列转换为张量。在PyTorch中,可以通过numpy数组创建张量。我们将首先将四个分类列中的数据转换为numpy数组,然后将所有列水平堆叠,如以下脚本所示:

上面的脚本输出分类列中前十条记录。输出如下:输出:

现在要从上述numpy数组创建张量,您只需将数组传递给模块的tensortorch

输出:

在输出中,您可以看到类别数据的numpy数组现在已转换为tensor对象。同样,我们可以将数值列转换为张量:

在输出中,您可以看到前五行,其中包含我们数据集中六个数字列的值。最后一步是将输出的numpy数组转换为tensor对象。输出:

输出:

现在,让我们绘制分类数据,数值数据和相应输出的形状: 输出:

在训练模型之前,有一个非常重要的步骤。我们将分类列转换为数值,其中唯一值由单个整数表示。例如,在该Geography列中,我们看到法国用0表示,德国用1表示。我们可以使用这些值来训练我们的模型。但是,更好的方法是以N维向量的形式表示分类列中的值,而不是单个整数。

我们需要为所有分类列定义矢量大小。关于维数没有严格的规定。定义列的嵌入大小的一个好的经验法则是将列中唯一值的数量除以2(但不超过50)。例如,对于该Geography列,唯一值的数量为3。该Geography列的相应嵌入大小将为3/2 = 1.5 = 2(四舍五入)。以下脚本创建一个元组,其中包含所有类别列的唯一值数量和维度大小:

输出:

使用训练数据对监督型深度学习模型(例如我们在本文中开发的模型)进行训练,并在测试数据集上评估模型的性能。因此,我们需要将数据集分为训练集和测试集,如以下脚本所示:

我们的数据集中有1万条记录,其中80%的记录(即8000条记录)将用于训练模型,而其余20%的记录将用于评估模型的性能。注意,在上面的脚本中,分类和数字数据以及输出已分为训练集和测试集。为了验证我们已正确地将数据分为训练和测试集:

输出:

创建预测模型

我们将数据分为训练集和测试集,现在是时候定义训练模型了。为此,我们可以定义一个名为的类Model,该类将用于训练模型。看下面的脚本:

接下来,要查找输入层的大小,将类别列和数字列的数量加在一起并存储在input_size变量中。之后,for循环迭代,并将相应的层添加到all_layers列表中。添加的层是:

  • Linear:用于计算输入和权重矩阵之间的点积
  • ReLu:用作激活函数
  • BatchNorm1d:用于对数字列应用批量归一化
  • Dropout:用于避免过拟合

在后for循环中,输出层被附加到的层的列表。由于我们希望神经网络中的所有层都按顺序执行,因此将层列表传递给nn.Sequential该类。

接下来,在该forward方法中,将类别列和数字列都作为输入传递。类别列的嵌入在以下几行中进行。

数字列的批量归一化可通过以下脚本应用:

最后,将嵌入的分类列x和数字列x_numerical连接在一起,并传递给sequence layers

训练模型

要训​​练模型,首先我们必须创建Model在上一节中定义的类的对象。

您可以看到我们传递了分类列的嵌入大小,数字列的数量,输出大小(在我们的例子中为2)以及隐藏层中的神经元。您可以看到我们有三个分别具有200、100和50个神经元的隐藏层。
让我们输出模型并查看:

输出:

您可以看到,在第一线性层中,in_features变量的值为11,因为我们有6个数字列,并且类别列的嵌入维数之和为5,因此6 + 5 = 11。out_features的值为2,因为我们只有2个可能的输出。

在实际训练模型之前,我们需要定义损失函数和将用于训练模型的优化器。以下脚本定义了损失函数和优化器:

现在,我们训练模型。以下脚本训练模型:

神经元元数设置为300,这意味着要训练模型,完整的数据集将使用300次。for为每次迭代期间循环的执行方式,损失是使用损耗函数来计算。每次迭代过程中的损失将添加到aggregated_loss列表中。

上面脚本的输出如下:

以下脚本绘制了各个时期的损失函数:

输出:

输出显示,最初损失函数迅速降低。在250个步长之后,损失几乎没有减少。

做出预测

最后一步是对测试数据进行预测。为此,我们只需要将categorical_test_datanumerical_test_data传递给model该类。然后可以将返回的值与实际测试输出值进行比较。以下脚本对测试类进行预测,并输出测试数据的交叉熵损失。

输出:

测试集上的损失为0.3685,比训练集上获得的0.3465略多,这表明我们的模型有些过拟合。由于我们指定输出层将包含2个神经元,因此每个预测将包含2个值。例如,前5个预测值如下所示:

输出:

这种预测的思想是,如果实际输出为0,则索引0处的值应大于索引1处的值,反之亦然。我们可以使用以下脚本检索列表中最大值的索引:

输出:现在让我们再次输出y_val列表的前五个值:

输出:

由于在最初预测的输出列表中,对于前五个记录,零索引处的值大于第一索引处的值,因此可以在已处理输出的前五行中看到0。

最后,我们可以使用从sklearn.metrics模块confusion_matrixaccuracy_score以及classification_report类找到了准确度,精密度和召回值,混淆矩阵。

输出:

输出结果表明,我们的模型达到了84.65%的精度,考虑到我们随机选择神经网络模型的所有参数这一事实,这非常令人印象深刻。我建议您尝试更改模型参数,例如训练/测试比例,隐藏层的数量和大小等,以查看是否可以获得更好的结果。

结论

PyTorch是Facebook开发的常用深度学习库,可用于各种任务,例如分类,回归和聚类。本文介绍了如何使用PyTorch库对表格数据进行分类。


可下载资源

关于作者

Kaizong Ye拓端研究室(TRL)的研究员。

本文借鉴了作者最近为《R语言数据分析挖掘必知必会 》课堂做的准备。

​非常感谢您阅读本文,如需帮助请联系我们!

 
QQ在线咨询
售前咨询热线
15121130882
售后咨询热线
0571-63341498