算法原理

随机森林是一个用随机方式建立的,包含多个决策树的集成分类器。其输出的类别由各个树投票而定(如果是回归树则取平均)。假设样本总数为n,每个样本的特征数为a,则随机森林的生成过程如下:

  • 从原始样本中采用有放回抽样的方法选取n个样本;
  • 对n个样本选取a个特征中的随机k个,用建立决策树的方法获得最佳分割点;
  • 重复m次,获得m个决策树;
  • 对输入样例进行预测时,每个子树都产生一个结果,采用多数投票机制输出。
    随机森林的随机性主要体现在两个方面:
    数据集的随机选取:从原始的数据集中采取有放回的抽样(bagging),构造子数据集,子数据集的数据量是和原始数据集相同的。不同子数据集的元素可以重复,同一个子数据集中的元素也可以重复。
    待选特征的随机选取:与数据集的随机选取类似,随机森林中的子树的每一个分裂过程并未用到所有的待选特征,而是从所有的待选特征中随机选取一定的特征,之后再在随机选取的特征中选取最优的特征。

以上两个随机性能够使得随机森林中的决策树都能够彼此不同,提升系统的多样性,从而提升分类性能。
随机森林的优点:

  • 实现简单,训练速度快,泛化能力强,可以并行实现,因为训练时树与树之间是相互独立的;
  • 相比单一决策树,能学习到特征之间的相互影响,且不容易过拟合;
  • 能处理高维数据(即特征很多),并且不用做特征选择,因为特征子集是随机选取的;
  • 对于不平衡的数据集,可以平衡误差;
  • 相比SVM,不是很怕特征缺失,因为待选特征也是随机选取;
  • 训练完成后可以给出哪些特征比较重要。

随机森林的缺点:

  • 在噪声过大的分类和回归问题还是容易过拟合;
  • 相比于单一决策树,它的随机性让我们难以对模型进行解释。

代码实现

在上一篇文章实现的决策树的基础上实现,代码如下:

#coding=utf-8
import decision_tree
from decision_tree import DecisionTree
from random import sample, choices, choice

class RandomForest(object):
    def __init__(self):
        self.trees = None
        self.tree_features = None

    def fit(self, X, y, n_estimators=10, max_depth=3, min_samples_split=2, max_features=None, n_samples=None):
        self.trees = []
        self.tree_features = []
        for _ in range(n_estimators):
            m = len(X[0])
            n = len(y)
            if n_samples:
                idx = choices(population=range(n), k=min(n, n_samples))
            else:
                idx = range(n)
            if max_features:
                n_features = min(m, max_features)
            else:
                n_features = int(m ** 0.5)
            features = sample(range(m), choice(range(1, n_features+1)))
            X_sub = [[X[i][j] for j in features] for i in idx]
            y_sub = [y[i] for i in idx]
            clf = DecisionTree()
            clf.fit(X_sub, y_sub, max_depth, min_samples_split)
            self.trees.append(clf)
            self.tree_features.append(features)

    def _predict(self, Xi):
        pos_vote = 0
        for tree, features in zip(self.trees, self.tree_features):
            score = tree._predict([Xi[j] for j in features])
            if score >= 0.5:
                pos_vote += 1
        neg_vote = len(self.trees) - pos_vote
        if pos_vote > neg_vote:
            return 1
        elif pos_vote < neg_vote:
            return 0
        else:
            return choice([0, 1])

    def predict(self, X):
        return [self._predict(Xi) for Xi in X]


@decision_tree.run_time
def main():
    print("Tesing the performance of RandomForest...")
    # Load data
    X, y = decision_tree.load_data()
    # Split data randomly, train set rate 70%
    X_train, X_test, y_train, y_test = decision_tree.train_test_split(X, y, random_state=40)

    # Train model
    rf = RandomForest()
    rf.fit(X_train, y_train, n_samples=300, max_depth=3, n_estimators=20)
    # Model evaluation
    y_hat = rf.predict(X_test)
    acc = decision_tree.get_acc(y_test, y_hat)
    print("Accuracy is %.3f" % acc)

在肺癌数据集上测试结果如下

参考文章

https://blog.csdn.net/login_sonata/article/details/73929426