乐趣区

关于python:Python中用PyTorch机器学习分类预测银行客户流失模型

分类问题属于机器学习问题的类别,其中给定一组性能,工作是预测离散值。分类问题的一些常见示例是,预测肿瘤是否为癌症,或者学生是否可能通过考试。

在本文中,鉴于银行客户的某些特色,咱们将预测客户在 6 个月后是否可能来到银行。客户来到组织的景象也称为客户散失。因而,咱们的工作是依据各种客户特色预测客户散失。

 $ pip install pytorch

数据集

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

`

  1. import torch
  2. import torch.nn as nn
  3. import numpy as np
  4. import pandas as pd
  5. import matplotlib.pyplot as plt
  6. import seaborn as sns
  7. %matplotlib inline

`

咱们能够应用库的 read_csv() 办法 pandas 来导入蕴含咱们的数据集的 CSV 文件。

dataset = pd.read_csv(r'E:Datasets\customer_data.csv')

让咱们打印数据集:

dataset.shape

输入:

(10000, 14)

输入显示该数据集具备 1 万条记录和 14 列。

咱们能够应用head()pandas 数据框的办法来打印数据集的前五行。

dataset.head()

输入:

您能够在咱们的数据集中看到 14 列。依据前 13 列,咱们的工作是预测第 14 列的值,即Exited

探索性数据分析

让咱们对数据集进行一些探索性数据分析。咱们将首先预测 6 个月后理论来到银行并应用饼图进行可视化的客户比例。

让咱们首先减少图形的默认绘图大小:

`

  1. fig_size = plt.rcParams[“figure.figsize”]
  2. fig_size[0] = 10
  3. fig_size[1] = 8
  4. plt.rcParams[“figure.figsize”] = fig_size

`

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

dataset.Exited.value_counts().plot(kind='pie', autopct='%1.0f%%', colors=['skyblue', 'orange'], explode=(0.05, 0.05))

输入:

输入显示,在咱们的数据集中,有 20%的客户来到了银行。这里 1 代表客户来到银行的状况,0 代表客户没有来到银行的状况。

让咱们绘制数据集中所有地理位置的客户数量:

输入:

输入显示,简直一半的客户来自法国,而西班牙和德国的客户比例别离为 25%。

当初,让咱们绘制来自每个惟一地理位置的客户数量以及客户散失信息。咱们能够应用库中的 countplot() 函数 seaborn 来执行此操作。

输入:

 

输入显示,只管法国客户总数是西班牙和德国客户总数的两倍,但法国和德国客户来到银行的客户比例是雷同的。同样,德国和西班牙客户的总数雷同,然而来到银行的德国客户数量是西班牙客户的两倍,这表明德国客户在 6 个月后来到银行的可能性更大。

数据预处理

在训练 PyTorch 模型之前,咱们须要预处理数据。如果查看数据集,您将看到它具备两种类型的列:数值列和分类列。数字列蕴含数字信息。CreditScoreBalanceAge等。相似地,GeographyGender 是分类列,因为它们含有分类信息,如客户的地位和性别。有几列能够视为数字列和类别列。例如,该 HasCrCard 列的值能够为 1 或 0。然而,那 HasCrCard 列蕴含无关客户是否领有信用卡的信息。然而,这齐全取决于数据集的畛域常识。

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

输入:

`

  1. Index([‘RowNumber’, ‘CustomerId’, ‘Surname’, ‘CreditScore’, ‘Geography’,
  2. ‘Gender’, ‘Age’, ‘Tenure’, ‘Balance’, ‘NumOfProducts’, ‘HasCrCard’,
  3. ‘IsActiveMember’, ‘EstimatedSalary’, ‘Exited’],
  4. dtype=’object’)

`

从咱们的数据列,咱们将不应用的 RowNumberCustomerId 以及 Surname 列,因为这些列的值是齐全随机的,并与输入无关。例如,客户的姓氏对客户是否来到银行没有影响。其中列的其余部分,GeographyGenderHasCrCard,和 IsActiveMember 列能够被视为类别列。让咱们创立这些列的列表:

除该列外,其余所有 列均可视为数字列。

numerical_columns = ['CreditScore', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'EstimatedSalary']

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

