随机森林简介

随机森林是机器学习一种常用的方法。它是以决策树为基础,用随机的方式排列建立的,森林里每个决策树之间都是没有关联的。 在得到森林之后,当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分别进行一下判断,看看这个样本应该属于哪一类(对于分类算法),然后看看哪一类被选择最多,就预测这个样本为那一类。随机森林可以用来进行无监督学习聚类和异常点检测。

决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。其每个非叶节点表示一个特征属性上的测试,每个分支代表这个特征属性在某个值域上的输出,而每个叶节点存放一个类别。使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。详情请前往先前的推送。

算法流程

随机森林里每棵树按照如下规则生成:

  • 1、如果训练集大小为N,对于每棵树而言,随机且有放回地从训练集中的抽取N个训练样本,作为该树的训练集;

PS:从这里我们可以知道:每棵树的训练集都是不同的,而且里面包含重复的训练样本。

  • 2、如果每个样本的特征维度为M,指定一个常数m<<M,随机地从M个特征中选取m个特征子集,每次树进行分裂时,从这m个属性中采用某种策略(比如说信息增益)来选择1个属性作为该节点的分裂属性。

  • 3、每棵树都尽最大程度的生长,并且没有剪枝过程。

  • 4、 按照步骤1~3建立大量的决策树,这样就构成了随机森林了。

一开始我们提到的随机森林中的“随机”就是指的这里的两个随机性。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)

随机森林优点

  • 它能够处理很高维度(feature很多)的数据,并且不用做特征选择(因为特征子集是随机选择的)。

  • 在创建随机森林的时候,对generlization error使用的是无偏估计,模型泛化能力强。

  • 训练速度快,容易做成并行化方法(训练时树与树之间是相互独立的)。

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

  • 对于不平衡的数据集来说,它可以平衡误差。

随机森林(Random Forest,简称RF)主要应用于回归和分类,且拥有广泛的应用前景,从市场营销到医疗保健保险,既可以用来做市场营销模拟的建模,统计客户来源,保留和流失,也可用来预测疾病的风险和病患者的易感性。最近几年的国内外大赛,包括2013年百度校园电影推荐系统大赛、2014年阿里巴巴天池大数据竞赛以及Kaggle数据科学竞赛,参赛者对随机森林的使用占有相当高的比例。而且随机森林在运算量没有显著提高的情况下精度得到了很大的改善。

代码实现

随机森林实现分类算法

