決策樹
1 基本概念
信息量
度量一個事件的不確定性程度,不確定性越高則信息量越大,一般通過事件發生的概率來定義不確定性,信息量則是基於概率密度函數的log運算
信息熵
衡量的是一個事件集合的不確定性程度,就是事件集合中所有事件的不確定性的期望
相對熵(KL散度)
表示用於兩個概率分布差異的非對稱衡量,kl散度也可以從信息理論的角度出發,從這個角度出發的kl散度我們也可以稱之為相對熵,實際上描述的是兩個概率分布信息熵的差值
交叉熵
交叉熵就是真值分布的信息熵與KL散度的和,而真值的熵是確定的,與模型的參數θ 無關,所以梯度下降求導時,優化交叉熵和優化kl散度(相對熵)是一樣的
聯合熵
衡量的是兩個事件集合,經過組合之后形成的新的大的事件集合的信息熵
條件熵
事件集合Y的條件熵=聯合熵-事件集合X的信息熵,用來衡量在事件集合X已知的基礎上,事件集合Y的不確定性的減少程度
2 決策樹原理
2.1 原理
決策樹(Decision Tree)是一種非參數的有監督學習方法,他能從一系列有特征和標簽的數據中總結出決策規則,並用樹狀圖的結構來呈現這些規則,已解決分類和回歸問題。
決策樹的目標是建立一個可以用於決策的樹結構,包含一個根節點和若干葉子節點和非葉子節點,葉子節點對應各個類別,非葉子節點對應一個屬性值。
2.2 流程
- 從根節點開始,計算每個特征的不純度,選擇不純度最高的作為當前節點的分裂值
- 根據當前分裂節點的值,將數據分成兩部分,然后在分別建立左子樹和右子樹
- 如果分裂前后的信息增益沒有超過閾值,或者當前節點屬於同一類別,則停止分裂
2.3 如何確定最佳分裂點
-
ID3 - 信息增益
計算分裂前后的信息增益,選擇信息增益最大的作為當前最佳分裂特征。
對於樣本集合D,類別數為K,數據集D的經驗熵表示為
\[H(D)=-\sum_{k=1}^{K} \frac{\left|C_{k}\right|}{|D|} \log _{2} \frac{\left|C_{k}\right|}{|D|} \]
公式里為什么取對數?為什么取負數?
其實這是定義里規定的,取對數是為了在計算上方便,將乘法轉換為加法計算;取負數是因為如何不包含負數,信息熵為負的,不符合我們的認知,即不確定性越高,信息熵應該越大。
其中\(C_k\)是樣本集合D中屬於第k類的樣本子集,\(|C_k|\)表示該子集的元素個數,\(|D|\)表示樣本集合的元素個數。
然后計算某個特征A對於數據集D的經驗條件熵\(H(D|A)\)為
其中,\(D_i\)表示D中特征A取第i個值的樣本子集,\(D_{ik}\)表示\(D_i\)中屬於第k類的樣本子集。
於是信息增益\(g(D,A)\)可以表示為二者之差,可得
總結:ID3對特征值數目較多的特征有所偏好,且只能處理離散型數據;對缺失值敏感
-
C4.5 - 信息增益率
特征A對於數據集D的信息增益比定義為
\[g_{R}(D, A)=\frac{g(D, A)}{H_{A}(D)} \]其中
\[H_{A}(D)=-\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \log _{2} \frac{\left|D_{i}\right|}{|D|} \]
總結:C4.5使用信息增益率克服信息增益偏好特征數目多的特征,實際上是引入了特征的信息熵作為分母,來對高基數類別進行懲罰。所以C4.5對特征值數目較少的特征有所偏好,既可以處理離散型數據,也可以處理連續型數據,可以是多叉樹。
-
CART - Gini指數
Gini描述的是數據的純度,與信息熵含義類似:
\[\operatorname{Gini}(D)=1-\sum_{k=1}^{n}\left(\frac{\left|C_{k}\right|}{|D|}\right)^{2} \]CART在每一次迭代中選擇基尼指數最小的特征及其對應的切分點進行分類。但與ID3、C4.5不同的是,CART是一顆二叉樹,采用二元切割法,每一步將數據按特征A的取值切成兩份,分別進入左右子樹。特征A的Gini指數定義為
\[\operatorname{Gini}(D \mid A)=\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \operatorname{Gini}\left(D_{i}\right) \]
總結:CART既可以分類,也可以回歸;既可以處理離散型數據也可以處理連續型數據;但只能是二叉樹,分裂過的特征還可以再次分裂。
2.4 什么情況停止分裂?
- 分裂前后的信息增益小於給定的閾值
- 當前節點的樣本數小於給定的閾值
- 當前節點的樣本屬於同一類別
- 當樹達到一定的深度時
2.5 怎么進行剪枝?
- 預剪枝:在訓練過程中,如果分裂后的預測准確率變小了,則放棄分裂
- 后剪枝:先訓練完整的一棵樹,從葉子結點開始,計算一次准確率;去掉當前節點,再計算一次准確率,如果去掉當前節點之后准確率提升,則進行剪枝
2.6 決策樹優缺點
優點
- 計算簡單,容易解釋
- 不用對數據進行歸一化處理
缺點
- 一棵樹容易過擬合
- 不穩定,微小的數據變化,會建立不同的決策樹
- 使用貪心算法,保證局部最優,但不能保證全局最優
3 決策樹案例
本章以CART樹為例,講述如何在真實數據集上建立決策樹和回歸樹。
3.1 分類樹
決策樹算法是一種對高維數據進行有效分類的數據挖掘方法. 通過對輸入的特征信息進行分析訓練,構造決策樹模型作為分類規則。
傳統的CART樹是一種應用廣泛的決策樹學習算法,主要通過計算基尼指數來選擇最優特征,得到二叉樹判斷准則進行分類。
在分類問題中,假設有\(K\)個類,樣本點屬於第\(k\)類的概率為\(p_k\),則概率分布的基尼指數定義為
如果二分類問題,若樣本點屬於第一類的概率為\(p\),則概率分布的基尼指數為
對於樣本\(D\),個數為\(|D|\),假設\(K\)個類別,第\(k\)個類別的數量為\(C_k\),則樣本\(D\)的基尼指數表達式為
對於樣本\(D\),個數為\(|D|\),根據特征A的某個特征值,將\(D\)分成\(D_l\)和\(D_r\),則在特征A的條件下,樣本\(D\)的基尼系數表達式為
以貸款數據集為例
如上表的貸款申請數據集,以\(A_1,A_2,A_3,A_4\)表示年齡,有工作,有自己的房子和信貸情況四個特征,並以1,2,3表示年齡的值為青年、中年和老年;以1,2表示有工作和有自己的房子中的是和否;以1,2,3表示信貸情況的值為非常好,好和一般。
求特征\(A_1\)的基尼指數
由於\(Gini(D,A_1=1)\)和\(Gini(D,A_1=3)\)相等,且最小,所以\(A_1=1\)和\(A_1=3\)都以作為\(A_1\)的最佳分裂點。
求特征\(A_2,A_3\)的基尼指數:
由於\(A_2,A_3\)只有一個切分點,所以他們就是最佳分裂點。
求特征\(A_4\)的基尼指數:
由於\(Gini(D,A_4=3)\)最小,所以\(A_4=3\)都以作為\(A_4\)的最佳分裂點。
在\(A_1,A_2,A_3,A_4\)四個特征中,\(Gini(D,A_3=1) = 0.27\)最小,所以選擇特征\(A_3\)為最優特征,\(A_3=1\)為最佳分裂點。按\(A_3=1\)將數據集分成兩部分,於是根節點生成兩個子節點。然后在兩個子節點繼續使用上述方法在\(A_1,A_2,A_4\)中選擇最優特征及其最佳分裂點。
重復上述步驟,直到所有節點都為葉節點為止。
3.2 回歸樹
如果數據集的標簽是一系列連續值的集合,就不能再使用基尼指數作為划分樹的指標。
在回歸問題中我們可以發現,對於連續數據,當數據分布比較分散時,各個數據與平均數的差的平方和較大,方差就較大;
當數據分布比較集中時,各個數據與平均數的差的平方和較小。
方差越大,數據的波動越大;方差越小,數據的波動就越小。
因此,對於連續的數據,可以使用樣本與平均值的差的平方和作為划分回歸樹的指標。
如上表所示為訓練數據集,\(x\)的取值范圍是[0.5,10.5],\(y\)的取值范圍是[5.0,10.0],訓練回歸樹模型。
首先通過優化問題
求解訓練數據的切分點\(s\);
其中
容易求得在\(R_1,R_2\)內使平方損失誤差達到最小值的\(c_1,c_2\)為
這里\(N_1,N_2\)是\(R_1,R_2\)的樣本個數。
求訓練數據的切分點,根據所給數據,考慮如下切分點:1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5
對各切分點,不難求出相應的\(R_1,R_2,c_1,c_2\)及
例如,當 \(s = 1.5\) 時, \(R_{1}=\{1\}, R_{2}=\{2,3, \cdots, 10\}, c_{1}=5.56, c_{2}=7.50\) ,
得到所有的s取值和對應的m(s),結果如下:
由上表所示,當s=6.5時,m(s)達到最小值,所以s=6.5為最佳分裂點。
4 python實現決策樹
4.1 建立一顆決策樹
步驟
- 實例化,建立評估模型對象
- 通過模型接口訓練模型
- 通過模型接口獲得需要的信息
sklearn中的決策樹
- 八個參數:Criterion, 兩個隨機性相關的參數(random_state, splitter),
五個剪枝參數(max_depth, min_samples_split,min_samples_leaf,max_feature,min_impurity_decrease)- 一個屬性:feature_importances_
- 四個接口:fit, score, apply, predict
代碼
def buildTree():
# 加載紅酒數據集
wine = load_wine()
print(wine.data.shape)
print(wine.target)
# 將wine整理成一張表
pd.concat([pd.DataFrame(wine.data), pd.DataFrame(wine.target)], axis=1)
print(wine.feature_names)
print(wine.target_names)
# 划分訓練集和測試集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data, wine.target, test_size=0.3)
print(Xtrain.shape)
print(Xtest.shape)
# 建立模型
# random_state 用來設置分支中的隨機模式的參數; splitter用來控制決策樹中的隨機選項
# max_depth 限制樹的最大深度;min_samples_leaf & min_samples_split 一個節點在分之后的每個子節點最少包含多少個訓練樣本
clf = tree.DecisionTreeClassifier(criterion="entropy", random_state=30)
clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest)
print(score)
# 畫出一棵樹
import graphviz
feature_name = wine.feature_names
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=['class_0', 'class_1', 'class_2'],
filled=True, rounded=True)
graph = graphviz.Source(dot_data)
print(graph)
# 探索決策樹的特征重要性
print(clf.feature_importances_)
feature_importance = [*zip(feature_name, clf.feature_importances_)]
print(feature_importance)
# apply()返回每個測試樣本所在的葉子節點的索引
print(clf.apply(Xtest))
# predict()返回每個測試樣本的分類結果:返回的是一個大小為n的一維數組,一維數組中的第i個值為模型預測第i個預測樣本的標簽
print(clf.predict(Xtest))
# predict_proba返回的是一個n行k列的數組,第i行第j列上的數值是模型預測第i個預測樣本的標簽為j的概率。此時每一行的和應該等於1
print(clf.predict_proba(Xtest))
4.2 通過網格搜索確定最優的剪枝參數
def max_parameter(Xtrain, Ytrain, Xtest, Ytest):
test = list()
for i in range(10):
clf = tree.DecisionTreeClassifier(max_depth=i + 1, criterion="entropy", random_state=30, splitter="random")
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()
4.3 K折交叉驗證
def cross_val():
"""
K折交叉驗證
:return:
"""
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)
val_score = cross_val_score(regressor, boston.data, boston.target, cv=10, scoring="neg_mean_squared_error")
print(val_score)
4.4 泰坦尼克號生還者預測
def titanic_example():
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
import numpy as np
data = pd.read_csv(r"data.csv", index_col=0)
print(data.head())
print(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))
# 查看處理后的數據集
print(data.head())
# 提取標簽和特征矩陣, 分測試集和訓練集
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)
# 修正測試集和訓練集的索引
for i in [Xtrain, Xtest, Ytrain, Ytest]:
i.index = range(i.shape[0])
# 查看分好的訓練集和測試集
print(Xtrain.head())
# 建立模型
clf = DecisionTreeClassifier(random_state=25)
clf = clf.fit(Xtrain, Ytrain)
score_ = clf.score(Xtest, Ytest)
print(score_)
score = cross_val_score(clf, X, y, cv=10).mean()
print(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()
# 用網格搜索調整參數
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)
print(GS.best_params_)
print(GS.best_score_)
參考文獻