随机森林

随机森林

随机森林与集成学习的关系:

说到随机森林,就不得不提一下机器学习中的集成学习方法
在这里插入图片描述

  • .随机性:
    随机体现在两个方面,生成单颗决策树时,需要进行样本的有放回抽样(自助采样法), 在生成单颗决策树时,每个结点处,进行特征的随机抽样。

  • .out-of-bag估计:
    每颗决策树的生成都需要自助采样,这时就会有1/3的数据未被选中,这部分数据称为袋外数据。
    可以根据这部分数据进行 森林泛化误差(BREIMAN论文中介绍说袋外估计的泛化误差近似==测试集大小和训练集大小相同时的测试误差)特征重要性的估计。在代码中实现了泛化误差的估计,特征重要性有时间再补上。
    (特征重要性的估计通常有两种方法:

    • 一是使用uniform或者gaussian抽取随机值替换原特征;一是通过permutation的方式将原来的所有N个样本的第i个特征值重新打乱分布。,
    • 第二种方法更加科学,保证了特征替代值与原特征的分布是近似的。这种方法叫做permutation test,即在计算第i个特征的重要性的时候,将N个样本的第i个特征重新洗牌,然后比较D和表现的差异性。如果差异很大,则表明第i个特征是重要的。)
  • 随机森林未用到决策树的剪枝,那怎样控制模型的过拟合呢?
    主要通过控制 树的深度(max_depth),结点停止分裂的最小样本数(min_size)等参数。

  • 缺失值处理
    缺失值可以分为出现在训练集中,出现在测试集中两种情况。(本文代码只实现了测试集中缺失值的自动处理)
    决策树(C4.5,CART等)具有缺失值自动处理能力。西瓜书中介绍了C4.5的缺失值处理原理。我对随机森林中的缺失值处理机制没有深究(个人理解:缺失值的处理需要针对不同的数据集和测试结果进行相应调整)。BREIMAN随机森林的网站上介绍的缺失值处理过程有很多种,这里写一下最简单的处理方式:

  • 训练集中数据缺失:若样本缺失值为非类别型属性值,则取样本所属J class中该属性未缺失值的中值。若为类别型属性值缺失,则从样本所属J class中选择该属性最常出现的类别进行填充。(直白点说就是对于未知的东西就把它判断为大众化的,虽然不是很精确,但是可以在有限的计算复杂度下尽量降低风险)

  • 测试集中数据缺失:When there is a test set, there are two different methods of replacement depending on whether labels exist for the test set.If they do, then the fills derived from the training set are used as replacements. If labels no not exist, then each case
    in the test set is replicated nclass times (nclass= number of classes). The first replicate of a case is assumed to be class 1 and the class one fills used to replace missing values. The 2nd replicate is assumed class 2 and the class 2 fills used on it.This
    augmented test set is run down the tree. In each set of replicates, the one receiving the most votes determines the class of the original case.(网站原文)

  • 随机森林重要的参数:

    • n_features:推荐值为特征数量的平方根
    • n_trees: 森林中的树按说越多越好,但是当数量多到一定程度时,精度的提升已经不明显,但是计算消耗却很大。
    • 评价函数:可以为Gini_index,信息增益,平方误差损失,方差等等,当然可以根据数据集类型的不同自己选择合适的评价函数(具体情况具体分析)
代码实现

代码实现流程

  • 导入文件并将所有特征转换为float形式
def loadDataSet(filename):
    """
    导入csv文件
    :param filename: 
    :return: 
    """
    dataset = []
    with open(filename, 'r') as fr:
        for line in fr.readlines():
            if not line:
                continue
            lineArr = []
            for featrue in line.split(','):
                # strip()返回移除字符串头尾指定的字符生成的新字符串
                str_f = featrue.strip()
                if str_f.isdigit():   # 判断是否是数字
                    # 将数据集的第column列转换成float形式
                    lineArr.append(float(str_f))
                else:
                    # 添加分类标签
                    lineArr.append(str_f)
            dataset.append(lineArr)
    return dataset
  • 将数据集分成n份,方便交叉验证
def cross_validation_split(dataset, n_folds):
    """cross_validation_split(将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次list的元素是无重复的)

    Args:
        dataset     原始数据集
        n_folds     数据集dataset分成n_flods份
    Returns:
        dataset_split    list集合,存放的是:将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次list的元素是无重复的
    """
    dataset_split = list()
    dataset_copy = list(dataset)       # 复制一份 dataset,防止 dataset 的内容改变
    fold_size = len(dataset) / n_folds
    for i in range(n_folds):
        fold = list()                  # 每次循环 fold 清零,防止重复导入 dataset_split
        while len(fold) < fold_size:   # 这里不能用 if,if 只是在第一次判断时起作用,while 执行循环,直到条件不成立
            # 有放回的随机采样,有一些样本被重复采样,从而在训练集中多次出现,有的则从未在训练集中出现,此则自助采样法。从而保证每棵决策树训练集的差异性
            index = randrange(len(dataset_copy))
            # 将对应索引 index 的内容从 dataset_copy 中导出,并将该内容从 dataset_copy 中删除。
            # pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
            # fold.append(dataset_copy.pop(index))  # 无放回的方式
            fold.append(dataset_copy[index])  # 有放回的方式
        dataset_split.append(fold)
    # 由dataset分割出的n_folds个数据构成的列表,为了用于交叉验证
    return dataset_split
  • 构造数据子集(随机采样),并在指定特征个数(假设m个,手动调参)下选取最优特征
