用Python構(gòu)建和可視化決策樹
決策樹
決策樹是當(dāng)今最強(qiáng)大的監(jiān)督學(xué)習(xí)方法的組成部分。決策樹基本上是一個(gè)二叉樹的流程圖,其中每個(gè)節(jié)點(diǎn)根據(jù)某個(gè)特征變量將一組觀測值拆分。
決策樹的目標(biāo)是將數(shù)據(jù)分成多個(gè)組,這樣一個(gè)組中的每個(gè)元素都屬于同一個(gè)類別。決策樹也可以用來近似連續(xù)的目標(biāo)變量。在這種情況下,樹將進(jìn)行拆分,使每個(gè)組的均方誤差最小。
決策樹的一個(gè)重要特性是它們很容易被解釋。你根本不需要熟悉機(jī)器學(xué)習(xí)技術(shù)就可以理解決策樹在做什么。決策樹圖很容易解釋。
利弊
決策樹方法的優(yōu)點(diǎn)是:
- 決策樹能夠生成可理解的規(guī)則。
- 決策樹在不需要大量計(jì)算的情況下進(jìn)行分類。
- 決策樹能夠處理連續(xù)變量和分類變量。
- 決策樹提供了一個(gè)明確的指示,哪些字段是最重要的。
決策樹方法的缺點(diǎn)是:
- 決策樹不太適合于目標(biāo)是預(yù)測連續(xù)屬性值的估計(jì)任務(wù)。
- 決策樹在類多、訓(xùn)練樣本少的分類問題中容易出錯(cuò)。
- 決策樹的訓(xùn)練在計(jì)算上可能很昂貴。生成決策樹的過程在計(jì)算上非常昂貴。在每個(gè)節(jié)點(diǎn)上,每個(gè)候選拆分字段都必須進(jìn)行排序,才能找到其最佳拆分。在某些算法中,使用字段組合,必須搜索最佳組合權(quán)重。剪枝算法也可能是昂貴的,因?yàn)樵S多候選子樹必須形成和比較。
Python決策樹
Python是一種通用編程語言,它為數(shù)據(jù)科學(xué)家提供了強(qiáng)大的機(jī)器學(xué)習(xí)包和工具。在本文中,我們將使用python最著名的機(jī)器學(xué)習(xí)包scikit-learn來構(gòu)建決策樹模型。我們將使用scikit learn提供的“DecisionTreeClassifier”算法創(chuàng)建模型,然后使用“plot_tree”函數(shù)可視化模型。
步驟1:導(dǎo)入包
我們構(gòu)建模型的主要軟件包是pandas、scikit learn和NumPy。按照代碼在python中導(dǎo)入所需的包。
- import pandas as pd # 數(shù)據(jù)處理
- import numpy as np # 使用數(shù)組
- import matplotlib.pyplot as plt # 可視化
- from matplotlib import rcParams # 圖大小
- from termcolor import colored as cl # 文本自定義
- from sklearn.tree import DecisionTreeClassifier as dtc # 樹算法
- from sklearn.model_selection import train_test_split # 拆分?jǐn)?shù)據(jù)
- from sklearn.metrics import accuracy_score # 模型準(zhǔn)確度
- from sklearn.tree import plot_tree # 樹圖
- rcParams['figure.figsize'] = (25, 20)
在導(dǎo)入構(gòu)建我們的模型所需的所有包之后,是時(shí)候?qū)霐?shù)據(jù)并對其進(jìn)行一些EDA了。
步驟2:導(dǎo)入數(shù)據(jù)和EDA
在這一步中,我們將使用python中提供的“Pandas”包來導(dǎo)入并在其上進(jìn)行一些EDA。我們將建立我們的決策樹模型,數(shù)據(jù)集是一個(gè)藥物數(shù)據(jù)集,它是基于特定的標(biāo)準(zhǔn)給病人開的處方。讓我們用python導(dǎo)入數(shù)據(jù)!
Python實(shí)現(xiàn):
- df = pd.read_csv('drug.csv')
- df.drop('Unnamed: 0', axis = 1, inplace = True)
- print(cl(df.head(), attrs = ['bold']))
輸出:
- Age Sex BP Cholesterol Na_to_K Drug
- 0 23 F HIGH HIGH 25.355 drugY
- 1 47 M LOW HIGH 13.093 drugC
- 2 47 M LOW HIGH 10.114 drugC
- 3 28 F NORMAL HIGH 7.798 drugX
- 4 61 F LOW HIGH 18.043 drugY
現(xiàn)在我們對數(shù)據(jù)集有了一個(gè)清晰的概念。導(dǎo)入數(shù)據(jù)后,讓我們使用“info”函數(shù)獲取有關(guān)數(shù)據(jù)的一些基本信息。此函數(shù)提供的信息包括條目數(shù)、索引號(hào)、列名、非空值計(jì)數(shù)、屬性類型等。
Python實(shí)現(xiàn):
- df.info()
輸出:
- <class 'pandas.core.frame.DataFrame'>
- RangeIndex: 200 entries, 0 to 199
- Data columns (total 6 columns):
- # Column Non-Null Count Dtype
- --- ------ -------------- -----
- 0 Age 200 non-null int64
- 1 Sex 200 non-null object
- 2 BP 200 non-null object
- 3 Cholesterol 200 non-null object
- 4 Na_to_K 200 non-null float64
- 5 Drug 200 non-null object
- dtypes: float64(1), int64(1), object(4)
- memory usage: 9.5+ KB
步驟3:數(shù)據(jù)處理
我們可以看到像Sex, BP和Cholesterol這樣的屬性在本質(zhì)上是分類的和對象類型的。問題是,scikit-learn中的決策樹算法本質(zhì)上不支持X變量(特征)是“對象”類型。因此,有必要將這些“object”值轉(zhuǎn)換為“binary”值。讓我們用python來實(shí)現(xiàn)
Python實(shí)現(xiàn):
- for i in df.Sex.values:
- if i == 'M':
- df.Sex.replace(i, 0, inplace = True)
- else:
- df.Sex.replace(i, 1, inplace = True)
- for i in df.BP.values:
- if i == 'LOW':
- df.BP.replace(i, 0, inplace = True)
- elif i == 'NORMAL':
- df.BP.replace(i, 1, inplace = True)
- elif i == 'HIGH':
- df.BP.replace(i, 2, inplace = True)
- for i in df.Cholesterol.values:
- if i == 'LOW':
- df.Cholesterol.replace(i, 0, inplace = True)
- else:
- df.Cholesterol.replace(i, 1, inplace = True)
- print(cl(df, attrs = ['bold']))
輸出:
- Age Sex BP Cholesterol Na_to_K Drug
- 0 23 1 2 1 25.355 drugY
- 1 47 1 0 1 13.093 drugC
- 2 47 1 0 1 10.114 drugC
- 3 28 1 1 1 7.798 drugX
- 4 61 1 0 1 18.043 drugY
- .. ... ... .. ... ... ...
- 195 56 1 0 1 11.567 drugC
- 196 16 1 0 1 12.006 drugC
- 197 52 1 1 1 9.894 drugX
- 198 23 1 1 1 14.020 drugX
- 199 40 1 0 1 11.349 drugX
- [200 rows x 6 columns]
我們可以觀察到所有的“object”值都被處理成“binary”值來表示分類數(shù)據(jù)。例如,在膽固醇屬性中,顯示“低”的值被處理為0,“高”則被處理為1。現(xiàn)在我們準(zhǔn)備好從數(shù)據(jù)中創(chuàng)建因變量和自變量。
步驟4:拆分?jǐn)?shù)據(jù)
在將我們的數(shù)據(jù)處理為正確的結(jié)構(gòu)之后,我們現(xiàn)在設(shè)置“X”變量(自變量),“Y”變量(因變量)。讓我們用python來實(shí)現(xiàn)
Python實(shí)現(xiàn):
- X_var = df[['Sex', 'BP', 'Age', 'Cholesterol', 'Na_to_K']].values # 自變量
- y_var = df['Drug'].values # 因變量
- print(cl('X variable samples : {}'.format(X_var[:5]), attrs = ['bold']))
- print(cl('Y variable samples : {}'.format(y_var[:5]), attrs = ['bold']))
輸出:
- X variable samples : [[ 1. 2. 23. 1. 25.355]
- [ 1. 0. 47. 1. 13.093]
- [ 1. 0. 47. 1. 10.114]
- [ 1. 1. 28. 1. 7.798]
- [ 1. 0. 61. 1. 18.043]]
- Y variable samples : ['drugY' 'drugC' 'drugC' 'drugX' 'drugY']
我們現(xiàn)在可以使用scikit learn中的“train_test_split”算法將數(shù)據(jù)分成訓(xùn)練集和測試集,其中包含我們定義的X和Y變量。按照代碼在python中拆分?jǐn)?shù)據(jù)。
Python實(shí)現(xiàn):
- X_train, X_test, y_train, y_test = train_test_split(X_var, y_var, test_size = 0.2, random_state = 0)
- print(cl('X_train shape : {}'.format(X_train.shape), attrs = ['bold'], color = 'black'))
- print(cl('X_test shape : {}'.format(X_test.shape), attrs = ['bold'], color = 'black'))
- print(cl('y_train shape : {}'.format(y_train.shape), attrs = ['bold'], color = 'black'))
- print(cl('y_test shape : {}'.format(y_test.shape), attrs = ['bold'], color = 'black'))
輸出:
- X_train shape : (160, 5)
- X_test shape : (40, 5)
- y_train shape : (160,)
- y_test shape : (40,)
現(xiàn)在我們有了構(gòu)建決策樹模型的所有組件。所以,讓我們繼續(xù)用python構(gòu)建我們的模型。
步驟5:建立模型和預(yù)測
在scikit學(xué)習(xí)包提供的“DecisionTreeClassifier”算法的幫助下,構(gòu)建決策樹是可行的。之后,我們可以使用我們訓(xùn)練過的模型來預(yù)測我們的數(shù)據(jù)。最后,我們的預(yù)測結(jié)果的精度可以用“準(zhǔn)確度”評(píng)估指標(biāo)來計(jì)算。讓我們用python來完成這個(gè)過程!
Python實(shí)現(xiàn):
- model = dtc(criterion = 'entropy', max_depth = 4)
- model.fit(X_train, y_train)
- pred_model = model.predict(X_test)
- print(cl('Accuracy of the model is {:.0%}'.format(accuracy_score(y_test, pred_model)), attrs = ['bold']))
輸出:
- Accuracy of the model is 88%
在代碼的第一步中,我們定義了一個(gè)名為“model”變量的變量,我們在其中存儲(chǔ)DecisionTreeClassifier模型。接下來,我們將使用我們的訓(xùn)練集對模型進(jìn)行擬合和訓(xùn)練。之后,我們定義了一個(gè)變量,稱為“pred_model”變量,其中我們將模型預(yù)測的所有值存儲(chǔ)在數(shù)據(jù)上。最后,我們計(jì)算了我們的預(yù)測值與實(shí)際值的精度,其準(zhǔn)確率為88%。
步驟6:可視化模型
現(xiàn)在我們有了決策樹模型,讓我們利用python中scikit learn包提供的“plot_tree”函數(shù)來可視化它。按照代碼從python中的決策樹模型生成一個(gè)漂亮的樹圖。
Python實(shí)現(xiàn):
- feature_names = df.columns[:5]
- target_names = df['Drug'].unique().tolist()
- plot_tree(model,
- feature_names = feature_names,
- class_names = target_names,
- filled = True,
- rounded = True)
- plt.savefig('tree_visualization.png')
輸出:

