机器学习_决策树_ID3算法_C4.5算法_CART算法及各个算法Python实现
下面的有些敘述基于我個人理解, 可能與專業書籍描述不同, 但是最終都是表達同一個意思, 如果有不同意見的小伙伴, 請在評論區留言, 我不勝感激.
參考:
周志華-機器學習
https://blog.csdn.net/xiaohukun/article/details/78112917
https://blog.csdn.net/fuqiuai/article/details/79456971
決策樹簡介
1. 問題背景
常規方式對樣本進行分類, 會導致很大的性能問題, 樣本需要不斷重復檢測每一項屬性(屬性之間還不能存在相關性), 例如: 人的體重由[年齡, 身高] 決定, 當獲得一組樣本 [20, 178], 可能我們會先將該數據歸到20歲的階段中, 然后判斷身高, 最后判斷體重, 我們也有可能先使用身高, 然后使用年齡, 最后判斷體重, 排列組合的方式得出預測值, 這種毫無準則的選擇判斷會導致很大的計算量; 然而決策樹使用**信息增益(當前屬性對樣本的貢獻率) **, 根據信息增益從大到小選用對應的屬性進行判斷, 有針對性的選擇可以有效地避免無用的計算, 通過選用合適的樣本屬性按照特定的規則進行判斷, 對樣本準確的分類, 現在遇到的問題就是如何構建一棵決策樹, 構建出來的決策樹還能準確預測結果, 下面將針對決策樹定義, 信息量, 建立決策樹需要遵守的規則, 優化等問題來講解.
2. 決策樹定義
決策樹是基于樹結構進行決策, 一棵決策樹包含一個Root根節點(里面包含所有測試集, 測試集從Root處出發, 按照一定的決策方式, 不同的決策將導致每個樣本最終擁有不同的預測結果), 以及若干內部節點和葉子節點, 葉子節點對應最終的決策結果, 每一個內部節點都是一個屬性測試, 從Root到葉子節點的路徑就是一個樣本的決策過程, 當樣本到達葉子節點的時候就是樣本的預測結果.
決策樹如下圖:
Root: 包含所有的樣本集
DECISION: 決策方式
Result: 決策結果
構造決策樹
1. 問題背景
構建決策樹需要有一個構建規則, 不同的構建規則將導致預測結果的不同, 當然, 不同的數據需要使用不同的構建規則, 這樣才能最好地預測出結果, 下面將介紹3中不同的構建規則: 信息增益, 信息增益率, 基尼指數
2. 信息熵
信息熵是度量樣本屬性對最終預測結果的影響程度, 當某一屬性的熵在正常情況下比其他屬性的熵要大的時候, 我們就可以先選用該屬性優先對樣本進行分類(越是影響大的屬性越容易影響預測結果, 越容易預測正確).
信息熵方程定義:
pk: 屬性在樣例中的比例
使用例子說明:
如下圖片, 使用age, income, student, credit屬性來判斷一個人能否買電腦
使用buys_computer計算總的信息熵:
buys_computer包含2個子集 {yes, no}, 正例比例: 9/14, 反例比例: 5/14, 最終得到 Info(D)
同理計算age:
age包含3個子集 {youth, middle_aged, senior}, 分別計算youth, middle_aged, senior各自相對于正反例的比例值, 道理同上, 然后得到 Info_youth(D), Info_middle_aged(D), Info_senior(D).
由于不同子集樣本相對于age數目不同, 所以:
最終age的信息熵計算結果:
3. 信息增益(ID3算法)
信息增益: 樣本利用某一屬性分類之后得到多少樣本, 例如: 樣本使用age屬性分類, Gain(D, age) = 總的熵(即Info(D)) - Info_age(D)
信息增益方程定義:
V代表屬性種類; D^V代表每種屬性對應的樣本數目;
Ent(D)代表Info(D); |D^v|/|D| 就是屬性數目在樣本中權重.
下面直接計算Gain(D, age), 計算age相對于總樣本D的信息增益
在前面信息熵的計算中, 我們已經得出Info(D), Info_age(D), 按照公式只需做差
同理我們可以計算出: Gain(income) = 0.029, Gain(student) = 0.151, Gain(credit_rating)=0.048
因為Gain(age)最大, 就可以得出age對樣本的影響程度最高, 因此構建決策樹的時候, 選用age作為Root, 得出如下決策樹:
注: 在離散型數據下, 使用過的屬性就不再使用, 因為該屬性對樣本的影響在之前已經計算過, 再次使用會導致結果錯誤
這只是決策樹第一層樹的建立, 建立第二層的時候, 同理, 用youth對應的分類樣本來說: 我們接下來從 income, student, credit來進行決策, 在當前樣本正返例比例的基礎上分別計算Info(D’), Info_income(D’), Info_student(D’), Info_credit(D’), 使用Info(D’) - Info_XXX(D’)計算信息增益, 然后從3個屬性中選出信息增益最大的屬性作為下一個節點, 根據該節點的子集進行分類決策. 整個過程是一個遞歸的過程, 不斷重復, 直到如下條件時終止:
- 給定結點的所有樣本屬于同一類(該樣本中全是正例或者反例)
- 沒有剩余屬性可以用來進一步劃分樣本(例如: 假如樣本經過一系列劃分后, 現在只有income, class兩個標簽, 只有income一個屬性, 沒有多余的屬性進行下一步劃分, 劃分停止), 在樣本中可能存在一部分反例, 一部分正例, 此時使用多數表決, 根據正返例個數決定當前葉子節點的類別.
詳細算法流程如下:
算法文字描述:
- 樹以代表訓練樣本的單個結點開始(步驟1)。
- 如果樣本都在同一個類,則該結點成為樹葉,并用該類標號(步驟2 和3)。
- 否則,算法使用稱為信息增益的基于熵的度量作為啟發信息,選擇能夠最好地將樣本分類的屬性(步驟6)。該屬性成為該結點的“測試”或“判定”屬性(步驟7)。在算法的該版本中,所有的屬性都是分類的,即離散值。連續屬性必須離散化。
- 對測試屬性的每個已知的值,創建一個分枝,并據此劃分樣本(步驟8-10)。
- 算法使用同樣的過程,遞歸地形成每個劃分上的樣本判定樹。一旦一個屬性出現在一個結點上,就不必該結點的任何后代上考慮它(步驟13)
注: 上面的決策樹建立方式是ID3算法, 這種算法很容易受信息增益的影響, 當某一屬性對應的樣本分布過于分散, 就比如使用樣本id作為屬性, 那么計算出來的Info_id(D)就會非常大, 對結果造成很大的影響, 下面將介紹信息增益率來減小這種影響
Python實現ID3:
4. 信息增益率(C4.5算法)
相比于ID3算法來說, C4.5算法使用不同的Gain計算方式(計算角度不同, 弱化無關因素對結果預測的影響), 具體Gain計算如下:
信息增益率:
使用age對上面的公式進行解釋:
Gain(D, age) = Info_age(D);
IV(a) = -5/14log(4/14) - 4/14log(4/14) - 5/14log(5/14)
Gain_ratio(D,age) = Info_age(D)/IV(a)
當屬性子集越多, 則IV(a)就會越大, 得到的Gain_ratio就會越小, 從而達到: 避免因為屬性子集過多而導致信息增長過大影響判斷結果.
注:增長率對子集過少的屬性也會有偏好, 就比如id與age, 增長率對age將會表現出偏向, 從而使用age作為節點, 而不是id, 避免結果的預測錯誤.
選用屬性方式:
使用C4.5算法與ID3算法的區別就是選取的Gain不同, 其他操作都是一樣的, 最后的遞歸終止條件與ID3是相同的.
算法描述:
while (當前節點”不純“) (1)計算當前節點的類別信息熵Info(D) (以類別取值計算) (2)計算當前節點各個屬性的信息熵Info(Ai) (以屬性取值下的類別取值計算) (3)計算各個屬性的信息增益Gain(Ai)=Info(D)-Info(Ai) (4)計算各個屬性的分類信息度量H(Ai) (以屬性取值計算) (5)計算各個屬性的信息增益率IGR(Ai)=Gain(Ai)/H(Ai) end while 當前節點設置為葉子節點代碼如下:
# encoding=utf-8import cv2 import time import numpy as np import pandas as pdfrom sklearn.cross_validation import train_test_split from sklearn.metrics import accuracy_score# 二值化 def binaryzation(img):cv_img = img.astype(np.uint8)cv2.threshold(cv_img,50,1,cv2.THRESH_BINARY_INV,cv_img)return cv_imgdef binaryzation_features(trainset):features = []for img in trainset:img = np.reshape(img,(28,28))cv_img = img.astype(np.uint8)img_b = binaryzation(cv_img)# hog_feature = np.transpose(hog_feature)features.append(img_b)features = np.array(features)features = np.reshape(features,(-1,feature_len))return featuresclass Tree(object):def __init__(self,node_type,Class = None, feature = None):self.node_type = node_type # 節點類型(internal或leaf)self.dict = {} # dict的鍵表示特征Ag的可能值ai,值表示根據ai得到的子樹 self.Class = Class # 葉節點表示的類,若是內部節點則為noneself.feature = feature # 表示當前的樹即將由第feature個特征劃分(即第feature特征是使得當前樹中信息增益最大的特征)def add_tree(self,key,tree):self.dict[key] = treedef predict(self,features): if self.node_type == 'leaf' or (features[self.feature] not in self.dict):return self.Classtree = self.dict.get(features[self.feature])return tree.predict(features)# 計算數據集x的經驗熵H(x) def calc_ent(x):x_value_list = set([x[i] for i in range(x.shape[0])])ent = 0.0for x_value in x_value_list:p = float(x[x == x_value].shape[0]) / x.shape[0]logp = np.log2(p)ent -= p * logpreturn ent# 計算條件熵H(y/x) def calc_condition_ent(x, y):x_value_list = set([x[i] for i in range(x.shape[0])])ent = 0.0for x_value in x_value_list:sub_y = y[x == x_value]temp_ent = calc_ent(sub_y)ent += (float(sub_y.shape[0]) / y.shape[0]) * temp_entreturn ent# 計算信息增益 def calc_ent_grap(x,y):base_ent = calc_ent(y)condition_ent = calc_condition_ent(x, y)ent_grap = base_ent - condition_entreturn ent_grap# C4.5算法 def recurse_train(train_set,train_label,features):LEAF = 'leaf'INTERNAL = 'internal'# 步驟1——如果訓練集train_set中的所有實例都屬于同一類Cklabel_set = set(train_label)if len(label_set) == 1:return Tree(LEAF,Class = label_set.pop())# 步驟2——如果特征集features為空class_len = [(i,len(list(filter(lambda x:x==i,train_label)))) for i in range(class_num)] # 計算每一個類出現的個數(max_class,max_len) = max(class_len,key = lambda x:x[1])if len(features) == 0:return Tree(LEAF,Class = max_class)# 步驟3——計算信息增益,并選擇信息增益最大的特征max_feature = 0max_gda = 0D = train_labelfor feature in features:# print(type(train_set))A = np.array(train_set[:,feature].flat) # 選擇訓練集中的第feature列(即第feature個特征)gda = calc_ent_grap(A,D)if calc_ent(A) != 0: ####### 計算信息增益比,這是與ID3算法唯一的不同gda /= calc_ent(A)if gda > max_gda:max_gda,max_feature = gda,feature# 步驟4——信息增益小于閾值if max_gda < epsilon:return Tree(LEAF,Class = max_class)# 步驟5——構建非空子集sub_features = list(filter(lambda x:x!=max_feature,features))tree = Tree(INTERNAL,feature=max_feature)max_feature_col = np.array(train_set[:,max_feature].flat)feature_value_list = set([max_feature_col[i] for i in range(max_feature_col.shape[0])]) # 保存信息增益最大的特征可能的取值 (shape[0]表示計算行數)for feature_value in feature_value_list:index = []for i in range(len(train_label)):if train_set[i][max_feature] == feature_value:index.append(i)sub_train_set = train_set[index]sub_train_label = train_label[index]sub_tree = recurse_train(sub_train_set,sub_train_label,sub_features)tree.add_tree(feature_value,sub_tree)return treedef train(train_set,train_label,features):return recurse_train(train_set,train_label,features)def predict(test_set,tree):result = []for features in test_set:tmp_predict = tree.predict(features)result.append(tmp_predict)return np.array(result)class_num = 10 # MINST數據集有10種labels,分別是“0,1,2,3,4,5,6,7,8,9” feature_len = 784 # MINST數據集每個image有28*28=784個特征(pixels) epsilon = 0.001 # 設定閾值if __name__ == '__main__':print("Start read data...")time_1 = time.time()raw_data = pd.read_csv('../data/train.csv', header=0) # 讀取csv數據data = raw_data.valuesimgs = data[::, 1::]features = binaryzation_features(imgs) # 圖片二值化(很重要,不然預測準確率很低)labels = data[::, 0]# 避免過擬合,采用交叉驗證,隨機選取33%數據作為測試集,剩余為訓練集train_features, test_features, train_labels, test_labels = train_test_split(features, labels, test_size=0.33, random_state=0)time_2 = time.time()print('read data cost %f seconds' % (time_2 - time_1))# 通過C4.5算法生成決策樹print('Start training...')tree = train(train_features,train_labels,list(range(feature_len)))time_3 = time.time()print('training cost %f seconds' % (time_3 - time_2))print('Start predicting...')test_predict = predict(test_features,tree)time_4 = time.time()print('predicting cost %f seconds' % (time_4 - time_3))# print("預測的結果為:")# print(test_predict)for i in range(len(test_predict)):if test_predict[i] == None:test_predict[i] = epsilonscore = accuracy_score(test_labels, test_predict)print("The accruacy score is %f" % score)5. 基尼指數(CART決策樹)
CART決策樹使用基尼指數(所謂的信息熵)作為判斷屬性是否可以作為劃分依據,
特點:
基尼指數計算:
Gini(D)描述從樣本中隨機抽取兩個樣本, 不一樣概率的大小, Gini越大說明該屬性的樣本越少, 因此該屬性對樣本的總影響就越小.
a屬性對樣本預測結果的影響, 影響越大,Gini_index(D, a)值越小,反之,則GINI值越大.
其中,pk表示節點中屬于類k的概率.
下面使用一個例子說明:
按照職業屬性劃分, 預測是否結婚
預測是否結婚:
Gini(married, occupation) = 3/7[1? (2/3)^2 ? (1/3)^2] + 4/7[1 ? (3/4)^2 ? (1/4)^2]=0.4
同理可計算看電視時間, 年齡對是否結婚的Gini
CART決策樹算法流程:
每次劃分的時候, 需要選擇基尼指數最小的屬性作為最優劃分屬性
Python使用sklearn代碼實現CRAT:
總結:
三種算法各有自己的Grain計算方式, 構建樹的過程, 區別就在于Grain計算的不同, 最優屬性選擇的不同, 剩下都是相同的.
如果有什么問題, 請在下方留言, 感覺不錯的話, 請為我點個贊, O(∩_∩)O謝謝
總結
以上是生活随笔為你收集整理的机器学习_决策树_ID3算法_C4.5算法_CART算法及各个算法Python实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python爬虫用什么软件写_pytho
- 下一篇: python怎么数据归一化_基于数据归一