当前位置: 代码迷 >> 综合 >> 机器篇——集成学习(二) 细说?随机森林(Rondoom Forest)?算法
  详细解决方案

机器篇——集成学习(二) 细说?随机森林(Rondoom Forest)?算法

热度:55   发布时间:2023-12-15 09:09:54.0

返回主目录

返回集成学习目录

上一章:机器篇——集成学习(一) 细说 Bagging 算法

下一章:机器篇——集成学习(三) 细说 提升(Boosting) 算法

 

本小节,细说 随机森林(Rondoom Forest) 算法,下一小节细说 提升(Boosting) 算法

 

二. 具体算法

2. 随机森林(Rondoom Forest, RF)

    Rondom Forest 是 Bagging 算法的进化版

    (1). Rondom Forest 顾名思义,就是用随机的方式建立一个森林,森林里面由很多的决策树组成,随机森林的每一棵决策树之间是没有关联的。在得到森林之后,当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分别进行一下判断,看看这个样本应该属于哪一类(对于分类算法),然后看看哪一类被选择最多,就预测这个样本为哪一类。

 

    (2). 在建立每一棵决策树的过程中,有两点需要注意的:采样和完全分裂。

           一般很多的决策树算法都有一个重要的步骤:剪枝。但是在随机森林里不这样干,由于之前的两个随机采样的过程保证了随机性,所以就算不剪枝,也不会出现 over-fitting。按这种算法得到的随机森林的每一棵都是很弱的,但是,全部组合起来就很厉害了。

 

    (3). 随机森林基本原理

           随机森林通过自助法(bootstrap)重采样技术,从原始训练样本集  中有放回地重复随机抽样  个样本生成新的训练样本集合,然后根据自助样本集生成  个分类树组成随机森林,新数据的分类结果按分类树投票多少形成的分数而定。其实质是对决策树算法的一种改进,将多个决策树合并在一起,每棵树的建立依赖于一个独立抽取的样品,森林中的每棵树具有相同的分布,分类误差取决于每一棵树的分类能力和它们的相关性。特征选择采用随机的方法去分裂每一个节点,然后比较不同情况下产生的误差。能够检测到的内在估计误差、分类能力和相关性决定选择特征的数目。单棵树的分类能力可能很小,但在随机产生大量的决策树后,一个测试样本可以通过每一棵树的分类结果经统计后选择最可能的分类。

 

    (4). 随机森林的三个主要超参数调整:

      ①. 节点规模

            随机森林不像决策树,每一棵树叶子节点所包含的观察样本数量可能十分少。该超参数的目标是生成树的时候尽可能保持小偏差

      ②. 树的数量

            在实践中选择数百棵树一般是比较好的选择

      ③. 预测器采样的数量

            一般来说,如果一共有  个预测器,那么可以在回归任务中使用  个预测器作为采样数,在分类任务中使用  个预测器作为抽样。

 

    (5). 随机森林的实现

      ①. 随机森林中的每一棵分类树为二叉树,其生成遵循自顶向下的递归分裂原则,即从根节点开始依次对训练集进行划分:在二叉树中,根节点包含全部训练数据,按照节点纯度最小原则,分裂为左节点和右节点。它们分别包含训练数据的一个子集,按照同样的规则节点继续分裂,直到满足分支停止规则而停止生长。若节点  上的分类树全部来自于同一类别,则此节点的纯度 。

      ②. 纯度度量方法:

      a. 分类树为 Gini 准则

               

               

      b. 回归树为方差计算准则

               

               

      ③. 具体实现过程

      a. 原始训练集为 ,应用 bootstrap 法有放回地随机抽取  个新的自助样本集,并由此构建  棵分类树,每次未被抽到的样本组成了  个袋外数据。

      b. 设有  个变量,则在每一棵树的每个节点处随机抽取  个变量 ,然后在  中选择一个最具有分类能力的变量,变量分类的阈值通过检查每一个分类点确定。

      c. 每棵树最大限度地生长,不做任何修剪

      d. 将生成的多棵分类树组成随机森林,用随机森林分类器对新的数据进行判别与分类,分类结果按树分类器的投票多少而定。

 

    (6). 随机森林的优点

      a. 具有较高的准确率

      b. 在数据集上表现良好,两个随机性的引入(样本随机选择,特征随机选择),使得随机森林不容易陷入过拟合。

      c. 在当前的很多数据集上,相对其他算法有着很大的有势,两个随机性的引入,使得随机森林具有很好的抗噪声能力,训练出的模型的方差小,泛化能力强。

      d. 它能够处理很高维度(feature 很多)的数据,并且不用做特征选择,对数据集的适应能力强:既能处理离散型数据,也能处理连续型数据,数据集无需规范化(归一化)。

      e. 可生成一个  矩阵,用于度量样本之间的相似性:, 表示样本  和  出现在随机森林中同一个叶子节点的次数; 表示随机森林中树的棵数。

      f. 在创建随机森林的时候,对 generlization error 使用的是无偏估计。

      g. 训练速度快,可以得到变量重要性排序(两种:基于 OOB误分率[Out Of Bag Error, OOB; 袋外错误率,袋外误分率]的增加量和基于分裂的 Gini 下降量)。

      h. 在训练过程中,能够检测到 feature 间的互相影响

      i. 容易做成并行化方法

      j. 实现比较简单

      k. 对部分特征缺失不敏感。

 

    (7). 随机森林的局限性

      ①. 当需要推测超出范围的独立变量或非独立变量,随机森林做得并不好,此时最好使用如 MARS 那样的算法

      ②. 在某些噪声较大的样本集上, RF 模型容易陷入过拟合。

      ③. 随机森林算法在训练和预测时都比较慢

      ④. 如果需要区分的类别十分多,随机森林的表现并不会很好。

      ⑤. 取值划分比较多的特征容易对 RF 的决策产生更大的影响,从而影响拟合模型的效果。

 

    (8). 随机森林应用

      ①. 随机森林主要应用在回归和分类 

            随机森林和使用决策树作为基本分类器 Bagging 有些相似。以决策树为基本模型的 Bagging 在每次 bootstrap 放回抽样之后,产生一棵决策树,抽多少样本就生成多少棵树,在生成这些树的时候,没有进行更多的干预。而随机森林也是进行 bootstrap 抽样,但 RF 与 Bagging 的区别是:在生成每棵树的时候,每个节点变量都仅仅在随机选出的少数变量中产生。因此,不但样本是随机的,连每个节点变量(features)的产生都是随机的。

      ②. 许多研究表明,组合分类器比单一分类器的分类效果好,随机森林 RF 是一种利用多个分类树对数据进行判别与分类的方法,它在对数据进行分类的同时,还可以给出各个变量(features)的重要性评估,评估各个变量在分类器中所起的作用。

 

    (9). 随机森林模型的注意点

     设有  个样本,每个样本有  个 features,决策树们其实都是随机地接受  个样本(对样本随机采样,即对行随机采样)的  个 features(对特征随机采样,即对列随机采样),每棵决策树的  个 features 相同。每棵决策树其实都是对特定的数据进行学习归纳出分类方法,而随机取样可以保证有重复样本被不同的决策树分类,这样就可以对不同决策树的分类能力做个评价。

 

    (9). 代码演示

     某一汽车公司调查城市人口的基本情况,来判断该人 是否贷款买车,找出潜在客户,提高销售业绩。

     

    代码的目录结构如下:

    

    百度云数据下载:链接:https://pan.baidu.com/s/1WCtjdhEetOgGyiTF43ZwgQ 
                                    提取码:rrbu

 

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# ============================================
# @Time     : 2020/01/10 22:50
# @Author   : WanDaoYi
# @FileName : random_forest.py
# ============================================import os
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics
from sklearn.model_selection import GridSearchCV, train_test_splitclass RandomForest(object):def __init__(self):self.base_path = os.getcwd()self.data_path_last = "data_info/car.csv"self.data_path = os.path.join(self.base_path, self.data_path_last)self.x_train, self.x_val, self.y_train, self.y_val = self.read_data()pass# 获取数据集def read_data(self):# 读取数据文件data_info = pd.read_csv(self.data_path, encoding="gbk")print(data_info.head())# 划分数据为训练集和验证集x = data_info.values[:, : -1]y = data_info.values[:, -1]x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.3)return x_train, x_val, y_train, y_valpass# 调节 RF 最大决策树的数量 和 最大深度; 如果电脑性能好的话,可以一次性把要调节的参数,都设置进去# 这样得到的参数总体会更好点,缺点就是很耗资源和耗时。def best_n_estimators_max_depth(self):best_param = {"n_estimators": range(10, 100, 10),'max_depth': range(3, 14, 1)}# 这里注释的两个参数,是用上面的 best_param 替代来调节了。best_gsearch = GridSearchCV(estimator=RandomForestClassifier(random_state=10,min_samples_split=100,min_samples_leaf=20,# n_estimators=100,# max_depth=10,max_features='sqrt'),param_grid=best_param, scoring='roc_auc',iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'max_depth': 7, 'n_estimators': 80}# best_score: 0.9297448979591838return best_gsearch.best_params_pass# min_samples_split和决策树其他的参数存在关联,# 需要内部节点再划分所需最小样本数min_samples_split# 和叶子节点最少样本数min_samples_leaf一起调参。def best_min_samples_split_leaf(self, estimator=80, depth=7):best_param = {'min_samples_split': range(80, 150, 10),'min_samples_leaf': range(10, 60, 10)}# 这里注释的两个参数,是用上面的 best_param 替代来调节了。best_gsearch = GridSearchCV(estimator=RandomForestClassifier(random_state=10,# min_samples_split=100,# min_samples_leaf=20,n_estimators=estimator,max_depth=depth,max_features='sqrt'),param_grid=best_param, scoring='roc_auc',iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'min_samples_leaf': 10, 'min_samples_split': 80}# best_score: 0.9284018704933248return best_gsearch.best_params_pass# 对最大特征数进行调参def best_max_features(self, estimator=80, depth=7, samples_leaf=10, samples_split=80):best_param = {'max_features': range(2, 20, 1)}# 这里注释的参数,是用上面的 best_param 替代来调节了。best_gsearch = GridSearchCV(estimator=RandomForestClassifier(random_state=10,# max_features='sqrt',min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimator,max_depth=depth),param_grid=best_param, scoring='roc_auc',iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'max_features': 11}# best_score: 0.9534226789307272return best_gsearch.best_params_pass# 寻找一个比较好的随机种子,这一步,先靠经验用一个较好的随机种子,# 到后面有时间,再寻找更好的,没时间就算了。随机种子范围很大,不好找。def best_random(self, estimator=80, depth=7, samples_leaf=10, samples_split=80,features=11):best_param = {'random_state': range(1, 200, 1)}# 这里注释的参数,是用上面的 best_param 替代来调节了。best_gsearch = GridSearchCV(estimator=RandomForestClassifier(max_features=features,# random_state=10,min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimator,max_depth=depth),param_grid=best_param, scoring='roc_auc',iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'random_state': 130}# best_score: 0.9574137931034483return best_gsearch.best_params_pass# 根据上面调节得到比较好的参数,进行模型训练和验证# 泛化能力如果依旧不给力?---> 增加数据集def best_param_rf(self, estimator=80, depth=7, samples_leaf=10, samples_split=80,features=11, random=130):best_model = RandomForestClassifier(max_features=features,random_state=random,min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimator,oob_score=True,max_depth=depth)best_model.fit(self.x_train, self.y_train)oob_score = best_model.oob_score_print("oob_score: {}".format(oob_score))y_pred = best_model.predict(self.x_val)acc_score = metrics.accuracy_score(self.y_val, y_pred)print("acc_score: {}".format(acc_score))print("score: {}".format(best_model.score(self.x_val, self.y_val)))print("AUC Score: {}".format(metrics.roc_auc_score(self.y_val, y_pred)))# y_proba = best_model.predict_proba(self.x_val)# # 预测为 0 的概率# y_zero = y_proba[:, 0]# # 预测为 1 的概率# y_one = y_proba[:, 1]# print("AUC Score: {}".format(metrics.roc_auc_score(self.y_val, y_one)))passif __name__ == "__main__":demo = RandomForest()# 调节 RF 各个参数# best_estimators_depth = demo.best_n_estimators_max_depth()# n_estimators = best_estimators_depth["n_estimators"]# max_depth = best_estimators_depth["max_depth"]# best_leaf_split = demo.best_min_samples_split_leaf(estimator=n_estimators, depth=max_depth)# min_samples_leaf = best_leaf_split["min_samples_leaf"]# min_samples_split = best_leaf_split["min_samples_split"]# best_max_features = demo.best_max_features(estimator=n_estimators, depth=max_depth,#                                            samples_leaf=min_samples_leaf,#                                            samples_split=min_samples_split)# max_features = best_max_features["max_features"]# best_random_state = demo.best_random(estimator=n_estimators,#                                      depth=max_depth,#                                      samples_leaf=min_samples_leaf,#                                      samples_split=min_samples_split,#                                      features=max_features#                                      )# random_state = best_random_state["random_state"]demo.best_param_rf()pass

      运行结果如下:

      

    使用 RF 想要效果好,关键是不断的调试,寻找较好的参数,利用这些较好的参数,来获取比较满意的预测结果。

     在这个项目里面,命中率不是很高,原因之一:是数据量还不够多;原因之二:金额部分为连续值,不好离散化,如果离散化后,理论上效果会好些。

 

 

 

                 

 

 

 

返回主目录

返回集成学习目录

上一章:机器篇——集成学习(一) 细说 Bagging 算法

下一章:机器篇——集成学习(三) 细说 提升(Boosting) 算法

 

 

 

 

 

 

  相关解决方案