結(jié)論
有很多技術(shù)和其他算法用于優(yōu)化決策樹和避免過擬合,比如剪枝。雖然決策樹通常是不穩(wěn)定的,這意味著數(shù)據(jù)的微小變化會(huì)導(dǎo)致最優(yōu)樹結(jié)構(gòu)的巨大變化,但其簡單性使其成為廣泛應(yīng)用的有力候選。在神經(jīng)網(wǎng)絡(luò)流行之前,決策樹是機(jī)器學(xué)習(xí)中最先進(jìn)的算法。其他一些集成模型,比如隨機(jī)森林模型,比普通決策樹模型更強(qiáng)大。
決策樹由于其簡單性和可解釋性而非常強(qiáng)大。決策樹和隨機(jī)森林在用戶注冊建模、信用評(píng)分、故障預(yù)測、醫(yī)療診斷等領(lǐng)域有著廣泛的應(yīng)用。我為本文提供了完整的代碼。
完整代碼:
- import pandas as pd # 數(shù)據(jù)處理
- import numpy as np # 使用數(shù)組
- import matplotlib.pyplot as plt # 可視化
- from matplotlib import rcParams # 圖大小
- from termcolor import colored as cl # 文本自定義
- from sklearn.tree import DecisionTreeClassifier as dtc # 樹算法
- from sklearn.model_selection import train_test_split # 拆分?jǐn)?shù)據(jù)
- from sklearn.metrics import accuracy_score # 模型準(zhǔn)確度
- from sklearn.tree import plot_tree # 樹圖
- rcParams['figure.figsize'] = (25, 20)
- df = pd.read_csv('drug.csv')
- df.drop('Unnamed: 0', axis = 1, inplace = True)
- print(cl(df.head(), attrs = ['bold']))
- df.info()
- for i in df.Sex.values:
- if i == 'M':
- df.Sex.replace(i, 0, inplace = True)
- else:
- df.Sex.replace(i, 1, inplace = True)
- for i in df.BP.values:
- if i == 'LOW':
- df.BP.replace(i, 0, inplace = True)
- elif i == 'NORMAL':
- df.BP.replace(i, 1, inplace = True)
- elif i == 'HIGH':
- df.BP.replace(i, 2, inplace = True)
- for i in df.Cholesterol.values:
- if i == 'LOW':
- df.Cholesterol.replace(i, 0, inplace = True)
- else:
- df.Cholesterol.replace(i, 1, inplace = True)
- print(cl(df, attrs = ['bold']))
- X_var = df[['Sex', 'BP', 'Age', 'Cholesterol', 'Na_to_K']].values # 自變量
- y_var = df['Drug'].values # 因變量
- print(cl('X variable samples : {}'.format(X_var[:5]), attrs = ['bold']))
- print(cl('Y variable samples : {}'.format(y_var[:5]), attrs = ['bold']))
- X_train, X_test, y_train, y_test = train_test_split(X_var, y_var, test_size = 0.2, random_state = 0)
- print(cl('X_train shape : {}'.format(X_train.shape), attrs = ['bold'], color = 'red'))
- print(cl('X_test shape : {}'.format(X_test.shape), attrs = ['bold'], color = 'red'))
- print(cl('y_train shape : {}'.format(y_train.shape), attrs = ['bold'], color = 'green'))
- print(cl('y_test shape : {}'.format(y_test.shape), attrs = ['bold'], color = 'green'))
- model = dtc(criterion = 'entropy', max_depth = 4)
- model.fit(X_train, y_train)
- pred_model = model.predict(X_test)
- print(cl('Accuracy of the model is {:.0%}'.format(accuracy_score(y_test, pred_model)), attrs = ['bold']))
- feature_names = df.columns[:5]
- target_names = df['Drug'].unique().tolist()
- plot_tree(model,
- feature_names = feature_names,
- class_names = target_names,
- filled = True,
- rounded = True)
- plt.savefig('tree_visualization.png')