[機器學習-Sklearn]決策樹學習與總結 (ID3, C4.5, C5.0, CART)

1. 什麼是決策樹

決策樹是什麼,我們來“決策樹”這個詞進行分詞,那麼就會是決策/樹。大家不妨思考一下,重點是決策還是樹呢?其實啊,決策樹的關鍵點在上。

我們平時寫代碼的那一串一串的If Else其實就是決策樹的思想了。看下面的圖是不是覺得很熟悉呢?

在這裏插入圖片描述

2. 決策樹介紹

決策樹之所以叫決策樹,就是因爲它的結構是樹形狀的,如果你之前沒了解過樹這種數據結構,那麼你至少要知道以下幾個名詞是什麼意思。

  • 根節點:最頂部的那個節點
  • 葉子節點:每條路徑最末尾的那個節點,也就是最外層的節點
  • 非葉子節點:一些條件的節點,下面會有更多分支,也叫做分支節點
  • 分支:也就是分叉

在這裏插入圖片描述

3. ID3 算法

  • ID3算法是在每個結點處選取能獲得最高信息增益的分支屬性進行分裂
  • 在每個決策結點處劃分分支、選取分支屬性的目的是將整個決策樹的樣本
    純度提升
  • 衡量樣本集合純度的指標則是

信息熵

在這裏插入圖片描述
舉例來說,如果有一個大小爲10的布爾值樣本集S𝑏,其中有6個真值、4個
假值,那麼該布爾型樣本分類的熵爲:
在這裏插入圖片描述

信息增益

  • 計算分支屬性對於樣本集分類好壞程度的度量——信息增益
  • 由於分裂後樣本集的純度提高,則樣本集的熵降低,熵降低的值即爲該分 裂方法的信息增益
    在這裏插入圖片描述
    脊椎動物分類訓練樣本集
    在這裏插入圖片描述
    共有14個樣本,其中8個正例,6個反例,設此樣本集爲 S,則分裂前的熵值爲
    在這裏插入圖片描述
    脊椎動物訓練樣本集以“飲食習性”作爲分支屬性的分裂情況
    在這裏插入圖片描述
    在這裏插入圖片描述
    設“飲食習性”屬性爲Y,由此可以計算得出,作爲分支屬性進行分裂之後的
    信息增益爲
    在這裏插入圖片描述
    同理, 計算可得,以“胎生動物”“水生動物”“會飛”作爲分支屬性時的信息 增益分別爲0.6893、0.0454、0.0454
    由此可知“胎生動物”作爲分支屬性時能獲得最大的信息增益,即具有最強的區分樣本的能力,所以在此選擇使用“胎生動物”作爲分支屬性對根結點進行劃分
    由根結點通過計算信息增益選取合適的屬性進行分裂,若新生成的結點的分類屬性不唯一,則對新生的結點繼續進行分裂,不斷重複此步驟,直至所有樣本屬於同 一類,或者達到要求的分類條件爲止

缺點

4. C4.5算法

C4.5算法總體思路與ID3類似,都是通過構造決策樹進行分類,其區別在於分支的處理,在分支屬性的選取上,ID3算法使用信息增益作爲度量,而C4.5算法引入了信息增益率作爲度量
在這裏插入圖片描述
由信息增益率公式中可見,當𝑣比較大時,信息增益率會明顯降低,從而在一定程度上能夠解決ID3算法存在的往往選擇取值較多的分支屬性的問題
在前面例子中,假設選擇“飲食習性”作爲分支屬性,其信息增益率
在這裏插入圖片描述

5. C5.0算法

  • C5.0算法是Quinlan在C4.5算法的基礎上提出的商用改進版本,目的是對含有 大量數據的數據集進行分析
  • C5.0算法與C4.5算法相比有以下優勢:
    – 決策樹構建時間要比C4.5算法快上數倍,同時生成的決策樹規模也更小,擁有更少的葉子結
    點數
    – 使用了提升法(boosting),組合多個決策樹來做出分類,使準確率大大提高
    – 提供可選項由使用者視情況決定,例如是否考慮樣本的權重、樣本錯誤分類成本等

6. CART算法

