Python代寫中用PyTorch機器學習分類預測銀行客戶流失模型


原文鏈接:http://tecdat.cn/?p=8522

 

 分類問題屬於機器學習問題的類別,其中給定一組功能,任務是預測離散值。分類問題的一些常見示例是,預測腫瘤是否為癌症,或者學生是否可能通過考試。

在本文中,鑒於銀行客戶的某些特征,我們將預測客戶在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模型之前,我們需要預處理數據。如果查看數據集,您將看到它具有兩種類型的列:數值列和分類列。數字列包含數字信息。CreditScoreBalanceAge等。類似地,GeographyGender是分類列,因為它們含有分類信息,如客戶的位置和性別。有幾列可以視為數字列和類別列。例如,該HasCrCard列的值可以為1或0。但是,那HasCrCard列包含有關客戶是否擁有信用卡的信息。 但是,這完全取決於數據集的領域知識。

讓我們再次輸出數據集中的所有列,並找出哪些列可以視為數字列,哪些列應該視為類別列。columns數據框的屬性顯示所有列名稱:


輸出:
Index(['RowNumber', 'CustomerId', 'Surname', 'CreditScore', 'Geography',
       'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard',
       'IsActiveMember', 'EstimatedSalary', 'Exited'],
      dtype='object')

從我們的數據列,我們將不使用的RowNumberCustomerId以及Surname列,因為這些列的值是完全隨機的,並與輸出無關。例如,客戶的姓氏對客戶是否離開銀行沒有影響。其中列的其余部分,GeographyGenderHasCrCard,和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

您可以看到Geographyand Gender列的類型是object,HasCrCardand 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數組創建張量,您只需將數組傳遞給模塊的tensortorch

 

 

輸出:

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_datamodel該類。然后可以將返回的值與實際測試輸出值進行比較。以下腳本對測試類進行預測,並打印測試數據的交叉熵損失。

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_matrixaccuracy_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庫對表格數據進行分類。

 

 

如果您有任何疑問,請在下面發表評論。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM