决策树可能会因高方差而导致其结果对所使用的训练数据过于敏感。
通过从训练数据样本中构建多个模型(称为 bagging)可以降低这种方差,但这些树的关联性很强。
随机森林是 bagging 的一种扩展,它除了根据训练数据的多个样本构建树之外,还限制了可用于构建树的特征,从而迫使树之间产生差异。这反过来可以提高性能。
在本教程中,您将了解如何用 Python 从零开始实现随机森林算法。
完成本教程后,您将了解:
- bagging 决策树与随机森林算法之间的区别。
- 如何构建具有更高方差的 bagging 决策树。
- 如何将随机森林算法应用于预测建模问题。
立即启动您的项目,阅读我的新书《从零开始的机器学习算法》,书中包含分步教程以及所有示例的Python源代码文件。
让我们开始吧。
- 更新于 2017 年 1 月:更改了 `cross_validation_split()` 中 `fold_size` 的计算,使其始终为整数。修复了 Python 3 的问题。
- 2017 年 2 月更新:修复了 build_tree 中的一个 bug。
- 2017 年 8 月更新:修复了 Gini 计算中的一个 bug,添加了按组大小对组 Gini 分数进行加权的功能(感谢 Michael!)。
- 2018 年 8 月更新:测试并更新以与 Python 3.6 配合使用。

