天天看點

python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

目錄

    • 決策樹
      • 控制決策樹複雜度
      • 分析決策樹
      • 特征重要性
      • 決策樹優缺點
    • 決策樹內建
      • 随機森林
        • 構造随機森林
        • 分析資料
        • 優缺點
      • 梯度提升回歸樹(梯度提升機)
        • 優缺點

決策樹

決策樹就是一棵樹,一顆決策樹包含一個根節點、若幹個内部結點和若幹個葉結點;葉結點對應于決策結果,其他每個結點則對應于一個屬性測試;每個結點包含的樣本集合根據屬性測試的結果被劃分到子結點中;根結點包含樣本全集,從根結點到每個葉子結點的路徑對應了一個判定測試序列。

本質上他是從一層一層的if/else問題中進行學習并得出結論。

如下圖所示,如果要區分四種動物鷹(hawk),企鵝(penguin),海豚(dolphin),熊(bear)。我們想通過盡可能少的判斷得到正确的結果。首先可以通過是否有羽毛(feather),将動物分為兩類(鷹和企鵝,海豚和熊),對于第一組我們可以在通過能否飛(fly)來區分企鵝和鷹;然後判斷是否有鳍(fins)區分熊和海。

下圖可用代碼展示:

import mglearn
import matplotlib.pyplot as plt
cancer = load_breast_cancer()
mglearn.plots.plot_animal_tree()
plt.show()
           
python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

此處代碼需要安裝graphviz:

  1. graphviz,安裝完成後将其配置到系統環境變量
此電腦-》屬性-》進階系統設定-》環境變量-》path
  添加   (安裝路徑)\Graphviz\bin
           

2.然後再到terminal中安裝graphviz第三方庫

pip install graphviz

如果執行第1步會報錯:

graphviz.backend.ExecutableNotFound: failed to execute [‘dot’, ‘-Tpng’, ‘-O’, ‘test.gv’], make sure the Graphviz executables are on your systems’ PATH

控制決策樹複雜度

通常來說,決策樹會構造樹知道所有節點為純葉子節點,這會導緻樹非常的深,模型十分複雜,也是的模型對訓練集拟合度高。純葉子節點說明樹的拟合程度為100%。

防止過拟合常見的兩種政策:預剪枝和後剪枝

預剪枝:及早的停止樹的建構,包括限制葉子結點個數,樹的最大深度

後剪枝:先構造樹,然後删除或者折疊資訊量少的枝

sklearn的tree子產品中分别實作了DecisionTreeRegressor和DecisionTreeClassifier。而且這兩個類中隻實作了預剪枝。

首先在乳腺癌資料集(cancer)上實作一次決策樹分類,不進行預剪枝,直到所有節點都為葉子結點。

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

cancer = load_breast_cancer()
data = cancer.data
target = cancer.target
xtrain, xtest, ytrain, ytest = train_test_split(data, target, stratify=target, random_state=42)

fit = DecisionTreeClassifier(random_state=0).fit(xtrain, ytrain)
print("training set score is {:.4f}".format(fit.score(xtrain, ytrain)))
print("test set score is {:.4}".format(fit.score(xtest, ytest)))
           

輸出

training set score is 1.0000

test set score is 0.9371

可以看到訓練集精度達到了100%,因為葉子結點是純的,樹的深度很大,而且模型過拟合,導緻泛化能力不佳。可以通過預剪枝來降低樹的深度:

fit2 = DecisionTreeClassifier(random_state=0, max_depth=4).fit(xtrain, ytrain)
print("training set score is {:.4f}".format(fit2.score(xtrain, ytrain)))
print("test set score is {:.4}".format(fit2.score(xtest, ytest)))
           

輸出

training set score is 0.9883

test set score is 0.951

通過設定樹最大深度後,雖然訓練集精度下降了,但是模型泛化能力得到提升。

分析決策樹

将上樹深度為4的決策樹可視化

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import export_graphviz
import graphviz

cancer = load_breast_cancer()
xtrain, xtest, ytrain, ytest = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=42)

fit2 = DecisionTreeClassifier(random_state=0, max_depth=4).fit(xtrain, ytrain)

export_graphviz(fit2, out_file="tree.dot", class_names=["m", "b"], feature_names=cancer.feature_names, impurity=False,
                filled=True)
                
f = open("tree.dot")
dor = f.read()
graph = graphviz.Source(dor)
graph.render("tree") #會生成一個pdf檔案
           

預覽

python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

觀察發現worst perimeter <= 106.1的左節點中259個樣例隻有11個良性樣本,這個節點之後的劃分也是用了更細的差別将這11個樣本分離。