基尼指數 Gini指標

CART算法在分支處理中分支屬性的度量指標
在這裏插入圖片描述
在前面例子中,假設選擇“會飛”作爲分支屬性,其Gini指標爲
在這裏插入圖片描述

7. 連續屬性離散化

如果是連續的數值型是如年齡,我們一般把它離散化,如離散化爲幼年,中年,老年
因爲你不可能讓把每個年齡都分成一個特徵,那樣會很多,也沒必要。

8. 過擬合的解決方案

  1. 一方面要注意數據訓練集的質量,選取具有代表性樣本的訓練樣本集
  2. 要避免決策樹過度增長,通過限制樹的深度來減少數據中的噪聲對於決策樹構建的影響,一般可以採取剪枝的方法
  3. 剪枝包括預剪枝後剪枝兩類
    預剪枝的思路是提前終止決策樹的增長,在形成完全擬合訓練樣本集的決 策樹之前就停止樹的增長,避免決策樹規模過大而產生過擬合
    後剪枝策略先讓決策樹完全生長,之後針對子樹進行判斷,用葉子結點或者子樹中最常用的分支替換子樹,以此方式不斷改進決策樹,直至無法改進爲止

9. 例子1 - 脊椎動物分類

脊椎動物分類訓練樣本集 test.csv 文件, 做了下面的變換
是:0, 否 : 1 ,雜食動物 : omnivorous animal, 肉食動物:carnivorous animals, : 草食動物, herbivore

omnivorous animal, 0, 1, 1, 0
omnivorous animal, 0, 1, 1, 0
carnivorous animals, 0, 1, 1, 0
carnivorous animals, 1, 1, 0, 1
carnivorous animals, 1, 0, 1, 1
carnivorous animals, 1, 1, 1, 1
omnivorous animal, 0, 1, 0, 0
herbivore, 0, 1, 1, 0
omnivorous animal, 1, 1, 0, 1
carnivorous animals, 1, 0, 1, 1
carnivorous animals, 0, 0, 1, 0
carnivorous animals, 1, 1, 1, 0
herbivore, 0, 1, 1, 0
carnivorous animals, 1, 1, 1, 1

代碼

import pandas as pd
import sklearn as sklearn
from sklearn.feature_extraction import DictVectorizer
from sklearn import tree
import pydotplus
from sklearn.externals.six import StringIO
# pandas 讀取 csv 文件,header = None 表示不將首行作爲列
data = pd.read_csv('data/test.csv', header=None)
# 指定列
data.columns = ['Diet Habits', 'viviparous animal', 'Aquatic animals', 'Can fly','mammal']

# sparse=False意思是不產生稀疏矩陣
vec = sklearn.feature_extraction.DictVectorizer(sparse=False)
# 先用 pandas 對每行生成字典,然後進行向量化
feature = data[['Diet Habits', 'viviparous animal', 'Aquatic animals']]

X_train = vec.fit_transform(feature.to_dict(orient='record'))
# 打印各個變量
print('show feature\n', feature)
print('show vector\n', X_train)
print('show vector name\n', vec.get_feature_names())
print('show vector name\n', vec.vocabulary_)
Y_train = data['mammal']
clf = tree.DecisionTreeClassifier(criterion='entropy')
clf.fit(X_train, Y_train)

dot_data = StringIO()
tree.export_graphviz(clf,feature_names=vec.get_feature_names(),out_file=dot_data)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf("test.pdf")
show feature
             Diet Habits  viviparous animal  Aquatic animals
0     omnivorous animal                  0                1
1     omnivorous animal                  0                1
2   carnivorous animals                  0                1
3   carnivorous animals                  1                1
4   carnivorous animals                  1                0
5   carnivorous animals                  1                1
6     omnivorous animal                  0                1
7             herbivore                  0                1
8     omnivorous animal                  1                1
9   carnivorous animals                  1                0
10  carnivorous animals                  0                0
11  carnivorous animals                  1                1
12            herbivore                  0                1
13  carnivorous animals                  1                1
show vector
 [[1. 0. 0. 1. 0.]
 [1. 0. 0. 1. 0.]
 [1. 1. 0. 0. 0.]
 [1. 1. 0. 0. 1.]
 [0. 1. 0. 0. 1.]
 [1. 1. 0. 0. 1.]
 [1. 0. 0. 1. 0.]
 [1. 0. 1. 0. 0.]
 [1. 0. 0. 1. 1.]
 [0. 1. 0. 0. 1.]
 [0. 1. 0. 0. 0.]
 [1. 1. 0. 0. 1.]
 [1. 0. 1. 0. 0.]
 [1. 1. 0. 0. 1.]]