# 找出分割数据集的最优特征,得到最优的特征 index,特征值 row[index],以及分割完的数据 groups(left, right)
def get_split(dataset, n_features):
    class_values = list(set(row[-1] for row in dataset))  # class_values =[0, 1]
    b_index, b_value, b_score, b_groups = 999, 999, 999, None
    features = list()
    while len(features) < n_features:
        index = randrange(len(dataset[0])-1)  # 往 features 添加 n_features 个特征( n_feature 等于特征数的根号),特征索引从 dataset 中随机取
        if index not in features:
            features.append(index)
    for index in features:                    # 在 n_features 个特征中选出最优的特征索引,并没有遍历所有特征,从而保证了每课决策树的差异性
        for row in dataset:
            groups = split_test(index, row[index], dataset)  # groups=(left, right), row[index] 遍历每一行 index 索引下的特征值作为分类值 value, 找出最优的分类特征和特征值
            gini = gini_index(groups, class_values)
            # 左右两边的数量越一样,说明数据区分度不高,gini系数越大
            if gini < b_score:
                b_index, b_value, b_score, b_groups = index, row[index], gini, groups  # 最后得到最优的分类特征 b_index,分类特征值 b_value,分类结果 b_groups。b_value 为分错的代价成本
    # print b_score
    return {'index': b_index, 'value': b_value, 'groups': b_groups}
  • 构造决策树
# Build a decision tree
def build_tree(train, max_depth, min_size, n_features):
    """build_tree(创建一个决策树)

    Args:
        train           训练数据集
        max_depth       决策树深度不能太深,不然容易导致过拟合
        min_size        叶子节点的大小
        n_features      选取的特征的个数
    Returns:
        root            返回决策树
    """

    # 返回最优列和相关的信息
    root = get_split(train, n_features)

    # 对左右2边的数据 进行递归的调用,由于最优特征使用过,所以在后面进行使用的时候,就没有意义了
    # 例如: 性别-男女,对男使用这一特征就没任何意义了
    split(root, max_depth, min_size, n_features, 1)
    return root
  • 创建随机森林(多个决策树的结合)
# Random Forest Algorithm
def random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features):
    """random_forest(评估算法性能,返回模型得分)

    Args:
        train           训练数据集
        test            测试数据集
        max_depth       决策树深度不能太深,不然容易导致过拟合
        min_size        叶子节点的大小
        sample_size     训练数据集的样本比例
        n_trees         决策树的个数
        n_features      选取的特征的个数
    Returns:
        predictions     每一行的预测结果,bagging 预测最后的分类结果
    """

    trees = list()
    # n_trees 表示决策树的数量
    for i in range(n_trees):
        # 随机抽样的训练样本, 随机采样保证了每棵决策树训练集的差异性
        sample = subsample(train, sample_size)
        # 创建一个决策树
        tree = build_tree(sample, max_depth, min_size, n_features)
        trees.append(tree)

    # 每一行的预测结果,bagging 预测最后的分类结果
    predictions = [bagging_predict(trees, row) for row in test]
    return predictions
  • 输入测试集并进行测试,输出预测结果
# 评估算法性能,返回模型得分
def evaluate_algorithm(dataset, algorithm, n_folds, *args):
    """evaluate_algorithm(评估算法性能,返回模型得分)

    Args:
        dataset     原始数据集
        algorithm   使用的算法
        n_folds     数据的份数
        *args       其他的参数
    Returns:
        scores      模型得分
    """

    # 将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次 list 的元素是无重复的
    folds = cross_validation_split(dataset, n_folds)
    scores = list()
    # 每次循环从 folds 从取出一个 fold 作为测试集,其余作为训练集,遍历整个 folds ,实现交叉验证
    for fold in folds:
        train_set = list(folds)
        train_set.remove(fold)
        # 将多个 fold 列表组合成一个 train_set 列表, 类似 union all
        """
        In [20]: l1=[[1, 2, 'a'], [11, 22, 'b']]
        In [21]: l2=[[3, 4, 'c'], [33, 44, 'd']]
        In [22]: l=[]
        In [23]: l.append(l1)
        In [24]: l.append(l2)
        In [25]: l
        Out[25]: [[[1, 2, 'a'], [11, 22, 'b']], [[3, 4, 'c'], [33, 44, 'd']]]
        In [26]: sum(l, [])
        Out[26]: [[1, 2, 'a'], [11, 22, 'b'], [3, 4, 'c'], [33, 44, 'd']]
        """
        train_set = sum(train_set, [])
        test_set = list()
        # fold 表示从原始数据集 dataset 提取出来的测试集
        for row in fold:
            row_copy = list(row)
            row_copy[-1] = None
            test_set.append(row_copy)
        predicted = algorithm(train_set, test_set, *args)
        actual = [row[-1] for row in fold]

        # 计算随机森林的预测结果的正确率
        accuracy = accuracy_metric(actual, predicted)
        scores.append(accuracy)
    return scores
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值