import pandas as pd
import numpy as np
import random
import math
import collections
from sklearn.externals.joblib import Parallel, delayedclass Tree(object):"""定义一棵决策树"""def __init__(self):self.split_feature = Noneself.split_value = Noneself.leaf_value = Noneself.tree_left = Noneself.tree_right = Nonedef calc_predict_value(self, dataset):"""通过递归决策树找到样本所属叶子节点"""if self.leaf_value is not None:return self.leaf_valueelif dataset[self.split_feature] <= self.split_value:return self.tree_left.calc_predict_value(dataset)else:return self.tree_right.calc_predict_value(dataset)def describe_tree(self):"""以json形式打印决策树,方便查看树结构"""if not self.tree_left and not self.tree_right:leaf_info = "{leaf_value:" + str(self.leaf_value) + "}"return leaf_infoleft_info = self.tree_left.describe_tree()right_info = self.tree_right.describe_tree()tree_structure = "{split_feature:" + str(self.split_feature) + \",split_value:" + str(self.split_value) + \",left_tree:" + left_info + \",right_tree:" + right_info + "}"return tree_structureclass RandomForestClassifier(object):def __init__(self, n_estimators=10, max_depth=-1, min_samples_split=2, min_samples_leaf=1,min_split_gain=0.0, colsample_bytree=None, subsample=0.8, random_state=None):"""随机森林参数----------n_estimators:      树数量max_depth:         树深度,-1表示不限制深度min_samples_split: 节点分裂所需的最小样本数量,小于该值节点终止分裂min_samples_leaf:  叶子节点最少样本数量,小于该值叶子被合并min_split_gain:    分裂所需的最小增益,小于该值节点终止分裂colsample_bytree:  列采样设置,可取[sqrt、log2]。sqrt表示随机选择sqrt(n_features)个特征,log2表示随机选择log(n_features)个特征,设置为其他则不进行列采样subsample:         行采样比例random_state:      随机种子,设置之后每次生成的n_estimators个样本集不会变,确保实验可重复"""self.n_estimators = n_estimatorsself.max_depth = max_depth if max_depth != -1 else float('inf')self.min_samples_split = min_samples_splitself.min_samples_leaf = min_samples_leafself.min_split_gain = min_split_gainself.colsample_bytree = colsample_bytreeself.subsample = subsampleself.random_state = random_stateself.trees = Noneself.feature_importances_ = dict()def fit(self, dataset, targets):"""模型训练入口"""assert targets.unique().__len__() == 2, "There must be two class for targets!"targets = targets.to_frame(name='label')if self.random_state:random.seed(self.random_state)random_state_stages = random.sample(range(self.n_estimators), self.n_estimators)# 两种列采样方式if self.colsample_bytree == "sqrt":self.colsample_bytree = int(len(dataset.columns) ** 0.5)elif self.colsample_bytree == "log2":self.colsample_bytree = int(math.log(len(dataset.columns)))else:self.colsample_bytree = len(dataset.columns)# 并行建立多棵决策树self.trees = Parallel(n_jobs=-1, verbose=0, backend="threading")(delayed(self._parallel_build_trees)(dataset, targets, random_state)for random_state in random_state_stages)def _parallel_build_trees(self, dataset, targets, random_state):"""bootstrap有放回抽样生成训练样本集,建立决策树"""subcol_index = random.sample(dataset.columns.tolist(), self.colsample_bytree)dataset_stage = dataset.sample(n=int(self.subsample * len(dataset)), replace=True, random_state=random_state).reset_index(drop=True)dataset_stage = dataset_stage.loc[:, subcol_index]targets_stage = targets.sample(n=int(self.subsample * len(dataset)), replace=True, random_state=random_state).reset_index(drop=True)tree = self._build_single_tree(dataset_stage, targets_stage, depth=0)print(tree.describe_tree())return treedef _build_single_tree(self, dataset, targets, depth):"""递归建立决策树"""# 如果该节点的类别全都一样/样本小于分裂所需最小样本数量,则选取出现次数最多的类别。终止分裂if len(targets['label'].unique()) <= 1 or dataset.__len__() <= self.min_samples_split:tree = Tree()tree.leaf_value = self.calc_leaf_value(targets['label'])return treeif depth < self.max_depth:best_split_feature, best_split_value, best_split_gain = self.choose_best_feature(dataset, targets)left_dataset, right_dataset, left_targets, right_targets = \self.split_dataset(dataset, targets, best_split_feature, best_split_value)tree = Tree()# 如果父节点分裂后,左叶子节点/右叶子节点样本小于设置的叶子节点最小样本数量,则该父节点终止分裂if left_dataset.__len__() <= self.min_samples_leaf or \right_dataset.__len__() <= self.min_samples_leaf or \best_split_gain <= self.min_split_gain:tree.leaf_value = self.calc_leaf_value(targets['label'])return treeelse:# 如果分裂的时候用到该特征,则该特征的importance加1self.feature_importances_[best_split_feature] = \self.feature_importances_.get(best_split_feature, 0) + 1tree.split_feature = best_split_featuretree.split_value = best_split_valuetree.tree_left = self._build_single_tree(left_dataset, left_targets, depth+1)tree.tree_right = self._build_single_tree(right_dataset, right_targets, depth+1)return tree# 如果树的深度超过预设值,则终止分裂else:tree = Tree()tree.leaf_value = self.calc_leaf_value(targets['label'])return treedef choose_best_feature(self, dataset, targets):"""寻找最好的数据集划分方式,找到最优分裂特征、分裂阈值、分裂增益"""best_split_gain = 1best_split_feature = Nonebest_split_value = Nonefor feature in dataset.columns:if dataset[feature].unique().__len__() <= 100:unique_values = sorted(dataset[feature].unique().tolist())# 如果该维度特征取值太多,则选择100个百分位值作为待选分裂阈值else:unique_values = np.unique([np.percentile(dataset[feature], x)for x in np.linspace(0, 100, 100)])# 对可能的分裂阈值求分裂增益,选取增益最大的阈值for split_value in unique_values:left_targets = targets[dataset[feature] <= split_value]right_targets = targets[dataset[feature] > split_value]split_gain = self.calc_gini(left_targets['label'], right_targets['label'])if split_gain < best_split_gain:best_split_feature = featurebest_split_value = split_valuebest_split_gain = split_gainreturn best_split_feature, best_split_value, best_split_gain@staticmethoddef calc_leaf_value(targets):"""选择样本中出现次数最多的类别作为叶子节点取值"""label_counts = collections.Counter(targets)major_label = max(zip(label_counts.values(), label_counts.keys()))return major_label[1]@staticmethoddef calc_gini(left_targets, right_targets):"""分类树采用基尼指数作为指标来选择最优分裂点"""split_gain = 0for targets in [left_targets, right_targets]:gini = 1# 统计每个类别有多少样本,然后计算ginilabel_counts = collections.Counter(targets)for key in label_counts:prob = label_counts[key] * 1.0 / len(targets)gini -= prob ** 2split_gain += len(targets) * 1.0 / (len(left_targets) + len(right_targets)) * ginireturn split_gain@staticmethoddef split_dataset(dataset, targets, split_feature, split_value):"""根据特征和阈值将样本划分成左右两份,左边小于等于阈值,右边大于阈值"""left_dataset = dataset[dataset[split_feature] <= split_value]left_targets = targets[dataset[split_feature] <= split_value]right_dataset = dataset[dataset[split_feature] > split_value]right_targets = targets[dataset[split_feature] > split_value]return left_dataset, right_dataset, left_targets, right_targetsdef predict(self, dataset):"""输入样本,预测所属类别"""res = []for _, row in dataset.iterrows():pred_list = []# 统计每棵树的预测结果,选取出现次数最多的结果作为最终类别for tree in self.trees:pred_list.append(tree.calc_predict_value(row))pred_label_counts = collections.Counter(pred_list)pred_label = max(zip(pred_label_counts.values(), pred_label_counts.keys()))res.append(pred_label[1])return np.array(res)if __name__ == '__main__':df = pd.read_csv("source/wine.txt")df = df[df['label'].isin([1, 2])].sample(frac=1, random_state=66).reset_index(drop=True)clf = RandomForestClassifier(n_estimators=5,max_depth=5,min_samples_split=6,min_samples_leaf=2,min_split_gain=0.0,colsample_bytree="sqrt",subsample=0.8,random_state=66)train_count = int(0.7 * len(df))feature_list = ["Alcohol", "Malic acid", "Ash", "Alcalinity of ash", "Magnesium", "Total phenols", "Flavanoids", "Nonflavanoid phenols", "Proanthocyanins", "Color intensity", "Hue", "OD280/OD315 of diluted wines", "Proline"]clf.fit(df.loc[:train_count, feature_list], df.loc[:train_count, 'label'])from sklearn import metricsprint(metrics.accuracy_score(df.loc[:train_count, 'label'], clf.predict(df.loc[:train_count, feature_list])))print(metrics.accuracy_score(df.loc[train_count:, 'label'], clf.predict(df.loc[train_count:, feature_list])))

