第二章 經典監督學習算法-決策樹
一、決策樹總體概覽
-
概念:是在已知各種情況發生概率的基礎上,通過構成決策樹來求取淨現值的期望值大於等於零的概率,評價項目風險,判斷其可行性的決策分析方法,是直觀運用概率分析的一種圖解法。由於這種決策分支畫成圖形很像一棵樹的枝幹,故稱決策樹。
-
能解決的問題
–分類問題(較多使用)
–迴歸問題 -
決策樹的種類(主要根據屬性劃分的依據來進行算法的分類)
–ID3決策樹
–C4.5決策樹
–CART(Classification And Regression Tree)決策樹 -
優缺點
二、信息論-信息熵
-
隨機事件及其信息
–概念:隨機事件是在隨機試驗中,可能出現也可能不出現,而在大量重複試驗中具有某種規律性的事件叫做隨機事件
–信息:對於一個隨機變量X,它的每一個可能值的出現都可以看作是一個信息,每當X的一個可能值被觀測到,我們稱不確定性減少了,即信息增加了
-
信息熵
–概念:是信息增益的數學期望
-
熱力學中的熵
–概念:表示系統混亂程度的量度 -
最大熵增原則
–概念:當根據不完整的信息作爲依據進行推斷時,應該由滿足分佈限制條件的具有最大熵的概率分佈推得
–推論:對於給定的方差,在任意的隨機變量中高斯隨機變量的熵最大
三、信息論-交叉熵與KL散度
-
聯合熵
–概念:若X,Y是一對離散型隨機變量,且聯合概率分佈爲p(x,y),則X,Y的聯合熵爲:
–作用:描述了一對隨機變量的平均信息量 -
條件熵
–概念:給定隨機變量X的情況下,隨機變量Y的條件熵爲:
-
熵的連鎖規則
-
互信息
–定義
–作用:反應的是知道了Y以後X的不確定性的減少量 -
相對熵(KL散度)
–定義
–推論
-
交叉熵(在深度學習中佔據重要地位)
–定義
–作用:衡量估計出來的概率分佈和真實概率分佈之間的差異情況
四、屬性選擇的依據
-
決策樹的結構及作用
–葉節點:輸出分類結果
–內部節點:用於屬性測試
–根節點:內節點的一種,位置特殊 -
決策樹的輸入
-
根節點的一些特殊情況
–若訓練集中的數據都屬於同一類,那麼將RootNode標記爲該類的葉節點。返回(返回也意味着算法的結束)
–若屬性集爲空集,或者訓練集中的所有數據的所有屬性都相等,那麼將RootNode標記爲訓練集中出現次數最多的類的葉節點,返回 -
決策樹的一般算法流程
-
信息增益
–計算樣本集的信息熵
–使用特定屬性a進行劃分的信息增益
–用法
A)某個屬性的信息增益越大,則使用該屬性進行劃分所得的“純度提升”越大
B)ID3算法使用信息增益作爲屬性選擇的依據
–缺點
A)對可取值數目較多的屬性有偏好 -
增益率
–定義
–缺點
A)對可取值數目較少的屬性有偏好
–缺點的解決辦法(C4.5算法):先從屬性中找到信息增益高於平均水平的屬性,然後再從中選出增益率最高的 -
基尼指數
–樣本集的基尼值公式
–基尼值作用:反映了隨機抽取兩個樣本,其所屬的類別不一致的概率
–屬性a的基尼指數定義
–選擇標準:選擇基尼指數最小的屬性
五、剪枝操作
-
決策樹中的過擬合
–概念:決策樹的生成過程有時候導致決策樹的分支過多(分支過多意味着考慮了過多的邊緣情況,邊緣情況很多時候由個體差異引起),從而導致過擬合 -
驗證集
–概念:決策樹在構造的過程中使用驗證集進行測試,來決定當前屬性是否要進行分裂 -
剪枝的定義:將本來爲內部節點的節點變成葉節點的過程叫做剪枝
-
預剪枝(決策樹邊生成邊進行剪枝操作)
–操作:在決策樹生成過程中,每個節點在劃分之前先在驗證集上進行一次測試,若當前節點的劃分無法提高泛化性能,則不做劃分處理,直接將此節點作爲葉節點(使得一些分支不能展開,降低了過擬合) -
後剪枝(決策樹完全生成之後纔開始剪枝,並且是自下向上的進行)
–優點:樹的分支相對於預剪枝較多;泛化性能優於預剪枝
–缺點:訓練時間較長
六、決策樹的拓展
- 連續值處理
–連續屬性離散化
A)C4.5算法採用“二分法”:假設有一個連續屬性a,那麼將數據劃分爲a≤t和a>t兩個部分
2.多變量決策樹
–屬性的線性組合
七、編程實現(Python)
from math import log
import operator
def calc_entropy(labels):
# 計算信息熵
label_num = len(labels)
label_show_up_times_dict = {}
for label in labels:
if label not in label_show_up_times_dict.keys():
label_show_up_times_dict[label] = 0
label_show_up_times_dict[label] += 1
entropy = 0.0
for key in label_show_up_times_dict:
prob = float(label_show_up_times_dict[key]) / label_num
entropy += prob * log(prob, 2)
return -entropy
def split_dataset(dataset, labels, index, value):
# 根據特徵所在的位置index和特徵的值value,從原數據集中分割出那些值等於value的子集和標籤子集
sub_dataset = []
sub_labels = []
fc_index = 0
for fc in dataset:
if fc[index] == value:
# 如果遇到了值相等的,那麼把這個索引剔除,然後把剔除該索引之後的特徵向量加入到子集中
temp = fc[:index]
temp.extend(fc[index + 1:])
sub_dataset.append(temp)
# 把該特徵向量對應的標籤也挑出來
fc_index += 1
return sub_dataset, sub_labels
def select_best_attribute(dataset, labels):
# 選擇最佳屬性,依據信息增益,即找到信息增益最大的屬性
feature_num = len(dataset[0]) # 特徵個數
base_entropy = calc_entropy(labels) # 當前數據集的信息熵
max_info_gain = -1 # 最大信息增益
best_feature = -1 # 最佳的特徵所在的索引
for i in range(feature_num):
# 當前特徵位置上所有值的List
feature_value_list = [example[i] for example in dataset]
# 獲取所有可能的值(不重複)
unique_vals = set(feature_value_list)
# 此特徵的信息熵
new_entropy = 0.0
for value in unique_vals:
# 獲取子集
sub_dataset, sub_labels = split_dataset(dataset, i, value)
# 子集佔的比例
prob = float(len(sub_dataset)) / len(dataset)
# new_entropy加上相應的部分
new_entropy += prob * calc_entropy(sub_labels)
# 計算當前特徵的信息增益
info_gain = base_entropy - new_entropy
if info_gain > max_info_gain:
# 如果比best_info_gain高,那麼更新best_info_gain和best_feature
max_info_gain = info_gain
best_feature = i
return best_feature
def majority_count(labels):
# 選出所佔比例最高的label
label_count = {}
for vote in labels:
if vote not in label_count.keys():
label_count[vote] = 0
label_count[vote] += 1
sorted_class_count = sorted(label_count.iteritem(), key=operator.itemgetter(1), reverse=True)
return sorted_class_count
def decision_tree(dataset, feature_names, labels):
if labels.count(labels[0]) == len(labels):
# label中所有元素都相等,及類別完全相同,停止劃分
return labels[0]
if len(dataset[0]) == 1:
# 如果只有一個特徵
return majority_count(labels)
# 選出根節點的最最佳屬性
best_feature_index = select_best_attribute(dataset, labels)
best_feature_name = feature_names[best_feature_index]
tree = {best_feature_name: {}}
del (feature_names[best_feature_index])
attr_values = [example[best_feature_index] for example in dataset]
unique_vals = set(attr_values)
for value in unique_vals:
sub_dataset, sub_labels = split_dataset(dataset, best_feature_index, value)
tree[best_feature_name][value] = decision_tree(sub_dataset, sub_labels)
return tree