特征重要性

檢視整個樹可能十分費勁,我們使用特征重要性對樹的決策重要性進行排序,每個重要性是介于0到1的一個小數。

輸出

Feature importance:

[0. 0. 0. 0. 0. 0.

0. 0. 0. 0. 0.01019737 0.04839825

0. 0. 0.0024156 0. 0. 0.

0. 0. 0.72682851 0.0458159 0. 0.

0.0141577 0. 0.018188 0.1221132 0.01188548 0. ]

可以自定義一個方法對資料進行可視化:

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt

cancer = load_breast_cancer()
xtrain, xtest, ytrain, ytest = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=42)
fit2 = DecisionTreeClassifier(random_state=0, max_depth=4).fit(xtrain, ytrain)

def plot_feature_importances_cancer(model):
    n_feature = cancer.data.shape[1]
    plt.barh(range(n_feature),model.feature_importances_,align='center')
    plt.yticks(np.arange(n_feature),cancer.feature_names)
    plt.xlabel("Feature importances")
    plt.ylabel("Feature name")
    plt.show()

plot_feature_importances_cancer(fit2)
           

預覽

python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

決策樹優缺點

決策樹有兩個優點:

  1. 得到的模型很容易可視化
  2. 算法完全不受資料縮放影響

缺點:即使做了預剪枝操作,也容易過拟合,泛化性能很差。

決策樹內建

內建:合并多個機器學習模型來建構更加強大的方法。

有兩種以決策樹為基礎的內建模型對大量分類和回歸資料集都是有效的:随機森林(random forest,RF)和梯度提升決策樹(gradient boosted decision tree)。

随機森林

思路:如果有很多決策樹,預測結果很好,都以不同的方式過拟合,那麼可以對這些樹取平均來降低過拟合。

這意味着我們需要建構很多樹,而且每顆樹的應該與其他樹不同。RF對樹的随機化有兩種:一種是選擇用于構造樹資料點,二是通過選擇每次劃分測試的特征。

構造随機森林

構造随機森林要确定構造樹的個數:在RandomForestRegressor或RandomForestClassifier中的n_estimators參數設定

  • 對于構造一棵樹,首先要進行自助采樣

    從n_samples個樣本中進行自助采樣,就是對樣本進行有放回的抽取,取到樣本大小為n_samples。這樣意味着同一個資料可能出現多次。

  • 然後基于自主采樣得到的新資料集構造決策樹。這一步會使用算法選擇資料點的特征的随機子集進行測試,而不是将所有的特征都進行測試。選擇子集的特征個數由參數max_features來控制
  • 通過自助采樣和随機特征子集,保證構造的每棵樹都不同。

對于回歸問題,最後的結果是随機森林中所有樹的結果平均值。而分類問題采用軟投票,即将每棵樹輸出的不同類别機率進行平均,取平均機率最大的類别作為結果。

分析資料

我們使用two_moons資料集,并構造五棵樹,我們将每棵樹的決策邊界可視化,也将RF的結果可視化:

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
import mglearn
import matplotlib.pyplot as plt

x, y = make_moons(n_samples=200, noise=0.25, random_state=3)

xtrain, xtest, ytrain, ytest = train_test_split(x, y, stratify=y, random_state=2)
forest = RandomForestClassifier(n_estimators=5, random_state=2)
forest.fit(xtrain, ytrain)
fig, axs = plt.subplots(2, 3, figsize=(20, 10))

for i, (tree, ax) in enumerate(zip(forest.estimators_, axs.ravel())):
    ax.set_title("Tree {}".format(i))
    mglearn.plots.plot_tree_partition(xtrain, ytrain, tree=tree, ax=ax)

mglearn.plots.plot_2d_separator(forest, xtrain, fill=True, ax=axs[-1, -1], alpha=0.4)
axs[-1, -1].set_title("random tree")
mglearn.discrete_scatter(xtrain[:, 0], xtrain[:, 1], ytrain)

plt.show()
           

預覽

python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

可以看到每棵樹都犯了一點錯誤,決策邊界也不相同,原因在于訓練集中因為自助采樣缺失了一些資料點。

随機森林比單獨每一棵樹過拟合都要小,給出的邊界也更符合很自覺,在任何實際應用中,我們會用到更多的樹,進而得到更平滑的邊界。

再舉一個例子,使用包含100棵樹的随機森林在乳腺癌資料集上:

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
import numpy as  np

cancer = load_breast_cancer()
Xtrain, Xtest, ytrain, ytest = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=2)
rf = RandomForestClassifier(n_estimators=100,random_state=0).fit(Xtrain,ytrain)