咱们曾经创立了分类,数字和输入列的列表。然而,目前,分类列的类型不是分类的。您能够应用以下脚本检查数据集中所有列的类型:

输入:

`

  1. RowNumber int64
  2. CustomerId int64
  3. Surname object
  4. CreditScore int64
  5. Geography object
  6. Gender object
  7. Age int64
  8. Tenure int64
  9. Balance float64
  10. NumOfProducts int64
  11. HasCrCard int64
  12. IsActiveMember int64
  13. EstimatedSalary float64
  14. Exited int64
  15. dtype: object

`

您能够看到 Geographyand Gender 列的类型是 object,HasCrCardand IsActive列的类型是 int64。咱们须要将分类列的类型转换为 category。咱们能够应用astype() 函数来做到这一点,如下所示:

当初,如果再次绘制数据集中各列的类型,您将看到以下后果:

输出量

`

  1. RowNumber int64
  2. CustomerId int64
  3. Surname object
  4. CreditScore int64
  5. Geography category
  6. Gender category
  7. Age int64
  8. Tenure int64
  9. Balance float64
  10. NumOfProducts int64
  11. HasCrCard category
  12. IsActiveMember category
  13. EstimatedSalary float64
  14. Exited int64
  15. dtype: object

`

当初让咱们查看 Geography 列中的所有类别:

 输入:

Index(['France', 'Germany', 'Spain'], dtype='object')

当您将列的数据类型更改为类别时,该列中的每个类别都会调配一个惟一的代码。例如,让咱们绘制列的前五行,Geography并打印前五行的代码值:

输入:

`

  1. 0 France
  2. 1 Spain
  3. 2 France
  4. 3 France
  5. 4 Spain
  6. Name: Geography, dtype: category
  7. Categories (3, object): [France, Germany, Spain]

`

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

输入:

`

  1. 0 0
  2. 1 2
  3. 2 0
  4. 3 0
  5. 4 2
  6. dtype: int8

`

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

将分类列与数字列离开的根本目标是,能够将数字列中的值间接输出到神经网络中。然而,必须首先将类别列的值转换为数字类型。分类列中的值的编码局部地解决了分类列的数值转换的工作。

因为咱们将应用 PyTorch 进行模型训练,因而须要将分类列和数值列转换为张量。

首先让咱们将分类列转换为张量。在 PyTorch 中,能够通过 numpy 数组创立张量。咱们将首先将四个分类列中的数据转换为 numpy 数组,而后将所有列程度重叠,如以下脚本所示:

`

  1. geo = dataset[‘Geography’].cat.codes.values

`

下面的脚本打印出分类列中前十条记录,这些记录是程度重叠的。输入如下:

输入:

`

  1. array([[0, 0, 1, 1],
  2. [2, 0, 0, 1],
  3. [0, 0, 1, 0],
  4. [0, 0, 0, 0],
  5. [2, 0, 1, 1],
  6. [2, 1, 1, 0],
  7. [0, 1, 1, 1],
  8. [1, 0, 1, 0],
  9. [0, 1, 0, 1],
  10. [0, 1, 1, 1]], dtype=int8)

`

当初要从上述 numpy 数组创立张量,您只需将数组传递给模块的 tensortorch

输入:

`

  1. tensor([[0, 0, 1, 1],
  2. [2, 0, 0, 1],
  3. [0, 0, 1, 0],
  4. [0, 0, 0, 0],
  5. [2, 0, 1, 1],
  6. [2, 1, 1, 0],
  7. [0, 1, 1, 1],
  8. [1, 0, 1, 0],
  9. [0, 1, 0, 1],
  10. [0, 1, 1, 1]])

`

在输入中,您能够看到类别数据的 numpy 数组当初已转换为 tensor 对象。

同样,咱们能够将数值列转换为张量:

`

  1. numerical_data = np.stack([dataset[col].values for col in numerical_columns], 1)

`

输入:

`

  1. tensor([[6.1900e+02, 4.2000e+01, 2.0000e+00, 0.0000e+00, 1.0000e+00, 1.0135e+05],
  2. [6.0800e+02, 4.1000e+01, 1.0000e+00, 8.3808e+04, 1.0000e+00, 1.1254e+05],
  3. [5.0200e+02, 4.2000e+01, 8.0000e+00, 1.5966e+05, 3.0000e+00, 1.1393e+05],
  4. [6.9900e+02, 3.9000e+01, 1.0000e+00, 0.0000e+00, 2.0000e+00, 9.3827e+04],
  5. [8.5000e+02, 4.3000e+01, 2.0000e+00, 1.2551e+05, 1.0000e+00, 7.9084e+04]])

`

在输入中,您能够看到前五行,其中蕴含咱们数据集中六个数字列的值。

最初一步是将输入的 numpy 数组转换为 tensor 对象。

...

输入:

tensor([1, 0, 1, 0, 0])

当初,让咱们绘制分类数据,数值数据和相应输入的形态:

...

输入:

`

  1. torch.Size([10000, 4])
  2. torch.Size([10000, 6])
  3. torch.Size([10000])

`

在训练模型之前,有一个十分重要的步骤。咱们将分类列转换为数值,其中惟一值由单个整数示意。例如,在该 Geography 列中,咱们看到法国用 0 示意,德国用 1 示意。咱们能够应用这些值来训练咱们的模型。然而,更好的办法是以 N 维向量的模式示意分类列中的值,而不是单个整数。

咱们须要为所有分类列定义嵌入尺寸(矢量尺寸)。对于维数没有严格的规定。定义列的嵌入大小的一个好的教训法令是将列中惟一值的数量除以 2(但不超过 50)。例如,对于该 Geography 列,惟一值的数量为 3。该 Geography 列的相应嵌入大小将为 3 /2 = 1.5 = 2(四舍五入)。

以下脚本创立一个元组,其中蕴含所有类别列的惟一值数量和维度大小:

`

  1. categorical_column_sizes = [len(dataset[column].cat.categories) for column in categorical_columns]

`

输入:

[(3, 2), (2, 1), (2, 1), (2, 1)]

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

`

  1. total_records = 10000
  2. ….

`

咱们的数据集中有 1 万条记录,其中 80%的记录(即 8000 条记录)将用于训练模型,而其余 20%的记录将用于评估模型的性能。留神,在下面的脚本中,分类和数字数据以及输入已分为训练集和测试集。

为了验证咱们已正确地将数据分为训练和测试集:

`

  1. print(len(categorical_train_data))
  2. print(len(numerical_train_data))
  3. print(len(train_outputs))
  4. print(len(categorical_test_data))
  5. print(len(numerical_test_data))
  6. print(len(test_outputs))

`

输入:

`

  1. 8000
  2. 8000
  3. 8000
  4. 2000
  5. 2000
  6. 2000

`

创立预测模型

咱们将数据分为训练集和测试集,当初是时候定义训练模型了。为此,咱们能够定义一个名为的类Model,该类将用于训练模型。看上面的脚本:

`

  1. class Model(nn.Module):
  2. def __init__(self, embedding_size, num_numerical_cols, output_size, layers, p=0.4):
  3. super().__init__()
  4. self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in embedding_size])
  5. self.embedding_dropout = nn.Dropout(p)
  6. self.batch_norm_num = nn.BatchNorm1d(num_numerical_cols)
  7. return x

`

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

  • Linear:用于计算输出和权重矩阵之间的点积
  • ReLu:用作激活性能
  • BatchNorm1d:用于对数字列利用批量归一化
  • Dropout:用于防止适度拟合

在后 for 循环中,输入层被附加到的层的列表。因为咱们心愿神经网络中的所有层都按程序执行,因而将层列表传递给 nn.Sequential 该类。

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

`

  1. embeddings = []

`

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

x_numerical = self.batch_norm_num(x_numerical)

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

训练模型

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

... 您能够看到咱们传递了分类列的嵌入大小,数字列的数量,输入大小(在咱们的例子中为 2)以及暗藏层中的神经元。您能够看到咱们有三个别离具备 200、100 和 50 个神经元的暗藏层。您能够依据须要抉择其余尺寸。

让咱们打印模型并查看:

print(model)

输入:

`

  1. Model(
  2. (all_embeddings): ModuleList(
  3. )
  4. )

`

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

在理论训练模型之前,咱们须要定义损失函数和将用于训练模型的优化器。

以下脚本定义了损失函数和优化器:

