◆版權聲明:本文出自胖喵~的博客,轉載必須注明出處。
轉載請注明出處:https://www.cnblogs.com/by-dream/p/10088976.html
前言
之前在測試建模分析中講過決策樹的概念,這里要說的機器學習的決策樹在構建上和最終目的與之前的決策樹是有一些不同的,但是同時他們又有很多的相似性,具體可以往下看。
機器學習可以分為四大塊:
classification (分類),
regression (回歸),
clustering (聚類),
dimensionality reduction (降維)
前面我們已經講過利用最小二乘解決線性回歸的問題,而這里講的決策樹是可以解決分類的問題。
使用場景
這里舉個實際的例子來說明決策樹的使用場景。
例如中關村某商家記錄了之前消費者購買電腦的記錄,如下:
| ID | 年齡 | 收入 | 學生 | 芝麻信譽等級 | 最終是否買了電腦 |
| 1 | 年輕 | 高 | 否 | 低 | 否 |
| 2 | 年輕 | 高 | 否 | 高 | 否 |
| 3 | 中年 | 高 | 否 | 低 | 是 |
| 4 | 老年 | 中 | 否 | 低 | 是 |
| 5 | 老年 | 低 | 是 | 低 | 是 |
| 6 | 老年 | 低 | 是 | 好 | 否 |
| 7 | 中年 | 低 | 是 | 好 | 是 |
| 8 | 年輕 | 中 | 否 | 低 | 否 |
| 9 | 年輕 | 低 | 是 | 低 | 是 |
| 10 | 老年 | 中 | 是 | 低 | 是 |
| 11 | 年輕 | 中 | 是 | 好 | 是 |
| 12 | 中年 | 中 | 否 | 好 | 是 |
| 13 | 中年 | 高 | 是 | 低 | 是 |
| 14 | 老年 | 中 | 否 | 好 | 否 |
這里他已經有了一些歷史的數據,他希望根據這些歷史的數據來預測當有一個人已經他的一些基本情況,能否預測出他是否要買電腦。假如我們根據之前的經驗畫出了這個決策數。如下:

