機器學習中的那些樹——決策樹(二)

前言

在上篇文章中介紹了決策樹的一些基本概念,在這篇文章中,將介紹決策樹ID3和C4.5算法的代碼實現以及一些優化。html

ID3實現

ID3算法的核心是在決策樹各個節點上應用信息增益準則選擇特徵,遞歸地構建決策樹,算法僞代碼在上篇文章中給出了。在這裏將給出其代碼實現。python

代碼

import pandas as pd
from math import log

def load(filename):
	'''
		input: 文件
		output: DataFrame數據
	'''
	df = pd.read_csv(filename)
	return df

def calEnt(df):
	'''
		input: 數據集
		output: 熵
		descripion: 計算給定數據集的熵
	'''
	# 返回數據行數
	numEntries = df.shape[0]
	# 字典,用於保存每一個標籤(label)出現次數
	labelCounts = {}
	cols = df.columns.tolist()
	# 獲取標籤
	classlabel = df[cols[-1]].tolist()
	# 對每組特徵向量進行統計
	for currentlabel in classlabel:
		if currentlabel not in labelCounts.keys():
			labelCounts[currentlabel] = 1
		else:
			labelCounts[currentlabel] += 1

	Ent = 0.0
	# 計算熵
	for key in labelCounts:
		prob = labelCounts[key]/numEntries
		Ent -= prob*log(prob, 2)

	return Ent

def splitDatsSet(df, axis, value):
	'''
		input: 數據集,所佔列,選擇值
		output: 劃分數據集
		description: 按照給定特徵劃分數據集;選擇所佔列中等於選擇值的項
	'''
	cols = df.columns.tolist()
	axisFeat = df[axis].tolist()
	# L = []
	# L.append(axis)

	# retDataset = df[list(set(cols)-set(L))]
	retDataset = pd.concat([df[feaVec] for feaVec in cols if feaVec != axis], axis=1)

	i = 0
	# 須要丟棄的行
	dropIndex = []
	for feaVec in axisFeat:
		if feaVec != value:
			dropIndex.append(i)
			i += 1
		else:
			i += 1
	# 劃分數據集
	newDataset = retDataset.drop(dropIndex)
	return newDataset.reset_index(drop=True)


def chooseBestFeatureToSplit(df):
	'''
		input: 數據集
		output: 最優特徵和信息增益
	'''
	# 獲取特徵數量
	numFeatures = len(df.columns) - 1
	# 計算熵
	Ent = calEnt(df)
	# 信息增益
	bestInfoGain = 0.0
	# 最優特徵索引值
	bestFeature = -1
	cols = df.columns.tolist()
	# 遍歷全部特徵
	for i in range(numFeatures):
		# 獲取第i個特徵的全部不一樣的取值
		equalVals = set(df[cols[i]].tolist())
		# 經驗條件熵
		newEnt = 0.0
		# 計算信息增益
		for value in equalVals:
			# 獲取劃分後的子集
			subDataset = splitDatsSet(df, cols[i], value)
			# 計算子集機率
			prob = subDataset.shape[0] / df.shape[0]
			# 根據公式計算條件熵
			newEnt += prob * calEnt(subDataset)
		infoGain = Ent - newEnt
		print(cols[i], infoGain)
		# 更新信息增益,找到最大的信息增益
		if infoGain > bestInfoGain:
			bestInfoGain = infoGain
			bestFeature = cols[i]
	return bestFeature, bestInfoGain



def majorityCnt(classList):
	'''
		input: 類別列表
		output: 子節點的分類
		description: 數據集已經處理了全部屬性,可是類標籤依然不是惟一的,
          採用多數判決的方法決定該子節點的分類
	'''
	classCount = {}
	# 統計classList中每一個元素出現的次數
	for clas in classList:
		if clas not in classCount.keys():
			classCount[clas] = 0
		classCount[clas] += 1
	# 根據字典值降序排列
	sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reversed=True)
	return sortedClassCount[0][0]

def createTree(df, dropcol):
	'''
		input: 數據集和需刪除特徵
		output: 決策樹
		description: 遞歸實現決策樹構建
	'''
	# cols = df.columns.tolist()[:-1]
	# 取分類標籤
	classList = df[df.columns.tolist()[-1]].tolist()

	# 若數據集中全部實例屬於同一類,將Ck做爲該節點的類標記
	if classList.count(classList[0]) == len(classList):
		return classList[0]

	# 若特徵集爲空集,將數據集中實例數最大的類Ck做爲該節點的類標記
	if len(df[0:1]) == 0:
		return majorityCnt(classList)

	# 獲取最優特徵與信息增益
	bestFeature, bestInfoGain = chooseBestFeatureToSplit(df)

	print('特徵集和類別:',df.columns.tolist())
	print('bestFeature:',bestFeature)
	# 根據最優特徵的標籤生成樹
	myTree = {bestFeature:{}}
	# 獲得最優特徵的屬性值
	featValues = df[bestFeature]
	# 去掉重複屬性值
	uniqueVals = set(featValues)
	# 遍歷建立決策樹
	for value in uniqueVals:
		myTree[bestFeature][value] = createTree(splitDatsSet(df, bestFeature, value), bestFeature)
	return myTree

