一,介绍
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:是否有放回的采样。