Random Forest

#Random Forest——随机森林 上一篇是讲到了决策树,这篇就来说一下树的集合,随机森林。 #①Aggregation Model 随机森林仍是没有脱离聚合模型这块,以前学过两个aggregation model,bagging和decision tree,一个是边learning边uniform。首先是boostrap方式获得数据D1,以后训练作平均;另外一个也是边learning可是作的是condition,直接用数据D作conditional切分。 git

bagging and decision tree
而bagging和decision tree各自都有一个很重要的特色。bagging能够减小不一样gt方差的variance的做用,由于bagging是经过各自投票,你们包含的相一样本都比较多,因此大体不会差异太远,后面uniform求平均也起到了求consensus的做用。可是decision tree有点不一样,每一次的切割都是切分剩下的数据,意味着每一次若是数据是不同的切分方式就不同,因此对不一样的数据会很敏感,因此不一样的D获得的variance会大一些。 可是以前说过,越不一样的g是越能反应事物的自己,好比看一我的,单单从成绩方面太狭隘了,成绩和行动力结合起来才会综合,这就是像是两个g结合在一块儿。因此若是g越不同表达的方面就越不同。既然decision tree是variance大,bagging的variance小,结合起来讲不定能够取得更好的成绩,因而random forest诞生了。

#②Random Forest github

因此random forest由两方面组成,bagging和random forest。
random forest有三个优势,1.决策树能够由不一样的主机生成,效率高。2.随机森林继承了CART的有点。3.以bagging的形式结合在一块儿,避免了过拟合。
上面所讲的指数基本的random forest,经过boostrap抽样获得数据D1而后训练decision tree,最后作uniform。除了这种方法以外,还有另一种方法,随机选取数据的feature,如今有100个特征,我选取30个来构成decision tree,又选择另外的30个来构成新的一棵随机数,每一棵树都不同。好比如今是d维,那么咱们只是选择_d(_d < d)维来构建决策树。也就是说_d维z空间其实就是d维x空间的一个随机子空间(subspace),Random Forest算法的做者建议在构建CART每一个分支b(x)的时候,均可以从新选择子特征来训练,从而获得更具备多样性的决策树。
因此改进一下:
subspace
上面咱们讲的是随机抽取特征,除此以外,还能够将现有的特征x,经过数组p进行线性组合,来保持多样性:
这种方法获得的就再也不是d的子空间集合了,而是获得线性的组合,好比在二维平面上只能是x,y维度。可是转换以后就是斜线。而不一样的pi是不同的,并且向量pi中大部分元素为零,由于咱们选择的只是一部分特征,这是一种低维映射:
因而,能力又强了一点,random-subspace变成了random-combination,这里就很像是perceptron模型了。事实上我想了一会才感受到很像......
下面的代码实现还不会用到这些,由于仍是麻烦了点,以后有时间再看看改进吧。 #③Out-Of-Bag Estimate 再来探讨一下对于boostrap的一些内容和理解。 咱们经过了boostrap获得了数据D1,数据D1里面既有原数据D里面有的,也可能没有里面有的。红色的*就表明没有,也叫作红色表示的样本被称为out-of-bag(OOB) example。
那么他到底有多少?能够根据有关公式算一下:
1/N就是抽中的几率。那么抽不中就(1 - 1/N),屡次就是N次方了。化简一下约等于1/e。其实这里的≈不太准确,由于N -> lim是有(1 + 1/N)^N = e。 因此,是有大约百分之30的数据是抽不到的。 貌似是和以前学过validation有点像,来对比一下:
在validation里面,Dtrain是用来获得gt的,Dtrain和Dval是倍数关系,而且没有交集,而OOB里面在bag外的数据是*号的,也没有参与到decision tree的create当中。那么咱们是否能够用OOB的数据来代替validation呢?其实彻底能够的,由于OOB类比过去就是Dval数据。那么整一个G的performance要怎么计算?咱们能够计算g的而后平均,好比当前有5个gt,咱们分别计算他手下的OOB的Eval做为这个g的performance,平均便可。
这种self-validation相比于validation来讲还有一个优势就是它不须要重复训练。以下图左边所示,在经过Dval选择到表现最好的g以后,还须要在Dtrain和Dval组成的全部样本集D上从新对该模型g训练一次,以获得最终的模型系数。可是self-validation在调整随机森林算法相关系数并获得最小的EooB以后,就完成了整个模型的创建,无需从新训练模型。更重要的是,random forest的self-validation在衡量G的表现上一般至关准确。

#④Feature Selection 在feature选择的过程当中,还有一类问题要注意。选择的过程当中有可能遇到多余的问题,好比抽取到生日和年龄,这就尴尬了。另外一种是不相关的。 算法

特征选择优势: 提升效率,特征越少,模型越简单 正则化,防止特征过多出现过拟合 去除无关特征,保留相关性大的特征,解释性强 缺点: 筛选特征的计算量较大 不一样特征组合过拟合是有可能出现的 容易选到无关特征,解释性差
在上一篇实现决策树的里面使用的decision stump就是一种feature selection。 问题来了,咱们要怎么选择有用的特征?
其中的一种方法就是作特征重要性的排序,选择前几个。这种方法若是在线性模型里面是很好计算的,线性可分模型。 若是是非线性可分的话,权值不是一一对应的,由于一般是作了feature transform的,因此权值是交叉再一块儿。因此非线性就会很难。
上图是linear model可使用的,而且效果不差。只须要选择最大的权值|W|就行了。 RF中,特征选择的核心思想是random test。random test的作法是对于某个特征,若是用另一个随机值替代它以后的表现比以前更差,则代表该特征比较重要,所占的权重应该较大,不能用一个随机值替代。相反,若是随机值替代后的表现没有太大差异,则代表该特征不那么重要,无关紧要。就好像班里面学习好的忽然有一天死了,老师立马就能够发现,可是对于成绩差的挂了一个学期都不必定知道。因此,经过比较某特征被随机值替代先后的表现,就能推断出该特征的权重和重要性。 问题来了,咱们应该如何选择随机值来替代? ①是使用uniform或者是Gaussian插入随机值。 ②是把数据的第i个特征打乱了看看打乱以后效果差异大不大。 相比起来,其实第二种更加好,由于若是原本的不是高斯分布可能就会打乱了数据的分布方式,而第二种在大致上数据的分布是不改变的。以后咱们能够比较variance,大的那么就是比较重要了。
这样就又有了一个问题,咱们要如何衡量改变dimension后的D和原数据D的performance呢? ①咱们能够延用以前OOB的作法,打乱i个特征,对每个维度都要训练,如何用OOB作validation评估performance与原数据D训练出来的作比较。 ②RF的做者提出了一种方法,就是把permutation的操做从原来的training上移到了OOB validation上去,记为Eoob(G(p))→E(p)oob(G)。也就是说,在训练的时候仍然使用D,可是在OOB验证的时候,将全部的OOB样本的第i个特征从新洗牌,验证G的表现。 第二种方法用的不少,计算方便,主要是效果也很好啊。

#⑤代码实现bootstrap

Random Forest in Action

最后,咱们经过实际的例子来看一下RF的特色。首先,仍然是一个二元分类的例子。以下图所示,左边是一个C&RT树没有使用bootstrap获得的模型分类效果,其中不一样特征之间进行了随机组合,因此有斜线做为分类线;中间是由bootstrap(N’=N/2)后生成的一棵决策树组成的随机森林,图中加粗的点表示被bootstrap选中的点;右边是将一棵决策树进行bagging后的分类模型,效果与中间图是同样的,都是一棵树。 数组

当t=100,即选择了100棵树时,中间的模型是第100棵决策树构成的,仍是只有一棵树;右边的模型是由100棵决策树bagging起来的,以下图所示:
当t=200时:
当t=300时:
当t=400时:
当t=500时:
当t=600时:
当t=700时:
当t=800时:
当t=900时:
当t=1000时:
随着树木个数的增长,咱们发现,分界线愈来愈光滑并且获得了large-margin-like boundary,和以前磕磕绊绊的相比光滑了很多,相似于SVM同样的效果。也就是说,树木越多,分类器的置信区间越大。

而后,咱们再来看一个比较复杂的例子,二维平面上分布着许多离散点,分界线形如sin函数。当只有一棵树的时候(t=1),下图左边表示单一树组成的RF,右边表示全部树bagging组合起来构成的RF。由于只有一棵树,因此左右两边效果一致。 bash

当t=6时:
当t=11时:
当t=16时:
当t=21时:
能够看到,当RF由21棵树构成的时候,分界线就比较平滑了,并且它的边界比单一树构成的RF要robust得多,更加平滑和稳定。 最后,基于上面的例子,再让问题复杂一点:在平面上添加一些随机噪声。当t=1时,以下图所示:
当t=6时:
当t=11时:
当t=16时:
当t=21时:
从上图中,咱们发现21棵树的时候,随机noise的影响基本上可以修正和消除。这种bagging投票的机制可以保证较好的降噪性,从而获得比较稳定的结果。 通过以上三个例子,咱们发现RF中,树的个数越多,模型越稳定越能表现得好。在实际应用中,应该尽量选择更多的树。值得一提的是,RF的表现同时也与random seed有关,即随机的初始值也会影响RF的表现。

以后就是真正的代码实现了 这里使用的仍是随机选择特征的方法。 首先是一个特征选择函数:app

def choose_samples(self, data, k):
      '''choose the feature from data input:data, type = list output:k '''
      n, d = np.shape(data)
      feature = []
      for j in range(k):
          feature.append(rd.randint(0, d - 2))
      index = []
      for i in range(n):
          index.append(rd.randint(0, n-1))
      data_samples = []
      for i in range(n):
          data_tmp = []
          for fea in feature:
              data_tmp.append(data[i][fea])
          data_tmp.append(data[i][-1])
          data_samples.append(data_tmp)
          pass
      return data_samples, feature
      pass
复制代码

在data数据里面选择出k维的数据。 以后就是随机森林的创建了,使用的决策树是上篇文章实现的决策树,尽可能作到全是本身实现的:dom

def random_forest(self, data, trees_num):
      '''create a forest input:data, type = list output:trees_result, trees_feature '''
      decisionTree = tree.decision_tree()
      trees_result = []
      trees_feature = []
      d = np.shape(data)[1]
      if d > 2:
          k = int(math.log(d - 1, 2)) + 1
      else:
          k = 1

      for i in range(trees_num):
          print('The ', i, ' tree. ')
          data_samples, feature = self.choose_samples(data, k)
          t = decisionTree.build_tree(data_samples)
          trees_result.append(t)
          trees_feature.append(feature)
          pass
      return trees_result, trees_feature
复制代码

其实都很常规,最后返回的是树的数量和选取的特征。 以后就是一个切割数据和加载数据的工具函数:函数

def split_data(data_train, feature):
  '''select the feature from data input:data, feature output:data, type = list '''
  m = np.shape(data_train)[0]
  data = []
  for i in range(m):
      data_tmp = []
      for x in feature:
          data_tmp.append(data_train[i][x])
      data_tmp.append(data_train[i][-1])
      data.append(data_tmp)
  return data

def load_data():
  '''use the boston dataset from sklearn'''
  print('loading data......')
  dataSet = load_breast_cancer()
  data = dataSet.data
  target = dataSet.target
  for i in range(len(target)):
      if target[i] == 0:
          target[i] = -1
  dataframe = pd.DataFrame(data)
  dataframe.insert(np.shape(data)[1], 'target', target)
  dataMat = np.mat(dataframe)
  X_train, X_test, y_train, y_test =  train_test_split(dataMat[:, 0:-1], dataMat[:, -1], test_size=0.3, random_state=0)
  data_train = np.hstack((X_train, y_train))
  data_train = data_train.tolist()
  X_test = X_test.tolist()

  return data_train, X_test, y_test
复制代码

load_data是把数据3,7切分,测试和训练。 而后就是预测函数和计算准确度的函数了:工具

def get_predict(self, trees_result, trees_feature, data_train):
      '''predict the result input:trees_result, trees_feature, data output:final_prediction '''
      decisionTree = tree.decision_tree()
      m_tree = len(trees_result)
      m = np.shape(data_train)[0]
      result = []
      for i in range(m_tree):
          clf = trees_result[i]
          feature = trees_feature[i]
          data = tool.split_data(data_train, feature)
          result_i = []
          for i in range(m):
              result_i.append( list((decisionTree.predict(data[i][0 : -1], clf).keys()))[0] )
          result.append(result_i)
      final_predict = np.sum(result, axis = 0)
      return final_predict

  def cal_correct_rate(self, target, final_predict):
      m = len(final_predict)
      corr = 0.0
      for i in range(m):
          if target[i] * final_predict[i] > 0:
              corr += 1
          pass
      return corr/m
      pass

复制代码

这个和以前决策树的差很少,也是调用了以前的代码。 最后就是入口函数:

def running():
  '''entrance'''
  data_train, text, target = load_data()
  forest = randomForest()
  predic = []
  for i in range(1, 20):
      trees, features = forest.random_forest(data_train, i)
      predictions = forest.get_predict(trees, features, text)
      accuracy = forest.cal_correct_rate(target, predictions)
      print('The forest has ', i, 'tree', 'Accuracy : ' , accuracy)
      predic.append(accuracy)

  plt.xlabel('Number of tree')
  plt.ylabel('Accuracy')
  plt.title('The relationship between tree number and accuracy')
  plt.plot(range(1, 20), predic, color = 'orange')
  plt.show()
  pass

if __name__ == '__main__':
  running()
复制代码

计算了1到20课树他们以前准确率的变化,画了对比图。

大体趋势仍是能够看得出是一直不断上升,最高应该是13到15这个区间吧,可是注意到2到5棵树的时候存在了必定的颠婆,这颠婆有点厉害,我的猜想应该是采集到了某些没有用的特征致使的,后面效果好了是由于树多了效果削弱了,而且有些的特征也采集不到。这个数据维度是30维的。有时间再作一个特征重要性的选择了。

全部代码GitHub上: github.com/GreenArrow2…

相关文章
相关标签/搜索