5.8 決策樹和隨機森林
譯者:飛龍
譯文沒有得到原作者授權,不保證與原文的意思嚴格一致。
之前,我們深入研究了簡單的生成分類器(見樸素貝葉斯分類)和強大的辨別分類器(參見支持向量機)。 這里我們來看看另一個強大的算法的動機 - 一種稱為隨機森林的非參數算法。 隨機森林是組合方法的一個例子,這意味著它依賴于更簡單估計器的整體聚合結果。 這種組合方法的結果令人驚訝,總和可以大于部分:即,多個估器中的多數表決最終可能比執行表決的任何個體的估計更好! 我們將在以下部分中看到這個例子。 我們從標準導入開始:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
隨機森林是一個例子,建立在決策樹上的組合學習器。 因此,我們將首先討論決策樹本身。
決策樹是分類或標注對象的非常直觀的方法:您只需要詢問一系列問題,它們為弄清楚分類而設計。 例如,如果您想建立一個決策樹,來分類您在遠足時遇到的動物,則可以構建如下所示的樹:
二元分割使其非常有效:在一個結構良好的樹中,每個問題都會將選項數量減少一半,即使在大量分類中也很快縮小選項。 當然,這個技巧是決定在每個步驟中要問哪些問題。 在決策樹的機器學習實現中,問題通常采用數據中軸對齊分割的形式:即,樹中的每個節點使用其中一個特征中的分割值將數據分成兩組。 現在來看一個例子。
創建決策樹
考慮以下二維數據,它擁有四個標簽之一:
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=300, centers=4,
random_state=0, cluster_std=1.0)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='rainbow');
根據這些數據建立的一個簡單的決策樹,將根據一些定量標準,沿著一個或另一個軸線迭代地分割數據,并且在每個級別,根據其中的多數表決來分配新區域的標簽。 該圖顯示了該數據的決策樹分類器的前四個級別的可視化:
請注意,在第一次拆分之后,上部分支中的每個點保持不變,因此無需進一步細分此分支。 除了包含所有一種顏色的節點,在每個級別,每個區域再次沿著兩個特征之一分裂。
將決策樹擬合到我們的數據的這個過程,可以在 Scikit-Learn 中使用DecisionTreeClassifier
估計器來完成:
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier().fit(X, y)
讓我們寫一個簡單的輔助函數,幫助我們展示分類器的輸出:
def visualize_classifier(model, X, y, ax=None, cmap='rainbow'):
ax = ax or plt.gca()
# Plot the training points
ax.scatter(X[:, 0], X[:, 1], c=y, s=30, cmap=cmap,
clim=(y.min(), y.max()), zorder=3)
ax.axis('tight')
ax.axis('off')
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# fit the estimator
model.fit(X, y)
xx, yy = np.meshgrid(np.linspace(*xlim, num=200),
np.linspace(*ylim, num=200))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
# Create a color plot with the results
n_classes = len(np.unique(y))
contours = ax.contourf(xx, yy, Z, alpha=0.3,
levels=np.arange(n_classes + 1) - 0.5,
cmap=cmap, clim=(y.min(), y.max()),
zorder=1)
ax.set(xlim=xlim, ylim=ylim)
現在我們可以檢測,決策樹看起來是什么樣子:
visualize_classifier(DecisionTreeClassifier(), X, y)
如果您現在正在運行這個筆記,您可以使用“在線附錄”中包含的幫助腳本,來啟動決策樹構建過程的交互式可視化:
# helpers_05_08 is found in the online appendix
import helpers_05_08
helpers_05_08.plot_tree_interactive(X, y);
請注意,隨著深度的增加,我們傾向于獲得非常奇怪的分類區域; 例如,在第五層,黃色和藍色區域之間有一個高而瘦的紫色區域。 很明顯,這不是真實的,固有的數據分布結果,更多的是數據的特定采樣或噪聲屬性的結果。 也就是說,這個決策樹,即使只有五個層次的深度,顯然對我們的數據過擬合了。
決策樹和過擬合
這種過度擬合是決策樹的一般屬性:在樹中很容易就走得太深,從而擬合特定數據的細節,而不是抽取它們分布的整體屬性。 查看這種過擬合的另一種方法是,查看在不同數據子集上訓練的模型 - 例如,在這個圖中,我們訓練兩種不同的樹,每種都是原始數據的一半:
很明顯,在一些地方,兩棵樹產生一致的結果(例如在四個角落),而在其他地方,這兩棵樹給出非常不同的分類(例如,在任何兩個簇之間的區域中)。 關鍵觀察是,分類不太確定的地方,會發生不一致,因此通過使用這兩種樹的信息,我們可能會得到更好的結果!
如果您正在運行這個筆記,以下功能允許您交互顯示樹的擬合,在數據的隨機子集上訓練:
# helpers_05_08 is found in the online appendix
import helpers_05_08
helpers_05_08.randomized_tree_interactive(X, y)
正如使用來自兩棵樹的信息,改善了我們的結果,我們可能希望使用來自許多樹的信息,進一步改善我們的結果。
估計器的組合:隨機森林
這個概念 - 多個過擬合估計器可以組合,來減少這種過擬合的影響 - 是一種稱為裝袋的組合方法。 這個方法使用了一組并行估計器,每個都對數據過擬合,并對結果進行平均以找到更好的分類。 隨機決策樹的一個組合被稱為隨機森林。
這種類型的裝袋分類,可以使用 Scikit-Learn 的BaggingClassifier
元估計器手動進行,如下所示:
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
tree = DecisionTreeClassifier()
bag = BaggingClassifier(tree, n_estimators=100, max_samples=0.8,
random_state=1)
bag.fit(X, y)
visualize_classifier(bag, X, y)
在這個例子中,我們將每個估計器擬合訓練點的 80% 隨機子集進行來隨機化數據。 在實踐中,通過在選擇分割的方式中添加一些隨機性,來更有效地隨機化決策樹:這樣,所有數據每次都有助于擬合,但是擬合的結果仍然具有所需的隨機性。 例如,當確定要分割的特征時,隨機化樹可以從前幾個特征中選擇。 您可以在 Scikit-Learn 文檔中閱讀這些隨機策略的更多技術細節和參考。
在 Scikit-Learn 中,隨機決策樹的優化組合在RandomForestClassifier
估計器中實現,它自動地處理所有的隨機化。 所有你需要做的是選擇一些估計器,它將很快(如果需要則并行)擬合樹的組合:
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=100, random_state=0)
visualize_classifier(model, X, y);
我們看到,通過對超過 100 個隨機擾動的模型取平均,我們最終得到一個整體模型,更接近我們關于如何分割參數空間的直覺。
隨機森林回歸
在上一節中,我們在分類范圍內考慮了隨機森林。 隨機森林也可以用于回歸(即連續而不是分類變量)。 用于此的估計器是RandomForestRegressor
,并且語法與我們之前看到的非常相似。
考慮以下數據,由快速和慢速振蕩的組合產生:
rng = np.random.RandomState(42)
x = 10 * rng.rand(200)
def model(x, sigma=0.3):
fast_oscillation = np.sin(5 * x)
slow_oscillation = np.sin(0.5 * x)
noise = sigma * rng.randn(len(x))
return slow_oscillation + fast_oscillation + noise
y = model(x)
plt.errorbar(x, y, 0.3, fmt='o');
使用隨機森林回歸器,我們可以找到最佳擬合曲線,
這里的真實模型以灰色平滑曲線中顯示,隨機森林模型由紅色鋸齒曲線顯示。 可以看出,非參數隨機森林模型足夠靈活,可以擬合多周期數據,而不需要指定多周期模型!
示例:隨機森林數字分類
早些時候我們快速瀏覽了手寫數字數據(參見 Scikit-Learn 介紹)。 讓我們再次使用它,來看看如何在這個上下文中使用隨機森林分類器。
from sklearn.datasets import load_digits
digits = load_digits()
digits.keys()
# dict_keys(['target', 'data', 'target_names', 'DESCR', 'images'])
為了提醒我們,我們正在觀察什么,我們展示前幾個數據點。
# set up the figure
fig = plt.figure(figsize=(6, 6)) # figure size in inches
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot the digits: each image is 8x8 pixels
for i in range(64):
ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
ax.imshow(digits.images[i], cmap=plt.cm.binary, interpolation='nearest')
# label the image with the target value
ax.text(0, 7, str(digits.target[i]))
我們可以快速使用隨機森林對這些數字分類,像這樣:
from sklearn.cross_validation import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(digits.data, digits.target,
random_state=0)
model = RandomForestClassifier(n_estimators=1000)
model.fit(Xtrain, ytrain)
ypred = model.predict(Xtest)
我們可以看一看分類器的分類報告:
from sklearn import metrics
print(metrics.classification_report(ypred, ytest))
precision recall f1-score support
0 1.00 0.97 0.99 38
1 1.00 0.98 0.99 44
2 0.95 1.00 0.98 42
3 0.98 0.96 0.97 46
4 0.97 1.00 0.99 37
5 0.98 0.96 0.97 49
6 1.00 1.00 1.00 52
7 1.00 0.96 0.98 50
8 0.94 0.98 0.96 46
9 0.96 0.98 0.97 46
avg / total 0.98 0.98 0.98 450
為了更好的度量,繪制混淆矩陣:
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest, ypred)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False)
plt.xlabel('true label')
plt.ylabel('predicted label');
我們發現,簡單無調整的隨機森林,產生了數據的非常準確的分類。
隨機森林總結
本節簡要介紹了組合估計器的概念,特別是隨機森林 - 隨機決策樹的整體。 隨機森林是一個強大的方法,具有幾個優點:
- 訓練和預測都非???,因為底層決策樹簡單。 此外,兩個任務都可以直接并行化,因為各個樹是完全獨立的實體。
- 多個樹提供了概率分類:估計器之間的多數表決提供了概率估計(在 Scikit-Learn 中使用
predict_proba()
方法來訪問)。 - 非參數模型是非常靈活的,因此可以在其他估計器擬合不足的任務上表現良好。
隨機森林的主要缺點是結果不容易解釋:即如果要對分類模型的含義作出總結,隨機森林可能不是最佳選擇。