天天看點

監督學習 | 決策樹之Sklearn實作1. Sklearn中決策樹的超參數2. 使用 Scikit-learn 實作決策樹算法3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶參考資料

文章目錄

  • 1. Sklearn中決策樹的超參數
    • 1.1 最大深度 max_depth
    • 1.2 每片葉子的最小樣本數 min_samples_leaf
    • 1.3 每次分裂的最小樣本數 min_samples_split
    • 1.4 最大特征數 max_features
    • 1.5 算法 criterion: string, optional (default=”gini”)
    • 1.6 模型複雜度系數 ccp_alpha: non-negative float, optional (default=0.0)
  • 2. 使用 Scikit-learn 實作決策樹算法
  • 3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶
    • 3.1 資料導入
    • 3.2 資料預處理
      • 3.2.1 One-Hot 編碼
      • 3.2.2 Pandas 實作 One-Hot 編碼
    • 3.3 訓練模型
    • 3.4 測試模型
    • 3.5 決策樹可視化
    • 3.6 預剪枝
  • 參考資料

相關文章:

機器學習 | 目錄

監督學習 | ID3 決策樹原理及Python實作

監督學習 | ID3 & C4.5 決策樹原理

監督學習 | CART 分類回歸樹原理

監督學習 | 決策樹之Sklearn實作

監督學習 | 決策樹之網絡搜尋

1. Sklearn中決策樹的超參數

1.1 最大深度 max_depth

  • 決策樹的最大深度指樹根和葉子之間的最大長度。當決策樹的最大深度為 k k k時,它最多可以擁有 2 k 2^k 2k片葉子,

    最大深度的設定相當于預剪枝處理

  • 較大的深度往往會導緻過拟合,這是因為過深的決策樹可以記憶資料。而較小的深度會使得模型過于簡單,導緻欠拟合。
監督學習 | 決策樹之Sklearn實作1. Sklearn中決策樹的超參數2. 使用 Scikit-learn 實作決策樹算法3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶參考資料

1.2 每片葉子的最小樣本數 min_samples_leaf

  • 在分裂節點時,很有可能一片葉子上有 99 個樣本,而另一片葉子上隻有 1 個樣本。這将使我們陷入困境,并造成資源和時間的浪費。如果想避免這種問題,我們可以設定每片葉子允許的最小樣本數。
  • 這個數字可以被指定為一個整數,也可以是一個浮點數。如果它是整數,它将表示這片葉子上的最小樣本數。如果它是個浮點數,它将被視作每片葉子上的最小樣本比例。比如,0.1 或 10% 表示如果一片葉子上的樣本數量小于該節點中樣本數量的 10%,這種分裂将不被允許。
  • 當每片葉子的樣本數量較小時,葉子上的樣本數量也有可能過于稀少,此時模型将記憶資料,也就是過拟合。當每片葉子的樣本數量較大時,決策樹能夠獲得足夠的彈性進行建構,這也許會導緻欠拟合。
監督學習 | 決策樹之Sklearn實作1. Sklearn中決策樹的超參數2. 使用 Scikit-learn 實作決策樹算法3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶參考資料

1.3 每次分裂的最小樣本數 min_samples_split

  • 與每片葉子上的最小樣本樹相同,隻不過是應用在節點的分裂當中。

1.4 最大特征數 max_features

  • 有時,我們會遇到特征數量過于龐大,而無法建立決策樹的情況。在這種狀況下,對于每一個分裂,我們都需要檢查整個資料集中的每一個特征。這種過程極為繁瑣。而解決方案之一是限制每個分裂中查找的特征數。如果這個數字足夠龐大,我們很有可能在查找的特征中找到良好特征(盡管也許并不是完美特征)。然而,如果這個數字小于特征數,這将極大加快我們的計算速度。

更多關于SKlearn決策樹算法的參數設定可以參考這篇文章:Python中決策樹分類器DecisionTreeClassifier參數和經驗總結

DecisionTreeClassifier官方文檔

1.5 算法 criterion: string, optional (default=”gini”)

  • ID3: criterion=“entropy”
  • CART: “gini”

1.6 模型複雜度系數 ccp_alpha: non-negative float, optional (default=0.0)

即後剪枝中懲罰函數的系數 α \alpha α,當 α \alpha α 确定時,子樹越大,往往與訓練資料的拟合越好,但是模型的複雜度越高;相反,子樹越小,模型複雜度就越低,但是往往與訓練資料的拟合不好,損失函數正好表示了對兩者的平衡。關于後剪枝的懲罰函數,可以參考這篇文章。

2. 使用 Scikit-learn 實作決策樹算法

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
model = DecisionTreeClassifier(max_depth = 7, min_samples_leaf = 10)              # 建立決策樹分類器
model.fit(x_values, y_values)                                                     # 模型拟合
y_pred = model.predict(X)                                                         # 模型預測
acc = accuracy_score(y,y_pred)                                                    # 模型評分
           

3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶

