随机森林定义
随机森林是一种比较新的机器学习模型。经典的机器学习模型是神经网络,有半个多世纪的历史了。神经网络预测精确,但是计算量很大。上世纪八十年代Breiman等人发明分类树的算法(Breiman et al. 1984),通过反复二分数据进行分类或回归,计算量大大降低。2001年Breiman把分类树组合成随机森林(Breiman 2001a),即通过对数据集的采样生成多个不同的数据集,并在每一个数据集上训练出一颗分类树,最终结合每一颗分类树的预测结果作为随机森林的预测结果。随机森林在运算量没有显著提高的前提下提高了预测精度。随机森林对多元公线性不敏感,结果对缺失数据和非平衡的数据比较稳健,可以很好地预测多达几千个解释变量的作用(Breiman 2001b),被誉为当前最好的算法之一(Iverson et al. 2008)。
随机森林顾名思义,是用随机的方式建立一个森林,森林里面有很多的决策树组成,随机森林的每一棵决策树之间是没有关联的。在得到森林之后,当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分别进行一下判断,看看这个样本应该属于哪一类(对于分类算法),然后看看哪一类被选择最多,就预测这个样本为那一类。随机森林可以既可以处理属性为离散值的量,比如ID3算法,也可以处理属性为连续值的量,比如C4.5算法。另外,随机森林还可以用来进行无监督学习聚类和异常点检测。
随机森林的构造过程:
1. 假如有N个样本,则有放回的随机选择N个样本(每次随机选择一个样本,然后返回继续选择)。这选择好了的N个样本用来训练一个决策树,作为决策树根节点处的样本。
2. 当每个样本有M个属性时,在决策树的每个节点需要分裂时,随机从这M个属性中选取出m个属性,满足条件m << M。然后从这m个属性中采用某种策略(比如说信息增益)来选择1个属性作为该节点的分裂属性。
3. 决策树形成过程中每个节点都要按照步骤2来分裂(很容易理解,如果下一次该节点选出来的那一个属性是刚刚其父节点分裂时用过的属性,则该节点已经达到了叶子节点,无须继续分裂了)。一直到不能够再分裂为止。注意整个决策树形成过程中没有进行剪枝。
4. 按照步骤1~3建立大量的决策树,这样就构成了随机森林了。
在建立每一棵决策树的过程中,有两点需要注意采样与完全分裂。
首先是两个随机采样的过程,random forest对输入的数据要进行行、列的采样。对于行采样,采用有放回的方式,也就是在采样得到的样本集合中,可能有重复的样本。假设输入样本为N个,那么采样的样本也为N个。这样使得在训练的时候,每一棵树的输入样本都不是全部的样本,使得相对不容易出现over-fitting。然后进行列采样,从M个feature中,选择m个(m << M)。
之后就是对采样之后的数据使用完全分裂的方式建立出决策树,这样决策树的某一个叶子节点要么是无法继续分裂的,要么里面的所有样本的都是指向的同一个分类。一般很多的决策树算法都一个重要的步骤——剪枝,但是这里不这样干,由于之前的两个随机采样的过程保证了随机性,所以就算不剪枝,也不会出现over-fitting。
下面,讲一下如何用代码实现随机森林。
代码实现流程:
(1) 导入文件并将所有特征转换为float形式
(2) 将数据集分成n份,方便交叉验证
(3) 构造数据子集(随机采样),并在指定特征个数(假设m个,手动调参)下选取最优特征
(4) 构造决策树
(5) 创建随机森林(多个决策树的结合)
(6) 输入测试集并进行测试,输出预测结果
(1)导入文件并将所有特征转换为float形式
#加载数据
def loadCSV(filename):
dataSet=[]
with open(filename,'r') as file:
csvReader=csv.reader(file)
for line in csvReader:
dataSet.append(line)
return dataSet
#除了判别列,其他列都转换为float类型
def column_to_float(dataSet):
featLen=len(dataSet[0])-1
for data in dataSet:
for column in range(featLen):
data[column]=float(data[column].strip())
(2)将数据集分成n份,方便交叉验证
#将数据集分成N块,方便交叉验证
def spiltDataSet(dataSet,n_folds):
fold_size=int(len(dataSet)/n_folds)
dataSet_copy=list(dataSet)
dataSet_spilt=[]
for i in range(n_folds):
fold=[]
while len(fold) < fold_size: #这里不能用if,if只是在第一次判断时起作用,while执行循环,直到条件不成立
index=randrange(len(dataSet_copy))
fold.append(dataSet_copy.pop(index)) #pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
dataSet_spilt.append(fold)
return dataSet_spilt
(3)构造数据子集(随机采样),并在指定特征个数(假设m个,手动调参)下选取最优特征
#构造数据子集
def get_subsample(dataSet,ratio):
subdataSet=[]
lenSubdata=round(len(dataSet)*ratio)
while len(subdataSet) < lenSubdata:
index=randrange(len(dataSet)-1)
subdataSet.append(dataSet[index])
#print len(subdataSet)
return subdataSet
#选取任意的n个特征,在这n个特征中,选取分割时的最优特征
def get_best_spilt(dataSet,n_features):
features=[]
class_values=list(set(row[-1] for row in dataSet))
b_index,b_value,b_loss,b_left,b_right=999,999,999,None,None
while len(features) < n_features:
index=randrange(len(dataSet[0])-1)
if index not in features:
features.append(index)
#print 'features:',features
for index in features:
for row in dataSet:
left,right=data_spilt(dataSet,index,row[index])
loss=spilt_loss(left,right,class_values)
if loss < b_loss:
b_index,b_value,b_loss,b_left,b_right=index,row[index],loss,left,right
#print b_loss
#print type(b_index)
return {'index':b_index,'value':b_value,'left':b_left,'right':b_right}
(4)构造决策树
#构造决策树
def build_tree(dataSet,n_features,max_depth,min_size):
root=get_best_spilt(dataSet,n_features)
sub_spilt(root,n_features,max_depth,min_size,1)
return root
(5)创建随机森林(多个决策树的结合)
#创建随机森林
def random_forest(train,test,ratio,n_feature,max_depth,min_size,n_trees):
trees=[]
for i in range(n_trees):
subTrain=get_subsample(train,ratio)
tree=build_tree(subTrain,n_features,max_depth,min_size)
#print 'tree %d: '%i,tree
trees.append(tree)
#predict_values = [predict(trees,row) for row in test]
predict_values = [bagging_predict(trees, row) for row in test]
return predict_values
(6) 输入测试集并进行测试,输出预测结果
#预测测试集结果
def predict(tree,row):
predictions=[]
if row[tree['index']] < tree['value']:
if isinstance(tree['left'],dict):
return predict(tree['left'],row)
else:
return tree['left']
else:
if isinstance(tree['right'],dict):
return predict(tree['right'],row)
else:
return tree['right']
# predictions=set(predictions)
对以上代码的一点总结:
训练部分:假设我们取dataset中的m个feature来构造决策树,首先,我们遍历m个feature中的每一个feature,再遍历每一行,通过spilt_loss函数(计算分割代价)来选取最优的特征及特征值,根据是否大于这个特征值进行分类(分成left,right两类),循环执行上述步骤,直至不可分或者达到递归限值(用来防止过拟合),最后得到一个决策树tree。
测试部分:对测试集的每一行进行判断,决策树tree是一个多层字典,每一层为一个二分类,将每一行按照决策树tree中的分类索引index一步一步向里层探索,直至不出现字典时探索结束,得到的值即为我们的预测值。