def plot_feature_importances_cancer(model):
    n_feature = cancer.data.shape[1]
    plt.barh(range(n_feature), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_feature), cancer.feature_names)
    plt.xlabel("Feature importances")
    plt.ylabel("Feature name")
    plt.show()

plot_feature_importances_cancer(rf)
           

輸出

Training set score is 1.0

Test set score is 0.965

我們可以看到随機森林精度為96.5%,比線性模型或者單棵決策樹結果都要好,我們也可以調節max_features參數或者對森林中的樹進行預剪枝,但通常預設參數得到的結果已經很好的。我們也可以使用上面代碼進行可視化:

python-機器學習打卡(四)--監督學習(三)--決策樹、決策樹內建

比起單顆樹,随機森林中有跟多特征重要性不為0。對于單顆決策樹特征重要性圖中可以看到"worst radius" 被給與了最大的重要性,而在随機森林中,也同樣很重要。

優缺點

  • 用于回歸和分類的随機森林是目前應用最廣的機器學習方法之一。
  • 随機森林有決策樹所有的優點,同時彌補了一些缺陷,如果要用可視化的方式向專家展示決策過程,單棵決策樹是最好的辦法。
  • 如果特征次元高且稀疏,随機森林表現往往不是很好。
  • 随機森林需要占用記憶體較多,訓練時間也長。
  • Max_features和n_estimators是RF的主要參數,當然也有樹的深度(max_depth)。max_features總是越大越好,但是随着max_features增大,模型運作占用記憶體就越大,訓練時間也越久。

梯度提升回歸樹(梯度提升機)

梯度提升機通過合并多個決策樹來建構一個更為強大的模型,雖然名字中有回歸,但是該模型既可以用于回歸,也可以用于分類。

與RF不同,該模型采用連續的方式構造樹,每棵樹會糾正前一顆樹的錯誤。預設梯度提升回歸樹中沒有随機化,而是用到了強預剪枝。樹的深度一般很小,這樣占用記憶體小,預測速度也更快。

梯度提升回歸樹的參數除了預剪枝與內建樹的數量外,還有一個很重要參數是learning_rate(學習率),用于控制每顆樹糾正前一顆樹的錯誤的強度。較高的學習率,意味着後一棵樹要進行較強的修改,會增加模型的複雜的度。增大樹的數量,樹的深度同樣也會導緻一個模型複雜度的增加。

我們在乳腺癌資料集上應用GradienBoostingClassifier模型,設定預設值(100顆樹,最大深度為3,學習率為0.1),最大深度為1,學習率為0.01三個模型對比:

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer

cancer = load_breast_cancer()

xtrain, xtest, ytrain, ytest = train_test_split(cancer.data, cancer.target, random_state=0)

classifier = GradientBoostingClassifier(random_state=0)
classifier.fit(xtrain, ytrain)
print("Training set with default setting score is {:.3}".format(classifier.score(xtrain, ytrain)))
print("Test set with default setting score is {:.3}".format(classifier.score(xtest, ytest)))

classifier2 = GradientBoostingClassifier(random_state=0 ,max_depth=1)
classifier2.fit(xtrain, ytrain)
print("Training set with max_depth 1  score is {:.3}".format(classifier2.score(xtrain, ytrain)))
print("Test set with max_depth 1  score is {:.3}".format(classifier2.score(xtest, ytest)))

classifier3 = GradientBoostingClassifier( random_state=0, learning_rate=0.01)
classifier3.fit(xtrain, ytrain)
print("Training set with learning rate 0.01 score is {:.3}".format(classifier3.score(xtrain, ytrain)))
print("Test set with learning rate 0.01 score is {:.3}".format(classifier3.score(xtest, ytest)))

           

輸出

Training set with default setting score is 1.0

Test set with default setting score is 0.965

Training set with max_depth 1 score is 0.991

Test set with max_depth 1 score is 0.972

Training set with learning rate 0.01 score is 0.988

Test set with learning rate 0.01 score is 0.965

在模型使用預設參數時,訓練集精度為100%,而測試集精度為96.5%,模型存在過拟合。為了降低過拟合,更改最大深度參數為1,模型泛化能力得到提高,降低學習率後,模型過拟合也降低了。

優缺點

  • 算法不需要對資料縮放就可以表現的很好,适用于二進制特征和聯系特征同時存在的資料集。
  • 梯度提升回歸樹主要缺點是需要仔細調參,而且訓練時間長。
  • 參數包括n_estimators和learning_rate,前者控制樹的個數,後者控制樹對前一顆樹的糾正強度,兩個參數增大都會增加模型的複雜度。