共计 8256 个字符,预计需要花费 21 分钟才能阅读完成。
分类问题属于机器学习问题的类别,其中给定一组性能,工作是预测离散值。分类问题的一些常见示例是,预测肿瘤是否为癌症,或者学生是否可能通过考试。
在本文中,鉴于银行客户的某些特色,咱们将预测客户在 6 个月后是否可能来到银行。客户来到组织的景象也称为客户散失。因而,咱们的工作是依据各种客户特色预测客户散失。
$ pip install pytorch
数据集
让咱们将所需的库和数据集导入到咱们的 Python 应用程序中:
`
- import torch
- import torch.nn as nn
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- import seaborn as sns
- %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 个月后理论来到银行并应用饼图进行可视化的客户比例。
让咱们首先减少图形的默认绘图大小:
`
- fig_size = plt.rcParams[“figure.figsize”]
- fig_size[0] = 10
- fig_size[1] = 8
- 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 模型之前,咱们须要预处理数据。如果查看数据集,您将看到它具备两种类型的列:数值列和分类列。数字列蕴含数字信息。CreditScore
,Balance
,Age
等。相似地,Geography
和 Gender
是分类列,因为它们含有分类信息,如客户的地位和性别。有几列能够视为数字列和类别列。例如,该 HasCrCard
列的值能够为 1 或 0。然而,那 HasCrCard
列蕴含无关客户是否领有信用卡的信息。然而,这齐全取决于数据集的畛域常识。
让咱们再次输入数据集中的所有列,并找出哪些列能够视为数字列,哪些列应该视为类别列。columns
数据框的属性显示所有列名称:
输入:
`
- Index([‘RowNumber’, ‘CustomerId’, ‘Surname’, ‘CreditScore’, ‘Geography’,
- ‘Gender’, ‘Age’, ‘Tenure’, ‘Balance’, ‘NumOfProducts’, ‘HasCrCard’,
- ‘IsActiveMember’, ‘EstimatedSalary’, ‘Exited’],
- dtype=’object’)
`
从咱们的数据列,咱们将不应用的 RowNumber
,CustomerId
以及 Surname
列,因为这些列的值是齐全随机的,并与输入无关。例如,客户的姓氏对客户是否来到银行没有影响。其中列的其余部分,Geography
,Gender
,HasCrCard
,和 IsActiveMember
列能够被视为类别列。让咱们创立这些列的列表:
除该列外,其余所有 列均可视为数字列。
numerical_columns = ['CreditScore', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'EstimatedSalary']
最初,输入(Exited
列中的值)存储在 outputs
变量中。
咱们曾经创立了分类,数字和输入列的列表。然而,目前,分类列的类型不是分类的。您能够应用以下脚本检查数据集中所有列的类型:
输入:
`
- RowNumber int64
- CustomerId int64
- Surname object
- CreditScore int64
- Geography object
- Gender object
- Age int64
- Tenure int64
- Balance float64
- NumOfProducts int64
- HasCrCard int64
- IsActiveMember int64
- EstimatedSalary float64
- Exited int64
- dtype: object
`
您能够看到 Geography
and Gender
列的类型是 object,HasCrCard
and IsActive
列的类型是 int64。咱们须要将分类列的类型转换为 category
。咱们能够应用astype()
函数来做到这一点,如下所示:
当初,如果再次绘制数据集中各列的类型,您将看到以下后果:
输出量
`
- RowNumber int64
- CustomerId int64
- Surname object
- CreditScore int64
- Geography category
- Gender category
- Age int64
- Tenure int64
- Balance float64
- NumOfProducts int64
- HasCrCard category
- IsActiveMember category
- EstimatedSalary float64
- Exited int64
- dtype: object
`
当初让咱们查看 Geography
列中的所有类别:
输入:
Index(['France', 'Germany', 'Spain'], dtype='object')
当您将列的数据类型更改为类别时,该列中的每个类别都会调配一个惟一的代码。例如,让咱们绘制列的前五行,Geography
并打印前五行的代码值:
输入:
`
- 0 France
- 1 Spain
- 2 France
- 3 France
- 4 Spain
- Name: Geography, dtype: category
- Categories (3, object): [France, Germany, Spain]
`
以下脚本在该列的前五行中绘制了值的代码Geography
:
输入:
`
- 0 0
- 1 2
- 2 0
- 3 0
- 4 2
- dtype: int8
`
输入显示法国已编码为 0,西班牙已编码为 2。
将分类列与数字列离开的根本目标是,能够将数字列中的值间接输出到神经网络中。然而,必须首先将类别列的值转换为数字类型。分类列中的值的编码局部地解决了分类列的数值转换的工作。
因为咱们将应用 PyTorch 进行模型训练,因而须要将分类列和数值列转换为张量。
首先让咱们将分类列转换为张量。在 PyTorch 中,能够通过 numpy 数组创立张量。咱们将首先将四个分类列中的数据转换为 numpy 数组,而后将所有列程度重叠,如以下脚本所示:
`
- geo = dataset[‘Geography’].cat.codes.values
- …
`
下面的脚本打印出分类列中前十条记录,这些记录是程度重叠的。输入如下:
输入:
`
- array([[0, 0, 1, 1],
- [2, 0, 0, 1],
- [0, 0, 1, 0],
- [0, 0, 0, 0],
- [2, 0, 1, 1],
- [2, 1, 1, 0],
- [0, 1, 1, 1],
- [1, 0, 1, 0],
- [0, 1, 0, 1],
- [0, 1, 1, 1]], dtype=int8)
`
当初要从上述 numpy 数组创立张量,您只需将数组传递给模块的 tensor
类torch
。
输入:
`
- tensor([[0, 0, 1, 1],
- [2, 0, 0, 1],
- [0, 0, 1, 0],
- [0, 0, 0, 0],
- [2, 0, 1, 1],
- [2, 1, 1, 0],
- [0, 1, 1, 1],
- [1, 0, 1, 0],
- [0, 1, 0, 1],
- [0, 1, 1, 1]])
`
在输入中,您能够看到类别数据的 numpy 数组当初已转换为 tensor
对象。
同样,咱们能够将数值列转换为张量:
`
- numerical_data = np.stack([dataset[col].values for col in numerical_columns], 1)
- …
`
输入:
`
- tensor([[6.1900e+02, 4.2000e+01, 2.0000e+00, 0.0000e+00, 1.0000e+00, 1.0135e+05],
- [6.0800e+02, 4.1000e+01, 1.0000e+00, 8.3808e+04, 1.0000e+00, 1.1254e+05],
- [5.0200e+02, 4.2000e+01, 8.0000e+00, 1.5966e+05, 3.0000e+00, 1.1393e+05],
- [6.9900e+02, 3.9000e+01, 1.0000e+00, 0.0000e+00, 2.0000e+00, 9.3827e+04],
- [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])
当初,让咱们绘制分类数据,数值数据和相应输入的形态:
...
输入:
`
- torch.Size([10000, 4])
- torch.Size([10000, 6])
- torch.Size([10000])
`
在训练模型之前,有一个十分重要的步骤。咱们将分类列转换为数值,其中惟一值由单个整数示意。例如,在该 Geography
列中,咱们看到法国用 0 示意,德国用 1 示意。咱们能够应用这些值来训练咱们的模型。然而,更好的办法是以 N 维向量的模式示意分类列中的值,而不是单个整数。
咱们须要为所有分类列定义嵌入尺寸(矢量尺寸)。对于维数没有严格的规定。定义列的嵌入大小的一个好的教训法令是将列中惟一值的数量除以 2(但不超过 50)。例如,对于该 Geography
列,惟一值的数量为 3。该 Geography
列的相应嵌入大小将为 3 /2 = 1.5 = 2(四舍五入)。
以下脚本创立一个元组,其中蕴含所有类别列的惟一值数量和维度大小:
`
- categorical_column_sizes = [len(dataset[column].cat.categories) for column in categorical_columns]
- …
`
输入:
[(3, 2), (2, 1), (2, 1), (2, 1)]
应用训练数据对监督型深度学习模型(例如咱们在本文中开发的模型)进行训练,并在测试数据集上评估模型的性能。因而,咱们须要将数据集分为训练集和测试集,如以下脚本所示:
`
- total_records = 10000
- ….
`
咱们的数据集中有 1 万条记录,其中 80%的记录(即 8000 条记录)将用于训练模型,而其余 20%的记录将用于评估模型的性能。留神,在下面的脚本中,分类和数字数据以及输入已分为训练集和测试集。
为了验证咱们已正确地将数据分为训练和测试集:
`
- print(len(categorical_train_data))
- print(len(numerical_train_data))
- print(len(train_outputs))
- print(len(categorical_test_data))
- print(len(numerical_test_data))
- print(len(test_outputs))
`
输入:
`
- 8000
- 8000
- 8000
- 2000
- 2000
- 2000
`
创立预测模型
咱们将数据分为训练集和测试集,当初是时候定义训练模型了。为此,咱们能够定义一个名为的类Model
,该类将用于训练模型。看上面的脚本:
`
- class Model(nn.Module):
- def __init__(self, embedding_size, num_numerical_cols, output_size, layers, p=0.4):
- super().__init__()
- self.all_embeddings = nn.ModuleList([nn.Embedding(ni, nf) for ni, nf in embedding_size])
- self.embedding_dropout = nn.Dropout(p)
- self.batch_norm_num = nn.BatchNorm1d(num_numerical_cols)
- …
- return x
`
接下来,要查找输出层的大小,将类别列和数字列的数量加在一起并存储在 input_size
变量中。之后,for
循环迭代,并将相应的层增加到 all_layers
列表中。增加的层是:
Linear
:用于计算输出和权重矩阵之间的点积ReLu
:用作激活性能BatchNorm1d
:用于对数字列利用批量归一化Dropout
:用于防止适度拟合
在后 for
循环中,输入层被附加到的层的列表。因为咱们心愿神经网络中的所有层都按程序执行,因而将层列表传递给 nn.Sequential
该类。
接下来,在该 forward
办法中,将类别列和数字列都作为输出传递。类别列的嵌入在以下几行中进行。
`
- embeddings = []
- …
`
数字列的批量归一化可通过以下脚本利用:
x_numerical = self.batch_norm_num(x_numerical)
最初,将嵌入的分类列 x
和数字列 x_numerical
连贯在一起,并传递给 sequence layers
。
训练模型
要训练模型,首先咱们必须创立 Model
在上一节中定义的类的对象。
... 您能够看到咱们传递了分类列的嵌入大小,数字列的数量,输入大小(在咱们的例子中为 2)以及暗藏层中的神经元。您能够看到咱们有三个别离具备 200、100 和 50 个神经元的暗藏层。您能够依据须要抉择其余尺寸。
让咱们打印模型并查看:
print(model)
输入:
`
- Model(
- (all_embeddings): ModuleList(
- …
- )
- )
`
您能够看到,在第一线性层中,in_features
变量的值为 11,因为咱们有 6 个数字列,并且类别列的嵌入维数之和为 5,因而 6 + 5 = 11。out_features
的值为 2,因为咱们只有 2 个可能的输入。
在理论训练模型之前,咱们须要定义损失函数和将用于训练模型的优化器。
以下脚本定义了损失函数和优化器:
`
- loss_function = nn.CrossEntropyLoss()
- …
`
当初,咱们领有训练模型所需的所有。以下脚本训练模型:
`
- epochs = 300
- aggregated_losses = []
- for i in range(epochs):
- …
- print(f’epoch: {i:3} loss: {single_loss.item():10.10f}’)
`
神经元元数设置为 300,这意味着要训练模型,残缺的数据集将应用 300 次。for
为 300 倍和在每次迭代期间循环的执行形式,损失是应用损耗函数来计算。每次迭代过程中的损失将增加到 aggregated_loss
列表中。要更新权重,将 backward()
调用 single_loss
对象的性能。最初,函数的 step()
办法 optimizer
更新突变。
下面脚本的输入如下:
`
- epoch: 1 loss: 0.71847951
- epoch: 26 loss: 0.57145703
- epoch: 51 loss: 0.48110831
- epoch: 76 loss: 0.42529839
- epoch: 101 loss: 0.39972275
- epoch: 126 loss: 0.37837571
- epoch: 151 loss: 0.37133673
- epoch: 176 loss: 0.36773482
- epoch: 201 loss: 0.36305946
- epoch: 226 loss: 0.36079505
- epoch: 251 loss: 0.35350436
- epoch: 276 loss: 0.35540250
- epoch: 300 loss: 0.3465710580
`
以下脚本绘制了各个期间的损失:
`
- plt.plot(range(epochs), aggregated_losses)
- plt.ylabel(‘Loss’)
- plt.xlabel(‘epoch’);
`
输入:
输入显示,最后损耗迅速升高。在第 250 个时代之后,损失简直没有缩小。
做出预测
最初一步是对测试数据进行预测。为此,咱们只须要将 categorical_test_data
和传递 numerical_test_data
给model
该类。而后能够将返回的值与理论测试输入值进行比拟。以下脚本对测试类进行预测,并打印测试数据的穿插熵损失。
`
- with torch.no_grad():
- …
`
输入:
Loss: 0.36855841
测试集上的损失为 0.3685,比训练集上取得的 0.3465 略多,这表明咱们的模型有些过拟合。
因为咱们指定输入层将蕴含 2 个神经元,因而每个预测将蕴含 2 个值。例如,前 5 个预测值如下所示:
print(y_val[:5])
输入:
`
- tensor([[1.2045, -1.3857],
- [1.3911, -1.5957],
- [1.2781, -1.3598],
- [0.6261, -0.5429],
- [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_matrix
,accuracy_score
以及 classification_report
类从 sklearn.metrics
模块找到了准确度,精密度和召回值测试集,与混同矩阵一起。
`
- from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
- print(confusion_matrix(test_outputs,y_val))
- print(classification_report(test_outputs,y_val))
- print(accuracy_score(test_outputs, y_val))
`
输入:
`
- [[1527 83]
- [224 166]]
- precision recall f1-score support
- 0 0.87 0.95 0.91 1610
- 1 0.67 0.43 0.52 390
- micro avg 0.85 0.85 0.85 2000
- macro avg 0.77 0.69 0.71 2000
- weighted avg 0.83 0.85 0.83 2000
- 0.8465
`
输入结果表明,咱们的模型达到了 84.65%的精度,思考到咱们随机抉择神经网络模型的所有参数这一事实,这十分令人印象粗浅。我建议您尝试更改模型参数,例如训练 / 测试比例,暗藏层的数量和大小等,以查看是否能够取得更好的后果。
论断
PyTorch 是 Facebook 开发的罕用深度学习库,可用于各种工作,例如分类,回归和聚类。本文介绍了如何应用 PyTorch 库对表格数据进行分类。