決策樹是監督學習算法之一,而且是一種基本的分類與迴歸方法;決策樹也分爲迴歸樹和分類樹,本文討論的是分類樹。若是瞭解或者學過數據結構,確定對"樹"這個概念是不陌生的,在此基礎上學習掌握決策樹也會更加容易,下面經過一個小例子幫助理解何爲決策樹。python
下圖所示流程圖即爲一個決策樹,矩形表明判斷模塊、橢圓形則表明終止模塊,表示已經得出結論能夠終止程序的運行;左右箭頭表示分支,能夠經過它到達另外一判斷模塊或終止模塊。
這個流程圖主要是假想一個擇偶系統,以前網上不流行這樣一句話嘛,"阿姨我不想努力了",該樹就以是否想繼續努力爲判斷依據,若是你不想繼續努力了,你能夠選擇找一個"富婆";反之,你想找一個女友一塊兒奮鬥,這裏又以女孩的性格爲判斷依據,若是喜歡性格溫柔的,即選擇"溫柔女孩",若喜歡性格高冷的,則選擇"酷女孩"。算法
整個決策樹能夠當作一個if—then規則,即"若是判斷條件,則……",而且須要注意如下三點:數據結構
構造決策樹的數據必需要充足,特徵較少的數據集可能會致使決策樹的正確率偏低。若數據特徵過多,不會選擇特徵也會影響決策樹的正確率。構建一個比較理想的決策樹,大體可分爲如下三步:特徵選擇、決策樹的生成與決策樹的修剪。ide
特徵選擇即決定用數據集中哪個特徵劃分特徵空間,主要在於選取對訓練數據具備分類能力的特徵。這樣作的目的是提升決策樹的效率,若是一個用特徵的分類結果和隨機分類的結果差異不大,那麼這個特徵是沒有什麼分類能力的,通常地講這類特徵去除對決策樹的精度影響不大。函數
下面表格是一份關於海洋生物的數據,數據的兩個特徵分別爲no surfacing(不浮出水面是否能生存)、flippers(是否有腳蹼),一個類標籤fish(是否爲魚類)。學習
no surfacing | flippers | fish |
---|---|---|
是 | 是 | 是 |
是 | 是 | 是 |
是 | 否 | 否 |
否 | 是 | 否 |
否 | 是 | 否 |
不論一個數據集有多少特徵,每次劃分數據集時只能選一個特徵,那麼第一次選擇哪一個特徵做爲劃分的參考屬性才能將數據更快的分類呢?答案必定是必定是分類能力最好的那個特徵,但問題來了,如何判斷哪個特徵分類能力最好呢?這時就要引入一個新的概念——信息增益。測試
什麼是信息增益呢?在劃分數據集以前以後信息發生的變化成爲信息增益,知道如何計算信息增益,咱們就能夠計算每一個特徵值劃分數據集得到的信息增益,得到信息增益最高的特徵就是最好的選擇。code
在計算信息增益以前,須要先知道"熵"這個概念,"熵"到底是什麼東西,沒必要去深究它,咱們只需瞭解熵定義爲信息的指望值,在信息論與機率統計中,熵是表示隨機變量不肯定性的度量,用一句通俗的話講就是這個體系的混亂程度是如何的。blog
假設有一個樣本爲n的數據集,第i類樣本爲Xi,那麼符號Xi的信息可定義:
其中其中p(Xi)是選擇該分類的機率。經過改變i的值便可得到數據集中全部類別的信息。遞歸
爲了計算熵,咱們須要計算全部類別全部可能值包含的信息指望值(數學指望),經過下面的公式獲得:
熵越高,變量的不肯定性越大,也就是數據的混合程度越高。
在建立數據集以前,對數據進行一下自定義簡化,用1表明"是",0表明"否"。
計算熵代碼以下:
#建立數據集 def createDataSet(): DataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] #矩陣轉化Dataframe DataSet = pd.DataFrame(DataSet,columns=['no surfacing','flippers','fish']) return DataSet #計算信息熵 def calculatent(DataSet): #統計類標籤的分類 fish_type = DataSet['fish'].value_counts() ''' no 3 yes 2 ''' #獲取樣本個數 m = DataSet.shape[0] #每一個分類的機率,即p(Xi) p = fish_type/m #計算熵值 ent = (-p*np.log2(p)).sum() return ent ''' 0.9709505944546686 '''
最後返回熵爲0.9709505944546686
這份數據中,最後的類標籤共有兩類"是魚類"、"不是魚類",前者佔總標籤個數的2/5,後者佔3/5。
因此這部分代碼的核心部分即套用求熵公式:
在獲得熵以後,就能夠按照最大信息增益的方法劃分數據集,下一部分開始計算信息增益。
計算過熵後,怎麼計算信息增益呢?信息增益的計算就是將父節點的熵減去其下全部子節點的熵之和,而且在求和時,因爲類別比重不一樣,須要對其實現加權平均。
以"no surfacing"這一列舉例,5個樣本中,"1"有3個,"0"有2個,因此兩者的權重一個爲3/5,另外一個爲2/5;
其中對於"1"這三個樣本,樣本標籤fish中的"是"有兩個,"否"有一個,因此分類的機率分別爲2/3和1/3,同理對於"0"這兩個樣本,樣本標籤fish都爲"否",因此分類機率爲1。
這一列的信息增益計算公式以下:
兩個特徵的信息增益計算結果以下:
計算每一個特徵信息增益的目的就是要選擇出每次分類時當前的最優特徵,因此必定會有一個比較過程,便於獲得最大的信息增益,而且返回該特徵的索引,而後就能夠利用這個最優特徵對數據集進行切割。
因此這裏構造兩個函數,一個選擇最優特徵函數,另外一個是分割數據集函數,具體代碼以下:
#最優特徵函數 def ChooseBF(DataSet): #獲取基礎信息熵 all_ent = calculatent(DataSet) BestGain = 0 #初始化一個信息增益 col = -1 #初始化一個最優特徵索引 for i in range(DataSet.shape[1]-1): #遍歷全部特徵 #獲取第i列的分類 index_ = DataSet.iloc[:,i].value_counts().index child_ent = 0 #初始化某列的信息熵 for j in index_: #將第i列按照類別分類 child_DataSet = DataSet[DataSet.iloc[:,i]==j] #計算某一類別的信息熵 ent = calculatent(child_DataSet) #將全部類別信息熵加權求和 child_ent += (child_DataSet.shape[0]/DataSet.shape[0])*ent print("第%s列的熵爲%s"%(i,child_ent)) TheGain = all_ent-child_ent #獲取信息增益 print("第%s列的信息增益爲%s"%(i,TheGain)) #得出最優特徵索引 if(TheGain>BestGain): BestGain = TheGain col = i print("最優特徵索引爲:%s"%col) return col
這個函數兩次調用計算信息熵函數calculatent,一次是爲了獲取基礎信息熵,即上面公式中ent('總');另外一次則是計算特徵中不一樣類別的信息熵,即上面公式中ent('是')、ent('否')。
代碼運行截圖以下:
返回的最優特徵索引爲0,也就是"no surfacing"這一列;而且兩列的信息增益都與上文手寫計算的結果一致,因此代碼是徹底沒有問題的。
下一步,利用最優特徵對數據集進行劃分,代碼以下:
#切分數據集函數 def splitSet(DataSet,col,value): #找到最優特徵索引標籤 index = (DataSet.iloc[:,col]).name #切分數據集,並把當前最優特徵刪去 resetdata = DataSet[DataSet.iloc[:,col]==value].drop(index,axis = 1) return resetdata
這個函數須要傳入三個參數,分別是須要切分的數據集、最優特徵索引、特徵中需保留的分類,可能有的人尚未理解這個value的做用,對比一下運行結果就懂了。
''' col = 0,value = 1 flippers fish 0 1 yes 1 1 yes 2 0 no col = 0,value = 0 flippers fish 3 1 no 4 1 no '''
上面兩個DataFrame,是分割數據集函數返回的結果,當value = 1時,保留下來的這三個樣本,都是"no surfacing"這一特徵中值爲1的;而當value = 0時,保留下來的兩個樣本,就是"no surfacing"這一特徵中值爲0的,這樣一對比就很容易理解value的做用了。
至此熵與信息增益的計算方法大體上已經介紹完畢,文中所取數據集特徵數不多,因此致使數據集分類次數也會不多,當數據特徵比較多時,通過第一次劃分以後,數據集向下傳遞到決策樹的分支的下一個結點,在這個結點上,咱們能夠再次劃分數據,所以須要採用遞歸的原則處理數據集。
下文將會介紹如何用遞歸構建決策樹以及決策樹的可視化,畢竟決策樹一個很大的優勢就是經過可視化更好的理解數據是如何一步一步劃分的。
公衆號【奶糖貓】後臺回覆「信息增益」便可獲取源碼供參考,感謝閱讀。