show vector name
 ['Aquatic animals', 'Diet Habits=carnivorous animals', 'Diet Habits=herbivore', 'Diet Habits=omnivorous animal', 'viviparous animal']
show vector name
 {'Diet Habits=omnivorous animal': 3, 'viviparous animal': 4, 'Aquatic animals': 0, 'Diet Habits=carnivorous animals': 1, 'Diet Habits=herbivore': 2}

在這裏插入圖片描述

10. 例子2

安裝panda 和 scikit-learn 如果你沒有安裝的話
conda install pandas
conda install scikit-learn

1. 準備數據及讀取

季節 時間已過 8 點 風力情況 要不要賴牀
spring no breeze yes
winter no no wind yes
autumn yes breeze yes
winter no no wind yes
summer no breeze yes
winter yes breeze yes
winter no gale yes
winter no no wind yes
spring yes no wind no
summer yes gale no
summer no gale no
autumn yes breeze no
spring,no,breeze,1
winter,no,no wind,1
autumn,yes,breeze,1
winter,no,no wind,1
summer,no,breeze,1
winter,yes,breeze,1
winter,no,gale,1
winter,no,no wind,1
spring,yes,no wind,0
summer,yes,gale,0
summer,no,gale,0
autumn,yes,breeze,0

2. 決策樹的特徵向量化

sklearn的DictVectorizer能對字典進行向量化。什麼叫向量化呢?比如說你有季節這個屬性有[春,夏,秋,冬]四個可選值,那麼如果是春季,就可以用[1,0,0,0]表示,夏季就可以用[0,1,0,0]表示。不過在調用DictVectorizer它會將這些屬性打亂,不會按照我們的思路來運行,但我們也可以一個方法查看,我們看看代碼就明白了

通過DictVectorizer,我們就能夠把字符型的數據,轉化成0 1的矩陣,方便後面進行運算。額外說一句,這種轉換方式其實就是one-hot編碼。

import pandas as pd
import sklearn as sklearn
from sklearn.feature_extraction import DictVectorizer
from sklearn import tree

# pandas 讀取 csv 文件,header = None 表示不將首行作爲列
data = pd.read_csv('data/laic.csv', header=None)
# 指定列
data.columns = ['season', 'after 8', 'wind', 'lay bed']

# sparse=False意思是不產生稀疏矩陣
vec = DictVectorizer(sparse=False)
# 先用 pandas 對每行生成字典,然後進行向量化
feature = data[['season', 'after 8', 'wind']]

X_train = vec.fit_transform(feature.to_dict(orient='record'))
# 打印各個變量
print('show feature\n', feature)
print('show vector\n', X_train)
print('show vector name\n', vec.get_feature_names())
print('show vector name\n', vec.vocabulary_)

執行結果

show feature
     season after 8     wind
0   spring      no   breeze
1   winter      no  no wind
2   autumn     yes   breeze
3   winter      no  no wind
4   summer      no   breeze
5   winter     yes   breeze
6   winter      no     gale
7   winter      no  no wind
8   spring     yes  no wind
9   summer     yes     gale
10  summer      no     gale
11  autumn     yes   breeze
show vector
 [[1. 0. 0. 1. 0. 0. 1. 0. 0.]
 [1. 0. 0. 0. 0. 1. 0. 0. 1.]
 [0. 1. 1. 0. 0. 0. 1. 0. 0.]
 [1. 0. 0. 0. 0. 1. 0. 0. 1.]
 [1. 0. 0. 0. 1. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0. 1. 1. 0. 0.]
 [1. 0. 0. 0. 0. 1. 0. 1. 0.]
 [1. 0. 0. 0. 0. 1. 0. 0. 1.]
 [0. 1. 0. 1. 0. 0. 0. 0. 1.]
 [0. 1. 0. 0. 1. 0. 0. 1. 0.]
 [1. 0. 0. 0. 1. 0. 0. 1. 0.]
 [0. 1. 1. 0. 0. 0. 1. 0. 0.]]
