決策樹
概述:通過分析每個數據特徵項在分類過程中所起到的所用比重,將數據劃分爲幾個數據子集,如果某個數據子集數據同一類型,則無需再繼續劃分數據分類,如果不屬於同一分類,則需要在對數據子集進行分割。
優點:計算複雜度不高。
缺點:可能會出現由於樣本特徵值對應的樣本數量不統一導致結果偏向於數量多的樣本對應的分類。
具體分類算法 ID3算法
在每次劃分數據集時我們會取一個特徵屬性來進行劃分,那麼這裏有一個問題,例如訓練樣本里面有20個特徵值,我們如何從這些特徵中選擇當前用於劃分的最適合的特徵項呢?我們需要找到一個有效的量化方法來劃分數據。
信息熵
樣本id | 是否需要浮出水面 | 是否有腳蹼 | 屬於魚 |
---|---|---|---|
1 | 是 | 是 | 是 |
2 | 是 | 是 | 是 |
3 | 是 | 否 | 是 |
4 | 否 | 是 | 否 |
5 | 否 | 是 | 否 |
我們劃分數據的原則是將無須的數據變得更加有序,這裏有一個信息增益的概念:即數據在劃分前後的信息發生的變化成爲信息增益。因此我們需要計算每個特徵值的數據劃分之後的信息增益值,選取最大的信息增益特徵值作爲當前數據劃分特徵。集合信息的度量方式成爲熵,我們需要計算我們的信息集合熵值。
熵的計算公式:
計算信息熵算法
#-*- coding=utf-8 -*-
from numpy import *
from math import log
import operator
def calcShannonEnt(dataSet):
labelCounts = {} #統計每個標籤的次數
for line in dataSet:
label = line[-1]
labelCounts[label] = labelCounts.get(label, 0) + 1
shannonEnt = 0.0
#統計信息熵
for key in labelCounts:
prob = float(labelCounts[key])/len(dataSet)
shannonEnt -= prob * log(prob, 2)
return shannonEnt
數據劃分code
由於是決策樹因此我們需要將集合劃分爲不同的子集
#例如上面的通過是否浮出水面劃分數據就能得到兩個子集
#子集1
#是否有腳蹼|屬於魚類
#是 | 是
#是 | 是
#否 | 是
#子集2
#是 | 否
#是 | 否
#axis表示特徵項,value表示需要匹配的特徵值
def splitDataSet(dataSet, axis, value):
resultDataSet = []
for line in dataSet:
if line[axis] == value:
reduceValue = line[:axis]
reduceValue.extend(line[axis+1 : ])
resultDataSet.append(reduceValue)
return resultDataSet
選擇最優的劃分屬性
由於有上面兩個方法作爲基礎,我們可以通過下面的代碼來尋找最適合劃分的特徵值
def chooseBestSplitFeature(dataSet):
featureCount = len(dataSet[0]) - 1
#計算當前的熵值
currentShannonEnt = calcShannonEnt(dataSet)
#定義信息增益
bestInfoGain = 0.0
#定義返回值
bestAxis = -1
#遍歷每個特徵項
for i in range(featureCount):
featureValues = set([value[i] for value in dataSet])
subShannonEnt = 0.0
for featureValue in featureValues:
subDataSet = splitDataSet(dataSet, i, featureValues)
subDataSetProb = float(subDataSet)/len(dataSet)
subShannonEnt += subDataSetProb * calcShannonEnt(subDataSet)
subInfoGain = currentShannonEnt - subShannonEnt
#判斷當先最優的劃分特徵
if subInfoGain > bestInfoGain :
bestInfoGain = subInfoGain
bestAxis = i
return bestAxis
構建決策樹
我們可以通過一個map來表示我們的決策樹
def createTree(dataSet):
classList = [value[-1] for value in dataSet]
#如果所有的classList一致則直接返回
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1:
##這裏的case場景是,已經沒有可用的特徵項時,針對剩餘的分類如何決定目標分類
#return dataSet[0]
#這裏採用取佔比最多的值
return major(dataSet)
#選擇分類屬性
axis = chooseBestSplitFeature(dataSet)
#定義tree樹 屬性 index->Map
myTree = {axis:{}}
featureList = set([value[axis] for value in dataSet])
for feature in featureList :
mytree[axis][feature] = createTree(splitDateSet(dataSet, axis, feature))
return mytree
#去佔比最多的分類值
def major(dataSet):
labelCount={}
for line in dataSet:
labelCount[line[-1] = labelCount.get(line[-1], 0) + 1
#排序
return sorted(labelCount.iteritems(), key = operator.itemgetter(1), reverse=True)[0][0]
關於python的序列化
決策樹構建完成之後,我們可以將決策樹序列化到一個文件中,以便以後直接使用,這裏就涉及到python的序列化模塊pickle
import pickle
def storeTree(myTree, fileName):
fw = open(fileName, 'w')
pickle.dumps(myTree, fw)
fw.close()
def readTree(fileName):
fr = open(fileName)
return pickle.load(fr)