def main():
	filename = "data.csv"
	dataset = load(filename)
	dropCol = []
	myTree = createTree(dataset, dropCol)
	print(myTree)

if __name__ == '__main__':
	main()

輸入

輸入選取統計學習方法中給出的數據集算法

age,work,hourse,loan,class
青年,否,否,通常,否
青年,否,否,好,否
青年,是,否,好,是
青年,是,是,通常,是
青年,否,否,通常,否
中年,否,否,通常,否
中年,否,否,好,否
中年,是,是,好,是
中年,否,是,很是好,是
中年,否,是,很是好,是
老年,否,是,很是好,是
老年,否,是,好,是
老年,是,否,好,是
老年,是,否,很是好,是
老年,否,否,通常,否

輸出

age 0.08300749985576883
work 0.32365019815155627
hourse 0.4199730940219749
loan 0.36298956253708536
特徵集和類別: ['age', 'work', 'hourse', 'loan', 'class']
bestFeature: hourse
age 0.2516291673878229
work 0.9182958340544896
loan 0.47385138961004514
特徵集和類別: ['age', 'work', 'loan', 'class']
bestFeature: work
{'hourse': {'是': '是', '否': {'work': {'是': '是', '否': '否'}}}}

ID3算法的缺點

  1. ID3算法對於缺失值沒有進行考慮
  2. 沒有考慮過擬合的狀況
  3. ID3沒有考慮連續特徵
  4. 上篇文章中有提到,ID3以信息增益做爲劃分訓練數據集的特徵,存在<u>偏向於選擇取值較多的特徵</u>的問題。
  5. ID3採用信息增益大的特徵優先創建決策樹的節點。很快就被人發現,在相同條件下,取值比較多的特徵比取值少的特徵信息增益大。

C4.5實現

C4.5就是在上面提出ID3的缺點中的第4條進行的改正,大部分代碼都相同,除了在劃分數據集的時候以信息增益比爲劃分標準,這在上篇文章中也提到過。app

def chooseBestFeatureToSplit(df):
	'''
		input: 數據集
		output: 最優特徵和信息增益
	'''
	# 獲取特徵數量
	numFeatures = len(df.columns) - 1
	# 計算熵
	Ent = calEnt(df)
	# 信息增益
	bestInfoGain = 0.0
	splitInfo = 0.0
	# 最優特徵索引值
	bestFeature = -1
	cols = df.columns.tolist()
	# 遍歷全部特徵
	for i in range(numFeatures):
		# 獲取第i個特徵的全部不一樣的取值
		equalVals = set(df[cols[i]].tolist())
		# 經驗條件熵
		newEnt = 0.0
		# 計算信息增益
		for value in equalVals:
			# 獲取劃分後的子集
			subDataset = splitDatsSet(df, cols[i], value)
			# 計算子集機率
			prob = subDataset.shape[0] / df.shape[0]
			# 根據公式計算條件熵
			newEnt += prob * calEnt(subDataset)
			# 計算特徵熵
			splitInfo += -prob * log(prob, 2)
		infoGain = Ent - newEnt
		print(cols[i], infoGain)
		# 計算信息增益比
		infoGainRatio = infoGain / splitInfo
		# 更新信息增益比,找到最大的信息增益比
		if infoGainRatio > bestInfoGain:
			bestInfoGain = infoGainRatio
			bestFeature = cols[i]
	return bestFeature, infoGainRatio

結語

這篇文章給出了ID3與C4.5的代碼實現,同時也提到了其中的一些缺點,對於缺點2中提到的過擬合現象,咱們能夠經過剪枝來簡化模型,從而下降這一現象的產生。對於剪枝的實現及介紹,將在後面介紹CART算法時再講述。機器學習

參考:學習

  1. 《統計學習方法》——李航
  2. 《機器學習實戰》
  3. 魚佬的文章 https://zhuanlan.zhihu.com/p/30352406
  4. http://www.javashuo.com/article/p-slgattie-gm.html

原文出處:https://www.cnblogs.com/csu-lmw/p/10575897.html優化

相關文章
相關標籤/搜索