文章目錄
- 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片葉子,
。最大深度的設定相當于預剪枝處理
- 較大的深度往往會導緻過拟合,這是因為過深的決策樹可以記憶資料。而較小的深度會使得模型過于簡單,導緻欠拟合。
1.2 每片葉子的最小樣本數 min_samples_leaf
- 在分裂節點時,很有可能一片葉子上有 99 個樣本,而另一片葉子上隻有 1 個樣本。這将使我們陷入困境,并造成資源和時間的浪費。如果想避免這種問題,我們可以設定每片葉子允許的最小樣本數。
- 這個數字可以被指定為一個整數,也可以是一個浮點數。如果它是整數,它将表示這片葉子上的最小樣本數。如果它是個浮點數,它将被視作每片葉子上的最小樣本比例。比如,0.1 或 10% 表示如果一片葉子上的樣本數量小于該節點中樣本數量的 10%,這種分裂将不被允許。
- 當每片葉子的樣本數量較小時,葉子上的樣本數量也有可能過于稀少,此時模型将記憶資料,也就是過拟合。當每片葉子的樣本數量較大時,決策樹能夠獲得足夠的彈性進行建構,這也許會導緻欠拟合。
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]
這樣做的好處主要有:
- 解決了分類器不好處理屬性資料的問題
- 在一定程度上也起到了擴充特征的作用
為什麼不使用 BOW ?
BOW 适用于每一個對象内包含多個單詞,使用 BOW 後,對象内的單詞将會被分散開來。
更多關于 One-Hot 編碼的知識,可以參考這一篇博文:獨熱編碼(One-Hot Encoding)和 LabelEncoder标簽編碼
3.2.2 Pandas 實作 One-Hot 編碼
- pd.get_dummies(data)
該方法可以講類别變量轉換成新增的虛拟變量/訓示變量。
常用參數:
-
(輸入的資料): array-like, Series, or DataFramedata
-
(轉換後列名的字首): string, list of strings, or dict of strings, default Noneprefix
-
(指定需要實作類别轉換的列名): list-like, default Nonecolumns
-
(增加一清單示空缺值,預設忽略空缺值): True, Falsedummy_na
-
(獲得k中的k-1個類别值,去除第一個): bool, default Falsedrop_first
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
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%,如果想要更高的準确率,可以通過網絡搜尋來确定最優參數。
從圖檔中可以看出,在泰坦尼克号的逃生中,大多數的女性都活了下來,而大多數的男性都沒有存活下來.
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.