随机森林 Python






4.96/5 (21投票s)
本文以一种简单易懂的方式提供了关于随机森林(一种流行的机器学习算法)的 Python 代码。
要求: 机器学习
随机森林简介
随机森林是一种流行的算法,用作分类和回归的集成学习方法。这意味着随机森林包含多个决策树。每棵决策树结果的平均值将是随机森林的最终输出。决策树存在一些缺点,例如在训练集上过拟合导致高方差,尽管随机森林通过 Bagging(Bootstrap Aggregating)解决了这个问题。首先,最好先了解决策树算法,然后再研究随机森林。因为随机森林是由许多决策树组成的。
决策树
决策树使用树状图,通过考虑图中的所有元素来做出可能的决策。例如,回想一下那位网球运动员,他有一个计划在不同天气条件下打球。现在,我们想知道这位运动员在第 15 天是否会打球?
寻找纯分支
有 15 天,其中 9 天他打了球,5 天他没打球;现在我们想知道他在特定情况下是否会打球。您应该仔细查看训练数据,其中有不同的特征和不同的值。我们必须查看每个特征的所有值(所有天数“打球=是”或“打球=否”),或者换句话说,上面表格中的哪个值颜色相同。
(湿度 = 高 所有天数: (打球 = 是) | (打球 = 否)) 否
(湿度 = 正常 所有天数: (打球 = 是) | (打球 = 否)) 否
(风 = 弱 所有天数: (打球 = 是) | (打球 = 否)) 否
(风 = 强 所有天数: (打球 = 是) | (打球 = 否)) 否
(外观 = 雨 所有天数: (打球 = 是) | (打球 = 否)) 否
(外观 = 晴 所有天数: (打球 = 是) | (打球 = 否)) 否
(外观 = 阴 所有天数: (打球 = 是) | (打球 = 否)) 是 √
所以,当“外观 = 阴”(D3、D7、D12、D13)时,我们的运动员总是打球;我们应该从“外观”特征开始建立分支并作为根。因此,下次,我们应该关注“外观 = 晴”,并尝试找出是“湿度”还是“风”是下一个。在 D1、D2、D8、D9、D11;只有 (D1、D2、D8) (湿度 = 高) 颜色相同或值相同,这次 (打球 = 否) 晴天:D1、D2、D8、D9、D11
- 晴天:D1、D2、D8 & (湿度 = 高) (打球 = 否)
- 晴天:D9、D11 & (湿度 = 正常) (打球 = 是)
所以我们的下一个分支是“湿度”,因为我们找到了“纯粹”的值。现在,看看当
“外观 = 雨” 雨天:D4、D5、D6、D10、D14 1. 雨天:D4、D5、D10 & (风 = 弱) (打球 = 是)
- 雨天:D6、D14 & (风 = 强) (打球 = 否)
现在让我们一目了然地比较一下表格和决策树。
因此,为了计算以下情况,我们首先考虑“外观 = 雨”,然后沿右分支,检查“风 = 弱”,所以我们深入到左侧,答案是“是”。提示:过拟合定义;在下面的图中;正如你所见,我们将每个节点拆分,直到达到“纯净”结果。例如,最后一个“叶子(树的末端节点)”中的所有“打球”结果都应该是相同的“是”或“否”。如果一直拆分直到完美纯净的集合,那么准确率将是 100%。这意味着每个叶子最终只有一个特定的值。拆分越多,准确率越高,树越大,因为树的生长越来越多。如果树的大小与数据集相同,那么我们就“过拟合”了。过拟合导致算法过于具体于(训练)数据,但它无法泛化测试数据。如何避免或停止过拟合是修剪树,以某种方式移除不适合未来测试数据的分支,然后查看结果移除是否会损害或改善我们的分析。
[ 如果 (“外观 = 晴” & “湿度 = 正常” 所有“打球 = 是”) ]
[ 如果 (“外观 = 雨” & “风 = 弱” 所有“打球 = 是”) ]
D15:“外观 = 雨” & “湿度 = 高” & “风 = 弱”
随机森林
随机森林是一种集成学习方法,非常适合监督学习,如分类和回归。在随机森林中,我们将训练集分成更小的部分,并将每个小部分作为独立的树,其结果不会影响其他树。随机森林获取训练集并使用“Bagging = Bootstrap Aggregating”算法对其进行划分,该算法通过防止过拟合和降低方差来提高准确性。它开始将数据集分成 60% 作为独立的决策树,30% 作为重叠数据。
它将训练集分成“B”棵不同的决策树(其中 60% 使用独特数据,30% 使用重复数据),然后开始计算每棵决策树的结果并拆分它们,直到达到适当的情况(当它足以泛化到测试数据时)。下面,您可以看到有两个“否”答案和三个“是”答案,因此答案的平均值是“是”。
随机森林中的错误计算:有一些方法可以增强随机森林的优化。交叉验证用于评估预测模型的结果如何泛化到其他独立测试数据。下面有一个训练集,其中输出被划分为“Y”,特征被划分为“X”。
引导
随机创建 T 棵树;T = { T1 , T2 , T3 , T4 , T5 },每次对每个数据进行 n 次抽样。袋外错误:如果我们随机选择特定的 j = (Xi , Yi) 并查找所有树中的 j,并找到一些不包含此值的树,那么它们将构成袋外错误。实际上,当 (Xi , Yi) 不在树中时,它重复 n 次的比例。
代码
我准备了训练集和测试集数据,以便用 Python 练习随机森林。训练集是与一些患者相关的数据,在训练集中,我们知道他们将根据年龄、性别和医院类别得到治愈。测试集是用于测试的数据,我们不知道谁会被治愈,我们应该用随机森林来检查它。我有一个空的 Excel 文件 result.csv 用于在其中写入结果。我选择了所有扩展名为“csv”,因为它们方便与 Python 一起使用。训练 测试 结果 随机森林是监督学习。要了解更多关于它的知识,请阅读本文。
下载 Python
如果您想使用舒适的 IDE 和专业的编辑器轻松上手,而无需安装库。您可以使用 Anaconda & Spider。
然后从开始菜单打开 Anaconda Navigator,选择“Spyder”。
有一些要点
- Python 是面向对象的
- 动态类型
- 丰富的库
- 易于阅读
- Python 区分大小写
- 缩进对 Python 很重要
Python 实现随机森林
步骤 1:导入重要库,如 numpy、用于 I/O 的 csv、sklearn
import numpy as np
import csv as csv
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.cross_validation import StratifiedKFold # Add important libs
步骤 2:训练和测试准备:读取数据并填充到数组中
请考虑您的路径并根据保存方式进行更改。例如,如果您将 train 和 test 保存在 D 盘根目录,请将其替换为 path1 = r'D:\train.csv'
。
train=[]
test=[] #Array Definition
path1 = r'D:\random forest\data set\train.csv' #Address Definition
path2 = r'D:\random forest\data set\test.csv'
with open(path1, 'r') as f1: #Open File as read by 'r'
reader = csv.reader(f1)
next(reader, None) #Skip header because file header is not needed
for row in reader: #fill array by file info by for loop
train.append(row)
train = np.array(train)
with open(path2, 'r') as f2:
reader2 = csv.reader(f2)
next(reader2, None)
for row2 in reader2:
test.append(row2)
test = np.array(test)
train = np.delete(train,[0],1) #delete first column of which is patientid
test = np.delete(test,[0],1)
步骤 3:删除与“PatientID”相关的第一个列,我们的算法不需要知道 patientid。
train = np.delete(train,[0],1) #delete first column of which is patientid
test = np.delete(test,[0],1)
步骤 4:手动优化数据,将性别替换为整数值
train[train[0::,3] == 'male', 3] = 1 #replacement gender with number
train[train[0::,3] == 'female', 3] = 0
test[test[0::,2] == 'male',2] = 1
test[test[0::,2] == 'female',2] = 0
步骤 5:优化数据
因为患者的治愈情况与他们的年龄直接相关,而且年龄列中有一些缺失值,所以通过了解患者的情况,[他的年龄是多少?] 或 [他的头衔是什么?] 或 [他使用了哪种医院类别?]。因此,首先提取他们的头衔,然后根据这些,安排缺失的年龄值。
for row in train:
Title = row[2].split(',')[1].split('.')[0].strip() #Extracting Name in order to gain title
row[2] = Title
for row in train: #Fill empty cell o rage column by the below logic
if (row[4]==''):
if (row[1]=='1' and row[2]=='Miss' and row[3]=='0'):
row[4] =30
if (row[1]=='1' and row[2]=='Mrs' and row[3]=='0'):
row[4] =40
if (row[1]=='2' and row[2]=='Miss' and row[3]=='0'):
row[4] =24
if (row[1]=='2' and row[2]=='Mrs' and row[3]=='0'):
row[4] =31.5
if (row[1]=='3' and row[2]=='Miss' and row[3]=='0'):
row[4] =18
if (row[1]=='3' and row[2]=='Mrs' and row[3]=='0'):
row[4] =31
if (row[1]=='1' and row[2]=='Master' and row[3]=='1'):
row[4] =4
if (row[1]=='1' and row[2]=='Mr' and row[3]=='1'):
row[4] =40
if (row[1]=='2' and row[2]=='Master' and row[3]=='1'):
row[4] =1
if (row[1]=='2' and row[2]=='Mr' and row[3]=='1'):
row[4] =31
if (row[1]=='3' and row[2]=='Master' and row[3]=='1'):
row[4] =4
if (row[1]=='3' and row[2]=='Mr' and row[3]=='1'):
row[4] =26
for row in test:
Title = row[1].split(',')[1].split('.')[0].strip()
row[1] = Title
for row in test:
if (row[3]==''):
if (row[0]=='1' and row[1]=='Miss' and row[2]=='0'):
row[3] =32
if (row[0]=='1' and row[1]=='Mrs' and row[2]=='0'):
row[3] =48
if (row[0]=='2' and row[1]=='Miss' and row[2]=='0'):
row[3] =19.5
if (row[0]=='2' and row[1]=='Mrs' and row[2]=='0'):
row[3] =29
if (row[0]=='3' and row[1]=='Miss' and row[2]=='0'):
row[3] =22
if (row[0]=='3' and row[1]=='Mrs' and row[2]=='0'):
row[3] =28
if (row[0]=='3' and row[1]=='Ms' and row[2]=='0'):
row[3] =22
if (row[0]=='1' and row[1]=='Master' and row[2]=='1'):
row[3] =9.5
if (row[0]=='1' and row[1]=='Mr' and row[2]=='1'):
row[3] =42
if (row[0]=='2' and row[1]=='Master' and row[2]=='1'):
row[3] =5
if (row[0]=='2' and row[1]=='Mr' and row[2]=='1'):
row[3] =28
if (row[0]=='3' and row[1]=='Master' and row[2]=='1'):
row[3] =7
if (row[0]=='3' and row[1]=='Mr' and row[2]=='1'):
row[3] =25
步骤 6:删除不必要的列
train = np.delete(train,[2],1) #Delete name column because it is not needed
test = np.delete(test,[1],1)
步骤 7:通过网格搜索优化算法
max_depth
:它告诉 RF 每棵决策树应该有多深,它接受不同值的数组,由于不确定最佳数量,我添加了两个值 [3,4]。n_estimators
:它告诉 RF 应该创建多少棵决策树以获得更好的精度。max_features
:在这个训练集示例中,有四个特征- 医院类别
- 名称
- 性别
- 年龄
当我们想拆分节点时,我们会选择这些特征中的一些或全部,这会影响准确的答案。如果等于 auto,则表示选择了所有特征。
min_samples_split
:它告诉 RF 对于每个创建的新节点应该有多少个拆分。min_samples_leaf
:叶子是末端节点,没有子节点,在每棵决策树的底部。bootstrap
:
parameter_gridsearch = {
'max_depth' : [3, 4], #depth of each decision tree
'n_estimators': [50, 20], #count of decision tree
'max_features': ['sqrt', 'auto', 'log2'],
'min_samples_split': [2],
'min_samples_leaf': [1, 3, 4],
'bootstrap': [True, False],
}
步骤 8:随机森林分类器
randomforest = RandomForestClassifier()
crossvalidation = StratifiedKFold(train[0::,0] , n_folds=5)
gridsearch = GridSearchCV(randomforest, #grid search for algorithm optimization
scoring='accuracy',
param_grid=parameter_gridsearch,
cv=crossvalidation)
gridsearch.fit(train[0::,1::], train[0::,0]) #train[0::,0] is as target
model = gridsearch
parameters = gridsearch.best_params_
步骤 9:得分计算
print('Best Score: {}'.format(gridsearch.best_score_))
Best Score: 0.8571428571428571
步骤 10:将答案写入 result.csv 文件
path3 = r'D:\random forest\data set\result.csv'
output = gridsearch.predict(test)
print(output)
with open(path3, 'w', newline='') as f3,
open(path2, 'r') as f4: # write output and other column from test
forest_Csv = csv.writer(f3)
forest_Csv.writerow(["PatientId", "healed", "Hospitalclass", "Name", "Sex", "Age"])
test_file_object = csv.reader(f4)
next(test_file_object, None)
i = 0
for row in test_file_object:
row.insert(1,output[i].astype(np.uint8))
forest_Csv.writerow(row)
i += 1
结论
如今,随机森林因其精度和优化功能而成为数据科学家非常受欢迎的选择。它包含多个决策树,我们可以决定需要多少棵树来进行数据导航。RF 解决了决策树的过拟合问题。过拟合是尽可能完整地训练训练数据集,达到 100% 拟合或全部训练集,但难以泛化到所有测试数据。随机森林使用 bagging 或 bootstrap aggregating 将训练集分成不同的独立决策树,并在不干扰其他树的情况下计算它们的结果,最后对所有结果取平均值。最后,它作为元估计器的能力,可以处理不同训练集子集中的决策树数量,以提高准确预测并良好地控制过拟合。
反馈
请随时对本文发表任何反馈;很高兴看到您的意见和对该代码的**投票**。如果您有任何问题,请随时在此处提问。
历史
- 2019 年 4 月 3 日:初始版本