一,介绍
Bagging算法:假定有m个训练集,我们采用自助采样法,每次随机抽取一个放入采样集中,然后再把样本放回训练集,一共抽取m次,获得一个用于训练的采样集(里面有m个样本)。根据需要我们一共抽取T个采样集,学习出T个基学习器。
在进行预测时,对于分类任务采用简单投票发;回归任务采用简单平均法。
随机森林:随机森林是Bagging算法的扩展。在以决策树为基学习器构建bagging集成的基础上,进一步在决策树的训练中,引入随机属性选择。其基本思想就是构造很多棵决策树,形成一个森林,每一棵树都会给出自己的分类选择,并由此进行“投票”,森林整体的输出结果将会是票数最多的分类选项。
在构建单个决策树时,不同于传统决策树,在构建子节点时,不是选择所有属性进行判定,而是随机抽取k个属性进行叶节点构建,一般情况下,推荐k=log2(d)。
二,代码实现
训练/测试数据:
1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,是 2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,是 3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,是 4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,是 5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,是 6,青绿,稍蜷,浊响,清晰,稍凹,软粘,是 7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,是 8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,是 9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,否 10,青绿,硬挺,清脆,清晰,平坦,软粘,否 11,浅白,硬挺,清脆,模糊,平坦,硬滑,否 12,浅白,蜷缩,浊响,模糊,平坦,软粘,否 13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,否 14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,否 15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,否 16,浅白,蜷缩,浊响,模糊,平坦,硬滑,否 17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,否
下面有自己写和用sklearn库直接实现两种方式:
自写:
import math
import random
import operator
# 加载数据
def loadDataSet(filename):
dataMat=[]
fr = open(filename)
for line in fr.readlines():
lineArr = line.strip().split(',')
dataMat.append(lineArr)
labelMat = ['编号', '色泽', '根蒂', '敲声', '纹理', '头部', '触感', '好瓜']
return dataMat,labelMat
# 自助采样法,dataMat训练集数据;times采样集个数
def bootstrapSampling(dataMat,times):
totalsampleData = []
for i in range(times):
sampleData = []
for j in range(17):
sampleData.append(dataMat[random.randint(0,16)]) # 从17个样本中随机抽取一个加入采样集
totalsampleData.append(sampleData) # 将所有采样集数据放入,作为训练数据
return totalsampleData
# 选取信息增益最高的列
def chooseBestFeatureToSplit(dataSet,randinfo):
baseEnt = calcShannonEnt(dataSet)
bestInfoGain = 0.0;bestFeature = []
length = 0
if (len(randinfo) ==0):
numFeatures = len(dataSet[0]) - 1
for i in range(1,numFeatures): # 遍历获取的属性
featList = [example[i] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitSubDataSet(dataSet, i, value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
infoGain = baseEnt - newEntropy # 计算信息增益
if (infoGain >= bestInfoGain): # 保存信息增益最高的列
bestInfoGain = infoGain
bestFeature = i
else:
for i in range(len(randinfo)): # 遍历获取的属性
featList = [example[randinfo[i]] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitSubDataSet(dataSet, randinfo[i], value)
prob = len(subDataSet) / float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
infoGain = baseEnt - newEntropy # 计算信息增益
if (infoGain >= bestInfoGain): # 保存信息增益最高的列
bestInfoGain = infoGain
bestFeature = randinfo[i]
return bestFeature
# 计算信息熵Ent(D)=-Σp*log2(p)
def calcShannonEnt(dataSet):
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1] #获取类别
if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 #新key加入字典赋值为0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key]) / numEntries
shannonEnt -= prob * math.log2(prob) # 计算信息熵
return shannonEnt
#获取特征值数据集
# dataSet --整个数据集
# axis --数据列
# value --类别
def splitSubDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
retDataSet.append([featVec[axis],featVec[-1]])
return retDataSet
def majorityCnt(classList):
classCount={}
for vote in classList:
if vote not in classCount.keys(): classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
#除去划分完成的决策树数据量
def splitDataSet(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
# 创建决策树
def createTree(dataSet,labels,randinfo,k):
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):
return classList[0]#当所有类都相同则不在分类
if (len(dataSet[0]) == 3): #没有更多特征值时不再分类
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet,randinfo) #选取信息增益最大的特征值
bestFeatLabel = labels[bestFeat] #获取特征值列头名
myTree = {bestFeatLabel:{}}
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues) # 获取特征值分类
del(labels[bestFeat]) # 删除已经建立节点的特征值
sam = []
if (len(labels) > 3): # 特征值大于2个则随机选择节点,否则全部选取
sam = random.sample(range(1, len(labels) - 1), k)
for value in uniqueVals:
subLabels = labels[:] # 复制出建立节点外的所有特征值
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels,sam,k) #建立子节点
return myTree
# 创建随机森林,totalsampleData训练集;labelMat列名
def createRandomForest(totalsampleData,labelMat):
k = int(math.log2(len(labelMat)-2)) # 计算随机属性个数(-2是去除第一列编号和最后一列分类标签)
randomForest = []
for dataSet in totalsampleData: # 遍历创建随机数,以构成随机森林
randinfo = random.sample(range(1, len(labelMat) - 2), k) # 获取k个随机属性
tmplabel = labelMat.copy()
randomForest.append(createTree(dataSet,tmplabel,randinfo,k))
return randomForest
# 决策树进行分类
def classify(inputTree,featLabels,testVec):
firstStr = list(inputTree.keys())[0] # 获取第一个节点
secondDict = inputTree[firstStr] # 获取剩余节点
featIndex = featLabels.index(firstStr)
key = testVec[featIndex] # 获取测试数据分支
valueOfFeat = {}
if(key in secondDict): # 存在则进入分支
valueOfFeat = secondDict[key]
else: # 不存在则返回空(实际上是需要再训练决策树的,这里简单处理)
return "None"
if isinstance(valueOfFeat, dict):
classLabel = classify(valueOfFeat, featLabels, testVec)
else: classLabel = valueOfFeat
return classLabel
def voteClassify(randomForest,featLabel,testVec):
terclass = []
for tree in randomForest:
classlabel = classify(tree,featLabel,testVec)
terclass.append(classlabel)
set(terclass)
yes = terclass.count('是')
no = terclass.count('否')
if(yes>no):
return '是'
else:
return '否'
if __name__=='__main__':
dataMat,labelMat = loadDataSet('watermelon.txt')
totalsampleData = bootstrapSampling(dataMat,5)
randomForest = createRandomForest(totalsampleData,labelMat)
testData, testlabel = loadDataSet('watermelon.txt')
for data in testData:
result = voteClassify(randomForest,testlabel,data)
print("输入:%s,结果:%s"%(data,result))
sklearn库实现:
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction import DictVectorizer
from sklearn.tree import DecisionTreeClassifier
if __name__=='__main__':
f = open('watermelon.csv')
data = pd.read_csv(f)
x = data[['色泽','根蒂','敲声','纹理','头部','触感']]
y = data['好瓜']
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.30, random_state=33)
print(x_test)
# 使用特征转换器进行特征抽取
vec = DictVectorizer()
# 类别型的数据会抽离出来 数据型的会保持不变
x_train = vec.fit_transform(x_train.to_dict(orient="record"))
x_test = vec.transform(x_test.to_dict(orient="record"))
# 初始化随机森林分类器
rfc = RandomForestClassifier()
# 训练
rfc.fit(x_train, y_train)
# 预测
rfc_y_predict = rfc.predict(x_test)
print(dtc_y_predict)
RandomForestClassifier()函数解释:
criterion参数:有两个取值"gini"或者"entropy",分别表示采用基尼不纯度或者信息增益来划分决策树,默认参数是"gini"。
splitter参数:有两个取值"best"和"random",分别表示选取最好的和随机选择属性后,在从中选择好的,默认参数是"best"。
max_depth参数:允许树的最大深度,默认是None。
min_samples_split参数:根据属性划分节点时,每个划分最少的样本数。可以设置为int,表示最小几个;或者float占总数百分比,默认是2个。
min_samples_leaf参数:叶子节点最少的样本数。可以设置为int,表示最小几个;或者float占总数百分比,默认是1个。
min_weight_fraction_leaf参数: 叶子节点所需要的最小权值。默认是0。
max_features参数:选择最适属性时划分的特征不能超过此值。如果是int则表示个数;如果是float则为特征值百分比;为str则:
- If "auto", then `max_features=sqrt(n_features)`. - If "sqrt", then `max_features=sqrt(n_features)`. - If "log2", then `max_features=log2(n_features)`. - If None, then `max_features=n_features`.
n_estimators:决策树的个数。 bootstrap:是否有放回的采样。