《Machine Learning in Action》—— hao朋友寝衫,快來玩啊,決策樹呦
在上篇文章中拐邪,《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什么“鬼”主要講述了決策樹的理論內(nèi)容慰毅,介紹了什么決策樹,以及生成決策樹時所需要優(yōu)先選取的三種決策標(biāo)準(zhǔn)扎阶。有學(xué)習(xí)的過SVM汹胃,或閱讀過Taoye之前寫的幾篇SVM內(nèi)容的文章可以發(fā)現(xiàn),決策樹相對于SVM來講要簡單很多乘陪,沒有太多且復(fù)雜的公式推導(dǎo)统台。
我們在把之前的內(nèi)容稍微回顧下:
- 屬性特征的信息增益越高,按道理來講應(yīng)當(dāng)被優(yōu)先選取啡邑,常用于
算法
- 屬性特征的增益率越高贱勃,按道理來講應(yīng)當(dāng)被優(yōu)先選取,常用與
算法
- 屬性特征的尼基指數(shù)低,按道理來講應(yīng)當(dāng)被優(yōu)先選取贵扰,常用于
算法
有了前面的內(nèi)容做基礎(chǔ)仇穗,閱讀本篇文章就會很輕松了。本篇主要講述以下幾部分的內(nèi)容:
- 基于ID3算法手動構(gòu)建決策樹戚绕,并通過Matplotlib進(jìn)行可視化
- 基于已經(jīng)構(gòu)建好的決策樹進(jìn)行分類預(yù)測
- 構(gòu)建好的決策樹模型應(yīng)當(dāng)如何保存和讀任谱?
- 通過鳶尾花(Iris)數(shù)據(jù)集舞丛,使用Sklearn構(gòu)建決策樹耘子,
一、基于ID3算法手動構(gòu)建決策樹球切,并通過Matplotlib進(jìn)行可視化
構(gòu)建決策樹的算法有好幾種谷誓,比如像ID3、C4.5吨凑、CART之類的捍歪,限于篇幅、時間和精力的關(guān)系鸵钝,本篇文章主要采用ID3算法來進(jìn)行構(gòu)建糙臼,使用到的決策標(biāo)準(zhǔn)(指標(biāo))是上篇文章中所提到的信息增益。關(guān)于C4.5和CART算法構(gòu)建決策樹恩商,有興趣的讀者可以參考上期中的增益率和尼基指數(shù)的內(nèi)容变逃。
本次構(gòu)建決策樹所需要用到的數(shù)據(jù)集仍然是李航——《統(tǒng)計學(xué)習(xí)方法》中的貸款數(shù)據(jù),這里再次把數(shù)據(jù)集放出來瞅瞅:
前面也有提到痕届,ID3算法主要是基于信息增益作為選取屬性特征的準(zhǔn)則韧献,在上期我們也計算過各個屬性特征所對應(yīng)的信息增益值,如下:
而根據(jù)ID3算法過程研叫,我們可以知道锤窑,需要優(yōu)先選取信息增益最大的屬性進(jìn)行決策,即房子嚷炉,也就是說將房子作為決策樹的根節(jié)點(diǎn)渊啰。由于房子這一個屬性特征有兩個取值,所以引申出兩個子節(jié)點(diǎn)申屹,一個對應(yīng)“有房子”绘证,另一個對應(yīng)“無房子”,而“有房子”的六個樣本的類別都是允許放款哗讥,也就是有同一類樣本點(diǎn)嚷那,所以它理應(yīng)成為一個葉子節(jié)點(diǎn),且節(jié)點(diǎn)的類不妨標(biāo)記為“允許”杆煞。
這樣一來魏宽,我們的根節(jié)點(diǎn)以及其中的一個葉子節(jié)點(diǎn)就確定了腐泻。接下來,我們需要將“無房子”所對應(yīng)的樣本集再次選取一個新的屬性特征進(jìn)行決策队询。注意:此次做決策的數(shù)據(jù)樣本總體就不再是初始數(shù)據(jù)了派桩,而是“無房子”所對應(yīng)的所有樣本,這一點(diǎn)需要格外注意蚌斩。
我們在“無房子的”的數(shù)據(jù)樣本中再次計算其他屬性所對應(yīng)的信息增益铆惑,我們不妨講此次的數(shù)據(jù)樣本集合記為,計算結(jié)果如下:
與上同樣分析送膳,可以發(fā)現(xiàn)此時工作所對應(yīng)的信息增益值最高员魏,也就是說第二個優(yōu)先選取的屬性為“工作”。而且肠缨,我們可以發(fā)現(xiàn)逆趋,在數(shù)據(jù)樣本集中,總共有9個晒奕,其中允許放款的有三個,拒絕的有6個名斟,且結(jié)果標(biāo)簽與工作值剛好完全都對應(yīng)上了脑慧,也就是說有工作的都允許放款了,沒工作的都拒絕放款了砰盐。所以闷袒,在第二個屬性特征選取完成之后,此時產(chǎn)生了倆個葉子節(jié)點(diǎn)岩梳,節(jié)點(diǎn)結(jié)果與是否有工作對應(yīng)囊骤。
通過如上分析,我們就得到了此次基于ID3算法所構(gòu)建出的決策樹冀值,決策樹如下:
<img src="https://gitee.com/tianxingjian123/my-images-repository/raw/master/img/jueceshu_7_.png" width="300">
接下來我們通過代碼來生成這顆決策樹也物,對于樹形結(jié)構(gòu)的數(shù)據(jù),我們可以通過字典或者說是json類型來進(jìn)行保存列疗。比如上圖中的決策樹滑蚯,我們可以通過如下結(jié)果來進(jìn)行表示:
{"房子": {
"1": "Y",
"0":{"工作": {
"1": "Y",
"0": "N"
}}
}}
上述表示的數(shù)據(jù)格式我們一般稱其為Json,這個在前后端抵栈、爬蟲告材,亦或是在其他各種領(lǐng)域中都是接觸的非常多的。另外古劲,我們可以發(fā)現(xiàn)在決策樹生成的過程中斥赋,在一個屬性特征選取完成之后,需要經(jīng)過同樣的操作再次選取一個屬性特征产艾,其實(shí)就相當(dāng)于一個周期疤剑,換句話講正好滿足了遞歸的特性滑绒,只是我們的數(shù)據(jù)總體發(fā)生了變化而已。既然我們明確了保存樹形結(jié)構(gòu)數(shù)據(jù)所需要的類型骚露,下面我們通過代碼來實(shí)現(xiàn):
此次的代碼相較于上篇文章中計算信息增益的變化主要有三個地方:
- 由于我們最終需要生成屬性的具體內(nèi)容蹬挤,而非僅僅索引,所以我們在創(chuàng)建數(shù)據(jù)的時候棘幸,除了返回數(shù)據(jù)本身之外焰扳,還需要返回對應(yīng)的屬性,修改
establish_data
方法如下所示:
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建訓(xùn)數(shù)據(jù)集
"""
def establish_data():
data = [[0, 0, 0, 0, 'N'], # 樣本數(shù)據(jù)集相關(guān)信息误续,前面幾項代表屬性特征吨悍,最后一項表示是否放款
[0, 0, 0, 1, 'N'],
[0, 1, 0, 1, 'Y'],
[0, 1, 1, 0, 'Y'],
[0, 0, 0, 0, 'N'],
[1, 0, 0, 0, 'N'],
[1, 0, 0, 1, 'N'],
[1, 1, 1, 1, 'Y'],
[1, 0, 1, 2, 'Y'],
[1, 0, 1, 2, 'Y'],
[2, 0, 1, 2, 'Y'],
[2, 0, 1, 1, 'Y'],
[2, 1, 0, 1, 'Y'],
[2, 1, 0, 2, 'Y'],
[2, 0, 0, 0, 'N']]
labels = ["年紀(jì)", "工作", "房子", "信用"]
return np.array(data), labels
- 在上篇文章中,我們的
handle_data
方法僅僅是找出對應(yīng)屬性特征值的樣本蹋嵌,比如找出所有年紀(jì)為青年的樣本數(shù)據(jù)集育瓜。而要想構(gòu)建決策樹,在第一次選取了屬性之后栽烂,應(yīng)當(dāng)將該屬性從數(shù)據(jù)集中移除躏仇,所以修改handle_data
如下:
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:找出對應(yīng)屬性特征值的樣本,比如找出所有年紀(jì)為青年的樣本數(shù)據(jù)集
"""
def handle_data(data, axis, value):
result_data = list()
for item in data:
if item[axis] == value:
reduced_data = item[: axis].tolist()
reduced_data.extend(item[axis + 1:])
result_data.append(reduced_data)
return result_data
- 第三個就是需要一個創(chuàng)建決策樹的方法
establish_decision_tree
腺办,具體代碼如下焰手,其中在選取完成一個屬性之后需要遞歸調(diào)用本身來選取第二個屬性
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建決策樹
"""
def establish_decision_tree(data, labels, feat_labels):
cat_list = [item[-1] for item in data]
if (cat_list.count(cat_list[0]) == len(cat_list)): return cat_list[0] # 數(shù)據(jù)集中的類別只有一種
best_feature_index = calc_information_gain(data) # 通過信息增益優(yōu)先選取最好的屬性特征
best_label = labels[best_feature_index] # 屬性特征對應(yīng)的標(biāo)簽內(nèi)容
# feat_labels表示已選取的屬性;新建一個決策樹節(jié)點(diǎn)怀喉;將屬性標(biāo)簽列表中刪除已選取的屬性
feat_labels.append(best_label); decision_tree = {best_label: dict()}; del(labels[best_feature_index])
feature_values = [item[best_feature_index] for item in data]
unique_values = set(feature_values) # 獲取最優(yōu)屬性對應(yīng)值的set集合
for value in unique_values:
sub_label = labels[:]
decision_tree[best_label][value] = establish_decision_tree(np.array(handle_data(data, best_feature_index, value)), sub_label, feat_labels)
return decision_tree
該部分的完整代碼如下所示:
import numpy as np
import pandas as pd
np.__version__
pd.__version__
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建訓(xùn)數(shù)據(jù)集
"""
def establish_data():
data = [[0, 0, 0, 0, 'N'], # 樣本數(shù)據(jù)集相關(guān)信息书妻,前面幾項代表屬性特征,最后一項表示是否放款
[0, 0, 0, 1, 'N'],
[0, 1, 0, 1, 'Y'],
[0, 1, 1, 0, 'Y'],
[0, 0, 0, 0, 'N'],
[1, 0, 0, 0, 'N'],
[1, 0, 0, 1, 'N'],
[1, 1, 1, 1, 'Y'],
[1, 0, 1, 2, 'Y'],
[1, 0, 1, 2, 'Y'],
[2, 0, 1, 2, 'Y'],
[2, 0, 1, 1, 'Y'],
[2, 1, 0, 1, 'Y'],
[2, 1, 0, 2, 'Y'],
[2, 0, 0, 0, 'N']]
labels = ["年紀(jì)", "工作", "房子", "信用"]
return np.array(data), labels
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:計算信息熵
"""
def calc_information_entropy(data):
data_number, _ = data.shape
information_entropy = 0
for item in pd.DataFrame(data).groupby(_ - 1):
proportion = item[1].shape[0] / data_number
information_entropy += - proportion * np.log2(proportion)
return information_entropy
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:找出對應(yīng)屬性特征值的樣本躬拢,比如找出所有年紀(jì)為青年的樣本數(shù)據(jù)集
"""
def handle_data(data, axis, value):
result_data = list()
for item in data:
if item[axis] == value:
reduced_data = item[: axis].tolist()
reduced_data.extend(item[axis + 1:])
result_data.append(reduced_data)
return result_data
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:計算最大的信息增益躲履,并輸出其所對應(yīng)的特征索引
"""
def calc_information_gain(data):
feature_number = data.shape[1] - 1 # 屬性特征的數(shù)量
base_entropy = calc_information_entropy(data) # 計算總體數(shù)據(jù)集的信息熵
max_information_gain, best_feature = 0.0, -1 # 初始化最大信息增益和對應(yīng)的特征索引
for index in range(feature_number):
feat_list = [item[index] for item in data]
feat_set = set(feat_list)
new_entropy = 0.0
for set_item in feat_set: # 計算屬性特征劃分后的信息增益
sub_data = handle_data(data, index, set_item)
proportion = len(sub_data) / float(data.shape[0]) # 計算子集的比例
new_entropy += proportion * calc_information_entropy(np.array(sub_data))
temp_information_gain = base_entropy - new_entropy # 計算信息增益
print("第%d個屬性特征所對應(yīng)的的增益為%.3f" % (index + 1, temp_information_gain)) # 輸出每個特征的信息增益
if (temp_information_gain > max_information_gain):
max_information_gain, best_feature = temp_information_gain, index # 更新信息增益,確定的最大的信息增益對應(yīng)的索引
return best_feature
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建決策樹
"""
def establish_decision_tree(data, labels, feat_labels):
cat_list = [item[-1] for item in data]
if (cat_list.count(cat_list[0]) == len(cat_list)): return cat_list[0] # 數(shù)據(jù)集中的類別只有一種
best_feature_index = calc_information_gain(data) # 通過信息增益優(yōu)先選取最好的屬性特征
best_label = labels[best_feature_index] # 屬性特征對應(yīng)的標(biāo)簽內(nèi)容
# feat_labels表示已選取的屬性聊闯;新建一個決策樹節(jié)點(diǎn)工猜;將屬性標(biāo)簽列表中刪除已選取的屬性
feat_labels.append(best_label); decision_tree = {best_label: dict()}; del(labels[best_feature_index])
feature_values = [item[best_feature_index] for item in data]
unique_values = set(feature_values) # 獲取最優(yōu)屬性對應(yīng)值的set集合
for value in unique_values:
sub_label = labels[:]
decision_tree[best_label][value] = establish_decision_tree(np.array(handle_data(data, best_feature_index, value)), sub_label, feat_labels)
return decision_tree
if __name__ == "__main__":
data, labels = establish_data()
print(establish_decision_tree(data, labels, list()))
運(yùn)行結(jié)果:
{'房子': {'1': 'Y', '0': {'工作': {'1': 'Y', '0': 'N'}}}}
可見,代碼運(yùn)行的結(jié)果與我們手動創(chuàng)建的決策樹如出一轍馅袁,完美域慷,哈哈哈~~~
可是,如上決策樹的顯示未免有點(diǎn)太不親民了汗销,生成的決策樹比較簡單那還好點(diǎn)犹褒,假如我們生成的決策樹比較復(fù)雜,那通過Json格式的數(shù)據(jù)來輸出決策樹就有點(diǎn)懵了弛针。
對此叠骑,我們需要將決策樹進(jìn)行可視化,可視化主要使用到了Matplotlib包削茁。關(guān)于Matplotlib的使用大家可以參考文檔及其他資料宙枷,Taoye后期也會整理出一篇自己常用的接口掉房。
import numpy as np
import pandas as pd
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建訓(xùn)數(shù)據(jù)集
"""
def establish_data():
data = [[0, 0, 0, 0, 'N'], # 樣本數(shù)據(jù)集相關(guān)信息,前面幾項代表屬性特征慰丛,最后一項表示是否放款
[0, 0, 0, 1, 'N'],
[0, 1, 0, 1, 'Y'],
[0, 1, 1, 0, 'Y'],
[0, 0, 0, 0, 'N'],
[1, 0, 0, 0, 'N'],
[1, 0, 0, 1, 'N'],
[1, 1, 1, 1, 'Y'],
[1, 0, 1, 2, 'Y'],
[1, 0, 1, 2, 'Y'],
[2, 0, 1, 2, 'Y'],
[2, 0, 1, 1, 'Y'],
[2, 1, 0, 1, 'Y'],
[2, 1, 0, 2, 'Y'],
[2, 0, 0, 0, 'N']]
labels = ["年紀(jì)", "工作", "房子", "信用"]
return np.array(data), labels
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:計算信息熵
"""
def calc_information_entropy(data):
data_number, _ = data.shape
information_entropy = 0
for item in pd.DataFrame(data).groupby(_ - 1):
proportion = item[1].shape[0] / data_number
information_entropy += - proportion * np.log2(proportion)
return information_entropy
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:找出對應(yīng)屬性特征值的樣本卓囚,比如找出所有年紀(jì)為青年的樣本數(shù)據(jù)集
"""
def handle_data(data, axis, value):
result_data = list()
for item in data:
if item[axis] == value:
reduced_data = item[: axis].tolist()
reduced_data.extend(item[axis + 1:])
result_data.append(reduced_data)
return result_data
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:計算最大的信息增益,并輸出其所對應(yīng)的特征索引
"""
def calc_information_gain(data):
feature_number = data.shape[1] - 1 # 屬性特征的數(shù)量
base_entropy = calc_information_entropy(data) # 計算總體數(shù)據(jù)集的信息熵
max_information_gain, best_feature = 0.0, -1 # 初始化最大信息增益和對應(yīng)的特征索引
for index in range(feature_number):
feat_list = [item[index] for item in data]
feat_set = set(feat_list)
new_entropy = 0.0
for set_item in feat_set: # 計算屬性特征劃分后的信息增益
sub_data = handle_data(data, index, set_item)
proportion = len(sub_data) / float(data.shape[0]) # 計算子集的比例
new_entropy += proportion * calc_information_entropy(np.array(sub_data))
temp_information_gain = base_entropy - new_entropy # 計算信息增益
print("第%d個屬性特征所對應(yīng)的的增益為%.3f" % (index + 1, temp_information_gain)) # 輸出每個特征的信息增益
if (temp_information_gain > max_information_gain):
max_information_gain, best_feature = temp_information_gain, index # 更新信息增益诅病,確定的最大的信息增益對應(yīng)的索引
return best_feature
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建決策樹
"""
def establish_decision_tree(data, labels, feat_labels):
cat_list = [item[-1] for item in data]
if (cat_list.count(cat_list[0]) == len(cat_list)): return cat_list[0] # 數(shù)據(jù)集中的類別只有一種
best_feature_index = calc_information_gain(data) # 通過信息增益優(yōu)先選取最好的屬性特征
best_label = labels[best_feature_index] # 屬性特征對應(yīng)的標(biāo)簽內(nèi)容
# feat_labels表示已選取的屬性哪亿;新建一個決策樹節(jié)點(diǎn);將屬性標(biāo)簽列表中刪除已選取的屬性
feat_labels.append(best_label); decision_tree = {best_label: dict()}; del(labels[best_feature_index])
feature_values = [item[best_feature_index] for item in data]
unique_values = set(feature_values) # 獲取最優(yōu)屬性對應(yīng)值的set集合
for value in unique_values:
sub_label = labels[:]
decision_tree[best_label][value] = establish_decision_tree(np.array(handle_data(data, best_feature_index, value)), sub_label, feat_labels)
return decision_tree
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:統(tǒng)計決策樹當(dāng)中的葉子節(jié)點(diǎn)數(shù)目贤笆,以及決策樹的深度
"""
def get_leaf_number_and_tree_depth(decision_tree):
leaf_number, first_key, tree_depth = 0, next(iter(decision_tree)), 0; second_dict = decision_tree[first_key]
for key in second_dict.keys():
if type(second_dict.get(key)).__name__ == "dict":
temp_number, temp_depth = get_leaf_number_and_tree_depth(second_dict[key])
leaf_number, curr_depth = leaf_number + temp_number, 1 + temp_depth
else: leaf_number += 1; curr_depth = 1
if curr_depth > tree_depth: tree_depth = curr_depth
return leaf_number, tree_depth
from matplotlib.font_manager import FontProperties
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:繪制節(jié)點(diǎn)
"""
def plot_node(node_text, center_pt, parent_pt, node_type):
arrow_args = dict(arrowstyle = "<-")
font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) # 設(shè)置字體
create_plot.ax1.annotate(node_text, xy=parent_pt, xycoords='axes fraction',
xytext=center_pt, textcoords='axes fraction',
va="center", ha="center", bbox=node_type, arrowprops=arrow_args, FontProperties=font)
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:標(biāo)注有向邊的值
"""
def tag_text(cntr_pt, parent_pt, node_text):
x_mid = (parent_pt[0] - cntr_pt[0]) / 2.0 + cntr_pt[0]
y_mid = (parent_pt[1] - cntr_pt[1]) / 2.0 + cntr_pt[1]
create_plot.ax1.text(x_mid, y_mid, node_text, va="center", ha="center", rotation=30)
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:繪制決策樹
"""
def plot_tree(decision_tree, parent_pt, node_text):
decision_node = dict(boxstyle="sawtooth", fc="0.8")
leaf_node = dict(boxstyle = "round4", fc = "0.8")
leaf_number, tree_depth = get_leaf_number_and_tree_depth(decision_tree)
first_key = next(iter(decision_tree))
cntr_pt = (plot_tree.xOff + (1.0 + float(leaf_number)) / 2.0 / plot_tree.totalW, plot_tree.yOff)
tag_text(cntr_pt, parent_pt, node_text); plot_node(first_key, cntr_pt, parent_pt, decision_node)
second_dict = decision_tree[first_key]
plot_tree.yOff = plot_tree.yOff - 1.0 / plot_tree.totalD
for key in second_dict.keys():
if type(second_dict[key]).__name__ == 'dict': plot_tree(second_dict[key], cntr_pt, str(key))
else:
plot_tree.xOff = plot_tree.xOff + 1.0 / plot_tree.totalW
plot_node(second_dict[key], (plot_tree.xOff, plot_tree.yOff), cntr_pt, leaf_node)
tag_text((plot_tree.xOff, plot_tree.yOff), cntr_pt, str(key))
plot_tree.yOff = plot_tree.yOff + 1.0 / plot_tree.totalD
from matplotlib import pyplot as plt
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:創(chuàng)建決策樹
"""
def create_plot(in_tree):
fig = plt.figure(1, facecolor = "white")
fig.clf()
axprops = dict(xticks = [], yticks = [])
create_plot.ax1 = plt.subplot(111, frameon = False, **axprops)
leaf_number, tree_depth = get_leaf_number_and_tree_depth(in_tree)
plot_tree.totalW, plot_tree.totalD = float(leaf_number), float(tree_depth)
plot_tree.xOff = -0.5 / plot_tree.totalW; plot_tree.yOff = 1.0
plot_tree(in_tree, (0.5,1.0), '')
plt.show()
if __name__ == "__main__":
data, labels = establish_data()
decision_tree = establish_decision_tree(data, labels, list())
print(decision_tree)
print("決策樹的葉子節(jié)點(diǎn)數(shù)和深度:", get_leaf_number_and_tree_depth(decision_tree))
create_plot(decision_tree)
手動可視化決策樹的結(jié)果如下所示:
實(shí)話實(shí)說蝇棉,通過Matplotlib手動對決策樹進(jìn)行可視化,對于之前沒什么經(jīng)驗的碼手來講確實(shí)有點(diǎn)不友好芥永。上述代碼能看懂就行篡殷,沒必要死揪著不放,后面的話會介紹通過Graphviz
來繪制決策樹埋涧。這里對上方的代碼做個簡短的說明:
-
get_leaf_number_and_tree_depth
主要用于統(tǒng)計決策樹當(dāng)中的葉子節(jié)點(diǎn)數(shù)目板辽,以及決策樹的深度。選取key
對應(yīng)的value
棘催,判斷value
是否為一個字典類型戳气,否的話說明是一個葉子節(jié)點(diǎn),是的話說明非葉子節(jié)點(diǎn)巧鸭,不同情況做不同處理 -
plot_node
方法用于繪制節(jié)點(diǎn),這里設(shè)置了font類型是在windows下的麻捻,如果是linux則需要額外設(shè)置 -
tag_text
用于標(biāo)注有向邊的屬性值纲仍,在這里主要是用1和0來進(jìn)行標(biāo)注,1代表對屬性的肯定贸毕,0表示否定 -
plot_tree
遍歷繪制決策樹郑叠,在這里需要調(diào)用前面所定義的幾個方法
二、基于已經(jīng)構(gòu)建好的決策樹進(jìn)行分類預(yù)測
依靠訓(xùn)練數(shù)據(jù)構(gòu)造了決策樹之后明棍,我們既可以通過該決策樹模型應(yīng)用于實(shí)際數(shù)據(jù)來進(jìn)行分類乡革。在對數(shù)據(jù)進(jìn)行分類時,需要使用決策樹以及用于構(gòu)造決策樹的標(biāo)簽向量摊腋;然后沸版,程序比較測試數(shù)據(jù)與決策樹上的數(shù)值,遞歸執(zhí)行該過程直到進(jìn)入到葉子節(jié)點(diǎn)兴蒸;最后將測試數(shù)據(jù)定義為葉子節(jié)點(diǎn)所屬的類型视粮。——《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》
在已經(jīng)獲取到?jīng)Q策樹模型的前提下對測試數(shù)據(jù)進(jìn)行分類還是挺好理解的
對此橙凳,我們定義一個classify
方法來進(jìn)行分類:
"""
Author: Taoye
微信公眾號: 玩世不恭的Coder
Explain:通過決策樹模型對測試數(shù)據(jù)進(jìn)行分類
"""
def classify(decision_tree, best_feature_labels, test_data):
first_node = next(iter(decision_tree))
second_dict = decision_tree[first_node]
feat_index = best_feature_labels.index(first_node)
for key in second_dict.keys():
if int(test_data[feat_index]) == int(key):
if type(second_dict[key]).__name__ == "dict": # 為字典說明還沒到葉子節(jié)點(diǎn)
result_label = classify(second_dict[key], best_feature_labels, test_data)
else: result_label = second_dict[key]
return result_label
我們分別對四個數(shù)據(jù)樣本進(jìn)行測試蕾殴,樣本分別是(有房子笑撞,沒工作),(有房子钓觉,有工作)茴肥,(沒房子,沒工作)荡灾,(沒房子瓤狐,有工作),使用列表表示分別是[1, 0], [1, 1], [0, 0], [0, 1]卧晓,運(yùn)行結(jié)果如下:
可見芬首,四組數(shù)據(jù)都能夠分類成功。
三逼裆、構(gòu)建好的決策樹模型應(yīng)當(dāng)如何保存和讀扔羯浴?
構(gòu)建好決策樹之后胜宇,我們要想保存該模型就可以通過pickle
模塊來進(jìn)行耀怜。
保存好模型之后,下次使用該模型就不需要的再次訓(xùn)練了桐愉,只需要加載模型即可财破。保存和加載模型的示例代碼如下(挺簡單的,就不多說了):
import pickle
with open("DecisionTreeModel.txt", "wb") as f:
pickle.dump(decision_tree, f) # 保存決策樹模型
f = open("DecisionTreeModel.txt", "rb")
decision_tree = pickle.load(f) # 加載決策樹模型
四从诲、通過鳶尾花(Iris)數(shù)據(jù)集左痢,使用Sklearn構(gòu)建決策樹
現(xiàn)在我們通過sklearn來實(shí)現(xiàn)一個小案例,數(shù)據(jù)集采用的是機(jī)器學(xué)習(xí)中比較常用的鳶尾花(Iris)數(shù)據(jù)集系洛。更多其他關(guān)于決策樹分類的案例俊性,大家可以去sklearn.tree.DecisionTreeClassifier
的文檔中學(xué)習(xí):https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html
在sklearn中實(shí)現(xiàn)決策樹分類主要用到的接口是sklearn.tree.DecisionTreeClassifier
,這個主要是通過數(shù)據(jù)樣本集構(gòu)建一個決策樹模型描扯。此外萝究,如果我們要想將決策樹可視化哩都,還需要使用到export_graphviz
冤馏。當(dāng)然了内列,在sklearn.tree`下還有其他接口可供大家調(diào)用,這里不做的過多介紹了恩够,讀者可自行學(xué)習(xí)卒落。
關(guān)于sklearn.tree.DecisionTreeClassifier
的使用,可參考:https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html玫鸟。其中內(nèi)置了很多的參數(shù)导绷,這里主要記錄8個參數(shù),也方便后期回顧屎飘,其他的有機(jī)會用到再查找資料:
-
criterion
:屬性選取的標(biāo)準(zhǔn)妥曲,默認(rèn)采用的是gini
贾费,也可以自行選擇entropy
,gini
是基尼值檐盟,entropy
是信息熵褂萧,這兩個我們在上篇文章中已經(jīng)講到過了 -
splitter
:特征劃分節(jié)點(diǎn)的選擇標(biāo)準(zhǔn),默認(rèn)是best葵萎,可以設(shè)置為random导犹。默認(rèn)的"best"適合樣本量不大的時候,而如果樣本數(shù)據(jù)量非常大羡忘,此時決策樹構(gòu)建推薦"random"谎痢。 -
max_depth
:決策樹最大深度,默認(rèn)是None卷雕。需要注意一點(diǎn)的是节猿,該深度是不包含根節(jié)點(diǎn)的。一般來說漫雕,數(shù)據(jù)少或者特征少的時候可以不管這個值滨嘱。如果模型樣本量多,特征也多的情況下浸间,推薦限制這個最大深度太雨,具體的取值取決于數(shù)據(jù)的分布。 -
max_features
:劃分時考慮的最大特征數(shù)魁蒜,默認(rèn)是None囊扳。一般來說,如果樣本特征數(shù)不多兜看,比如小于50宪拥,我們用默認(rèn)的"None"就可以了,如果特征數(shù)非常多铣减,我們可以靈活使用其他取值來控制劃分時考慮的最大特征數(shù),以控制決策樹的生成時間脚作。用到的時候查看下文檔即可葫哗。 -
min_samples_split
:內(nèi)部節(jié)點(diǎn)再劃分所需最小樣本數(shù),默認(rèn)為2球涛。意思就是說劣针,比如我們某個屬性對應(yīng)樣本數(shù)目小于min_samples_split
,即使它滿足優(yōu)先選取條件亿扁,依然會被剔除掉捺典。 -
min_samples_leaf
:葉子節(jié)點(diǎn)最少樣本數(shù),默認(rèn)是1从祝。這個值限制了葉子節(jié)點(diǎn)最少的樣本數(shù)襟己,如果某葉子節(jié)點(diǎn)數(shù)目小于樣本數(shù)引谜,則會和兄弟節(jié)點(diǎn)一起被剪枝。葉結(jié)點(diǎn)需要最少的樣本數(shù)擎浴,也就是最后到葉結(jié)點(diǎn)员咽,需要多少個樣本才能算一個葉結(jié)點(diǎn)。如果設(shè)置為1贮预,哪怕這個類別只有1個樣本贝室,決策樹也會構(gòu)建出來。 -
max_leaf_nodes
:最大葉子節(jié)點(diǎn)數(shù)仿吞,默認(rèn)是None滑频。通過限制最大葉子節(jié)點(diǎn)數(shù),可以防止過擬合唤冈。如果加了限制峡迷,算法會建立在最大葉子節(jié)點(diǎn)數(shù)內(nèi)最優(yōu)的決策樹。如果特征不多务傲,可以不考慮這個值凉当,但是如果特征分成多的話,可以加以限制售葡,具體的值可以通過交叉驗證得到看杭。 -
random_state
:隨機(jī)數(shù)種子,默認(rèn)是None挟伙。如果沒有設(shè)置隨機(jī)數(shù)楼雹,隨機(jī)出來的數(shù)與當(dāng)前系統(tǒng)時間有關(guān),每個時刻都是不同的尖阔。如果設(shè)置了隨機(jī)數(shù)種子贮缅,那么相同隨機(jī)數(shù)種子,不同時刻產(chǎn)生的隨機(jī)數(shù)也是相同的介却。
此外谴供,在DecisionTreeClassifier
下也有很多方法可供調(diào)用,詳情可參考文檔進(jìn)行使用齿坷,如下:
接下來桂肌,我們就用sklearn來對鳶尾花數(shù)據(jù)集進(jìn)行分類吧。參考資料:https://scikit-learn.org/stable/auto_examples/tree/plot_iris_dtc.html#sphx-glr-auto-examples-tree-plot-iris-dtc-py
構(gòu)建決策樹本身的代碼并不難永淌,主要在于可視化崎场,其中涉及到了Matplotlib的不少操作,以增強(qiáng)可視化效果遂蛀,完整代碼如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, plot_tree
class IrisDecisionTree:
"""
Explain:屬性的初始化
Parameters:
n_classes: 鳶尾花的類別數(shù)
plot_colors: 不同類別花的顏色
plot_step: meshgrid網(wǎng)格的步長
"""
def __init__(self, n_classes, plot_colors, plot_step):
self.n_classes = n_classes
self.plot_colors = plot_colors
self.plot_step = plot_step
"""
Explain: 通過load_iris構(gòu)建數(shù)據(jù)集
"""
def establish_data(self):
iris_info = load_iris()
return iris_info.data, iris_info.target, iris_info.feature_names, iris_info.target_names
"""
Explain:分類的可視化
"""
def show_result(self, x_data, y_label, feature_names, target_names):
# 選取兩個屬性來構(gòu)建決策樹谭跨,以方便可視化,其中列表內(nèi)部元素代表屬性對應(yīng)的索引
for index, pair in enumerate([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]):
sub_x_data, sub_y_label = x_data[:, pair], y_label
clf = DecisionTreeClassifier().fit(sub_x_data, sub_y_label) # 選取兩個屬性構(gòu)建決策樹
plt.subplot(2, 3, index + 1)
x_min, x_max = sub_x_data[:, 0].min() - 1, sub_x_data[:, 0].max() + 1 # 第一個屬性
y_min, y_max = sub_x_data[:, 1].min() - 1, sub_x_data[:, 1].max() + 1 # 第二個屬性
xx, yy = np.meshgrid(np.arange(x_min, x_max, self.plot_step), np.arange(y_min, y_max, self.plot_step))
plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape) # 預(yù)測meshgrid內(nèi)部每個元素的分類
cs = plt.contourf(xx, yy, Z, cmap = plt.cm.RdYlBu) # 繪制帶有顏色的網(wǎng)格圖
plt.xlabel(feature_names[pair[0]]); plt.ylabel(feature_names[pair[1]]) # 標(biāo)注坐標(biāo)軸標(biāo)簽
for i, color in zip(range(self.n_classes), self.plot_colors):
idx = np.where(sub_y_label == i)
plt.scatter(sub_x_data[idx, 0], sub_x_data[idx, 1], c=color, label=target_names[i],
cmap=plt.cm.RdYlBu, edgecolor='black', s=15) # 繪制數(shù)據(jù)樣本集的散點(diǎn)圖
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) # 定義中文字體
plt.suptitle("通過決策樹對鳶尾花數(shù)據(jù)進(jìn)行可視化", fontproperties=font)
plt.legend(loc='lower right', borderpad=0, handletextpad=0)
plt.axis("tight")
plt.figure()
clf = DecisionTreeClassifier().fit(x_data, y_label) # 針對鳶尾花數(shù)據(jù)集的多重屬性來構(gòu)建決策樹
plot_tree(clf, filled=True)
plt.show()
if __name__ == "__main__":
iris_decision_tree = IrisDecisionTree(3, "ryb", 0.02)
x_data, y_label, feature_names, target_names = iris_decision_tree.establish_data()
iris_decision_tree.show_result(x_data, y_label, feature_names, target_names)
運(yùn)行結(jié)果如下所示:
通過可視化結(jié)果,我們可以發(fā)現(xiàn)主要有兩個結(jié)果螃宙,我們分別對其進(jìn)行說明下:于鳶尾花數(shù)據(jù)集來講蛮瞄,總共有四種屬性特征以及三種標(biāo)簽結(jié)果。為了方便可視化污呼,第一張圖只選取了兩個屬性來構(gòu)建決策樹裕坊,四種屬性,選兩個燕酷,很簡單籍凝,學(xué)過排列組合的都應(yīng)該知道有種可能,所第一張圖中的每張子圖分別對應(yīng)一種可能苗缩。且顏色不同代表不同的分類饵蒂,假如數(shù)據(jù)集顏色與網(wǎng)格內(nèi)部顏色一致,則說明分類正確酱讶。因此退盯,從直觀上來看,選取
sepal length和petal length
這兩種屬性構(gòu)建的決策樹相對較好泻肯。而第二張圖是針對數(shù)據(jù)集中的所有屬性特征來構(gòu)建決策樹渊迁。具體的結(jié)果可自行運(yùn)行上方代碼查看(由于設(shè)置了font字體,所以上方代碼需在windows下運(yùn)行)
我們可以發(fā)現(xiàn)灶挟,上面代碼可視化決策樹的時候采用的是sklearn.tree.plot_tree
琉朽,前面我們在講解通過Matplotlib繪制決策樹的時候也有說到,使用graphviz
亦可可視化決策樹稚铣,下面我們不妨來看看吧箱叁!
graphviz
不能采用pip進(jìn)行安裝,采用anaconda安裝的時候也會很慢惕医,甚至多次嘗試都可能安裝失敗耕漱,前幾天幫同學(xué)安裝就出現(xiàn)這種情況(windows下是這樣的,linux環(huán)境下會很方便)抬伺,所以這里我們采用直接通過whl
文件來安裝螟够。
建議:對于使用Python有過一段時間的Pyer來講,都會經(jīng)常安裝一些第三方模塊峡钓,有些可以直接通過pip或者conda完美的解決齐鲤,而有些在安裝的過程中會遇到各種不明所以的錯誤。所以椒楣,對于在安裝過程中遇到錯誤的讀者不妨嘗試通過whl
文件進(jìn)行安裝,whl
目標(biāo)地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#wordcloud牡肉,其中整合了各種Python模塊的whl
文件捧灰。
打開上述url地址 --> ctrl + f
搜索graphviz
--> 下載需要的graphviz
安裝文件
在本地目標(biāo)路徑中執(zhí)行安裝即可:
pip install graphviz?0.15?py3?none?any.whl
此外對于windows來將 ,還需要前往官網(wǎng)安裝graphviz的exe文件,然后將bin目錄添加到環(huán)境變量即可毛俏。exe文件的下載地址:https://graphviz.org/download/
如果是Linux用戶炭庙,那就比較方便了,直接通過命令安裝即可:
$ sudo apt install graphviz # Ubuntu
$ sudo apt install graphviz # Debian
至此煌寇,Graphviz就已經(jīng)安裝好了焕蹄。我們通過它來實(shí)現(xiàn)決策樹的可視化吧,在IrisDecisionTree
下添加如下show_result_by_graphviz
方法:
"""
Explain:通過graphviz實(shí)現(xiàn)決策樹的可視化
"""
def show_result_by_graphviz(self, x_data, y_label):
clf = DecisionTreeClassifier().fit(x_data, y_label)
iris_dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True, rounded=True,
special_characters=True)
import graphviz
graph = graphviz.Source(iris_dot_data); graph.render("iris")
運(yùn)行之后會在當(dāng)前目錄下生成一個pdf文件阀溶,其中就是可視化之后的決策樹腻脏。注意:以上只是實(shí)現(xiàn)簡單的鳶尾花的決策樹分類,讀者可通過調(diào)解DecisionTreeClassifier
的參數(shù)構(gòu)建不同的決策樹银锻,以此來判別各個決策樹的優(yōu)劣永品。
以上就是本篇全部內(nèi)容了,決策樹的相關(guān)內(nèi)容暫時就更新到這了击纬,其他內(nèi)容像過擬合鼎姐、剪枝等等以后有時間再更新,下期的機(jī)器學(xué)習(xí)系列文章就是肝SVM的非線性模型了更振。
就不嘮嗑了~~~
我是Taoye炕桨,愛專研,愛分享肯腕,熱衷于各種技術(shù)献宫,學(xué)習(xí)之余喜歡下象棋、聽音樂乎芳、聊動漫遵蚜,希望借此一畝三分地記錄自己的成長過程以及生活點(diǎn)滴,也希望能結(jié)實(shí)更多志同道合的圈內(nèi)朋友奈惑,更多內(nèi)容歡迎來訪微信公主號:玩世不恭的Coder吭净。
參考資料:
<font style="font-size:13px;opacity:0.7">[1] 《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》:Peter Harrington 人民郵電出版社</font>
<font style="font-size:13px;opacity:0.7">[2] 《統(tǒng)計學(xué)習(xí)方法》:李航 第二版 清華大學(xué)出版社</font>
<font style="font-size:13px;opacity:0.7">[3] 《機(jī)器學(xué)習(xí)》:周志華 清華大學(xué)出版社</font>
<font style="font-size:13px;opacity:0.7">[4] Python Extension Packages:https://www.lfd.uci.edu/~gohlke/pythonlibs/#wordcloud</font>
<font style="font-size:13px;opacity:0.7">[5] sklearn.tree.DecisionTreeClassifier:https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html</font>
<font style="font-size:13px;opacity:0.7">[6] Graphviz官網(wǎng):https://graphviz.org/</font>
推薦閱讀
《Machine Learning in Action》—— Taoye給你講講決策樹到底是支什么“鬼”
《Machine Learning in Action》—— 剖析支持向量機(jī),優(yōu)化SMO
《Machine Learning in Action》—— 剖析支持向量機(jī)肴甸,單手狂撕線性SVM
print( "Hello寂殉,NumPy!" )
干啥啥不行原在,吃飯第一名
Taoye滲透到一家黑平臺總部友扰,背后的真相細(xì)思極恐
《大話數(shù)據(jù)庫》-SQL語句執(zhí)行時,底層究竟做了什么小動作庶柿?
那些年村怪,我們玩過的Git,真香
基于Ubuntu+Python+Tensorflow+Jupyter notebook搭建深度學(xué)習(xí)環(huán)境
網(wǎng)絡(luò)爬蟲之頁面花式解析
手握手帶你了解Docker容器技術(shù)