決策樹在sklearn中的實現
sklearn 入門
scikit-learn,又寫作 sklearn,是一個開源的基於 python 語言的機器學習工具包。它通過 NumPy, SciPy 和 Matplotlib 等 python 數值計算的庫實現高效的算法應用,並且涵蓋了幾乎所有主流機器學習算法。
http://scikit-learn.org/stable/index.html
在工程應用中,用 python 手寫代碼來從頭實現一個算法的可能性非常低,這樣不僅耗時耗力,還不一定能夠寫出 構架清晰,穩定性強的模型。更多情況下,是分析采集到的數據,根據數據特征選擇適合的算法,在工具包中調用 算法,調整算法的參數,獲取需要的信息,從而實現算法效率和效果之間的平衡。而 sklearn,正是這樣一個可以 幫助我們高效實現算法應用的工具包。
sklearn 有一個完整而豐富的官網,里面講解了基於 sklearn 對所有算法的實現和簡單應用。然而,這個官網是全英 文的,並且現在沒有特別理想的中文接口,市面上也沒有針對 sklearn 非常好的書。因此,這門課的目的就是由簡 向繁地向大家解析 sklearn 的全面應用,幫助大家了解不同的機器學習算法有哪些可調參數,有哪些可用接口,這 些接口和參數對算法來說有什么含義,又會對算法的性能及准確性有什么影響。我們會講解 sklearn 中對算法的說 明,調參,屬性,接口,以及實例應用。注意,本門課程的講解不會涉及詳細的算法原理,只會專注於算法在 sklearn 中的實現,如果希望詳細了解算法的原理,建議閱讀下面這本兩本書:
決策樹
1 概述
1.1 決策樹是如何工作的
決策樹(Decision Tree)是一種非參數的有監督學習方法,它能夠從一系列有特征和標簽的數據中總結出決策規 則,並用樹狀圖的結構來呈現這些規則,以解決分類和回歸問題。決策樹算法容易理解,適用各種數據,在解決各 種問題時都有良好表現,尤其是以樹模型為核心的各種集成算法,在各個行業和領域都有廣泛的應用。
關鍵概念:節點
根節點:沒有進邊,有出邊。包含最初的,針對特征的提問。
中間節點:既有進邊也有出邊,進邊只有一條,出邊可以有很多條。都是針對特征的提問。
葉子節點:有進邊,沒有出邊,每個葉子節點都是一個類別標簽。
子節點和父節點:在兩個相連的節點中,更接近根節點的是父節點,另一個是子節點。
決策樹算法的核心是要解決兩個問題:
1) 如何從數據表中找出最佳節點和最佳分枝?
2) 如何讓決策樹停止生長,防止過擬合?
幾乎所有決策樹有關的模型調整方法,都圍繞這兩個問題展開。這兩個問題背后的原理十分復雜,我們會在講解模 型參數和屬性的時候為大家簡單解釋涉及到的部分。在這門課中,我會盡量避免讓大家太過深入到決策樹復雜的原 理和數學公式中(盡管決策樹的原理相比其他高級的算法來說是非常簡單了),這門課會專注於實踐和應用。如果 大家希望理解更深入的細節,建議大家在聽這門課之前還是先去閱讀和學習一下決策樹的原理。
1.2 sklearn中的決策樹
- 模塊 sklearn.tree
sklearn 中決策樹的類都在"tree"這個模塊之下。這個模塊總共包含五個類:
模塊名 | 類別 |
---|---|
tree.DecisionTreeClassifier | 分類樹 |
tree.DecisionTreeRegressor | 回歸樹 |
tree.export_graphviz | 將生成的決策樹導出為 DOT 格式,畫圖專用 |
tree.ExtraTreeClassifier | 高隨機版本的分類樹 |
tree.ExtraTreeRegressor | 高隨機版本的回歸樹 |
- sklearn 的基本建模流程
在那之前,我們先來了解一下 sklearn 建模的基本流程。
在這個流程下,分類樹對應的代碼是:
from sklearn import tree #導入需要的模塊
clf = tree.DecisionTreeclassifier() #實例化
clf = clf.fit (X_train, y_train) #用訓練集數據訓練模型
result = clf.score(X_test,y_test) #導入測試集,從接口中調用需要的信息
2 DecisionTreeClassifier 與紅酒數據集
class sklearn.tree.DecisionTreeclassifier (criterion=,gini,l splitter='best', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random _state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
2.1 重要參數
2.1.1 criterion
為了要將表格轉化為一棵樹,決策樹需要找出最佳節點和最佳的分枝方法,對分類樹來說,衡量這個"最佳"的指標 叫做"不純度"。通常來說,不純度越低,決策樹對訓練集的擬合越好。現在使用的決策樹算法在分枝方法上的核心 大多是圍繞在對某個不純度相關指標的最優化上。
不純度基於節點來計算,樹中的每個節點都會有一個不純度,並且子節點的不純度一定是低於父節點的,也就是 說,在同一棵決策樹上,葉子節點的不純度一定是最低的。
Criterion 這個參數正是用來決定不純度的計算方法的。sklearn 提供了兩種選擇:
-
輸入”entropy",使用信息熵(Entropy)
-
輸入"gini",使用基尼系數(Gini Impurity)
其中 t 代表給定的節點,i 代表標簽的任意分類,\(p(i|t)\) 代表標簽分類 i 在節點 t 上所占的比例。注意,當使用信息熵 時,sklearn 實際計算的是基於信息熵的信息增益(Information Gain),即父節點的信息熵和子節點的信息熵之差。
比起基尼系數,信息熵對不純度更加敏感,對不純度的懲罰最強。但是在實際使用中,信息熵和基尼系數的效果基 本相同。信息熵的計算比基尼系數緩慢一些,因為基尼系數的計算不涉及對數。另外,因為信息熵對不純度更加敏 感,所以信息熵作為指標時,決策樹的生長會更加"精細",因此對於高維數據或者噪音很多的數據,信息熵很容易 過擬合,基尼系數在這種情況下效果往往比較好。當模型擬合程度不足的時候,即當模型在訓練集和測試集上都表 現不太好的時候,使用信息熵。當然,這些不是絕對的。
參數 | criterion |
---|---|
如何影響模型? | 確定不純度的計算方法,幫忙找出最佳節點和最佳分枝,不純度越低,決策樹對訓練集 的擬合越好 |
可能的輸入有哪 些? | 不填默認基尼系數,填寫 gini 使用基尼系數,填寫 entropy 使用信息增益 |
怎樣選取參數? | 通常就使用基尼系數 數據維度很大,噪音很大時使用基尼系數 維度低,數據比較清晰的時候,信息熵和基尼系數沒區別 當決策樹的擬合程度不夠的時候,使用信息熵 兩個都試試,不好就換另外一個 |
到這里,決策樹的基本流程其實可以簡單概括如下:
- 計算全部特征的不純度指標
- 選取不純度指標最優的特征來分枝
- 在第一個特征的分枝下,計算全部特征的不純度指標
- 選取不純度指標最優的特征繼續分枝,直到沒有更多的特征可用,或整體的不純度指標已經最優,決策樹就會停止生長。
-
建立一棵樹
-
導入需要的算法庫和模塊
from sklearn import tree from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split
-
探索數據
wine=load_wine() # 加載數據 wine.data.shape # 數據維度 wine.target # 數據集的標簽 wine.feature_names # 特征名 wine.target_names # 標簽名
# 將數據以表的形式呈現 # 用到pandas import pandas as pd pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1)
-
分訓練集和測試集
Xtrain,Xtest,Ytrain,Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
-
建立模型
clf = tree.DecisionTreeClassifier(criterion="entropy") clf = clf.fit(Xtrain, Ytrain) score = clf.score(Xtest, Ytest) score
-
畫出一棵樹吧
feature_name = ['酒精','蘋果酸','灰','灰的鹼性','鎂','總酚','類黃酮','非黃烷類酚類','花青素','顏色強度','色調','od280/od315稀釋葡萄酒','脯氨酸'] import graphviz dot_data = tree.export_graphviz(clf ,out_file = None ,feature_names= feature_name ,class_names=["琴酒","雪莉","貝爾摩德"] ,filled=True #填充顏色 ,rounded=True ) graph = graphviz.Source(dot_data) graph
-
探索決策樹
看看每個特征在模型當中的重要性,即權重。
clf.feature_importances_ [*zip(feature_name, clf.feature_importances_)]
-
我們已經在只了解一個參數的情況下,建立了一棵完整的決策樹。但是回到步驟 4 建立模型,score 會在某個值附近 波動,引起步驟 5 中畫出來的每一棵樹都不一樣。它為什么會不穩定呢?如果使用其他數據集,它還會不穩定嗎?
我們之前提到過,無論決策樹模型如何進化,在分枝上的本質都還是追求某個不純度相關的指標的優化,而正如我 們提到的,不純度是基於節點來計算的,也就是說,決策樹在建樹時,是靠優化節點來追求一棵優化的樹,但最優 的節點能夠保證最優的樹嗎?集成算法被用來解決這個問題:sklearn 表示,既然一棵樹不能保證最優,那就建更 多的不同的樹,然后從中取最好的。怎樣從一組數據集中建不同的樹?在每次分枝時,不從使用全部特征,而是隨 機選取一部分特征,從中選取不純度相關指標最優的作為分枝用的節點。這樣,每次生成的樹也就不同了。
2.1.2 random_state & splitter
random_state 用來設置分枝中的隨機模式的參數,默認 None,在高維度時隨機性會表現更明顯,低維度的數據 (比如鳶尾花數據集),隨機性幾乎不會顯現。輸入任意整數,會一直長出同一棵樹,讓模型穩定下來。
splitter 也是用來控制決策樹中的隨機選項的,有兩種輸入值,輸入"best",決策樹在分枝時雖然隨機,但是還是會 優先選擇更重要的特征進行分枝(重要性可以通過屬性 feature_importances_查看),輸入"random",決策樹在 分枝時會更加隨機,樹會因為含有更多的不必要信息而更深更大,並因這些不必要信息而降低對訓練集的擬合。這 也是防止過擬合的一種方式。當你預測到你的模型會過擬合,用這兩個參數來幫助你降低樹建成之后過擬合的可能 性。當然,樹一旦建成,我們依然是使用剪枝參數來防止過擬合。
clf = tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30
,splitter="random"
)
2.1.3 剪枝參數
在不加限制的情況下,一棵決策樹會生長到衡量不純度的指標最優,或者沒有更多的特征可用為止。這樣的決策樹 往往會過擬合,這就是說,它會在訓練集上表現很好,在測試集上卻表現糟糕。我們收集的樣本數據不可能和整體 的狀況完全一致,因此當一棵決策樹對訓練數據有了過於優秀的解釋性,它找出的規則必然包含了訓練樣本中的噪 聲,並使它對未知數據的擬合程度不足。
# 我們的樹對訓練集的擬合程度如何?
# 注意,此時用到的是訓練集數據
score_train = clf.score(Xtrain, Ytrain)
score_train
為了讓決策樹有更好的泛化性,我們要對決策樹進行剪枝。剪枝策略對決策樹的影響巨大,正確的剪枝策略是優化 決策樹算法的核心。sklearn 為我們提供了不同的剪枝策略:
-
max_depth
限制樹的最大深度,超過設定深度的樹枝全部剪掉
這是用得最廣泛的剪枝參數,在高維度低樣本量時非常有效。決策樹多生長一層,對樣本量的需求會增加一倍,所 以限制樹深度能夠有效地限制過擬合。在集成算法中也非常實用。實際使用時,建議從=3 開始嘗試,看看擬合的效 果再決定是否增加設定深度。
-
min_samples_leaf & min_samples_split
min_samples_leaf 限定,一個節點在分枝后的每個子節點都必須包含至少 min_samples_leaf 個^練樣本,否則分 枝就不會發生,或者,分枝會朝着滿足每個子節點都包含 min_samples_leaf 個樣本的方向去發生
一般搭配 max_depth 使用,在回歸樹中有神奇的效果,可以讓模型變得更加平滑。這個參數的數量設置得太小會引 起過擬合,設置得太大就會阻止模型學習數據。一般來說,建議從=5 開始使用。如果葉節點中含有的樣本量變化很 大,建議輸入浮點數作為樣本量的百分比來使用。同時,這個參數可以保證每個葉子的最小尺寸,可以在回歸問題 中避免低方差,過擬合的葉子節點出現。對於類別不多的分類問題,=1 通常就是最佳選擇。
min_samples_split 限定,一個節點必須要包含至少 min_samples_split 個訓練樣本,這個節點才允許被分枝,否則 分枝就不會發生。
clf = tree.DecisionTreeClassifier(criterion="entropy" ,random_state=30 ,max_depth=3 ,splitter="random" ,min_samples_leaf=10 ,min_samples_split=10 ) clf = clf.fit(Xtrain,Ytrain)
-
max_features & min_impurity_decrease
一般 max_depth 使用,用作樹的”精修"
max_features 限制分枝時考慮的特征個數,超過限制個數的特征都會被舍棄。和 max_depth 異曲同工, max_features 是用來限制高維度數據的過擬合的剪枝參數,但其方法比較暴力,是直接限制可以使用的特征數量 而強行使決策樹停下的參數,在不知道決策樹中的各個特征的重要性的情況下,強行設定這個參數可能會導致模型 學習不足。如果希望通過降維的方式防止過擬合,建議使用 PCA,ICA 或者特征選擇模塊中的降維算法。
min_impurity_decrease 限制信息增益的大小,信息增益小於設定數值的分枝不會發生。這是在 0.19 版本中更新的 功能,在 0.19 版本之前時使用 min_impurity_split。
-
確認最優的剪枝參數
那具體怎么來確定每個參數填寫什么值呢?這時候,我們就要使用確定超參數的曲線來進行判斷了,繼續使用我們 已經訓練好的決策樹模型 clf。超參數的學習曲線,是一條以超參數的取值為橫坐標,模型的度量 J 指標為縱坐標的曲 線,它是用來衡量不同超參數取值下模型的表現的線。在我們建好的決策樹里,我們的模型度量指標就是 score。
import matplotlib.pyplot as plt test = [] for i in range(10): clf = tree.DecisionTreeClassifier(criterion="entropy" ,random_state=30 ,splitter="random" ,max_depth = i + 1 ) clf = clf.fit(Xtrain,Ytrain) score = clf.score(Xtest,Ytest) test.append(score) plt.plot(range(1,11),test,color="red",label="max_depth") plt.legend() plt.show()
思考:
- 剪枝參數一定能夠提升模型在測試集上的表現嗎?
- 調參沒有絕對的答案,一切都是看數據本身。
- 這么多參數,一個個畫學習曲線?
- 在泰坦尼克號的案例中,我們會解答這個問題。
無論如何,剪枝參數的默認值會讓樹無盡地生長,這些樹在某些數據集上可能非常巨大,對內存的消耗也非常巨 大。所以如果你手中的數據集非常巨大,你已經預測到無論如何你都是要剪枝的,那提前設定這些參數來控制樹的 復雜性和大小會比較好。
2.1.4 目標權重參數
-
class_weight & min_weight_fraction_leaf
完成樣本標簽平衡的參數。樣本不平衡是指在一組數據集中,標簽的一類天生占有很大的比例。比如說,在銀行要 判斷'一個辦了信用卡的人是否會違約”,就是是 vs 否(1%: 99%)的比例。這種分類狀況下,即便模型什么也不 做,全把結果預測成"否”,正確率也能有 99%。因此我們要使用 class_weight 參數對樣本標簽進行一定的均衡,給 少量的標簽更多的權重,讓模型更偏向少數類,向捕獲少數類的方向建模。該參數默認 None,此模式表示自動給 與數據集中的所有標簽相同的權重。
有了權重之后,樣本量就不再是單純地記錄數目,而是受輸入的權重影響了,因此這時候剪枝,就需要搭配 min_ weight_fraction_leaf這個基於權重的剪枝參數來使用。另請注意,基於權重的剪枝參數(例如min_weight_ fraction_leaf)將比不知道樣本權重的標准(比如 min_samples_leaf)更少偏向主導類。如果樣本是加權的,則使 用基於權重的預修剪標准來更容易優化樹結構,這確保葉節點至少包含樣本權重的總和的一小部分。
2.2 重要屬性和接口
屬性是在模型訓練之后,能夠調用查看的模型的各種性質。對決策樹來說,最重要的是 feature_importances_,能 夠查看各個特征對模型的重要性。
sklearn 中許多算法的接口都是相似的,比如說我們之前已經用到的 fit 和 score,幾乎對每個算法都可以使用。除了 這兩個接口之外,決策樹最常用的接口還有 apply 和 predict。apply 中輸入測試集返回每個測試樣本所在的葉子節 點的索引,predict 輸入測試集返回每個測試樣本的標簽。返回的內容一目了然並且非常容易,大家感興趣可以自己 下去試試看。
在這里不得不提的是,所有接口中要求輸AX_train 和乂二。$七的部分,輸入的特征矩陣必須至少是一個二維矩陣。
sklearn 不接受任何一維矩陣作為特征矩陣被輸入。如果你的數據的確只有一個特征,那必須用 reshape(-1,1)來給 矩陣增維;如果你的數據只有一個特征和一個樣本,使用 resh ape(1,-1)來給你的數據增維。
# apply返回每個測試樣本所在的葉子節點的索引
clf.apply(Xtest)
# predict返回每個測試樣本的分類/回歸結果
clf.predict(Xtest)
至此,我們已經學完了分類樹 DecisionTreeClassifier 和用決策樹繪圖(export_graphviz)的所有基礎。我們講解 了決策樹的基本流程,分類樹的八個參數,一個屬性,四個接口,以及繪圖所用的代碼。
八個參數:Criterion,兩個隨機性相關的參數(random_state,splitter),五個剪枝參數(max_depth, min_samples_split,min_samples_leaf,max_feature,min_impurity_decrease)
一個屬性:feature_importances_
四個接口: fit,score,apply,predict
有了這些知識,基本上分類樹的使用大家都能夠掌握了,接下來再到實例中去磨練就好。
3 DecisionTreeRegressor
class sklearn.tree.DecisionTreeRegressor (criterion='mse‘, splitter='best‘, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0,0l min_impurity_split=None, presort=False)
幾乎所有參數,屬性及接口都和分類樹一模一樣。需要注意的是,在回歸樹種,沒有標簽分布是否均衡的問題,因 此沒有 class_weight 這樣的參數。
3.1 重要參數,屬性及接口
criterion
回歸樹衡量分枝質量的指標,支持的標准有三種:
-
輸入"mse”使用均方誤差 mean squared error(MSE),父節點和葉子節點之間的均方誤差的差額將被用來作為 特征選擇的標准,這種方法通過使用葉子節點的均值來最小化 L2 損失
-
輸入"friedman_mse"使用費爾德曼均方誤差,這種指標使用弗里德曼針對潛在分枝中的問題改進后的均方誤差
-
輸入”mae”使用絕對平均誤差 MAE(mean absolute error),這種指標使用葉節點的中值來最小化 L1 損失
屬性中最重要的依然是 feature_importances_,接口依然是 apply, fit, predict, score 最核心。
其中 N 是樣本數量,i 是每一個數據樣本,fi 是模型回歸出的數值,yi 是樣本點 i 實際的數值標簽。所以 MSE 的本質, 其實是樣本真實數據與回歸結果的差異。在回歸樹中,MSE 不只是我們的分枝質量衡量指標,也是我們最常用的衡 量回歸樹回歸質量的指標,當我們在使用交叉驗證,或者其他方式獲取回歸樹的結果時,我們往往選擇均方誤差作 為我們的評估(在分類樹中這個指標是 score 代表的預測准確率)。在回歸中,我們追求的是,MSE 越小越好。
然而,回歸樹的接口 score 返回的是 R 平方,並不是 MSE。R 平方被定義如下:
其中\(u\)是殘差平方和(MSE * N),\(v\)是總平方和,N 是樣本數量,i 是每一個數據樣本,\(f_i\)是模型回歸出的數值,\(y_i\) 是樣本點實際的數值標簽。\(\widehat{y_i}\)是真實數值標簽的平均數。R 平方可以為正為負(如果模型的殘差平方和遠遠大於 模型的總平方和,模型非常糟糕,R 平方就會為負),而均方誤差永遠為正。
值得一提的是,雖然均方誤差永遠為正,但是 sklearn 當中使用均方誤差作為評判標准時,卻是計算"負均方誤差“(neg_mean_squared_error)。這是因為 sklearn 在計算模型評估指標的時候,會考慮指標本身的性質,均 方誤差本身是一種誤差,所以被 sklearn 划分為模型的一種損失(loss),因此在 sklearn 當中,都以負數表示。真正的 均方誤差 MSE 的數值,其實就是 neg_mean_squared_error 去掉負號的數字。
簡單看看回歸樹是怎樣工作的
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeRegressor
boston = load_boston()
regressor = DecisionTreeRegressor(random_state = 0)
# 交叉驗證
cross_val_score(regressor
,boston.data
,boston.target
,cv=10
,scoring="neg_mean_squared_error"
)
交叉驗證是用來觀察模型的穩定性的一種方法,我們將數據划分為 n 份,依次使用其中一份作為測試集,其他 n-1 份 作為訓練集,多次計算模型的精確性來評估模型的平均准確程度。訓練集和測試集的划分會干擾模型的結果,因此 用交叉驗證 n 次的結果求出的平均值,是對模型效果的一個更好的度量。
3.2 實例:一維回歸的圖像繪制
接下來我們到二維平面上來觀察決策樹是怎樣擬合一條曲線的。我們用回歸樹來擬合正弦曲線,並添加一些噪聲來 觀察回歸樹的表現。
-
導入需要的庫
import numpy as np from sklearn.tree import DecisionTreeRegressor import matplotlib.pyplot as plt
-
創建一條含有噪聲的正弦曲線
在這一步,我們的基本思路是,先創建一組隨機的,分布在 0~5 上的橫坐標軸的取值(x),然后將這一組值放到 sin 函 數中去生成縱坐標的值(y),接着再到 y 上去添加噪聲。全程我們會使用 numpy 庫來為我們生成這個正弦曲線。
決策樹用到的數據必須是二維以上的。
rng = np.random.RandomState(1) # 隨機數生成器 X = np.sort(5 * rng.rand(80,1), axis=0) y = np.sin(X).ravel() # ravel降維函數 y[::5] += 3 * (0.5 - rng.rand(16))
-
實例化&訓練模型
regr_1 = DecisionTreeRegressor(max_depth=2) regr_2 = DecisionTreeRegressor(max_depth=5) regr_1.fit(X, y) regr_2.fit(X, y)
-
測試集導入模型,預測結果
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis] # 升維切片np.newaxis y_1 = regr_1.predict(X_test) y_2 = regr_2.predict(X_test)
-
繪制圖像
plt.figure() plt.scatter(X, y, s=20, edgecolors="black", c="darkorange", label="data") plt.plot(X_test, y_1, color="cornflowerblue", label="max_depth=2", linewidth=2) plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2) plt.xlabel("data") plt.ylabel("target") plt.title("Decision Tree Regression") plt.legend() plt.show()
可見,回歸樹學習了近似正弦曲線的局部線性回歸。我們可以看到,如果樹的最大深度(由 max_depth 參數控制) 設置得太高,則決策樹學習得太精細,它從訓練數據中學了很多細節,包括噪聲得呈現,從而使模型偏離真實的正 弦曲線,形成過擬合。
4 實例:泰坦尼克號幸存者的預測
泰坦尼克號的沉沒是世界上最嚴重的海難事故之一,今天我們通過分類樹模型來預測一下哪些人可能成為幸存者。 數據集來自https://www.kaggle.eom/c/titanic。數據集包含兩個 csv 格式文件,data 為我們接下來要使用的數據,test 為 kaggle 提供的測試集。
-
導入所需要的庫
import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.model_selection import GridSearchCV from sklearn.model_selection import cross_val_score import matplotlib.pyplot as plt
-
導入數據集,探索數據
pd.read_csv 函數讀取文件時有兩種填寫路徑的方法:
- 在文件路徑前加上 r ,此時是右斜杠:pd.read_csv(r"D:\data\data.csv")
- 無需 r 將右斜杠改成左斜桿:pd.read_csv("D:/data/data.csv")
data = pd.read_csv("taitanic_data.csv") data.head() data.info()
-
對數據集進行預處理
決策樹的結點必須為數字,對象特征要轉成數字。
# 刪除缺失值過多的列,和觀察判斷來說和預測的y沒有關系的列 data.drop(["Cabin","Name","Ticket"], inplace=True,axis=1) # 處理缺失值,對缺失值較多的列進行填補,有一些特征只確實一兩個值,可以采取直接刪除記錄的方法 data["Age"] = data["Age"].fillna(data["Age"].mean()) data = data.dropna() # 將二分類變量轉換為數值型變量 # astype能夠將一個pandas對象轉換為某種類型,和apply(int(x))不同,astype可以將文本類轉換為數字,用這個方式可以很便捷地將二分類特征轉換為0~1 data["Sex"] = (data["Sex"] == "male").astype("int") # 將三分類變量轉換為數值型變量 labels = data["Embarked"].unique().tolist() data["Embarked"] = data["Embarked"].apply(lambda x:labels.index(x))
-
提取標簽和特征矩陣,分測試集和訓練集
X = data.iloc[:,data.columns != "Survived"] y = data.iloc[:,data.columns == "Survived"] Xtrain, Xtest, Ytrain, Ytest = train_test_split(X, y, test_size=0.3)
只要索引變亂,而且不是有意保持亂序索引的,就要將索引重新排序(從 0 開始):
Xtrain.index = range(Xtrain.shape[0])
X.reset_index(drop=True,inplace=True)# 修正測試集和訓練集的索引 for i in [Xtrain, Xtest, Ytrain, Ytest]: i.index = range(i.shape[0])
-
導入模型,粗略跑一下查看結果
clf = DecisionTreeClassifier(random_state=25) clf = clf.fit(Xtrain, Ytrain) score_ = clf.score(Xtest, Ytest) score_ #交叉驗證 score = cross_val_score(clf,X,y,cv=10).mean() score
-
在不同 max_depth 下觀察模型的擬合狀況
畫出訓練集和測試集的評分結果,可以有效查看模型是否過擬合。
tr = [] te = [] for i in range(10): clf = DecisionTreeClassifier(random_state=25 ,max_depth=i+1 ,criterion="entropy" ) clf = clf.fit(Xtrain,Ytrain) score_tr = clf.score(Xtrain, Ytrain) score_te = cross_val_score(clf,X,y,cv=10).mean() tr.append(score_tr) te.append(score_te) print(max(te)) plt.plot(range(1,11),tr,color="red",label="train") plt.plot(range(1,11),te,color="blue",label="test") plt.xticks(range(1,11)) plt.legend() plt.show()
注意:這里為什么^"entropy”?因為我們注意到,在最大深度=3 的時候,模型擬合不足,在訓練集和測試集上的表現接 近,但卻都不是非常理想,只能夠達到 83%左右,所以我們要使用 entropy。
-
用網格搜索調整參數
import numpy as np gini_thresholds = np.linspace(0,0.5,20) parameters = {'splitter':('best','random') ,'criterion':("gini","entropy") ,"max_depth":[*range(1,10)] ,'min_samples_leaf':[*range(1,50,5)] ,'min_impurity_decrease':[*np.linspace(0,0.5,20)] } clf = DecisionTreeClassifier(random_state=25) GS = GridSearchCV(clf,parameters,cv=10) GS.fit(Xtrain,Ytrain) GS.best_params_ GS.best_score_
網格搜索就是在給定參數中完成所有可能的組合,通過
best_parms_
和best_score_
可查看最好模型對應的參數和結果。網格搜索的最好結果不一定會比之前自己選擇時強,因為它只能在給定的參數組合中選擇,這是它的缺點。
5 決策樹的優缺點
決策樹優點:
-
易於理解和解釋,因為樹木可以畫出來被看見
-
需要很少的數據准備。其他很多算法通常都需要數據規范化,需要創建虛擬變量並刪除空值等。但請注意, sklearn 中的決策樹模塊不支持對缺失值的處理。
-
使用樹的成本(比如說,在預測數據的時候)是用於訓練樹的數據點的數量的對數,相比於其他算法,這是 一個很低的成本。
-
能夠同時處理數字和分類數據,既可以做回歸又可以做分類。其他技術通常專門用於分析僅具有一種變量類 型的數據集。
-
能夠處理多輸出問題,即含有多個標簽的問題,注意與一個標簽中含有多種標簽分類的問題區別開
-
是一個白盒模型,結果很容易能夠被解釋。如果在模型中可以觀察到給定的情況,則可以通過布爾邏輯輕松 解釋條件。相反,在黑盒模型中(例如,在人工神經網絡中),結果可能更難以解釋。
-
可以使用統計測試驗證模型,這讓我們可以考慮模型的可靠性。
-
即使其假設在某種程度上違反了生成數據的真實模型,也能夠表現良好。
決策樹的缺點:
-
決策樹學習者可能創建過於復雜的樹,這些樹不能很好地推廣數據。這稱為過度擬合。修剪,設置葉節點所 需的最小樣本數或設置樹的最大深度等機制是避免此問題所必需的,而這些參數的整合和調整對初學者來說 會比較晦澀
-
決策樹可能不穩定,數據中微小的變化可能導致生成完全不同的樹,這個問題需要通過集成算法來解決。
-
決策樹的學習是基於貪婪算法,它靠優化局部最優(每個節點的最優)來試圖達到整體的最優,但這種做法 不能保證返回全局最優決策樹。這個問題也可以由集成算法來解決,在隨機森林中,特征和樣本會在分枝過 程中被隨機采樣。
-
有些概念很難學習,因為決策樹不容易表達它們,例如 XOR,奇偶校驗或多路復用器問題。
-
如果標簽中的某些類占主導地位,決策樹學習者會創建偏向主導類的樹。因此,建議在擬合決策樹之前平衡 數據集。
6 附錄
6.1 分類樹參數列表
參數 | 意義 |
---|---|
criterion | 字符型,可不填,默認基尼系數 Cgini') 用來衡量分枝質量的指標,即衡量不純度的指標輸入"gini"使用基尼系數,或輸入"entropy* 使用信息增益(Information Gain) |
splitter | 字符型,可不填,默認最佳分枝('best') 確定每個節點的分枝策略 輸入“best”使用最佳分枝,或輸入"random”使用最佳隨機分枝 |
max_depth | 整數或 None,可不填,默認 None 樹的最大深度。如果是 None,樹會持續生長直到所有葉子節點的不純度為 0,或者直到每個 葉子節點所含的樣本量都小於參數 min_samples_split 中輸入的數字 |
min_samples_ split | 整數或浮點數,可不填,默認=2 一個中間節點要分枝所需要的最小樣本量。如果—節點包含的樣本量小於 min_samples_split 中填寫的數字,這個節點的分枝就不會發生,也就是說,這個節點一^會 成為 f 葉子節點 1) 如果輸入整數,則認為輸入的數字是分枝所需的最 1 辟本量 2) 如果輸入浮點數,則認為輸入的浮點數是比例,輸入的浮點數輸入模型的數據集的樣 本量(n samples)是分枝所需的最小樣本量 浮點功能是 0.18 版本以上的 sklearn 才可以使用 |
min_sample leaf | 整數浮點數,可不填,默認=1 一個葉節點要存在所需要的最®本量• f 節點在分枝后的每個子節點中,必須要包含至 少 min_sample_leaf 個訓練樣本,否則分枝就不會發生.這個參數可能會有着使模型更平滑 的效果,尤其是在回歸中 1) 如果輸入整數,則認為輸入的數字是葉節點存在所需的最小樣本量 2) 如果輸入浮點數,則認為輸入的浮點數是比例,輸入模型的數據集的樣本量(n samples)是葉節點存在所需的最小樣本量 |
min_weight fraction_leaf | 浮點數,可不填,默認=0. 一個葉節點要存在所需要的權重占輸入模型的數據集的總權重的比例. 總權重由 fit 接口中的 sample_weight 參數確定,當 sample_weight 是 None 時,默認所有樣 本的權重相同 |
max_features | 整數,浮點數,字符型或 None,可不填,默認 None 在做最佳分枝的時候,考慮的特征個數 1) 輸入整數,則每一次分枝都考慮 max_features個特征 2) 輸入浮點數,則認為輸入的浮點數是比例,每次分枝考慮的特征數目是max_features 輸入模型的數據集的特征個數(n_features) 3) 輸入"auto",采用n_features的平方根作為分枝時考慮毓征數目 4) 輸入"sqrt",采用n_features的平方根作為分枝時考慮的特征數目 5) 輸入"log2",采用log2(n_/reatures)作為分枝時考慮的特征數目 6) 輸入"None” , n_features 就是分枝時考慮的特征數目 注意:如果在限制的 max_features 中,決策樹無法找到節點樣本上至少 f 敬曲分枝,那 對分枝的搜索不會停止,決策樹搭會檢直比限制的 max_features 數目更多的特征 |
random_state | 整數,sklearn 中設定好的 RandomState 實例,或 None,可不填,默認 None 1)輸入整數,random_state 是由隨機數生成器生成的隨機數種子 2)輸入 Randomstate 實例,則 random_state 是一個隨機數生成器 3)輸入 None,隨機數生成器會是 np.random 模塊中的一個 RandomState 實例 |
max_leaf_nodes | 整數或 None,可不填,默認 None 最大葉節點數量.在最佳枝方式下,以 max_leaf_nodes 為限制來生長樹.如果是 None, 則沒有葉節點數量的限制. |
min_impurity_ decrease | 浮點數,可以不填,默認=0. 當一個節點的分枝后引起的不純度的降低大於或等於 min_impurity_decrease中輸入的數 值,則這個分枝則會被保留,不會被剪枝。 帶權重的不純度下降可以表示為: \(\frac{N_t}{N}\):不純度 \(\frac{N_{t_R}}{N}\):右側樹枝的不純度 \(\frac{N_{t_L}}{N}\):左側樹枝的不純度 其中N是樣本總量,\(N_t\)是節點t中的樣本量,\(N_{t_L}\)是左側子節點的樣本量,\(N_{t_R}\)是右側子節 點的樣本量 注意:如果sample_weight在fit接口中有值,則N, \(N_t\), \(N_{t_R}\), \(N_{t_L}\)都是指樣本量的權重,而非單純的樣本數量 僅在 0.19 以本中提供此功能 |
class_weight | 字典,字典的列表,"balanced”或者“None”,默認 None 與標簽相關聯的權重,表現方式是{標簽的值:權重}.如果為 None,則默認所有的標簽持有 相同的權重。對於多輸出問題,字典中權重的順序需要與各個 y 在標簽數據集中睇冽順序相同 注意,對於多輸出問題(包括多標簽問題),定義的權重必須具體到每個標簽下的每個類, 其中類是字典鍵值對中的鍵,權重是鍵值對中的值.比如說,對於有四個標簽,且每個標簽 是二分類(0 和 1)的分類問題而言,權重應該被表示為: [{0:1,1:1), (0:1,1:5), {0:1,1:1), (0:1,1:1)] 而不是: [{1:1}, {2:5}, (3:1}, (4:1}] 如果使用"balanced"模式,將會使用 y 的值自動調整與輸入數據中的類頻率成反比的權 重,比如 \(\frac{N_{samples}}{n_{classes}*np.bincount(y)}\) 對於多輸出問題,每一列 y 的權重將被相乘 注意:如果指定了 sample_weight,這些權重將通過 fit 接口與 sample_weight 相乘 |
min_impurity_ split | 浮點數 防止樹生長的閾值之一.如果 f 節點的不純度高於 min_impurity_split,這個節點就會被分 枝,否則的話這個節點就只能是葉子節點. 在 0.19 以上版本中,這個參數的功能由被 min_impurity_decrease 取代,在 0.21 版本中這 個參會被刪除,請使用 min_impurity_decrease |
presort | 布爾值,可不填,默認 False 是否預先分配數據以加快枇合中最隹分枝的發現.在大型數據集上使用默認設置決策樹時. 將這個參數設置為 true 可能會延長訓隧過程,降低訓練速度.當使用較小的數據集或限制書 的深度時,設置這個參數為 true 可能會加快訓練速度. |
6.2 分類樹屬性列表
參數 | 意義 |
---|---|
classes_ | 輸出組(array)或者 f 數組的列表(list),結構為標簽的數目(n_classes) 輸出所有標簽 |
feature_importances_ | 輸出組,結構為特征的數目(n_features) 返回每個特征的重要性,一般是這個特征在多次分枝中產生的信息增益的綜合,也被稱為 "基尼重要性"(Gini Importance) |
max_features_ | 輸出整數 參數 m a xfeatu res 的推斷值 |
n_classes_ | 輸出整數或列表 標簽類別的數據 |
n_features_ | 在訓練模型(fit)時使用翊匏饅 |
n_outputs_ | 在訓練模型(fit)時輸出的結果的個數 |
tree_ | 輸出 f 可以導出建好的樹結構的端口,通過這個端口,可以訪問樹的結構種球屬性,包括但不僅限於查看: 1) 二叉樹的結構 2) 每個節點的深度以及它是否是葉子 3) 使用 decision_path 方法的示例到達的節點 4) 用 apply 這個接口取樣出的葉子 5) 用於預測樣本的規則 6) 一組樣本共享的決策路徑 |
tree_的更多內容可以參考:
6.3 分類樹接口列表
接口 | 意義 |
---|---|
apply (X[, check input]) | 輸入測試集或樣本點,返回每個樣本被分到的葉節點的索引 check_input 是接口 apply 的參數,輸入布爾值,默認 True,通常不使用 |
decision_path(X[, check_ input]) | 輸入測試集或樣本點,返回樹中的決策樹結構 Check_input 同樣是參數 |
fit(X,y[, sample weight, check input,...]) | 訓練模型的接口,其中 X 代表訓練樣本的特征,y 代表目標數據,即標簽,X 和 y 都必須是類數組結構,一般我們都使用 ndarray 來導入 sample_weight 是 fit 的參數,用來為樣本標簽設置權重,輸入的格式是一個和測試集樣 本量一致長度的數字數組,數組中所帶有的數字表示每個樣本量所占的權重,數組中數 字的綜合代表整個測試集權重總數 返回訓練完畢的模型 |
get _params([deep]) | 布爾值,獲取這個模型評估對象的參數.接口本身的參數 deep,默認為 True,表示返 回此估計器的參數並包含作為飾器的子對象. 返回模型評估對象在實例化時的參數設置 |
predict(X[, check input]) | 預測所提供的測試集 X 中樣本點的標簽,這里的測試集 X 必須和 fit 中提供的訓練集結構 一致 返回模型預測的浜假樣本的標簽或回歸值 |
predict_log_proba(X) | 預測所提供的測試集 X 中樣本點歸屬於各個標簽的對數概率 |
predict_proba(XL check_ input]) | 預測所提供的測試集 X 中樣本點歸屬於各個標簽的概率 返回測試集中每個樣本點對應的每個標簽的概率,各個標簽按詞典順序扣 E 序.預測的類 概率是葉中相同類的樣本的分數. |
score(X, y[, sample weig ht]) | 用給定測試數據和標簽的平均准確度作為模型的評分標准,分數越高模型越好.其中 X 是測試集,y 是測試集的真實標簽.sample_weight 是 score 的參數,用法與 fit 的參數一致 返回給定策樹數據和標簽的平均准確度,在多標簽分類中,這個指標是子集精度. |
set_params(**params) | 可以為已經建立的評估器重設參數 返回重新設置的評估器本身 |