如何用 Python 从零开始实现随机森林
照片由 InspireFate Photography 提供,保留部分权利。
描述
本节简要介绍了随机森林算法和本教程使用的声纳数据集。
随机森林算法
决策树在每一步都通过贪婪地选择数据集中的最佳分裂点。
如果不进行剪枝,该算法会使决策树容易产生高方差。这种高方差可以通过创建具有不同训练数据集样本(问题的不同视角)的多个树并组合它们的预测来加以利用并降低。这种方法称为 bootstrap aggregation,简称 bagging。
bagging 的一个限制是,每棵树的创建都使用了相同的贪婪算法,这意味着每棵树中很可能选择相同的或非常相似的分裂点,从而使不同的树非常相似(树将是相关的)。这反过来又使得它们的预测相似,从而削弱了最初追求的方差。
我们可以通过限制贪婪算法在创建树时每个分裂点可以评估的特征(行)来强制决策树产生差异。这就是随机森林算法。
与 bagging 类似,将采用训练数据的多个样本,并对每个样本训练不同的树。不同之处在于,在数据中进行分裂并添加到树中的每个点,只能考虑固定数量的属性。
对于分类问题,也就是本教程将要探讨的问题类型,用于分裂的属性数量限制为输入特征总数的平方根。
1 |
num_features_for_split = sqrt(total_input_features) |
这个小小的改变带来的结果是,树彼此之间的差异更大(不相关),预测也更多样化,并且组合预测的性能通常优于单独的树或单独的 bagging。
声纳数据集
本教程中将使用的数据集是声纳数据集。
这是一个描述声纳信号返回在不同表面上反射的数据集。60个输入变量是不同角度的返回强度。这是一个二元分类问题,需要一个模型来区分岩石和金属圆柱体。共有208个观测。
这是一个众所周知的数据集。所有变量都是连续的,通常在 0 到 1 的范围内。输出变量是一个字符串,“M”代表水雷,“R”代表岩石,需要转换为整数 1 和 0。
通过预测数据集中观测值最多的类别(M 或地雷),零规则算法可以达到 53% 的准确率。
您可以在 UCI 机器学习存储库了解有关此数据集的更多信息。
免费下载数据集,并将其放置在您的工作目录中,文件名为 **sonar.all-data.csv**。
教程
本教程分为 2 个步骤。
- 计算分裂点。
- 声纳数据集案例研究。
这些步骤提供了您将随机森林算法实现并应用于自己的预测建模问题所需的基础。
1. 计算分裂点
在决策树中,分裂点的选择是通过查找属性及其值来找到成本最低的。
对于分类问题,这种成本函数通常是 Gini 指数,它计算分裂点创建的数据组的纯度。Gini 指数为 0 表示完美纯度,其中类值在两个组中完美分离,在二分类问题中是这样。
在决策树中找到最佳分裂点涉及评估训练数据集中每个输入变量每个值对应的成本。
对于 bagging 和随机森林,此过程将在带有放回的训练数据集样本上执行。带放回抽样意味着同一行可能被选择并添加到样本中不止一次。
我们可以更新随机森林的此过程。与其枚举搜索中所有输入属性的值来查找成本最低的分裂点,不如创建要考虑的输入属性的样本。
这个输入属性样本可以随机选择且不放回,这意味着在查找成本最低的分裂点时,每个输入属性只需要考虑一次。
下面是一个名为 get_split() 的函数,它实现了此过程。它接受一个数据集和一个要评估的固定数量的输入特征作为输入参数,其中数据集可以是实际训练数据集的样本。
辅助函数 test_split() 用于按候选分裂点分割数据集,gini_index() 用于通过创建的行组评估给定分裂点的成本。
我们可以看到,通过随机选择特征索引并将其添加到列表(称为 features)中来创建特征列表,然后对该特征列表进行枚举,并评估训练数据集中的特定值作为分裂点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 为数据集选择最佳分割点 def get_split(dataset, n_features): class_values = list(set(row[-1] for row in dataset)) 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) if index not in features: features.append(index) for index in features: for row in dataset: groups = test_split(index, row[index], dataset) gini = gini_index(groups, class_values) if gini < b_score: b_index, b_value, b_score, b_groups = index, row[index], gini, groups return {'index':b_index, 'value':b_value, 'groups':b_groups} |
现在我们知道了决策树算法如何修改以用于随机森林算法,我们可以将其与 bagging 的实现结合起来,并应用于实际数据集。
2. 声纳数据集案例研究
在本节中,我们将把随机森林算法应用于声纳数据集。
该示例假设数据集的 CSV 副本位于当前工作目录中,文件名为 sonar.all-data.csv。
首先加载数据集,将字符串值转换为数字,并将输出列从字符串转换为 0 和 1 的整数值。这是通过辅助函数 load_csv()、str_column_to_float() 和 str_column_to_int() 来加载和准备数据集实现的。
我们将使用 k 折交叉验证来估计模型在未见过的数据上的性能。这意味着我们将构建和评估 k 个模型,并将性能估计为平均模型误差。分类准确率将用于评估每个模型。这些行为由辅助函数 cross_validation_split()、accuracy_metric() 和 evaluate_algorithm() 提供。
我们还将使用一个分类回归树 (CART) 算法的实现,该算法已针对 bagging 进行调整,包括用于将数据集分割成组的辅助函数 test_split(),用于评估分裂点的 gini_index(),我们修改过的 get_split() 函数(在前一步中讨论过),用于创建单个决策树的 to_terminal()、split() 和 build_tree(),用于使用决策树进行预测的 predict(),用于创建训练数据集子样本的 subsample(),以及用于使用决策树列表进行预测的 bagging_predict()。
开发了一个名为 random_forest() 的新函数,该函数首先从训练数据集的子样本中创建决策树列表,然后使用它们进行预测。
正如我们上面所说,随机森林与 bagging 决策树之间的关键区别在于创建树的方式上有一个小的改动,这里是在 get_split() 函数中。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# 在声纳数据集上实现随机森林算法 from random import seed from random import randrange from csv import reader from math import sqrt # 加载 CSV 文件 def load_csv(filename): dataset = list() with open(filename, 'r') as file: csv_reader = reader(file) for row in csv_reader: if not row: continue dataset.append(row) return dataset # 将字符串列转换为浮点数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float(row[column].strip()) # 将字符串列转换为整数 def str_column_to_int(dataset, column): class_values = [row[column] for row in dataset] unique = set(class_values) lookup = dict() for i, value in enumerate(unique): lookup[value] = i for row in dataset: row[column] = lookup[row[column]] return lookup # 将数据集分成 k 折 def cross_validation_split(dataset, n_folds): dataset_split = list() dataset_copy = list(dataset) fold_size = int(len(dataset) / n_folds) for i in range(n_folds): fold = list() while len(fold) < fold_size: index = randrange(len(dataset_copy)) fold.append(dataset_copy.pop(index)) dataset_split.append(fold) return dataset_split # 计算准确率百分比 def accuracy_metric(actual, predicted): correct = 0 for i in range(len(actual)): if actual[i] == predicted[i]: correct += 1 return correct / float(len(actual)) * 100.0 # 使用交叉验证分割评估算法 def evaluate_algorithm(dataset, algorithm, n_folds, *args): folds = cross_validation_split(dataset, n_folds) scores = list() for fold in folds: train_set = list(folds) train_set.remove(fold) train_set = sum(train_set, []) test_set = list() for row in fold: row_copy = list(row) test_set.append(row_copy) row_copy[-1] = None predicted = algorithm(train_set, test_set, *args) actual = [row[-1] for row in fold] accuracy = accuracy_metric(actual, predicted) scores.append(accuracy) 返回 分数 # 根据属性和属性值分割数据集 def test_split(index, value, dataset): left, right = list(), list() for row in dataset: if row[index] < value: left.append(row) else: right = list() return left, right # 计算分割数据集的基尼指数 def gini_index(groups, classes): # 计算分割点处的所有样本 n_instances = float(sum([len(group) for group in groups])) # 对每个组的加权基尼指数求和 gini = 0.0 for group in groups: size = float(len(group)) # 避免除以零 if size == 0: continue score = 0.0 # 根据每个类别的分数对组进行评分 for class_val in classes: p = [row[-1] for row in group].count(class_val) / size score += p * p # 根据其相对大小对组分数进行加权 gini += (1.0 - score) * (size / n_instances) return gini # 为数据集选择最佳分割点 def get_split(dataset, n_features): class_values = list(set(row[-1] for row in dataset)) 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) if index not in features: features.append(index) for index in features: for row in dataset: groups = test_split(index, row[index], dataset) gini = gini_index(groups, class_values) if gini < b_score: b_index, b_value, b_score, b_groups = index, row[index], gini, groups return {'index':b_index, 'value':b_value, 'groups':b_groups} # 创建一个终端节点值 def to_terminal(group): outcomes = [row[-1] for row in group] return max(set(outcomes), key=outcomes.count) # 为节点创建子分割或使其成为终端 def split(node, max_depth, min_size, n_features, depth): left, right = node['groups'] del(node['groups']) # 检查是否没有分割 if not left or not right: node['left'] = node['right'] = to_terminal(left + right) return # 检查是否达到最大深度 if depth >= max_depth: node['left'], node['right'] = to_terminal(left), to_terminal(right) return # 处理左子节点 if len(left) <= min_size: node['left'] = to_terminal(left) else: node['left'] = get_split(left, n_features) split(node['left'], max_depth, min_size, n_features, depth+1) # 处理右子节点 if len(right) <= min_size: node['right'] = to_terminal(right) else: node['right'] = get_split(right, n_features) split(node['right'], max_depth, min_size, n_features, depth+1) # 构建决策树 def build_tree(train, max_depth, min_size, n_features): root = get_split(train, n_features) split(root, max_depth, min_size, n_features, 1) return root # 使用决策树进行预测 def predict(node, row): if row[node['index']] < node['value']: if isinstance(node['left'], dict): return predict(node['left'], row) else: return node['left'] else: if isinstance(node['right'], dict): return predict(node['right'], row) else: return node['right'] # 从数据集中创建带替换的随机子样本 def subsample(dataset, ratio): sample = list() n_sample = round(len(dataset) * ratio) while len(sample) < n_sample: index = randrange(len(dataset)) sample.append(dataset[index]) return sample # 使用一组装袋树进行预测 def bagging_predict(trees, row): predictions = [predict(tree, row) for tree in trees] return max(set(predictions), key=predictions.count) # 随机森林算法 def random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features): trees = list() for i in range(n_trees): sample = subsample(train, sample_size) tree = build_tree(sample, max_depth, min_size, n_features) trees.append(tree) predictions = [bagging_predict(trees, row) for row in test] return(predictions) # 测试随机森林算法 seed(2) # 加载并准备数据 filename = 'sonar.all-data.csv' dataset = load_csv(filename) # 将字符串属性转换为整数 for i in range(0, len(dataset[0])-1): str_column_to_float(dataset, i) # 将类别列转换为整数 str_column_to_int(dataset, len(dataset[0])-1) # 评估算法 n_folds = 5 max_depth = 10 min_size = 1 sample_size = 1.0 n_features = int(sqrt(len(dataset[0])-1)) for n_trees in [1, 5, 10]: scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features) print('Trees: %d' % n_trees) print('Scores: %s' % scores) print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores)))) |
交叉验证使用了 5 的 k 值,每次迭代让每个折叠有 208/5 = 41.6 或略高于 40 条记录进行评估。
构建了深度为 10 的深层树,每个节点中的最小训练行数为 1。创建的训练数据集样本大小与原始数据集相同,这是随机森林算法的默认期望。
在每次分裂点考虑的特征数量设置为 sqrt(num_features) 或 sqrt(60)=7.74 四舍五入为 7 个特征。
评估了 3 种不同数量的树进行比较,显示随着树的数量增加,技能也在提高。
运行示例会为每种配置打印每个折叠的分数和平均分数。
1 2 3 4 5 6 7 8 9 10 11 |
Trees: 1 Scores: [56.09756097560976, 63.41463414634146, 60.97560975609756, 58.536585365853654, 73.17073170731707] Mean Accuracy: 62.439% Trees: 5 Scores: [70.73170731707317, 58.536585365853654, 85.36585365853658, 75.60975609756098, 63.41463414634146] Mean Accuracy: 70.732% Trees: 10 Scores: [75.60975609756098, 80.48780487804879, 92.6829268292683, 73.17073170731707, 70.73170731707317] Mean Accuracy: 78.537% |
扩展
本节列出了您可能感兴趣的本教程的扩展内容。
- 算法调优。本教程中使用的配置是通过一些试错找到的,但并未优化。尝试更多的树、更多的特征,甚至是不同的树配置来提高性能。
- 更多问题。将该技术应用于其他分类问题,甚至通过新的成本函数和新的组合树预测的方法来适应回归问题。
你尝试过这些扩展吗?
在下面的评论中分享您的经验。
回顾
在本教程中,您将了解如何从头开始实现随机森林算法。
具体来说,你学到了:
- 随机森林与装袋决策树的区别。
- 如何更新决策树的创建以适应随机森林过程。
- 如何将随机森林算法应用于真实的预测建模问题。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
嗨,Jason,
首先,感谢您在此网站上的工作——我发现这是一个很好的资源,可以开始我的 Python 机器学习探索!
现在,我正在学习您的 Python 机器学习迷你课程,目前到了第 9 课:算法的抽样检查。您建议测试随机森林,这让我找到了这篇博文,我正在尝试运行其中的示例,但遇到了以下错误
回溯(最近一次调用)
File “test.py”, line 203, in
scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features)
File “test.py”, line 57, in evaluate_algorithm
folds = cross_validation_split(dataset, n_folds)
File “test.py”, line 42, in cross_validation_split
index = randrange(len(dataset_copy))
File “//anaconda/lib/python3.5/random.py”, line 186, in randrange
raise ValueError(“empty range for randrange()”)
ValueError: randrange() 的范围为空
我花了将近一个小时试图找出我可能做错了什么……不幸的是,我真的很不擅长编码,所以我发现这非常困难。我认为我已经缩小到以下可能性
1. 可能是 evaluate_algorithm 函数的定义有问题?
2. 可能是使用 python 3.5.2 中的 randrange 有问题?
3. 可能是“dataset”定义有问题?
我认为要么是 #1,因为代码在第 202 行之前都没有问题,要么是 #3,因为 dataset 是从返回的错误行中常见的线索……?
非常感谢您的指导!
再次感谢!
marco
你好,
是否可以轻松地将此实现改编以适应特征向量的元组?
谢谢,
D.
嗨 Jason,我能够运行代码并获得与本页面上发布的结果。我的问题是接下来做什么?如何使用这些结果对新数据进行分类?
谢谢 Jeff
您可以拟合一个最终模型到所有训练数据上,然后开始进行预测。
请参阅这篇关于开发最终模型文章
https://machinelearning.org.cn/train-final-machine-learning-model/
你能给我发一个视频,展示用 Python 从头开始实现随机森林的算法吗
抱歉,我没有视频。
搞定了!是 Python 3.5.2 的问题。我切换到 2.7 就好了!
谢谢
marco
很高兴听到这个消息,Marco。
回溯(最近一次调用)
File “rf2.py”, line 203, in
scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features)
File “rf2.py”, line 68, in evaluate_algorithm
predicted = algorithm(train_set, test_set, *args)
File “rf2.py”, line 181, in random_forest
tree = build_tree(sample, max_depth, min_size, n_features)
File “rf2.py”, line 146, in build_tree
split(root, max_depth, min_size, n_features, 1)
File “rf2.py”, line 120, in split
left, right = node[‘groups’]
TypeError: ‘NoneType’ object is not iterable
我遇到了同样的问题
在 Python 3.x 中也有效。第 45 行的除法
fold_size = len(dataset) / n_folds
产生一个浮点数,当 dataset_copy 的长度为零时,它仍然有效。randrange(0) 会导致此错误。
将此行替换为
fold_size = len(dataset) // n_folds
会得到一个整数,并且循环可以正常执行。
谢谢 beedotkiran。
我建议强制转换结果,以防 Python 初学者不熟悉双斜杠运算符。
我已经更新了上面示例中的 cross_validation_split() 函数,以解决 Python 3 的问题。
这是一个非常棒的教程,非常感谢您花时间来做这件事!我想知道您是否对序列化或将树用于其他类似数据集有任何建议,腌制(pickling)对这种结构有用吗?谢谢您的帮助!
嗨 Jake,对学习到的对象使用 pickle 会是一个不错的起点。
嗨 Jason,很棒的教程!只是关于 build_tree 函数的一个问题:当您评估树的根时,您不应该使用训练样本而不是整个数据集吗?
我的意思是
root = get_split(train, n_features) 而不是
root = get_split(dataset, n_features)
我还可以问一下,如果您想将此算法改编为回归问题而不是分类问题,那么主要区别是什么?
非常感谢!祝您一切顺利
抱歉,我没看到您已经解决了这个问题。
没关系,抓得好!
你好 Jason,很好的方法。我想知道您是否有关于将上述代码转换为支持多标签分类的技巧。
非常感谢!!!
暂时没有,抱歉 Mike。我需要做一些研究。
可以搜索 Google Scholar,或者考虑 scikit-learn 中的一些多标签方法
https://scikit-learn.cn/stable/modules/multiclass.html#multilabel-classification-format
你好 Jason,我喜欢这种允许人们“深入了解”这些机器学习方法的方法。我期待以这种方式学习更多机器学习方法。
随机森林对我来说是全新的。我有一个可以利用随机森林回归的数据集。我想知道需要进行哪些更改才能将随机森林分类代码(上面)转换为随机森林回归。 Alessandro 之前也问过这个问题,但我没有理解他的回答。据我所知,随机森林回归的解释并不清楚。
谢谢。
谢谢 Steve。
作为开始,可以考虑在 sklearn 库中使用随机森林回归。
https://machinelearning.org.cn/ensemble-machine-learning-algorithms-python-scikit-learn/
Jason,
感谢关于随机森林回归的建议。
在声纳数据集上,我绘制了数据中 60x60 的相关矩阵。许多连续的行,甚至不那么接近的行,都高度相关。例如,第 17 行和第 18 列的相关性如下
观察数:131
自由度:2
R平方:0.870
RMSE:0.1046
F统计量 863。
并且第 14 列和第 15 列的相关性为
观察数:131
自由度:2
R平方:0.8554
RMSE:0.0708
F统计量 763。
这种相关性对随机森林的使用有什么影响?如何消除或衡量相关性的影响?
另外,对于这个数据集,我获得了以下结果
n_folds = 5
max_depth = 12
min_size = 1
sample_size = 0.75
for n_trees in [1, 5, 19]
71.875%, 73.438%, 82.031%
感谢您的辛勤工作。我正在努力吸收所有内容。
结果不错。
我不认为 RF 会受到高度相关特征的太大影响。尽管如此,尝试删除一些特征,看看它们对模型技能的影响。我很想听听您的发现。
你是如何找到相关性的,为什么它会造成问题?我对此不太了解,所以我想从像您这样的专家那里了解这些。谢谢。
你好 Jason,感谢您提供的精彩教程,您能解释一下以下几点吗?
1. 这行代码的作用是什么:row_copy[-1] = None:因为它在没有这行代码的情况下也能完美运行
2. 当我尝试 n_trees=[3,5,10] 时,返回的结果是准确性随着树的数量增加而降低:
Trees: 3
Scores: [63.41463414634146, 51.21951219512195, 68.29268292682927, 68.29268292682927, 63.41463414634146]
Mean Accuracy: 62.927%
Trees: 5
Scores: [65.85365853658537, 60.97560975609756, 60.97560975609756, 60.97560975609756, 58.536585365853654]
Mean Accuracy: 61.463%
Trees: 10
Scores: [48.78048780487805, 60.97560975609756, 58.536585365853654, 70.73170731707317, 53.65853658536586]
Mean Accuracy: 58.537%
1. 为了清除输出值,以便算法/开发者不会意外地作弊。
2. 是的,调整算法以适应问题很重要。
您愿意帮助我吗?我是一名学生,我正在为此使用我在网上找到的一个问题 >https://github.com/barotdhrumil21/road_sign_prediction_using_random_forest_classifier/tree/master
我建议联系该代码的作者。
您建议我如何使用这个:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/learn/random_forest_mnist.py
或者我可以使用它吗,这与您所做的相同吗?
随意使用您喜欢的任何代码。
做得好!进行回归问题时,我应该使用哪种成本函数?
好问题,可以考虑均方误差或平均绝对误差。
test_split 返回两个值,但这里 groups = test_split(index, row[index], dataset) 只有一个变量,有人能解释一下吗?非常感谢!
返回的数组被赋值给一个名为 groups 的变量。
嗨,Jason,
我正在通过您的示例来学习随机森林。但在运行代码时,我遇到了一个错误。我正在使用Ipython Notebook。
in split(node, max_depth, min_size, n_features, depth)
6 # Create child splits for a node or make terminal
7 def split(node, max_depth, min_size, n_features, depth)
—-> 8 left, right = node[‘groups’]
9 del(node[‘groups’])
10 # check for a no split
TypeError: ‘NoneType’ object is not iterable
请帮忙。
错误链列表
TypeError Traceback (most recent call last)
in ()
16 n_features = int(sqrt(len(dataset[0])-1))
17 for n_trees in [1, 5, 10]
—> 18 scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features)
19 print(‘Trees: %d’ % n_trees)
20 print(‘Scores: %s’ % scores)
在 evaluate_algorithm(dataset, algorithm, n_folds, *args)中
12 test_set.append(row_copy)
13 row_copy[-1] = None
—> 14 predicted = algorithm(train_set, test_set, *args)
15 actual = [row[-1] for row in fold]
16 accuracy = accuracy_metric(actual, predicted)
in random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features)
18 for i in range(n_trees)
19 sample = subsample(train, sample_size)
—> 20 tree = build_tree(sample, max_depth, min_size, n_features)
21 trees.append(tree)
22 predictions = [bagging_predict(trees, row) for row in test]
in build_tree(train, max_depth, min_size, n_features)
2 def build_tree(train, max_depth, min_size, n_features)
3 root = get_split(train, n_features)
—-> 4 split(root, max_depth, min_size, n_features, 1)
5 return root
6
in split(node, max_depth, min_size, n_features, depth)
6 # Create child splits for a node or make terminal
7 def split(node, max_depth, min_size, n_features, depth)
—-> 8 left, right = node[‘groups’]
9 del(node[‘groups’])
10 # check for a no split
TypeError: ‘NoneType’ object is not iterable
抱歉,我不使用 Notebook。请确认 Python 版本为 2。
在计算基尼指数之前,数据集是否应该按某个特征排序?
你好,Jason
非常感谢您的课程。您的代码运行完美。
我现在正在尝试使用另一个包含字符串值的数据集。并因此遇到困难。这可能吗?我一直收到无法将字符串转换为整数的错误。
谢谢。
您必须将字符串转换为整数或实数值。也许您需要使用独热编码?
你好,
是否需要对每个分裂的加权基尼指数进行求和?
谢谢
丹尼
Jason,我已将此协议发布到 YouTube 作为参考 @ https://youtu.be/Appc0Hpnado
感谢您花时间教我们这种方法!
杰夫
Jeffrey,做得好!
Jason,您的实现帮助了我很多!但是,我有一个问题:在每次分割时,算法都会从总特征中随机选择一个特征子集,然后选择具有最佳基尼得分的最佳特征。那么,一棵树是否有可能在不同的分裂中使用同一个特征?因为在get_split()中,line index = randrange(len(dataset[0])-1)基本上是从整个池中选取特征。您能解释一下吗?谢谢!
它不会选择最佳分割,而是选择最佳分割中的一个随机分割。
如果从基尼得分的角度来看,您可以使用单个特征进行多次分割。
是的,我意识到了这一点。谢谢!
rf_model = training(training_data2,RandomForestClassifier())
print rf_model
test(rf_model,test_data2)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion=’gini’,
max_depth=None, max_features=’auto’, max_leaf_nodes=None,
min_impurity_split=1e-07, min_samples_leaf=1,
min_samples_split=2, min_weight_fraction_leaf=0.0,
n_estimators=10, n_jobs=1, oob_score=False, random_state=None,
verbose=0, warm_start=False)
我尝试使用树的数量=1, 5, 10,如您的示例所示,但不起作用,您能告诉我需要进行哪些更改吗?而且,当我设置randomstate = none时,每次执行我的准确率都在变化,但当我设置一个随机状态值时,准确率是相同的。
你好,
我想将代码更改为适用于 90% 的训练数据和 10% 的测试数据,不进行交叉验证。如果我使用 n_folds = 1,我会收到一个错误。我该如何更改代码才能使其正常工作?
也许使用 scikit-learn 来拟合您的模型会更好。
https://machinelearning.org.cn/ensemble-machine-learning-algorithms-python-scikit-learn/
非常感谢。我想使用您的代码,因为我对算法进行了另一项内部更改,而 scikit-learn 无法实现。我认为主要的(也许是唯一的)更改在 evaluate_algorithm 函数中。
我们能否在 MATLAB 中使用 fitctree 实现随机森林?
有一个函数 TreeBagger 可以实现随机森林。但是,如果我们使用这个函数,我们就无法控制每个单独的树。我们能否使用 MATLAB 函数 fitctree(它构建决策树)来实现随机森林?非常感谢。
抱歉,我没有 MATLAB 的资料。
嗨,Jason,
我正在尝试使用 RF 解决分类问题,每次我运行 RandomForestClassifier 对我的训练数据时,特征重要性都会显示每次运行的模型都不同。我如何才能确保每次运行模型时都获得相同的排名前 5 的特征?请告诉我。
model_rc = RandomForestClassifier(n_estimators=10,max_depth=None,min_samples_split=2,random_state=0)
rc_fit=model_rc.fit(X_train, y_train.values.ravel())
是的,这是算法的一个特性。
请看这篇文章
https://machinelearning.org.cn/randomness-in-machine-learning/
我想知道 sklearn 随机森林和自己实现的随机森林算法有什么区别。sklearn 随机森林有什么弱点或问题吗?
我建议您仅出于学习目的自己实现该算法,我预计 sklearn 的实现会更健壮、更高效。
您能实现旋转森林算法吗?
感谢您的建议。
您的博客和教程在我的整个博士期间都帮助了我。感谢您投入如此多的时间和精力来分享这些信息。
很高兴听到这个!
亲爱的 Jason,
非常感谢您的这个实现,太棒了!
是否可以了解哪些特征对于手头的任务最具辨别力
以及这些特征各自的重要性程度?
提前表示感谢!!!
Jason Brownlee 2018年2月5日 上午7:47 #
K 2018年2月28日 下午4:22 #
https://machinelearning.org.cn/an-introduction-to-feature-selection/
嗨,Jason,
我是一名生物统计学硕士生,正在做一个论文项目,该项目应用了修改版的随机森林(没有现有实现)来解决一个问题。我习惯于 R。我尝试努力改进我的代码,并将部分代码用 C++(通过 Rcpp 包)实现,但它仍然很慢……我注意到 R 或 Python 中的随机森林包都在调用核心是用 C 编写的代码。
那么,您介意估计一下您的实现与主流实现相比有多快(例如,比 Scikit-learn 慢 10 倍)吗?
我是 Python 新手。我真的很应该自己试试,但就是忍不住想快速得到这个答案来激励我学习 Python!🙂
Kehao
谢谢!
Jason Brownlee 2018年3月1日 上午6:07 #
Kirtika Bhatt 2018年3月23日 上午6:55 #
Jason Brownlee 2018年3月23日 上午8:28 #
一个好的起点是这里
https://machinelearning.org.cn/start-here/#python
嗨,Jason,
我只想感谢您提供信息丰富的网站。
这篇帖子也非常全面,充满了综合的想法和主题。
如果 Python 项目可用,我将不胜感激。
niloofar
此致,
Jason Brownlee 2018年4月7日 上午6:22 #
谢谢,很高兴它帮到了你!
你好
Jason Brownlee 2018年5月25日 上午9:26 #
好问题,我在这里回答了
https://machinelearning.org.cn/faq/single-faq/how-do-i-evaluate-a-machine-learning-algorithm
恭喜您完成这项了不起的工作,先生。我有一个小问题,先生。您花了多长时间写出如此出色的代码,以及您使用了哪些资源来帮助您,先生?
谢谢您,致以诚挚的问候。
Jason Brownlee 2018年5月31日 上午6:16 #
Piyasi Choudhury 2018年6月30日 下午12:16 #
Jason Brownlee 2018年7月1日 上午6:22 #
我看不出为什么不。
你好…
Jason Brownlee 2018年9月25日 上午6:18 #
Then 2018年9月25日 下午4:14 #
https://machinelearning.org.cn/start-here/#python
嗨!
Jason Brownlee 2018年9月26日 上午6:10 #
谢谢。
Fraser 2018年10月24日 上午2:50 #
你好 Jason,
我在 PyCharm 中使用 python 3.6 运行您的代码,我注意到如果我注释掉
函数,代码就能正常运行,但准确率得分变成了
def str_column_to_int(dataset, column)
Scores: [82.92682926829268, 75.60975609756098, 97.5609756097561, 80.48780487804879, 68.29268292682927]
Trees: 1
Scores: [56.09756097560976, 63.41463414634146, 60.97560975609756, 58.536585365853654, 73.17073170731707]
Mean Accuracy: 62.439%
Trees: 5
Scores: [70.73170731707317, 58.536585365853654, 85.36585365853658, 75.60975609756098, 63.41463414634146]
Mean Accuracy: 70.732%
Trees: 10
Mean Accuracy: 80.976%
10 棵树的准确率有所提高。
Process finished with exit code 0
有什么想法吗?
Jason Brownlee 2018年10月24日 上午6:32 #
Shipika Singh 2018年10月24日 上午6:12 #
你好,
Jason Brownlee 2018年10月24日 上午6:34 #
Elizabeth 2018年10月24日 下午12:22 #
嗨 Jason,
Jason Brownlee 2018年10月24日 下午2:46 #
谢谢!
树对相关输入是不变的。
仅 CV 或 train/test 可能就足够了,可能不需要两者都用。
amjad 2018年12月16日 下午9:22 #
Jason Brownlee 2018年12月17日 上午6:20 #
Julie 2019年1月4日 上午3:23 #
嗨,Jason,
我在 Spyder 中使用 python 3.7 运行您的代码,但我遇到了这个错误:
“left, right = node[‘groups’]
TypeError: cannot unpack non-iterable NoneType object”。
我不明白为什么……您有什么想法吗?
Jason Brownlee 2019年1月4日 上午6:33 #
谢谢。
bbrighttaer 2019年1月15日 下午5:35 #
https://machinelearning.org.cn/faq/single-faq/how-do-i-run-a-script-from-the-command-line
Jason博士您好,
我浏览了您的教程,获得了与教程中相同的准确率。我意识到属性是带放回抽样的,所以我进行了修改,并将交叉熵损失应用于 n_trees = [1, 5, 10, 15, 20]。我获得了以下准确率指标:
Scores: [68.29268292682927, 63.41463414634146, 65.85365853658537, 73.17073170731707, 75.60975609756098]
Trees: 1
Mean Accuracy: 69.268%
Scores: [73.17073170731707, 82.92682926829268, 70.73170731707317, 70.73170731707317, 75.60975609756098]
Trees: 5
Mean Accuracy: 74.634%
Scores: [80.48780487804879, 75.60975609756098, 65.85365853658537, 75.60975609756098, 87.8048780487805]
Trees: 10
Mean Accuracy: 77.073%
Trees: 15
Scores: [90.2439024390244, 70.73170731707317, 78.04878048780488, 73.17073170731707, 80.48780487804879]
Trees: 20
Mean Accuracy: 78.537%
Scores: [65.85365853658537, 75.60975609756098, 85.36585365853658, 87.8048780487805, 85.36585365853658]
Mean Accuracy: 80.000%
Jason Brownlee 2019年1月16日 上午5:42 #
干得好!
你好 Jason,
我受到启发,并根据本网站的代码实现了一个 Python 随机森林分类器。我更进一步,决定实现自适应随机森林算法。但我遇到了很多问题。
我实现了存储示例的窗口。但不幸的是,我无法执行分类。我无法将学习步骤转化为稍微自适应的。我卡住了。
您能给我一些建议、示例,告诉我如何克服这些问题吗?
Jason Brownlee 2019年1月25日 上午8:45 #
Marcin 2019年1月25日 下午11:20 #
不幸的是,这并不是一个常见的话题。
Jason Brownlee 2019年1月26日 上午6:14 #
Marcin 2019年2月2日 上午1:31 #
你好 Jason,
这意味着我们实际上没有实现随机机制。
Jason Brownlee 2019年2月2日 上午6:23 #
Jaswant Singh 2019年3月15日 下午3:39 #
你可以在这里了解更多
https://machinelearning.org.cn/introduction-to-random-number-generators-for-machine-learning/
Jason Brownlee 2019年3月16日 上午7:47 #
很高兴它有帮助。
嗨,Jason,
输入数据集 + 预测类别
“M”代表我的,“R”代表岩石。
您能告诉我如何做到这一点吗?
您可以将预测的整数映射回类别标签并打印类别标签。
抱歉,我无法为您准备代码示例。
如何在 Python 或 R 中实现网络引导森林(Network Guided Forest)
什么是“网络引导森林”?
你好,Jason
据我所知,树应该继续进行分割,直到达到 max_depth 或左侧的观测值完全纯净。但此代码即使在节点已经纯净(gini = 0)时仍进行分割,这意味着它从同一节点创建了两个都具有类别值为零的叶子,这是不可行的。
为了更清楚:如果您将 number of rows 的值给 get_split(),其中包含相同类别值的行,它仍然会进行分割,尽管它已经纯净了。
我们是否应该将此条件添加到 get_split() 函数中,或者我是否理解错了什么?
SKlearn 中的随机森林分类器在同一情况下表现如何?
提前感谢您的回复。
诚挚的问候,
Arkadiy
是的,这听起来是个很棒的改进。
Jason 你好,对于随机森林,我们可以将回归问题转换为分类问题吗?
是的,您可以。我无法为您执行此转换。
嗨
给定应用于 cross_validation_split 函数的 208 行的声纳数据集,我们只考虑给定数据集的前 205 行,因此最后 3 行被忽略了。这是故意的吗?
所以我希望将其更改为类似
谢谢
您是否知道如何在您的网站上正确添加代码片段?也许可以在常见问题解答中添加一个条目?
使用 HTML pre 标签
https://w3schools.org.cn/tags/tag_pre.asp
是的,以保持折叠大小相同。
谢谢你的回复。
我理解您的理由,但这会以丢失那额外三行提供的信息为代价。这不好吗?
所有折叠大小都相同的目的是什么?我相应地修改了该函数中的代码,显然得到了与您不同的准确率。
也许可以。
所有折叠大小相同意味着在评估分数样本上计算的汇总统计量是适当独立的同分布。
嗨
以上那行代码的原理是什么?
train_set = sum(train_set, [])
如果我这样做,它会有同样的效果吗?
train_set = train_set[0]
谢谢
我认为这一行是多余的。
嗨
如果我删除那一行,就会出现错误。
回溯(最近一次调用)
File “implement-random-forest-scratch-python.py”, line 210, in
scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features)
File “implement-random-forest-scratch-python.py”, line 67, in evaluate_algorithm
predicted = algorithm(train_set, test_set, *args)
File “implement-random-forest-scratch-python.py”, line 188, in random_forest
tree = build_tree(sample, max_depth, min_size, n_features)
File “implement-random-forest-scratch-python.py”, line 152, in build_tree
root = get_split(train, n_features)
File “implement-random-forest-scratch-python.py”, line 105, in get_split
class_values = list(set(row[-1] for row in dataset))
TypeError: unhashable type: ‘list’
我已验证,在该行之前,train_set 列表的维度始终是
(4, 41, 61)
在该行之后,它变成
(164, 61)
这是 sum 函数的副作用,它将第一个和第二个维度合并为一个,就像在 numpy 中执行类似操作一样:
> X.shape
[a, b, c]
X = X.reshape([a*b, c])
> X.shape
[a*b, c]
啊,是的,我明白了。我写这篇教程已经很多年了。
嗨
根据我的理解,要计算给定特征的基尼指数,首先我们需要迭代所有行,并考虑给定行所对应的该特征的值,然后将条目添加到组中,并一直保留它们,直到我们处理完数据集的所有行。只有这样,我们才能继续计算给定特征的基尼指数。
然而,查看 get_split 函数,情况似乎并非如此,因为我们在每一步都基于单行计算基尼指数。
谢谢
是的。它枚举了所有行的索引。
先生,
我尝试了这段代码来处理我的数据集,准确率是 86.6%。
如何对未标记数据进行预测?
yhat = model.predict(X)。对于这个语句,应该是‘model’。fit 和 predict 方法显示错误。你能给我一些建议吗?因为我是面向对象概念的初学者。
是的,您可以修改它以进行预测而不是评估模型。
如果您觉得这很有挑战性,我反而建议直接使用 scikit-learn 库。
https://machinelearning.org.cn/start-here/#python
嗨,Jason,
我想知道您是否有关于随机森林中非平稳输入方面的帖子。
我正在做一个关于非平稳数据的项目,我发现当直接使用非平稳数据作为输入时,我的 Scikit-learn 随机森林模型比差分化以实现平稳性的模型具有更高的预测准确率,因此我想看看随机森林如何处理非平稳性。
附注:我喜欢您的帖子!
我可能会,我现在记不清了,抱歉。
尝试在建模前使数据平稳。
你好
感谢这篇精彩的帖子。
我遇到了一个错误,在使用我的新数据运行时,但在使用您的数据时运行良好。
错误是
—————————————————————————
IndexError Traceback (最近一次调用)
in
1 for n_trees in [1,5,10]
—-> 2 scores = evaluate_algorithm(data, random_forest, n_folds, max_depth, min_size, sample_size,n_trees,n_features)
3 print(‘Trees: %d’ % n_trees)
4 print(‘Scores: %s’ % scores)
5 print(‘Mean accuracy: %.3f%%’ % (sum(scores)/float(len(scores))))
在 evaluate_algorithm(dataset, algorithm, n_folds, *args)中
60 test_set.append(row_copy)
61 row_copy[-1] = None
—> 62 predicted = algorithm(train_set, test_set, *args)
63 actual = [row[-1] for row in fold]
64 accuracy = accuracy_metric(actual, predicted)
in random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features)
181 for i in range(n_trees)
182 sample = subsample(train, sample_size)
–> 183 tree = build_tree(sample, max_depth, min_size, n_features)
184 trees.append(tree)
185 predictions = [bagging_predict(trees, row) for row in test]
in build_tree(train, max_depth, min_size, n_features)
145 # Build a decision tree
146 def build_tree(train, max_depth, min_size, n_features)
–> 147 root = get_split(train, n_features)
148 split(root, max_depth, min_size, n_features, 1)
149 return root
in get_split(dataset, n_features)
102 features = list()
103 while len(features) 104 index = randrange(len(dataset[0])-1)
105 if index not in features
106 features.append(index)
IndexError: 列表索引超出范围
任何帮助都将非常有帮助,提前感谢。
这些技巧会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
嗨 Jason,请告诉我 RF 中随机分割和自定义分割的区别。
RF。
抱歉,我没听说过“自定义分割”这个词。
在 RF 中,随机分割和自定义分割是什么意思?在用于推荐时,能否请您详细说明一下?
RF 会选择一个随机的特征子集,然后从中选择最佳分割。这不是随机分割。