`

  1. loss_function = nn.CrossEntropyLoss()

`

当初,咱们领有训练模型所需的所有。以下脚本训练模型:

`

  1. epochs = 300
  2. aggregated_losses = []
  3. for i in range(epochs):
  4. print(f’epoch: {i:3} loss: {single_loss.item():10.10f}’)

`

神经元元数设置为 300,这意味着要训练模型,残缺的数据集将应用 300 次。for为 300 倍和在每次迭代期间循环的执行形式,损失是应用损耗函数来计算。每次迭代过程中的损失将增加到 aggregated_loss 列表中。要更新权重,将 backward() 调用 single_loss 对象的性能。最初,函数的 step() 办法 optimizer 更新突变。

下面脚本的输入如下:

`

  1. epoch: 1 loss: 0.71847951
  2. epoch: 26 loss: 0.57145703
  3. epoch: 51 loss: 0.48110831
  4. epoch: 76 loss: 0.42529839
  5. epoch: 101 loss: 0.39972275
  6. epoch: 126 loss: 0.37837571
  7. epoch: 151 loss: 0.37133673
  8. epoch: 176 loss: 0.36773482
  9. epoch: 201 loss: 0.36305946
  10. epoch: 226 loss: 0.36079505
  11. epoch: 251 loss: 0.35350436
  12. epoch: 276 loss: 0.35540250
  13. epoch: 300 loss: 0.3465710580

`

以下脚本绘制了各个期间的损失:

`

  1. plt.plot(range(epochs), aggregated_losses)
  2. plt.ylabel(‘Loss’)
  3. plt.xlabel(‘epoch’);

`

输入:

输入显示,最后损耗迅速升高。在第 250 个时代之后,损失简直没有缩小。

做出预测

最初一步是对测试数据进行预测。为此,咱们只须要将 categorical_test_data 和传递 numerical_test_datamodel该类。而后能够将返回的值与理论测试输入值进行比拟。以下脚本对测试类进行预测,并打印测试数据的穿插熵损失。

`

  1. with torch.no_grad():

`

输入:

Loss: 0.36855841

测试集上的损失为 0.3685,比训练集上取得的 0.3465 略多,这表明咱们的模型有些过拟合。

因为咱们指定输入层将蕴含 2 个神经元,因而每个预测将蕴含 2 个值。例如,前 5 个预测值如下所示:

print(y_val[:5])

输入:

`

  1. tensor([[1.2045, -1.3857],
  2. [1.3911, -1.5957],
  3. [1.2781, -1.3598],
  4. [0.6261, -0.5429],
  5. [2.5430, -1.9991]])

`

这种预测的思维是,如果理论输入为 0,则索引 0 处的值应大于索引 1 处的值,反之亦然。咱们能够应用以下脚本检索列表中最大值的索引:

y_val = np.argmax(y_val, axis=1)

输入:

当初让咱们再次打印 y_val 列表的前五个值:

print(y_val[:5])

输入:

tensor([0, 0, 0, 0, 0])

因为在最后预测的输入列表中,对于前五个记录,零索引处的值大于第一索引处的值,因而能够在已解决输入的前五行中看到 0。

最初,咱们能够应用 confusion_matrixaccuracy_score 以及 classification_report 类从 sklearn.metrics 模块找到了准确度,精密度和召回值测试集,与混同矩阵一起。

`

  1. from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
  2. print(confusion_matrix(test_outputs,y_val))
  3. print(classification_report(test_outputs,y_val))
  4. print(accuracy_score(test_outputs, y_val))

`

输入:

`

  1. [[1527 83]
  2. [224 166]]
  3. precision recall f1-score support
  4. 0 0.87 0.95 0.91 1610
  5. 1 0.67 0.43 0.52 390
  6. micro avg 0.85 0.85 0.85 2000
  7. macro avg 0.77 0.69 0.71 2000
  8. weighted avg 0.83 0.85 0.83 2000
  9. 0.8465

`

输入结果表明,咱们的模型达到了 84.65%的精度,思考到咱们随机抉择神经网络模型的所有参数这一事实,这十分令人印象粗浅。我建议您尝试更改模型参数,例如训练 / 测试比例,暗藏层的数量和大小等,以查看是否能够取得更好的后果。

论断

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

退出移动版