資料科學和人工智慧技術筆記 十三、樹和森林
十三、樹和森林
作者:Chris Albon
譯者:飛龍
Adaboost 分類器
# 載入庫
from sklearn.ensemble import AdaBoostClassifier
from sklearn import datasets
# 載入資料
iris = datasets.load_iris()
X = iris.data
y = iris.target
最重要的引數是base_estimator
,n_estimators
和learning_rate
。
base_estimator
n_estimators
是迭代式訓練的模型數。learning_rate
是每個模型對權重的貢獻,預設為1
。 降低學習率將意味著權重將增加或減少到很小的程度,迫使模型訓練更慢(但有時會產生更好的表現得分)。loss
是AdaBoostRegressor
獨有的,它設定了更新權重時使用的損失函式。 這預設為線性損失函式,但可以更改為square
或exponential
。
# 建立 adaboost 決策樹分類器物件
clf = AdaBoostClassifier(n_estimators=50,
learning_rate=1,
random_state=0)
# 訓練模型
model = clf.fit(X, y)
決策樹分類器
# 載入庫
from sklearn.tree import DecisionTreeClassifier
from sklearn import datasets
# 載入資料
iris = datasets.load_iris()
X = iris.data
y = iris. target
# 建立使用 GINI 的決策樹分類器物件
clf = DecisionTreeClassifier(criterion='gini', random_state=0)
# 訓練模型
model = clf.fit(X, y)
# 生成新的觀測
observation = [[ 5, 4, 3, 2]]
# 預測觀測的類別
model.predict(observation)
# array([1])
# 檢視三個類別的預測概率
model.predict_proba(observation)
# array([[ 0., 1., 0.]])
決策樹迴歸
# 載入庫
from sklearn.tree import DecisionTreeRegressor
from sklearn import datasets
# 載入只有兩個特徵的資料
boston = datasets.load_boston()
X = boston.data[:,0:2]
y = boston.target
決策樹迴歸的工作方式類似於決策樹分類,但不是減少基尼雜質或熵,而是測量潛在的分割點,它們減少均方誤差(MSE)的程度:
其中 是目標的真實值, 是預測值。
# 建立決策樹迴歸器物件
regr = DecisionTreeRegressor(random_state=0)
# 訓練模型
model = regr.fit(X, y)
# 生成新的觀測
observation = [[0.02, 16]]
# 預測觀測的值
model.predict(observation)
# array([ 33.])
特徵的重要性
# 載入庫
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
# 載入資料
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 建立決策樹分類器物件
clf = RandomForestClassifier(random_state=0, n_jobs=-1)
# 訓練模型
model = clf.fit(X, y)
# 計算特徵重要性
importances = model.feature_importances_
# 對特徵重要性降序排序
indices = np.argsort(importances)[::-1]
# 重新排列特徵名稱,使它們匹配有序的特徵重要性
names = [iris.feature_names[i] for i in indices]
# 建立繪圖
plt.figure()
# 建立繪圖示題
plt.title("Feature Importance")
# 新增條形
plt.bar(range(X.shape[1]), importances[indices])
# 新增特徵名稱作為 x 軸標籤
plt.xticks(range(X.shape[1]), names, rotation=90)
# 展示繪圖
plt.show()
使用隨機森林的特徵選擇
通常在資料科學中,我們有數百甚至數百萬個特徵,我們想要一種方法來建立僅包含最重要特徵的模型。 這有三個好處。 首先,我們使模型更易於解釋。 其次,我們可以減少模型的方差,從而避免過擬合。 最後,我們可以減少訓練模型的計算開銷(和時間)。 僅識別最相關特徵的過程稱為“特徵選擇”。
資料科學工作流程中,隨機森林通常用於特徵選擇。 原因是,隨機森林使用的基於樹的策略,自然按照它們如何改善節點的純度來排序。 這意味著所有樹的不純度的減少(稱為基尼不純度)。 不純度減少最多的節點出現在樹的開始處,而不純度減少最少的節點出現在樹的末端。 因此,通過在特定節點下修剪樹,我們可以建立最重要特徵的子集。
在這個教程中,我們將要:
- 準備資料集
- 訓練隨機森林分類器
- 識別最重要的特徵
- 建立新的“有限特徵的”資料集,僅僅包含那些特徵
- 在新資料集上訓練第二個分類器
- 將“全部特徵的”分類器的準確率,和“有限特徵的”分類器比較
注:還有其他重要定義,但在本教程中,我們將討論限制為基尼重要性。
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectFromModel
from sklearn.metrics import accuracy_score
本教程中使用的資料集是著名的鳶尾花資料集鳶尾花資料包含來自三種鳶尾y
和四個特徵變數X
的 50 個樣本。
# 載入鳶尾花資料集
iris = datasets.load_iris()
# 建立特徵名稱列表
feat_labels = ['Sepal Length','Sepal Width','Petal Length','Petal Width']
# 從特徵中建立 X
X = iris.data
# 從目標中建立 y
y = iris.target
# 檢視特徵
X[0:5]
'''
array([[ 5.1, 3.5, 1.4, 0.2],
[ 4.9, 3\. , 1.4, 0.2],
[ 4.7, 3.2, 1.3, 0.2],
[ 4.6, 3.1, 1.5, 0.2],
[ 5\. , 3.6, 1.4, 0.2]])
'''
# 檢視目標資料
y
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
'''
# 將資料分為 40% 的測試和 60% 的訓練集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)
# 建立隨機森林分類器
clf = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
# 訓練分類器
clf.fit(X_train, y_train)
# 列印每個特徵的名稱和基尼重要性
for feature in zip(feat_labels, clf.feature_importances_):
print(feature)
'''
('Sepal Length', 0.11024282328064565)
('Sepal Width', 0.016255033655398394)
('Petal Length', 0.45028123999239533)
('Petal Width', 0.42322090307156124)
'''
上面的得分是每個變數的重要性得分。 有兩點需要注意。 首先,所有重要性得分加起來為 100%。 其次,“花瓣長度”和“花瓣寬度”遠比其他兩個特徵重要。結合起來,“花瓣長度”和“花瓣寬度”的重要性約為 0.86!顯然,這些是最重要的特徵。
# 建立一個選擇器物件,
# 該物件將使用隨機森林分類器來標識重要性大於 0.15 的特徵
sfm = SelectFromModel(clf, threshold=0.15)
# 訓練選擇器
sfm.fit(X_train, y_train)
'''
SelectFromModel(estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
verbose=0, warm_start=False),
prefit=False, threshold=0.15)
'''
# 列印最重要的特徵的名稱
for feature_list_index in sfm.get_support(indices=True):
print(feat_labels[feature_list_index])
'''
Petal Length
Petal Width
'''
# 轉換資料來建立僅包含最重要特徵的新資料集
# 注意:我們必須將變換應用於訓練 X 和測試 X 資料。
X_important_train = sfm.transform(X_train)
X_important_test = sfm.transform(X_test)
# 為最重要的特徵建立新的隨機森林分類器
clf_important = RandomForestClassifier(n_estimators=10000, random_state=0, n_jobs=-1)
# 在包含最重要特徵的新資料集上訓練新分類器
clf_important.fit(X_important_train, y_train)
'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10000, n_jobs=-1, oob_score=False, random_state=0,
verbose=0, warm_start=False)
'''
# 將全部特徵的分類器應用於測試資料
y_pred = clf.predict(X_test)
# 檢視我們的全部特徵(4 個特徵)的模型的準確率
accuracy_score(y_test, y_pred)
# 0.93333333333333335
# 將全部特徵的分類器應用於測試資料
y_important_pred = clf_important.predict(X_important_test)
# 檢視我們有限特徵(2 個特徵)的模型的準確率
accuracy_score(y_test, y_important_pred)
# 0.8833333333333333
從準確率得分可以看出,包含所有四個特徵的原始模型準確率為 93.3%,而僅包含兩個特徵的“有限”模型準確率為 88.3%。 因此,為了精確率的低成本,我們將模型中的特徵數量減半。
在隨機森林中處理不平衡類別
# 載入庫
from sklearn.ensemble import RandomForestClassifier
import numpy as np
from sklearn import datasets
# 載入資料
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 通過移除前 40 個觀測,生成高度不平衡的類別
X = X[40:,:]
y = y[40:]
# 建立目標向量,表示類別是否為 0
y = np.where((y == 0), 0, 1)
當使用RandomForestClassifier
時,有用的設定是class_weight = balanced
,其中類自動加權,與它們在資料中出現的頻率成反比。具體來說:
其中 是類 的權重, 是觀測數, 是類 中的觀測數, 是類的總數。
# 建立決策樹分類器物件
clf = RandomForestClassifier(random_state=0, n_jobs=-1, class_weight="balanced")
# 訓練模型
model = clf.fit(X, y)
隨機森林分類器
# 載入庫
from sklearn.ensemble import RandomForestClassifier
from sklearn import datasets
# 載入資料
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 建立使用熵的隨機森林分類器
clf = RandomForestClassifier(criterion='entropy', random_state=0, n_jobs=-1)
# 訓練模型
model = clf.fit(X, y)
# 建立新的觀測
observation = [[ 5, 4, 3, 2]]
# 預測觀測的類別
model.predict(observation)
array([1])
隨機森林分類器示例
本教程基於 Yhat 2013 年的[ Python 中的隨機森林]教程(http://blog.yhat.com/posts/random-forests-in-python.html)。 如果您想要隨機森林的理論和用途的總結,我建議您檢視他們的指南。 在下面的教程中,我對文章末尾提供的隨機森林的簡短程式碼示例進行了註釋,更正和擴充套件。 具體來說,我(1)更新程式碼,使其在最新版本的 pandas 和 Python 中執行,(2)編寫詳細的註釋,解釋每個步驟中發生的事情,以及(3)以多種方式擴充套件程式碼。
讓我們開始吧!
資料的註解
本教程的資料很有名。 被稱為鳶尾花資料集,它包含四個變數,測量了三個鳶尾花物種的各個部分,然後是帶有物種名稱的第四個變數。 它在機器學習和統計社群中如此著名的原因是,資料需要很少的預處理(即沒有缺失值,所有特徵都是浮點數等)。
# 載入鳶尾花資料集
from sklearn.datasets import load_iris
# 載入 sklearn 的隨機森林分類器
from sklearn.ensemble import RandomForestClassifier
# 載入 pandas
import pandas as pd
# 載入 numpy
import numpy as np
# 設定隨機種子
np.random.seed(0)
# Create an object called iris with the iris data
iris = load_iris()
# 建立帶有四個特徵變數的資料幀
df = pd.DataFrame(iris.data, columns=iris.feature_names)
# 檢視前五行
df.head()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | |
---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 |
1 | 4.9 | 3.0 | 1.4 | 0.2 |
2 | 4.7 | 3.2 | 1.3 | 0.2 |
3 | 4.6 | 3.1 | 1.5 | 0.2 |
4 | 5.0 | 3.6 | 1.4 | 0.2 |
# 新增帶有物種名稱的新列,我們要嘗試預測它
df['species'] = pd.Categorical.from_codes(iris.target, iris.target_names)
# 檢視前五行
df.head()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | species | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
# 建立一個新列,每列生成一個0到1之間的隨機數,
# 如果該值小於或等於.75,則將該單元格的值設定為 True
# 否則為 False。這是一種簡潔方式,
# 隨機分配一些行作為訓練資料,一些作為測試資料。
df['is_train'] = np.random.uniform(0, 1, len(df)) <= .75
# 檢視前五行
df.head()
sepal length (cm) | sepal width (cm) | petal length (cm) | petal width (cm) | species | is_train | |
---|---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa | True |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa | True |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa | True |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa | True |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa | True |
# 建立兩個新的資料幀,一個包含訓練行,另一個包含測試行
train, test = df[df['is_train']==True], df[df['is_train']==False]
# 顯示測試和訓練資料幀的觀測數
print('Number of observations in the training data:', len(train))
print('Number of observations in the test data:',len(test))
'''
Number of observations in the training data: 118
Number of observations in the test data: 32
'''
# 建立特徵列名稱的列表
features = df.columns[:4]
# 檢視特徵
features
'''
Index(['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)',
'petal width (cm)'],
dtype='object')
'''
# train['species'] 包含實際的物種名稱。
# 在我們使用它之前,我們需要將每個物種名稱轉換為數字。
# 因此,在這種情況下,有三種物種,它們被編碼為 0, 1 或 2。
y = pd.factorize(train['species'])[0]
# 檢視目標
y
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2])
'''
# 建立隨機森林分類器。按照慣例,clf 表示“分類器”
clf = RandomForestClassifier(n_jobs=2, random_state=0)
# 訓練分類器,來接受訓練特徵
# 並瞭解它們與訓練集 y(物種)的關係
clf.fit(train[features], y)
'''
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10, n_jobs=2, oob_score=False, random_state=0,
verbose=0, warm_start=False)
'''
好哇! 我們做到了! 我們正式訓練了我們的隨機森林分類器! 現在讓我們玩玩吧。 分類器模型本身儲存在clf
變數中。
如果你一直跟著,你會知道我們只在部分資料上訓練了我們的分類器,留出了剩下的資料。 在我看來,這是機器學習中最重要的部分。 為什麼? 因為省略了部分資料,我們有一組資料來測試我們模型的準確率!
讓我們現在實現它。
# 將我們訓練的分類器應用於測試資料
# (記住,以前從未見過它)
clf.predict(test[features])
'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2])
'''
你在上面看到什麼? 請記住,我們將三種植物中的每一種編碼為 0, 1 或 2。 以上數字列表顯示,我們的模型基於萼片長度,萼片寬度,花瓣長度和花瓣寬度,預測每種植物的種類。 分類器對於每種植物有多自信? 我們也可以看到。
# 檢視前 10 個觀測值的預測概率
clf.predict_proba(test[features])[0:10]
'''
array([[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ],
[ 0.9, 0.1, 0\. ],
[ 1\. , 0\. , 0\. ],
[ 1\. , 0\. , 0\. ]])
'''
有三種植物,因此[1, 0, 0]
告訴我們分類器確定植物是第一類。 再舉一個例子,[0.9, 0.1, 0]
告訴我們,分類器給出植物屬於第一類的概率為90%,植物屬於第二類的概率為 10%。 因為 90 大於 10,分類器預測植物是第一類。
現在我們已經預測了測試資料中所有植物的種類,我們可以比較我們預測的物種與該植物的實際物種。
# 為每個預測的植物類別
# 建立植物的實際英