show vector name
  ['after 8=no', 'after 8=yes', 'season=autumn', 'season=spring', 'season=summer', 'season=winter', 'wind=breeze', 'wind=gale', 'wind=no wind']
show vector name
 {'season=spring': 3, 'after 8=no': 0, 'wind=breeze': 6, 'season=winter': 5, 'wind=no wind': 8, 'season=autumn': 2, 'after 8=yes': 1, 'season=summer': 4, 'wind=gale': 7}

3. 決策樹訓練

Y_train = data['lay bed']
clf = tree.DecisionTreeClassifier(criterion='entropy')
clf.fit(X_train, Y_train)

4. 決策樹可視化

當完成一棵樹的訓練的時候,我們也可以讓它可視化展示出來,不過sklearn沒有提供這種功能,它僅僅能夠讓訓練的模型保存到dot文件中。但我們可以藉助其他工具讓模型可視化,先看保存到dot的代碼:

with open("out.dot", 'w') as f :
    f = tree.export_graphviz(clf, out_file = f,
            feature_names = vec.get_feature_names())

5 預測結果

result = clf.predict([[1., 0.,  0. ,1. , 0. , 0. , 1. , 0. , 0.]])
print(result)
[1]

然後可以執行下面命令生成一個out.pdf

dot out.dot -T pdf -o out.pdf

在這裏插入圖片描述

after 8=no after 8=yes season=autumn season=spring season=summer season=winter wind=breeze wind=gale wind=no wind lay bed
1. 0. 0. 1. 0. 0. 1. 0. 0. 1
1. 0. 0. 0. 0. 1. 0. 0. 1. 1
0. 1. 1. 0. 0. 0. 1. 0. 0. 1
1. 0. 0. 0. 0. 1. 0. 0. 1. 1
1. 0. 0. 0. 1. 0. 1. 0. 0. 1
0. 1. 0. 0. 0. 1. 1. 0. 0. 1
1. 0. 0. 0. 0. 1. 0. 1. 0. 1
1. 0. 0. 0. 0. 1. 0. 0. 1. 1
0. 1. 0. 1. 0. 0. 0. 0. 1. 0
0. 1. 0. 0. 1. 0. 0. 1. 0. 0
1. 0. 0. 0. 1. 0. 0. 1. 0. 0
0. 1. 1. 0. 0. 0. 1. 0. 0. 0

6. Module persistence

兩種方式保持我們的模型
參考Sklearn 官網

1) 用Python有的pickle對我們訓練好的模型保存

import pickle
with open('decisive_tree_module.txt', 'wb') as f:
    pickle.dump(clf, f)
with open('decisive_tree_module.txt', 'rb') as f:
    clf2 = pickle.load(f)
#s = pickle.dumps(clf)
#clf2 = pickle.loads(s)
predict2 =  clf2.predict([[1., 0.,  0. ,1. , 0. , 0. , 1. , 0. , 0.]])
print('Predict result via loading pickle saved module :', predict2)
Predict result via loading pickle saved module : [1]

2) 用joblib’s保持如果你的模型裏有大量的 numpy arrays的話

from joblib import dump, load
dump(clf, 'jdecisive_tree_module.joblib')
clf3 = load('jdecisive_tree_module.joblib')
predict3 =  clf3.predict([[1., 0.,  0. ,1. , 0. , 0. , 1. , 0. , 0.]])
print('Predict result via loading joblib saved module :', predict3)
Predict result via loading joblib saved module : [1]

7. 自己算驗證熵的結果