3.1 資料導入

資料可以在我的Github上下載下傳

# Import libraries necessary for this project
import numpy as np
import pandas as pd
from IPython.display import display # Allows the use of display() for DataFrames

# Pretty display for notebooks
%matplotlib inline

# Set a random seed
import random
random.seed(42)

# Load the dataset
in_file = 'data/titanic_data.csv'
full_data = pd.read_csv(in_file)

# Print the first few entries of the RMS Titanic data
display(full_data.head())
           
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
1 3 Braund, Mr. Owen Harris male 22.0 1 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 113803 53.1000 C123 S
4 5 3 Allen, Mr. William Henry male 35.0 373450 8.0500 NaN S

下面是每位乘客具備的各種特征:

  • PassengerID:乘客編号
  • Survived:存活結果(0 = 存活;1 = 未存活)
  • Pclass:社會階層(1 = 上層;2 = 中層;3 = 底層)
  • Name:乘客姓名
  • Sex:乘客性别
  • Age:乘客年齡(某些條目為

    NaN

  • SibSp:一起上船的兄弟姐妹和配偶人數
  • Parch:一起上船的父母和子女人數
  • Ticket:乘客的票号
  • Fare:乘客支付的票價
  • Cabin:乘客的客艙号(某些條目為

    NaN

  • Embarked:乘客的登船港(C = 瑟堡;Q = 皇後鎮;S = 南安普頓)

因為我們對每位乘客或船員的存活情況感興趣,是以我們可以從此資料集中删除 Survived 特征,并将其存儲在單獨的變量

outcome

中。我們将使用這些結果作為預測目标。

從資料集中删除特征 Survived 并将其存儲到

outcome

中:

# Store the 'Survived' feature in a new variable and remove it from the dataset
outcomes = full_data['Survived']
features_raw = full_data.drop('Survived', axis = 1)

# Show the new dataset with 'Survived' removed
display(features_raw.head())
           
PassengerId Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
1 3 Braund, Mr. Owen Harris male 22.0 1 A/5 21171 7.2500 NaN S
1 2 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 PC 17599 71.2833 C85 C
2 3 3 Heikkinen, Miss. Laina female 26.0 STON/O2. 3101282 7.9250 NaN S
3 4 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 113803 53.1000 C123 S
4 5 3 Allen, Mr. William Henry male 35.0 373450 8.0500 NaN S

3.2 資料預處理

我們在之前的樸素貝葉斯之Sklearn實作中講過詞袋(Bag of Words)的概念,BOW的目的是将文本轉換為單詞頻率表,原因是Scikit-learn 隻處理數字值。類似的,在決策樹中,對于“Name”、“Sex”等離散特征,其取值之間沒有大小的意義,是以可以使用獨熱編碼(One-Hot Encoding)将分類值轉換為數值型。

3.2.1 One-Hot 編碼

獨熱編碼即 One-Hot 編碼,又稱一位有效編碼。其方法是使用N位狀态寄存器來對N個狀态進行編碼,每個狀态都由他獨立的寄存器位,并且在任意時候,其中隻有一位有效。

可以這樣了解,對于每一個特征,如果它有 m m m個可能值,那麼經過獨熱編碼後,就變成了 m m m個二進制特征(如成績這個特征有好,中,差變成 one-hot 就是100, 010, 001)。并且,這些特征互斥,每次隻有一個激活。是以,資料會變成稀疏的。[1]

這樣做的好處主要有:

  1. 解決了分類器不好處理屬性資料的問題
  2. 在一定程度上也起到了擴充特征的作用
為什麼不使用 BOW ?
BOW 适用于每一個對象内包含多個單詞,使用 BOW 後,對象内的單詞将會被分散開來。
           

更多關于 One-Hot 編碼的知識,可以參考這一篇博文:獨熱編碼(One-Hot Encoding)和 LabelEncoder标簽編碼

3.2.2 Pandas 實作 One-Hot 編碼

  • pd.get_dummies(data)

該方法可以講類别變量轉換成新增的虛拟變量/訓示變量。

常用參數:

  • data

    (輸入的資料): array-like, Series, or DataFrame
  • prefix

    (轉換後列名的字首): string, list of strings, or dict of strings, default None
  • columns

    (指定需要實作類别轉換的列名): list-like, default None
  • dummy_na

    (增加一清單示空缺值,預設忽略空缺值): True, False
  • drop_first

    (獲得k中的k-1個類别值,去除第一個): bool, default False
features = pd.get_dummies(features_raw)      # 獨熱編碼 将不是數字類型的資料轉換為數字類型,如Embarked分為了Embarked_S、Embarked_C...
display(features.head())
           
PassengerId Pclass Age SibSp Parch Fare Name_Abbing, Mr. Anthony Name_Abbott, Mr. Rossmore Edward Name_Abbott, Mrs. Stanton (Rosa Hunt) Name_Abelson, Mr. Samuel ... Cabin_F G73 Cabin_F2 Cabin_F33 Cabin_F38 Cabin_F4 Cabin_G6 Cabin_T Embarked_C Embarked_Q Embarked_S
1 3 22.0 1 7.2500 ... 1
1 2 1 38.0 1 71.2833 ... 1
2 3 3 26.0 7.9250 ... 1
3 4 1 35.0 1 53.1000 ... 1
4 5 3 35.0 8.0500 ... 1

5 rows × 1730 columns

現在用 0 填充任何空白處:

features = features.fillna(0.0)
display(features.head())
           
PassengerId Pclass Age SibSp Parch Fare Name_Abbing, Mr. Anthony Name_Abbott, Mr. Rossmore Edward Name_Abbott, Mrs. Stanton (Rosa Hunt) Name_Abelson, Mr. Samuel ... Cabin_F G73 Cabin_F2 Cabin_F33 Cabin_F38 Cabin_F4 Cabin_G6 Cabin_T Embarked_C Embarked_Q Embarked_S
1 3 22.0 1 7.2500 ... 1
1 2 1 38.0 1 71.2833 ... 1
2 3 3 26.0 7.9250 ... 1
3 4 1 35.0 1 53.1000 ... 1
4 5 3 35.0 8.0500 ... 1

5 rows × 1730 columns

3.3 訓練模型

我們已經準備好在 sklearn 中訓練模型了。首先,将資料拆分為訓練集和測試集。然後用訓練集訓練模型。

保留20%的資料作為測試集:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(features, outcomes, test_size=0.2, random_state=42)
           

導入決策樹模型并訓練模型:

# Import the classifier from sklearn
from sklearn.tree import DecisionTreeClassifier

# TODO: Define the classifier, and fit it to the data
model = DecisionTreeClassifier()
model.fit(X_train,y_train)
           
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=6,
                       max_features=None, max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=6, min_samples_split=10,
                       min_weight_fraction_leaf=0.0, presort=False,
                       random_state=None, splitter='best')
           

3.4 測試模型

現在看看模型的效果。我們計算下訓練集和測試集的準确率:

# Making predictions
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# Calculate the accuracy
from sklearn.metrics import accuracy_score
train_accuracy = accuracy_score(y_train, y_train_pred)
test_accuracy = accuracy_score(y_test, y_test_pred)
print('The training accuracy is', train_accuracy)
print('The test accuracy is', test_accuracy)
           
The training accuracy is 0.8707865168539326
The test accuracy is 0.8547486033519553
           

可以看到訓練集準确率為100%,但測試集準确率隻有81%,這是因為我們沒有對模型進行任何預剪枝處理,是以此時的模型有點過拟合了,這個模型偏向于“記住資料”,是以在測試劑上表現并不好。

我們将通過可視化決策樹來直覺的确定是否過拟合:

3.5 決策樹可視化

# 可視化
from sklearn.tree import export_graphviz
export_graphviz(model,out_file="tree.dot",class_names=['Survived','Dead'],impurity=False,filled=True)

# 通過 Graphviz 打開.dot檔案

# 或者通過terminal cd到檔案夾
# 運作 dot -Tpdf Tree.dot -o output.pdf 直接輸出為pdf
           
監督學習 | 決策樹之Sklearn實作1. Sklearn中決策樹的超參數2. 使用 Scikit-learn 實作決策樹算法3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶參考資料

3.6 預剪枝

從圖檔中可以看出,決策樹确實是過拟合了,是以我們可以通過預剪枝,來防止過拟合:

model = DecisionTreeClassifier(max_depth=6, min_samples_leaf=6, min_samples_split=10)
model.fit(X_train,y_train)
# Making predictions
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# Calculate the accuracy
from sklearn.metrics import accuracy_score
train_accuracy = accuracy_score(y_train, y_train_pred)
test_accuracy = accuracy_score(y_test, y_test_pred)
print('The training accuracy is', train_accuracy)
print('The test accuracy is', test_accuracy)
export_graphviz(model,out_file="tree.dot",class_names=['Survived','Dead'],impurity=False,filled=True)
           
The training accuracy is 0.8707865168539326
The test accuracy is 0.8547486033519553
           

可以看到,經過預剪枝後,該模型在測試集上的正确率已經達到了85%,如果想要更高的準确率,可以通過網絡搜尋來确定最優參數。

監督學習 | 決策樹之Sklearn實作1. Sklearn中決策樹的超參數2. 使用 Scikit-learn 實作決策樹算法3. 執行個體:使用決策樹探索泰坦尼克号乘客存活情況¶參考資料

從圖檔中可以看出,在泰坦尼克号的逃生中,大多數的女性都活了下來,而大多數的男性都沒有存活下來.

Survived 1
Sex
female 81 233
male 468 109

參考資料

[1] 理想幾歲.資料預處理:獨熱編碼(One-Hot Encoding)和 LabelEncoder标簽編碼 [EB/OL].https://www.cnblogs.com/zongfa/p/9305657.html, 2018-07-13.