算法步骤:随机森林由LeoBreiman于2001年提出,它通过自助法(Bootstrap)重采样技术,从原始训练样本集N中有放回地重复随机抽取k个样本生成新的训练样本集合。
然后根据自助样本集生成k个分类树,这k个分类树组成随机森林。
新数据的分类结果按各分类树投票多少形成的分数而定。
采样与完全分裂
两个随机采样的过程,Random Forest对输入的数据要进行、列的采样。
行采样得到不同的训练数据来训练模型,列采样则主要涉及到一个随机特征选择的问题(见后文)
对采样之后的数据使用完全分裂的方式建立出决策树,这样决策树的某一个叶子节点要么是无法继续分裂的,要么里面的所有样本的都是指向的同一个分类。
完全随机的取样方式使得每棵树都有过学习的可能,但是因为数量足够多使得最后的模型过学习的可能性大大降低(集成学习的优势)
随机森林在最后输出时采取的是Majority-voting。

随机属性选择

随机森林的基学习器虽然是决策树,但是随机森林与决策树的不同之处在于,决策树是在所有的属性上进行搜索,而随机森林的每一棵树是在一个随机的属性子集上进行搜索(可能会从属性数目为M的集合中随机选出k个属性进行搜索,没一个节点都这样进行),这样做不仅做到了数据集有扰动,属性也会有扰动,所以虽然单个决策树的泛化能力不强,但是随机森林的泛化能力会很强!这也是为什么RF的集成效果会比决策树使用bagging集成的效果更好一点。

深入理解集成学习集成之好处

特征选择

随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行的机器学习算法之一。随机森林提供了两种特征选择的方法:mean decrease impurity和mean decrease accuracy。

mean decrease impurity

随机森林由多棵决策树构成,决策树的每一个节点都是一个特征的重要条件,一般来说会根据特征所降低的集合的不纯度来衡量节点的重要性(基尼不纯度或者信息增益),当训练决策树的时候,可以计算出每个特征减少了多少树的不纯度。对于一个决策树森林来说,可以算出每个特征平均减少了多少不纯度,并把它平均减少的不纯度作为特征选择的值。这也是所谓的随机森林模型中特征的重要性排序。使用基于不纯度的方法的时候,要记住:

  • 这种方法存在偏向,对具有更多类别的变量会更有利;
  • 对于存在关联的多个特征,其中任意一个都可以作为指示器(优秀的特征),并且一旦某个特征被选择之后,其他特征的重要度就会急剧下降,因为不纯度已经被选中的那个特征降下来了,其他的特征就很难再降低那么多不纯度了,这样一来,只有先被选中的那个特征重要度很高,其他的关联特征重要度往往较低。
  • 在理解数据时,这就会造成误解,导致错误的认为先被选中的特征是很重要的,而其余的特征是不重要的,但实际上这些特征对响应变量的作用确实非常接近的(这跟Lasso是很像的,Lasso回归在存在多重共线性的特征之间进行选择时会选出其中最重要的变量,其余与之相关的变量的权重系数都会变为0)。

需要注意的一点是,关联特征的打分存在不稳定的现象,这不仅仅是随机森林特有的,大多数基于模型的特征选择方法都存在这个问题。

Mean decrease accuracy

另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响。主要思路是打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。很明显,对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的变量来说,打乱顺序就会降低模型的精确率。

from sklearn.cross_validation import ShuffleSplit
from sklearn.metrics import r2_score
from collections import defaultdict

X = boston["data"]
Y = boston["target"]

rf = RandomForestRegressor()
scores = defaultdict(list)

#crossvalidate the scores on a number of different random splits of the data
for train_idx, test_idx in ShuffleSplit(len(X), 100, .3):
X_train, X_test = X[train_idx], X[test_idx]
Y_train, Y_test = Y[train_idx], Y[test_idx]
r = rf.fit(X_train, Y_train)
acc = r2_score(Y_test, rf.predict(X_test))
for i in range(X.shape[1]):
X_t = X_test.copy()
np.random.shuffle(X_t[:, i])
shuff_acc = r2_score(Y_test, rf.predict(X_t))
scores[names[i]].append((acc-shuff_acc)/acc)
print("Features sorted by their score:")
print(sorted([(round(np.mean(score), 4), feat) for
feat, score in scores.items()], reverse=True))

在这个例子当中,LSTAT和RM这两个特征对模型的性能有着很大的影响,打乱这两个特征的特征值使得模型的性能下降了73%和57%。注意,尽管这些我们是在所有特征上进行了训练得到了模型,然后才得到了每个特征的重要性测试,这并不意味着我们扔掉某个或者某些重要特征后模型的性能就一定会下降很多,因为即便某个特征删掉之后,其关联特征一样可以发挥作用,让模型性能基本上不变。