拓端数据(tecdat):Python中⽤PyTorch机器学习分类预测银
⾏客户流失模型
原⽂链接:tecdat/?p=8522
分类问题属于机器学习问题的类别,其中给定⼀组功能,任务是预测离散值。分类问题的⼀些常见⽰例是,预测肿瘤是否为癌症,或者学⽣是否可能通过考试。
在本⽂中,鉴于银⾏客户的某些特征,我们将预测客户在6个⽉后是否可能离开银⾏。客户离开组织的现象也称为客户流失。因此,我们的任务是根据各种客户特征预测客户流失。
$ pip install pytorch
数据集
让我们将所需的库和数据集导⼊到我们的Python应⽤程序中:
exitedimport torch as nn import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn a s 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()
输出:
need-to-insert-img
您可以在我们的数据集中看到14列。根据前13列,我们的任务是预测第14列的值,即Exited。
探索性数据分析
让我们对数据集进⾏⼀些探索性数据分析。我们将⾸先预测6个⽉后实际离开银⾏并使⽤饼图进⾏可视化的客户⽐例。
让我们⾸先增加图形的默认绘图⼤⼩:
fig_size = Params["figure.figsize"] fig_size[0] = 10 fig_size[1] = Params["figure.figsize"] = fig_size
以下脚本绘制该Exited列的饼图。
dataset.Exited.value_counts().plot(kind='pie', autopct='%1.0f%%', colors=['skyblue', 'orange'], explode=(0.05, 0.05))
输出:
need-to-insert-img
输出显⽰,在我们的数据集中,有20%的客户离开了银⾏。这⾥1代表客户离开银⾏的情况,0代表客户没有离开银⾏的情况。
让我们绘制数据集中所有地理位置的客户数量:
输出:
need-to-insert-img
输出显⽰,⼏乎⼀半的客户来⾃法国,⽽西班⽛和德国的客户⽐例分别为25%。
现在,让我们绘制来⾃每个唯⼀地理位置的客户数量以及客户流失信息。我们可以使⽤库中的countplot()函数seaborn来执⾏此操作。
输出:
need-to-insert-img
输出显⽰,尽管法国客户总数是西班⽛和德国客户总数的两倍,但法国和德国客户离开银⾏的客户⽐例是相同的。同样,德国和西班⽛客户的总数相同,但是离开银⾏的德国客户数量是西班⽛客户的两倍,这表明德国客户在6个⽉后离开银⾏的可能性更⼤。
数据预处理
在训练PyTorch模型之前,我们需要预处理数据。如果查看数据集,您将看到它具有两种类型的列:数值列和分类列。数字列包含数字信息。CreditScore,Balance,Age等。类似地,Geography和Gender是分类列,因为它们含有分类信息,如客户的位置和性别。有⼏列可以视为数字列和类别列。例如,该HasCrCard列的值可以为1或0。但是,那HasCrCard列包含有关客户是否拥有信⽤卡的信息。 但是,这完全取决于数据集的领域知识。
让我们再次输出数据集中的所有列,并出哪些列可以视为数字列,哪些列应该视为类别列。columns数据框的属性显⽰所有列名称:
输出:
Index(['RowNumber', 'CustomerId', 'Surname', 'CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'H asCrCard', 'IsActiveMember', 'EstimatedSalary', 'Exited'], dtype='object')
从我们的数据列,我们将不使⽤的RowNumber,CustomerId以及Surname列,因为这些列的值是完全随机的,并与输出⽆关。例如,客户的姓⽒对客户是否离开银⾏没有影响。其中列的其余部分,Ge
ography,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 int 64 Balance float64 NumOfProducts int64 HasCrCard int64 IsActiveMember int64 EstimatedSalary float64 Exited int64 dtype: object
您可以看到GeographyandGender列的类型是object,HasCrCardandIsActive列的类型是int64。我们需要将分类列的类型转换为category。我们可以使⽤astype()函数来做到这⼀点,如下所⽰:
现在,如果再次绘制数据集中各列的类型,您将看到以下结果:
输出量
RowNumber int64 CustomerId int64 Surname object CreditScore int64 Geography category Gender category Age int64 Tenur e int64 Balance float64 NumOfProducts int64 HasCrCard category IsActiveMember category EstimatedSalary float64 Exited in t64 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'].des.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+0
2, 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]) bedding_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连接在⼀起,并传递给sequencelayers。
训练模型
要训练模型,⾸先我们必须创建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() ...
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论