众所周知,树模型是高方差、低偏差的模型。因此,它们容易过度拟合训练数据。如果我们不修剪树模型或引入早期停止标准(例如每个叶节点的最小实例数),我们可以概括一下树模型的作用,这很吸引人。好吧,它尝试沿着特征拆分数据,直到实例对于目标特征的值来说是纯的,没有剩下的数据,或者没有剩下的特征可以吐出数据集。如果上述之一成立,我们就会生长一个叶节点。结果是树模型增长到最大深度,并因此尝试尽可能精确地重塑训练数据,这很容易导致过度拟合。像(ID3 或 CART)这样的经典树模型的另一个缺点是它们相对不稳定。
例如,考虑使用分类缩放特征 *A* 作为“根节点特征”的情况。接下来,这个特征从数据集中被替换,不再存在于子树中。现在想象一下我们替换数据集中的单行的情况,这种变化导致现在特征 *B* 分别具有最大的信息增益或方差减少的情况。这意味着什么?好吧,现在特征 *B* 比特征 *A* 更受欢迎,因为它作为“根节点特征”会导致完全不同的树,因为我们改变了数据集中的一个实例。这种情况不仅可能发生在根节点上,也可能发生在树的所有内部节点上。
请注意,在上图中,建议将目标特征列中的“X”作为实际值的通配符。随机森林方法已被证明是解决过度拟合和不稳定问题的最有用的方法之一。
随机森林方法基于两个概念,称为装袋和子空间采样。Bagging 是*bootstrapaggregation* 的缩写形式。在这里,我们创建了大量与从原始数据集提取的原始数据集相同长度的数据集,并进行了替换(装袋中的 *bootstrap*)。然后,我们为每个自举数据集训练一个树模型,并将这些模型的大多数预测作为我们的预测(装袋中的 *聚合*)。这里我们取回归树模型的均值或中位数,以及分类树模型的众数。
你可能会问为什么我们抽取样本有替换?好吧,让我们假设我们的原始数据集有 100 个实例(行),并且我们想要创建一个由 10 棵树组成的随机森林模型,其中每棵树都在与原始数据集长度相同的数据集上进行训练。如果我们现在从原始数据集中抽取 100 个样本而不进行替换,会发生什么?确切地说,没有什么,因为我们形象地说只是将数据集从一个容器转移到另一个容器。如果我们这样做 10 次并在每个数据集上训练一个树模型,我们将获得 10 倍完全相同的数据集(假设模型参数相同)。如果我们现在预测一个看不见的查询实例并对 10 个树模型的结果进行平均,即运行随机森林程序,我们将一无所获。这让我们回到最初的问题,为什么我们使用装袋方法?我们使用bagging方法(记住重采样)是因为我们知道单树模型对数据的变化非常敏感并且有很大的差异。为了解决这个问题,我们在不同组成的数据集上创建多个模型,并取其预测的平均值。这里我们应用了平均多个模型的方差可以减少方差的原理。
我们可以通过一个代表不同树模型并从相对较远的距离向目标射击的枪手来用简化的术语来说明这一点。因此,步枪(我们的数据集)的小幅移动将导致完全不同的分数(我们模型的输出)。但平均得分击中了靶心。这意味着平均值的方差小于单个模型的方差
伏一个r(X¯)=σ2n 瓦H电子r电子 σ2=伏一个r(X))
随机森林方法所基于的第二个概念是子空间采样的概念。Bagging 使我们朝着拥有更强大模型创建更准确结果的目标前进。不幸的是,事实证明袋装模型是相关的,因此通常相对相等。也就是说,基于相关性,它们会产生相似的结果。这可以归结为 Bagging 使用每个模型的整个特征集(所有描述性特征)。现在假设有一个或两个非常强大的特征,它们在“预测性”方面超越了所有其他特征(例如,这两个特征的信息增益比其他特征大得多)。即使我们通过替换采样来改变数据的组成,这两个很可能仍然是主要特征,因此将分别作为根节点或第一个隐藏节点层。结果,这些树看起来都非常相似。
现在事实证明,如果我们使用纯装袋,数据中隐藏的结构可能会丢失。为了唤起这些隐藏的结构,除了这两个强大的特征之外,我们还必须加强它们,让它们有投票的声音。我们怎样才能做到这一点?好吧,最简单的方法是从我们的数据集中移动这两个。显然,这不是一个好主意,因为我们想要拥有它们,但也想要考虑并合并不那么占优势的特征的声音(投票)。长话短说,我们通过随机绘制一些米⊂p 树中每个分裂的不同特征,其中 p表示每个分割的特征空间。这里文献推荐米=p.bagging 和子空间采样的结合给了我们想要的随机森林模型,它的奥秘在于假设大量弱学习器在预测准确性方面比一个强学习器更好 - 或者你为什么认为“问“观众生命线”被设置为*谁想成为百万富翁*节目中的生命线??-. **完成 - 恭喜!您现在知道随机森林背后的概念。** 这是最强大的机器学习算法之一。
幸运的是,几乎没有我们以前从未见过的“随机森林”特定数学这样的东西。基本数学原理与分类 和回归树相同。
与随机森林的主要区别在于,我们多次执行我们在决策树和回归树上所做的所有步骤。也就是说,如果我们有一个分类缩放的目标特征和平均值或中位数,如果我们有一个连续缩放的目标特征,我们会种植大量的树,让它们做出决策并通过最频繁(模式)的决策来聚合这些决策。因此,对于分类缩放的目标特征:
最大参数吨 ∈ 吨 吨r电子电子米○d电子升秒 在哪里
最大参数 代表模式(多数票), 吨 表示单个树模型的结果(预测), 吨 是树模型创建的结果空间(所有结果)和 吨r电子电子米○d电子升秒 是单树模型,每个模型都产生一个结果 吨. 因此最常发生的结果吨 ∈ 吨 作为预测返回。
如上所述,随机森林算法基于自举聚合和子空间采样原理的组合。因此:
Tree_Outcomes = [] 对于 i=1 到 n:从原始数据创建引导样本 使用通用停止标准在该引导数据上训练树模型,其中: 对于每个分割:子空间样本数 m=sqrt(p)特征空间p在这个节点选择最好的特征(最高IG,最低方差)来分割数据沿特征分割数据删除特征将每棵树的结果添加到Tree_Outcomes Random_Forest_Outcome = Majority vote(模式) Tree_Outcomes 中元素的数量 # 对于分类 Random_Forest_Outcome = Tree_Outcomes 中元素的多数(平均值/中位数)# 对于回归返回 Random_Forest_Outcome
幸运的是,对于随机森林分类模型,我们可以使用分类树章节中创建的大部分分类树代码(对于随机森林回归模型也是如此)。我们必须在实际的树构建代码中实现的唯一真正改变是我们在每次拆分时使用一个大小为的随机特征样本米=p 在哪里 p表示该节点的特征空间。所有其他更改都是“围绕”树构建代码进行的。也就是说,从代码的角度来看,随机森林是通过为树代码提供一种很好的“超级英雄套件”来创建的。
我们必须实现的第三个变化是,随机森林模型实际上不能像普通树模型一样可视化,因此可视化部分是过时的事件,内部每棵树都是构建的,我们实际上可以绘制每棵树来推理决策随机森林模型。但是,当树模型的数量增长到数百或数千时,这没有用。
第四个变化是我们必须添加一个列表,其中存储单树模型的预测,最终返回该列表的众数值作为预测。
在这里,只评论了由于随机森林的创建而导致的造树代码的变化。有关树构建代码本身的进一步评论,请参阅分类树页面。为了可视化,我们将在 这里使用UCI 蘑菇数据集。
"""
导入需要的python包
"""
import pandas as pd
import numpy as np
from pprint import pprint
import scipy.stats as sps
数据集 = pd 。read_csv ( 'data\mushroom.csv' , header = None )
dataset = dataset 。样本( frac = 1 )
数据集。列 = [ '目标' , '帽形状' , '帽表面' , '帽颜色' , '瘀伤' , '气味' , '鳃附件' , '鳃间距' ,
'鳃大小' , '鳃色' ,'茎根' ,'茎表面-环' ,'茎-表面-环' ,'茎-色-环' ,
'茎-色-环' ,'面纱型' , '面纱颜色' , '环号' , '环型' , '孢子印色' , '人口' ,
'栖息地' ]
############################################### ############################################### #######
########################################### ############################################### ##############
def entropy ( target_col ):
elements , counts = np 。unique ( target_col , return_counts = True )
entropy = np 。总和([(-计数[我] / NP 。总和(计数))* NP 。LOG2 (计数[我] / NP 。总和(计数)) for i 在 范围内(len (元素))])
返回 熵
############################################### ############################################### #######
########################################### ############################################### ##############
def InfoGain ( data , split_attribute_name , target_name = "target" ):
#计算总数据集的熵
total_entropy = entropy ( data [ target_name ])
##计算数据集的熵
#计算分割属性
vals的值和相应的计数,counts = np 。唯一(数据[ split_attribute_name ], return_counts = True )
#计算加权熵
Weighted_Entropy = np . sum ([( counts [ i ] / np . sum ( counts )) * entropy ( data . where ( data [ split_attribute_name ] == vals [ i ]) . dropna ()[ target_name ]) for i in range ( len ( vals ) ))])
#计算信息增益
Information_Gain = total_entropy - Weighted_Entropy
return Information_Gain
############################################### ############################################### #######
########################################### ############################################### ##############
def ID3 ( data , originaldata , features , target_attribute_name = "target" , parent_node_class = None ): #定义
停止条件 --> 如果满足其中之一,我们要返回一个叶子节点#
#如果所有 target_values 的值相同,则返回此值
if len ( np . unique ( data [ target_attribute_name ])) <= 1 :
return np . 唯一(数据[目标属性名称])[ 0 ]
#如果数据集为空,则返回原始数据集中的模式目标特征值
elif len ( data ) == 0 :
return np . 唯一( originaldata [ target_attribute_name ]) [ np . argmax (NP 。独特(originaldata [ target_attribute_name ],return_counts =真)[ 1 ])]
#如果特征空间为空,则返回直接父节点的模式目标特征值 --> 注意#
直接父节点是调用ID3算法当前运行的节点,因此#
模式目标特征值存储在 parent_node_class 变量中。
elif len ( features ) == 0 :
返回 parent_node_class
#如果以上都不成立,那就种树吧!
else :
#设置此节点的默认值 --> 当前节点的模式目标特征值
parent_node_class = np . 唯一(数据[目标属性名称])[ np . argmax (NP 。独特(数据[ target_attribute_name ],return_counts =真)[ 1 ])]
############################################### ############################################### ############
############!!!!!!!!!实现子空间采样。绘制多个 m = sqrt(p) 特征!!!!!!!!!#############
################## ############################################### #########################################
功能 = np 。随机的。选择(特征,大小= NP 。INT (NP 。SQRT (len个(特征))),取代=假)
#选择最能分割数据集的特征
item_values = [ InfoGain ( data , feature , target_attribute_name ) for feature in features ] #返回数据集中特征的信息增益值
best_feature_index = np 。argmax ( item_values )
best_feature = features [ best_feature_index ]
#创建树结构。根
在第一次运行
树中 获取具有最大信息#gain的特征名称 (best_feature) = { best_feature :{}}
#从特征空间中移除具有最佳信息增益的特征
features = [ i for i in features if i != best_feature ]
#为根节点特征的每个可能值在根节点下生长一个分支
对于 价值 的 NP 。unique ( data [ best_feature ]):
value = value
#按照信息增益最大的特征
值分割数据集,然后创建 sub_datasets sub_data = data 。其中(数据[ best_feature ] == 值)。滴滴()
#使用新参数为每个子数据集调用ID3算法-->递归来了!
subtree = ID3 ( sub_data , dataset , features , target_attribute_name , parent_node_class )
#添加子树,从sub_dataset生长到根节点
树下的树[ best_feature ][ value ] = subtree
返回(树)
############################################### ############################################### #######
########################################### ############################################### ##############
def 预测(查询,树,默认 = 'p' ):
对于 关键 的 列表(查询。键()):
如果 键 在 列表(树。键()):
尝试:
结果 = 树[关键] [查询[关键]
除了:
返回 默认的
结果 = 树[关键] [查询[ key ]]
if isinstance ( result , dict ):
return 预测(查询,结果)
else :
返回 结果
############################################### ############################################### #######
########################################### ############################################### ##############
def train_test_split ( dataset ):
training_data = dataset 。iloc [: round ( 0.75 * len ( dataset ))] 。reset_index ( drop = True ) #我们删除索引分别重新标记索引
#starting form 0,因为我们不想遇到关于行标签/索引
testing_data = dataset 的错误。iloc [ round ( 0.75 * len ( dataset )):] 。reset_index ( drop = True )
返回 training_data , testing_data
training_data = train_test_split (数据集)[ 0 ]
testing_data = train_test_split (数据集)[ 1 ]
############################################### ############################################### #######
########################################### ############################################### ##############
#######训练随机森林模型###########
def RandomForest_Train ( dataset , number_of_Trees ):
#创建一个存储单森林的列表
random_forest_sub_tree = []
#Create a number of n models
for i in range ( number_of_Trees ):
#Create a number of bootstrap sampled datasets from the original dataset
bootstrap_sample = dataset . 样本(frac = 1 ,replace = True )
#通过调用train_test_split函数来创建训练和测试数据集
bootstrap_training_data = train_test_split ( bootstrap_sample )[ 0 ]
bootstrap_testing_data = train_test_split ( bootstrap_sample )[ 1 ]
#为每个训练数据生成一个树模型
#我们在 ID3 算法本身中实现了子空间采样。因此,看看上面的 ID3 算法!
random_forest_sub_tree 。追加(ID3 (bootstrap_training_data ,bootstrap_training_data ,bootstrap_training_data 。压降(标签= [ '目标' ],轴= 1 )。列))
返回 random_forest_sub_tree
random_forest = RandomForest_Train (数据集,50 )
#######预测一个新的查询实例###########
def RandomForest_Predict ( query , random_forest , default = 'p' ):
predictions = []
for tree in random_forest :
predictions 。append ( predict ( query , tree , default ))
返回 sps 。模式(预测)[ 0 ][ 0 ]
查询 = testing_data 。iloc [ 0 ,:] 。下降('目标' )。to_dict ()
query_target = testing_data 。ILOC [ 0 ,0 ]
打印('目标:' ,query_target )
预测 = RandomForest_Predict (查询,random_forest )
打印('预测:' ,预测)
#######在测试数据上测试模型并返回准确度###########
def RandomForest_Test ( data , random_forest ):
data [ 'predictions' ] = None
for i in range ( len (数据)):
查询 = 数据。iloc [ i ,:] 。下降('目标' )。to_dict ()
数据。loc [ i , '预测' ] = RandomForest_Predict ( query , random_forest , default = 'p' )
accuracy = sum ( data [ 'predictions' ] == data [ 'target' ]) / len ( data ) * 100
#print('预测准确度为:',sum (data['predictions'] == data['target'])/len(data)*100,'%')
返回 精度
RandomForest_Test (testing_data ,random_forest )
目标:e
预测:e
c:\users\tobia\python\lib\site-packages\scipy\stats\stats.py:245: RuntimeWarning: 无法正确检查输入数组的 nan 值。nan 值将被忽略。
“值。nan 值将被忽略。”,运行时警告)
预测准确率为:88.72476612506155%
88.72476612506155
############################################### ############################################### ##########
##########画出预测精度相对于树中的随机森林#############数
## ############################################### ############################################### ########
import matplotlib.pyplot as plt
from matplotlib import style
style 。使用('五三十八' )
无花果 = plt 。图( figsize = ( 15 , 10 ))
ax0 = 图。add_subplot ( 111 )
准确率 = []
对于 i 在 范围( 1 , 11 , 1 ):
random_forest = RandomForest_Train ( dataset , i )
accuracy 。追加(RandomForest_Test (testing_data ,random_forest ))
对于 i 在 范围( 10 , 110 , 10 ):
random_forest = RandomForest_Train ( dataset , i )
准确度。追加(RandomForest_Test (testing_data ,random_forest ))
对于 i 在 范围( 100 , 1100 , 100 ):
random_forest = RandomForest_Train ( dataset , i )
精度。追加(RandomForest_Test (testing_data ,random_forest ))
打印(准确度)
ax0 。绘图( np . logspace ( 0 , 3 , 30 ),准确度)
ax0 . set_yticks ( np . linspace ( 50 , 100 , 50 ))
ax0 . set_title (“关于随机森林中树木数量的准确性” )
ax0 。set_xscale ( 'log' )
ax0 。set_xlabel (“树的数量” )
ax0 。set_ylabel ( '准确度(%)' )
PLT 。显示()
c:\users\tobia\python\lib\site-packages\scipy\stats\stats.py:245: RuntimeWarning: 无法正确检查输入数组的 nan 值。nan 值将被忽略。“值。nan 值将被忽略。”,运行时警告)
预测准确率为:78.72968980797637%
预测准确率为:76.71097981290005 %
预测准确率为:82.7178729689808%
预测准确率为:92.66371245691778%
预测准确率为:88.13392417528311%
预测准确率为:90.00492368291482%
预测准确率为:89.51255539143278%
预测准确率为:89.51255539143278%
预测准确率为:85.37666174298376%
预测准确率为:88.33087149187593%
预测准确率为:80.20679468242245%
预测准确率为:89.85721319547021%
预测准确率为:89.75873953717381%
预测准确率为:89.561792220581%
预测准确率为:90.15263417035942%
预测准确率为:89.75873953717381%
预测准确率为:92.31905465288035%
预测准确率为:88.87247661250616 %
预测准确率为:90.98966026587888%
预测准确率为:89.36484490398819%
预测准确率为:89.6110290497292%
预测准确率为:89.75873953717381%
预测准确率为:89.6602658788774%
预测准确率为:90.15263417035942%
预测准确率为:89.90645002461841%
预测准确率为:90.15263417035942%
预测准确率为:89.7095027080256%
预测准确率为:90.00492368291482%
预测准确率为:89.51255539143278%
预测准确率为:90.15263417035942%
[78.72968980797637,76.71097981290005,82.7178729689808,92.66371245691778,88.13392417528311,90.00492368291482,89.51255539143278,89.51255539143278,85.37666174298376,88.33087149187593,80.20679468242245,89.85721319547021,89.75873953717381,89.561792220581,90.15263417035942,89.75873953717381,92.31905465288035,88.87247661250616,90.98966026587888,89.36484490398819,89.6110290497292,89.75873953717381,89.6602658788774,90.15263417035942,89.90645002461841 , 90.15263417035942, 89.7095027080256, 90.00492368291482, 89.51255539143278, 90.15263417035942]
鉴于数据集和这种模型,我们实现了大约 90% 的准确率,考虑到我们的模型是“从头开始硬编码”,既不是高度优化的,也不是稳健的随机森林模型,这是相当不错的。我们也没有改变每个分割随机选择的特征数 *m*,这也是一个可以影响预测准确性的参数。
正如我们所看到的,一旦树的数量增长到很大的 n(注意对数缩放的 x 轴),精度曲线就会变平。尽管据说随机森林模型“不能过度拟合数据”,但进一步增加树的数量不会进一步提高模型的准确性。然而,随机森林模型的一个缺点是它们需要相对较长的时间来训练,尤其是在树的数量设置为非常高的情况下。尽管有这个缺点,例如 Caruana 和 Niculescu-Mizil 2006 *(监督学习算法的实证比较)* 表明,与其他监督学习算法相比,随机森林模型通常具有非常好的预测准确性。
我们现在将使用预先打包的 sklearn 随机森林分类模型RandomForestClassifier。
从 sklearn.ensemble 导入 RandomForestClassifier
从 sklearn.preprocessing 导入 LabelEncoder
从 sklearn.model_selection 导入 cross_validate
#Encode这是字符串整数特征值
的 标签 中的 数据集。列:
数据集[标签] = LabelEncoder () 。适合(数据集[标签])。变换(数据集[标签])
X = 数据集。drop ([ 'target' ], axis = 1 )
Y = dataset [ 'target' ]
#用 100 棵树和熵作为分割标准
实例化 模型Random_Forest_model = RandomForestClassifier ( n_estimators = 100 , criteria = "entropy" )
#交叉验证
精度 = cross_validate ( Random_Forest_model , X , Y , cv = 10 )[ 'test_score' ]
print ( '准确度为:' , sum ( accuracy ) / len ( accuracy ) * 100 , '%' )
准确度为:100.0%
好吧,牛眼!