import math
root_node_entropy = -(8/12)*(math.log(8/12, 2)) - (4/12)*(math.log(4/12, 2))
node1_left = (-(3/7)*(math.log(3/7, 2)) - (4/7)*(math.log(4/7,2)))
#node1_right =  (-(5/5)*(math.log(5/5, 2)) - (0/5)*(math.log(0/5,2)))
node1_right =  (-(5/5)*(0) - 0)
#node2_left =  -(3/3)*(math.log(3/3, 2)) - (0/3)*(math.log(0/3, 2))
node2_left =  -(3/3)*(0) - 0
node2_right = -(3/4)*(math.log(3/4, 2)) - (1/4)*(math.log(1/4, 2))

print('Entropy of season=winter ', root_node_entropy)
print('Entropy of wind=breeze ', node1_left)
print('Entropy of wind=breeze ', node1_right)
print('Entropy of node2_left', node2_left)
print('Entropy of node2_right', node2_right)
Entropy of season=winter  0.9182958340544896
Entropy of wind=breeze  0.9852281360342516
Entropy of wind=breeze  -0.0
Entropy of node2_left -0.0
Entropy of node2_right 0.8112781244591328

8. 如果你用基尼指數, 也就是CART算法

只需要把entropy 改成 gini就可以了

clf = tree.DecisionTreeClassifier(criterion='gini')

在這裏插入圖片描述

9. 自己算驗證基尼指數的結果

import math
root_node_entropy = 1 -(8 / 12) * (8 / 12) - (4 / 12) *(4 / 12)
node1_left = 1-(3 / 7) * (3 / 7)  - (4 / 7) * (4 / 7)
# node1_right =  (-(5/5)*(math.log(5/5, 2)) - (0/5)*(math.log(0/5,2)))
node1_right = (-(5 / 5) * (0) - 0)
# node2_left =  -(3/3)*(math.log(3/3, 2)) - (0/3)*(math.log(0/3, 2))
node2_left = -(3 / 3) * (0) - 0
node2_right = 1-(3 / 4) * (3 / 4) - (1 / 4) *(1 / 4)
node3_left = -(2 / 2) * (0) - 0
node3_right = 1-(1 / 2) * (1 / 2) - (1 / 2) *(1 / 2)

print('Entropy of season=winter ', root_node_entropy)
print('Entropy of wind=breeze ', node1_left)
print('Entropy of wind=breeze ', node1_right)
print('Entropy of node2_left', node2_left)
print('Entropy of node2_right', node2_right)
print('Entropy of node3_left', node3_left)
print('Entropy of node3_right', node3_right)
Entropy of season=winter  0.4444444444444445
Entropy of wind=breeze  0.489795918367347
Entropy of wind=breeze  -0.0
Entropy of node2_left -0.0
Entropy of node2_right 0.375
Entropy of node3_left -0.0
Entropy of node3_right 0.5

10. 把數據集全部改成數字不用DictVectorizer做向量化

spring :1 , summer : 2, spring : 3 , winter : 4
時間已過 8 點-no : 0
時間已過 8 點-yes :1
breeze : 1 , no wind : 2 , gale :3

laic1.csv 文件

1,0,1,1
4,0,2,1
3,1,1,1
4,0,2,1
2,0,1,1
4,1,1,1
4,0,3,1
4,0,2,1
1,1,2,0
2,1,3,0
2,0,3,0
3,1,1,0

代碼

import pandas as pd
from sklearn import tree
data = pd.read_csv('data/laic1.csv', header=None)
# 指定列
data.columns = ['season', 'after 8', 'wind', 'lay bed']
X_train = data[['season', 'after 8', 'wind']]
Y_train = data['lay bed']
clf = tree.DecisionTreeClassifier(criterion='entropy')
clf.fit(X_train, Y_train)
with open("out1.dot", 'w') as f :
    f = tree.export_graphviz(clf, out_file = f,
            feature_names =['season', 'after 8', 'wind'])

結果, 可以看到決策樹圖其實都一樣的。
在這裏插入圖片描述
預測結果

result = clf.predict([[1,1,1]])
print('Predict result:', result)
Predict result: [0]

可能遇到問題

如果你這個graphvis 的問題 (GraphViz’s executables not found), 可以根據下面這個link解決它

https://blog.csdn.net/qq_40304090/article/details/88594813

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章