现在实现随机森林基本上都是可以直接调用sklearn里面的API。

from sklearn.ensemble import RandomForestClassifier

参考资料

[1]https://blog.csdn.net/yangyin007/article/details/82385967

[2]https://zhuanlan.zhihu.com/p/22097796

[3]https://github.com/zhaoxingfeng/RandomForest/blob/master/RandomForestClassification.py

机器学习十大经典算法之随机森林相关推荐

  1. 机器学习十大经典算法之岭回归和LASSO回归

    机器学习十大经典算法之岭回归和LASSO回归(学习笔记整理:https://blog.csdn.net/weixin_43374551/article/details/83688913

  2. 机器学习十大经典算法:深入浅出聊贝叶斯决策(贝叶斯公式,最小风险贝叶斯,最小错误贝叶斯)

    前言    常听人说,在学习一个东西时,如果能够深入浅出的讲给别人听,才算是真的懂了.最近正好在学模式识别,于是就用它来练笔了.贝叶斯决策(Bayes Decision) 是十大经典机器学习算法之一, ...

  3. pagerank数据集_机器学习十大经典算法-PageRank(附实践代码)

    Yo, yo, check it out. 保证看完不晕倒... 如果公式让你脑瓜疼,请忽略公式,或者忽略脑瓜. Kagging咖金:推荐系统之关联规则(附实践代码)​zhuanlan.zhihu.c ...

  4. 机器学习十大经典算法之决策树

    机器学习经典十大算法 机器学习/人工智能的子领域在过去几年越来越受欢迎.目前大数据在科技行业已经炙手可热,而基于大量数据来进行预测或者得出建议的机器学习无疑是非常强大的.一些最常见的机器学习例子,比如 ...

  5. 机器学习十大经典算法

    本文介绍了机器学习新手需要了解的 10 大算法,包括线性回归.Logistic 回归.朴素贝叶斯.K 近邻算法等. 在机器学习中,有一种叫做「没有免费的午餐」的定理.简而言之,它指出没有任何一种算法对 ...

  6. 机器学习十大经典算法之KNN最近邻算法

    KNN简介 KNN(K-NearestNeighbor)是机器学习入门级的分类算法,非常简单.它实现将距离近的样本点划为同一类别:KNN中的K指的是近邻个数,也就是最近的K个点 :根据它距离最近的K个 ...

  7. 机器学习十大经典算法之K-Means聚类算法

    聚类介绍 聚类在机器学习,数据挖掘,模式识别,图像分析以及生物信息等领域有广泛的应用.聚类是把相似的对象通过静态分类的方法分成不同的组别或者更多的子集(subset),这样让在同一个子集中的成员对象都 ...

  8. 机器学习十大经典算法之逻辑回归

    逻辑回归简介 逻辑回归虽然名称有回归两字,但是用来做分类算法的,大家都熟悉线性回归,一般形式是Y=aX+bY=aX+bY=aX+b,y的取值范围是[-∞, +∞].因其简单而受到工业界的关注. Y的取 ...

  9. 机器学习十大经典算法之K-近邻算法(学习笔记整理)

    一.算法概述 K-近邻算法(k-Nearest Neighbor,KNN)是机器学习算法中最简单最容易理解的算法.该算法的思路是:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的 ...

最新文章

  1. 计算程序运行时间(time_t, clock_t)
  2. 使用wmi的snmp类查询计算机网卡信息的源代码,在java中使用SNMP和/或wmi / wmic进行进程到端口的映射...
  3. import是引进外部函数吗_你必须要知道的Python中的main函数
  4. 研究动机(Motivation)-如何写好科技论文之我见(一)
  5. Linux系统下部署Tomcat
  6. canvas合成图片 圣诞节新技能戴帽
  7. MySQL查询语句转postGRE语句_PostgreSQL DBA常用SQL查询语句
  8. python yield: send, close, throw
  9. 夺命雷公狗ThinkPHP项目之----企业网站13之文章列表页的实现(主要是分页的实现)...
  10. 根据sessionId获取Session对象
  11. (转)ASP.NET MVC 3.0:基于Ajax的表单提交,A页面认证失败后页面被强转至登录页面,待登录成功将如何回到A页面?...
  12. malloc和free的常识性问题
  13. 做java前端需要学习哪些知识,2022最新
  14. 【计算机系统组成】IO设备知识要点
  15. 一维优化 方法c语言,第三章一维优化方法
  16. 别人总结归纳很全的三方库
  17. App Store拒绝原因
  18. 面试官:说说微信小程序的支付流程?
  19. ExtCertPathValidatorException: Could not validate certificate: null
  20. 理解MySQL复制(Replication)

热门文章

  1. java解析excel生成sql文件
  2. JavaScript get set方法 ES5/ES6写法
  3. NSString 截取字符串
  4. 下载加载linux下用vmware-mount挂载vmdk虚拟硬盘分区
  5. 【译】《Pro ASP.NET MVC4 4th Edition》第三章(二)
  6. JavaScript split() 方法
  7. 计算机国家实验教学示范中心,教育部、财政部关于批准2007年国家级实验教学示范中心建设单位的通知...
  8. dbforge studio for oracle 注册,dbForge Studio for Oracle
  9. openmv 神经网络 超出内存_【openmv】openmv各种bug问题记录
  10. 数据3分钟丨MariaDB将借壳上市;前融云CTO杨攀加入涛思数据;​Elastic 8.0正式发布...