當然這個是我們自己憑感覺去畫的這顆決策樹,但是也算是一顆比較完整的決策樹了,當有一個新的用戶來了后,我們就可以按照上面划分,從跟節點一直遍歷到子節點,最終到達葉子節點最底端,從而得到結論。看到這里有些人可能就會有疑問了,那么我究竟應該怎么選取跟節點呢?也就是說,我第一步究竟用“年齡”“收入”或者其他條件哪個做為跟節點先進行分離的話,我構造的決策樹越簡單明了呢? 往下看
信息增益
前面我們介紹過信息熵(entropy)的概念,信息熵大小代表信息的不確定的大小,信息熵越大,信息不確定性越高,信息熵越小,不確定性越小。
這里我們就用這個概念來決定使用哪個條件作為我們的跟節點,具體的思路是:首先根據已有的數據算出用戶是否會購買電腦的信息熵,然后加入一個條件,例如年齡,計算出不同年齡的人購買電腦的信息熵,然后用之前的信息熵減去這個信息熵,這里得到的差值的意義是:年齡這個條件的引入帶來的信息混亂程度的大小。這里我們需要思考下,我們首選的跟節點肯定希望是划分之后,能夠非常明顯的區分開這兩類東西,而非常明顯的區分開也就是分開之后信息熵需要變小,那么我們就需要尋找一個目前這幾個條件中對信息混亂程度影響最大的那一個。這個差值就叫做信息增益,而我們的目的其實就是找到信息增益最大的那一個。
$信息增益_{年齡} = Info - Info_{年齡} $
上式中的Info代表的就是信息熵。這里我們實際計算一下年齡的信息增益。首先購買電腦的信息熵是(上面一共14個人,9個人買了,5個人沒買):
$Info = -\sum\limits_{i=1}^{n} P(X_{i})\log_{2}P(X_{i} ) = -\frac{9}{14}\log_{2}\frac{9}{14} - \frac{5}{14}\log_{2}\frac{5}{14} = 0.940bits$
不同年齡下的信息熵(5個年輕人中2個買3個不買,4個中年人都買,5個老年人3個買2個不買)是:
$Info_{年齡} = \frac{5}{14}(-\frac{2}{5}\log_{2}\frac{2}{5}-\frac{3}{5}\log_{2}\frac{3}{5})+\frac{4}{14}(-\frac{4}{4}\log_{2}\frac{4}{4} - \frac{0}{4}\log_{2}\frac{0}{4})+\frac{5}{14}(-\frac{3}{5}\log_{2}\frac{3}{5} - \frac{2}{5}\log_{2}\frac{2}{5}) = 0.694bits$
以此類推我們算出其他幾個條件的信息增益為:Gain(收入)=0.029 Gain(學生)=0.151 Gain(信譽)=0.048$
因此根據按照信息增益最大進行分裂,我們就選擇了年齡來做為跟節點進行划分。
連續型變量
有些細心的人可能已經發現,這里我的所有的條件(其實應該叫features)都是可以枚舉的,也就是有限的,但實際過程中其實像年齡這樣的字段很有可能是17、18、20、26等這樣的值,那么這種情況下,我們肯定不能在划分決策樹的時候將所有的年齡都當作一個分枝來划分開,這樣的話就會過擬合。因此我們需要選一些關鍵的分割點將年齡划分開,那么這個又是怎么划分的呢?其實也是求信息熵,具體就是我們需要找到一個分割點m,當m點的信息熵比m+1和m-1兩邊的信息熵都小的時候我們就將這個點做為切分點來划分。通常做法可能是通過二分法來找這個點。求熵的過程我這里就不再贅述了。
構造決策樹
其實通過前面的講述,我們已經基本掌握了如何去構造一顆決策樹,這里我再列一下這個過程:
1、所有記錄看作一個節點;
2、遍歷每種特征的分隔方式,找到最好的分隔點;
3、將數據分隔為兩個節點部分N1和N2;
4、對N1和N2分別繼續執行2-3步,直到每個節點中的項滿足停止條件:
決策樹算法的停止條件:
1、當每個子節點只有一種類型的時候停止構建;
2、當前節點中記錄數小於某個閥值,同時迭代次數達到給定值時,停止構建過程;
我們在實際構造決策樹的過程中很容易造成樹枝過多,過擬合的問題。比如上面提到的第一停止的方法。一般會采取剪枝法對生成的決策樹進行剪枝。剪枝分為“前剪枝”和“后剪枝”。前剪枝是在決策樹構造的時候就開始進行,例如上面提到的第二種停止的方法,后剪枝是在決策樹生成完成之后進行的剪枝,具體這里也先暫時不展開討論了,有興趣的同學可以再查閱相關的資料。
常見的決策樹模型有:ID3、C4.5、CART。其實我們上面講到的利用信息增益來生成決策樹就是ID3模型,另外兩個模型和ID3都是貪心的思路,區別就是使用的是其他條件來決定跟節點的選取,這里不敢再展開討論了,否則感覺這篇文章寫不完了。后面我可以單獨再針對這兩個算法看有沒有時間單獨再寫個文章。
最后我們說一下決策樹的優缺點
優點:
1、決策樹算法中學習簡單的決策規則建立決策樹模型的過程非常容易理解;
2、決策樹模型可以可視化,非常直觀;
3、應用范圍廣,可用於分類和回歸(CART),而且非常容易做多類別的分類;
4、能夠處理數值型和連續的樣本特征
缺點:
1、很容易在訓練數據中生成復雜的樹結構,造成過擬合(overfitting)。(剪枝可以緩解過擬合的負作用,常用方法是限制樹的高度、葉子節點中的最少樣本數量。)
2、決策樹是基於啟發式的貪心算法建立的,這種算法不能保證建立全局最優的決策樹。(隨機森林Random Forest 引入隨機能緩解這個問題。)
代碼
如果理解了上面的過程去實現一個決策樹的算法代碼其實並不是很難,但是這里我不打算說這個代碼的事情,因為有很多優秀的三方庫已經幫我們做好了這個的封裝。例如scikit-learn,安裝過程可以自行搜索,我這里也先不說了。我說下在程序處理過程中的一個小步驟。
one-hot編碼
我們在使用三方庫調用的時候,在傳入參數的時候不能直接傳入青年,中年這樣的內容,因為機器是無法識別的,那么針對枚舉這樣的類型需要怎么來搞呢?特征工程中講到我們可以將年齡划分為青年、中年、老年那么青年的表示方法就是100,中年是010,老年是001。也就是將之前是一個字段的裂變成來三個字段以0、1的形式傳遞給算法去進行處理。好看代碼:
# -×- encoding=utf-8 -*- from sklearn.feature_extraction import DictVectorizer import csv from sklearn import preprocessing from sklearn import tree from sklearn.externals.six import StringIO # 從csv中將上面例子中的數據讀出來 allElectronicsData = open('play.csv','rb') reader = csv.reader(allElectronicsData) header=reader.next() print(header) featureList=[] labelList=[] for row in reader: labelList.append(row[len(row)-1]) rowDict={} for i in range(1,len(row)-1): rowDict[header[i]]=row[i] featureList.append(rowDict) # featureList 構造出來是一個類似jsonArray的東西 print(featureList) # 轉為one-hot編碼 vec = DictVectorizer() dumpyX = vec.fit_transform(featureList).toarray() print("dunmpyX "+ str(dumpyX) ) print("feature_name"+str(vec.get_feature_names())) print("labelList "+ str(labelList)) #vectorize class lables lb = preprocessing.LabelBinarizer() dummyY = lb.fit_transform(labelList) print("dummyY:" +str(dummyY)) # 注意這里entropy就是代表使用信息增益來構造決策樹 clf = tree.DecisionTreeClassifier(criterion='entropy') clf = clf.fit(dumpyX,dummyY) #構造決策樹 # 打印構造決策樹采用的參數 print("clf : "+str(clf)) # 驗證數據,取一行數據,修改幾個屬性預測結果 oneRowX=dumpyX[0,:] print("oneRowX: "+str(oneRowX)) newRowX = oneRowX newRowX[0]=1 newRowX[2]=0 print("newRowX:"+str(newRowX)) predictedY = clf.predict(newRowX) print("predictedY:"+str(predictedY))
在寫本篇文章的時候,越寫越發現里面的東西比較多,如果全部展開講可能會有非常多的內容,所以很多地方都是簡略帶過,所以如果想了解細節知識的讀者可以下來再單獨研究~
