Skip to content

Commit b842385

Browse files
补充了随机森林内容
1 parent 95a2b41 commit b842385

6 files changed

Lines changed: 173 additions & 43 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# MachineLearning
22

3-
**Machine Learning in Action (python) | [ApacheCN(apache中文网)](http://www.apache.wiki)**
3+
* **欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远**
4+
* **Machine Learning in Action (python) | [ApacheCN(apache中文网)](http://www.apache.wiki)**
45

56
## 第一部分 分类
67

@@ -18,6 +19,8 @@
1819
* [支持向量机](./docs/6.支持向量机.md)
1920
* 7) 利用AdaBoost元算法提高分类
2021
* [利用AdaBoost元算法提高分类](./docs/7.利用AdaBoost元算法提高分类.md)
22+
* 7) 随机森林的使用(个人补充,非课本内容)
23+
* [随机森林的使用](./docs/7.随机森林的使用.md)
2124

2225
## 第二部分 利用回归预测数值型数据
2326

docs/7.随机森林的使用.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# 第7章 随机森林的使用(个人补充,非课本内容)
2+
3+
## 基本介绍
4+
5+
* 随机森林指的是利用多棵树对样本进行训练并预测的一种分类器。
6+
* 决策树相当于一个大师,通过自己在数据集中学到的知识对于新的数据进行分类。但是俗话说得好,一个诸葛亮,玩不过三个臭皮匠。随机森林就是希望构建多个臭皮匠,希望最终的分类效果能够超过单个大师的一种算法。
7+
8+
## 构造流程
9+
10+
* 那随机森林具体如何构建呢?有两个方面:数据的随机性选取,以及待选特征的随机选取。
11+
12+
> 数据的随机选取:
13+
14+
```
15+
首先,从原始的数据集中采取有放回的抽样,构造子数据集,子数据集的数据量是和原始数据集相同的。不同子数据集的元素可以重复,同一个子数据集中的元素也可以重复。
16+
第二,利用子数据集来构建子决策树,将这个数据放到每个子决策树中,每个子决策树输出一个结果。
17+
最后,如果有了新的数据需要通过随机森林得到分类结果,就可以通过对子决策树的判断结果的投票,得到随机森林的输出结果了。
18+
如下图,假设随机森林中有3棵子决策树,2棵子树的分类结果是A类,1棵子树的分类结果是B类,那么随机森林的分类结果就是A类。
19+
```
20+
![数据重抽样](/images/7.RandomForest/数据重抽样.jpg)
21+
22+
> 待选特征的随机选取
23+
24+
```
25+
与数据集的随机选取类似,随机森林中的子树的每一个分裂过程并未用到所有的待选特征,而是从所有的待选特征中随机选取一定的特征,之后再在随机选取的特征中选取最优的特征。这样能够使得随机森林中的决策树都能够彼此不同,提升系统的多样性,从而提升分类性能。
26+
下图中,蓝色的方块代表所有可以被选择的特征,也就是目前的待选特征。黄色的方块是分裂特征。左边是一棵决策树的特征选取过程,通过在待选特征中选取最优的分裂特征(别忘了前文提到的ID3算法,C4.5算法,CART算法等等),完成分裂。右边是一个随机森林中的子树的特征选取过程。
27+
```
28+
![特征重抽样](/images/7.RandomForest/特征重抽样.jpg)
29+
30+
> 随机森林-流程图
31+
32+
![随机森林-流程图](/images/7.RandomForest/RandomForest_Flow.jpg)
85.4 KB
Loading
109 KB
Loading
14.7 KB
Loading

src/python/7.RandomForest/randomForest.py

Lines changed: 137 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
'''
55
Created 2017-04-25
66
Random Forest Algorithm on Sonar Dataset
7-
@author: Flying_sfeng/jiangzhonglian
7+
@author: Flying_sfeng/片刻
8+
---
9+
源代码网址:http://www.tuicool.com/articles/iiUfeim
10+
Flying_sfeng博客地址:http://blog.csdn.net/flying_sfeng/article/details/64133822
11+
在此表示感谢你的代码和注解, 我重新也完善了你的注解
812
'''
913
from random import seed, randrange, random
10-
from math import sqrt
11-
from math import log
1214

1315

1416
# 导入csv文件
@@ -32,27 +34,29 @@ def loadDataSet(filename):
3234
return dataset
3335

3436

35-
# Split a dataset into k folds
36-
def cross_validation_split(dataset, n_folds): #将数据集dataset分成n_flods份,每份包含len(dataset) / n_folds个值,每个值由dataset数据集的内容随机产生,每个值被使用一次
37+
def cross_validation_split(dataset, n_folds):
38+
"""cross_validation_split(将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次list的元素是无重复的)
39+
40+
Args:
41+
dataset 原始数据集
42+
n_folds 数据集dataset分成n_flods份
43+
Returns:
44+
dataset_split list集合,存放的是:将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次list的元素是无重复的
45+
"""
3746
dataset_split = list()
3847
dataset_copy = list(dataset) #复制一份dataset,防止dataset的内容改变
3948
fold_size = len(dataset) / n_folds
4049
for i in range(n_folds):
4150
fold = list() #每次循环fold清零,防止重复导入dataset_split
4251
while len(fold) < fold_size: #这里不能用if,if只是在第一次判断时起作用,while执行循环,直到条件不成立
52+
# 有放回的随机采样,有一些样本被重复采样,从而在训练集中多次出现,有的则从未在训练集中出现,此则自助采样法。从而保证每棵决策树训练集的差异性
4353
index = randrange(len(dataset_copy))
44-
fold.append(dataset_copy.pop(index)) #将对应索引index的内容从dataset_copy中导出,并将该内容从dataset_copy中删除。pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
54+
# 将对应索引index的内容从dataset_copy中导出,并将该内容从dataset_copy中删除。
55+
# pop()函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
56+
fold.append(dataset_copy.pop(index))
4557
dataset_split.append(fold)
46-
return dataset_split #由dataset分割出的n_folds个数据构成的列表,为了用于交叉验证
47-
48-
49-
# Calculate accuracy percentage
50-
def accuracy_metric(actual, predicted): #导入实际值和预测值,计算精确度
51-
correct = 0
52-
for i in range(len(actual)):
53-
if actual[i] == predicted[i]:
54-
correct += 1
55-
return correct / float(len(actual)) * 100.0
58+
# 由dataset分割出的n_folds个数据构成的列表,为了用于交叉验证
59+
return dataset_split
5660

5761

5862
# Split a dataset based on an attribute and an attribute value #根据特征和特征值分割数据集
@@ -79,12 +83,12 @@ def gini_index(groups, class_values): #个人理解:计算代价,分类越
7983
return gini
8084

8185

82-
# Select the best split point for a dataset #找出分割数据集的最优特征,得到最优的特征index,特征值row[index],以及分割完的数据groups(left,right)
86+
# 找出分割数据集的最优特征,得到最优的特征index,特征值row[index],以及分割完的数据groups(left,right)
8387
def get_split(dataset, n_features):
8488
class_values = list(set(row[-1] for row in dataset)) #class_values =[0,1]
8589
b_index, b_value, b_score, b_groups = 999, 999, 999, None
8690
features = list()
87-
while len(features) < n_features:
91+
while len(features) < n_features:
8892
index = randrange(len(dataset[0])-1) #往features添加n_features个特征(n_feature等于特征数的根号),特征索引从dataset中随机取
8993
if index not in features:
9094
features.append(index)
@@ -132,8 +136,22 @@ def split(node, max_depth, min_size, n_features, depth): #max_depth = 10,min_
132136

133137
# Build a decision tree
134138
def build_tree(train, max_depth, min_size, n_features):
135-
#root = get_split(dataset, n_features)
139+
"""build_tree(创建一个决策树)
140+
141+
Args:
142+
train 训练数据集
143+
max_depth 决策树深度不能太深,不然容易导致过拟合
144+
min_size 叶子节点的大小
145+
n_features 选取的特征的个数
146+
Returns:
147+
root 返回决策树
148+
"""
149+
150+
# 返回最有列和相关的信息
136151
root = get_split(train, n_features)
152+
153+
# 对左右2变的数据 进行递归的调用,由于最优特征使用过,所以在后面进行使用的时候,就没有意义了
154+
# 例如: 性别-男女,对男使用这一特征就没任何意义了
137155
split(root, max_depth, min_size, n_features, 1)
138156
return root
139157

@@ -154,46 +172,124 @@ def predict(node, row): #预测模型分类结果
154172

155173
# Make a prediction with a list of bagged trees
156174
def bagging_predict(trees, row):
157-
predictions = [predict(tree, row) for tree in trees] #使用多个决策树trees对测试集test的第row行进行预测,再使用简单投票法判断出该行所属分类
175+
"""bagging_predict(bagging预测)
176+
177+
Args:
178+
trees 决策树的集合
179+
row 测试数据集的每一行数据
180+
Returns:
181+
返回随机森林中,决策树结果出现次数做大的
182+
"""
183+
184+
# 使用多个决策树trees对测试集test的第row行进行预测,再使用简单投票法判断出该行所属分类
185+
predictions = [predict(tree, row) for tree in trees]
158186
return max(set(predictions), key=predictions.count)
159187

160188

161189
# Create a random subsample from the dataset with replacement
162190
def subsample(dataset, ratio): #创建数据集的随机子样本
191+
"""random_forest(评估算法性能,返回模型得分)
192+
193+
Args:
194+
dataset 训练数据集
195+
ratio 训练数据集的样本比例
196+
Returns:
197+
sample 随机抽样的训练样本
198+
"""
199+
163200
sample = list()
164-
n_sample = round(len(dataset) * ratio) #round() 方法返回浮点数x的四舍五入值。
201+
# 训练样本的按比例抽样。
202+
# round() 方法返回浮点数x的四舍五入值。
203+
n_sample = round(len(dataset) * ratio)
165204
while len(sample) < n_sample:
166-
index = randrange(len(dataset)) #有放回的随机采样,有一些样本被重复采样,从而在训练集中多次出现,有的则从未在训练集中出现,此则自助采样法。从而保证每棵决策树训练集的差异性
205+
# 有放回的随机采样,有一些样本被重复采样,从而在训练集中多次出现,有的则从未在训练集中出现,此则自助采样法。从而保证每棵决策树训练集的差异性
206+
index = randrange(len(dataset))
167207
sample.append(dataset[index])
168208
return sample
169209

170210

171211
# Random Forest Algorithm
172212
def random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features):
213+
"""random_forest(评估算法性能,返回模型得分)
214+
215+
Args:
216+
train 训练数据集
217+
test 测试数据集
218+
max_depth 决策树深度不能太深,不然容易导致过拟合
219+
min_size 叶子节点的大小
220+
sample_size 训练数据集的样本比例
221+
n_trees 决策树的个数
222+
n_features 选取的特征的个数
223+
Returns:
224+
predictions 每一行的预测结果,bagging 预测最后的分类结果
225+
"""
226+
173227
trees = list()
174-
for i in range(n_trees): #n_trees表示决策树的数量
175-
sample = subsample(train, sample_size) #随机采样保证了每棵决策树训练集的差异性
176-
tree = build_tree(sample, max_depth, min_size, n_features) #建立一个决策树
228+
# n_trees表示决策树的数量
229+
for i in range(n_trees):
230+
# 随机抽样的训练样本, 随机采样保证了每棵决策树训练集的差异性
231+
sample = subsample(train, sample_size)
232+
# 创建一个决策树
233+
tree = build_tree(sample, max_depth, min_size, n_features)
177234
trees.append(tree)
235+
236+
# 每一行的预测结果,bagging 预测最后的分类结果
178237
predictions = [bagging_predict(trees, row) for row in test]
179-
return(predictions)
238+
return predictions
180239

181240

182-
# Evaluate an algorithm using a cross validation split
183-
def evaluate_algorithm(dataset, algorithm, n_folds, *args): #评估算法性能,返回模型得分
241+
# Calculate accuracy percentage
242+
def accuracy_metric(actual, predicted): #导入实际值和预测值,计算精确度
243+
correct = 0
244+
for i in range(len(actual)):
245+
if actual[i] == predicted[i]:
246+
correct += 1
247+
return correct / float(len(actual)) * 100.0
248+
249+
250+
# 评估算法性能,返回模型得分
251+
def evaluate_algorithm(dataset, algorithm, n_folds, *args):
252+
"""evaluate_algorithm(评估算法性能,返回模型得分)
253+
254+
Args:
255+
dataset 原始数据集
256+
algorithm 使用的算法
257+
n_folds 树的个数
258+
*args 其他的参数
259+
Returns:
260+
scores 模型得分
261+
"""
262+
263+
# 将数据集进行抽重抽样 n_folds 份,数据可以重复重复抽取,每一次list的元素是无重复的
184264
folds = cross_validation_split(dataset, n_folds)
185265
scores = list()
186-
for fold in folds: #每次循环从folds从取出一个fold作为测试集,其余作为训练集,遍历整个folds,实现交叉验证
266+
# 每次循环从folds从取出一个fold作为测试集,其余作为训练集,遍历整个folds,实现交叉验证
267+
for fold in folds:
187268
train_set = list(folds)
188269
train_set.remove(fold)
189-
train_set = sum(train_set, []) #将多个fold列表组合成一个train_set列表
270+
# 将多个fold列表组合成一个train_set列表, 类似 union all
271+
"""
272+
In [20]: l1=[[1, 2, 'a'], [11, 22, 'b']]
273+
In [21]: l2=[[3, 4, 'c'], [33, 44, 'd']]
274+
In [22]: l=[]
275+
In [23]: l.append(l1)
276+
In [24]: l.append(l2)
277+
In [25]: l
278+
Out[25]: [[[1, 2, 'a'], [11, 22, 'b']], [[3, 4, 'c'], [33, 44, 'd']]]
279+
In [26]: sum(l, [])
280+
Out[26]: [[1, 2, 'a'], [11, 22, 'b'], [3, 4, 'c'], [33, 44, 'd']]
281+
"""
282+
train_set = sum(train_set, [])
190283
test_set = list()
191-
for row in fold: #fold表示从原始数据集dataset提取出来的测试集
284+
# fold表示从原始数据集dataset提取出来的测试集
285+
for row in fold:
192286
row_copy = list(row)
193287
test_set.append(row_copy)
194288
row_copy[-1] = None
195289
predicted = algorithm(train_set, test_set, *args)
196290
actual = [row[-1] for row in fold]
291+
292+
# 计算随机森林的预测结果的正确率
197293
accuracy = accuracy_metric(actual, predicted)
198294
scores.append(accuracy)
199295
return scores
@@ -205,18 +301,17 @@ def evaluate_algorithm(dataset, algorithm, n_folds, *args): #评估算法性
205301
dataset = loadDataSet('input/7.RandomForest/sonar-all-data.txt')
206302
# print dataset
207303

208-
n_folds = 5 #分成5份数据,进行交叉验证
209-
#max_depth = 10 #递归十次
210-
max_depth = 20 #调参(自己修改) #决策树深度不能太深,不然容易导致过拟合
211-
min_size = 1
212-
sample_size = 1.0
213-
#n_features = int(sqrt(len(dataset[0])-1))
214-
n_features =15 #调参(自己修改) #准确性与多样性之间的权衡
215-
for n_trees in [1,5,10]: #理论上树是越多越好
304+
n_folds = 5 # 分成5份数据,进行交叉验证
305+
max_depth = 20 # 调参(自己修改) #决策树深度不能太深,不然容易导致过拟合
306+
min_size = 1 # 决策树的叶子节点最少的元素数量
307+
sample_size = 1.0 # 做决策树时候的样本的比例
308+
# n_features = int(sqrt(len(dataset[0])-1))
309+
n_features =15 # 调参(自己修改) #准确性与多样性之间的权衡
310+
for n_trees in [1, 5, 10]: # 理论上树是越多越好
216311
scores = evaluate_algorithm(dataset, random_forest, n_folds, max_depth, min_size, sample_size, n_trees, n_features)
217312
# 每一次执行本文件时都能产生同一个随机数
218313
seed(1)
219314
print 'random=', random()
220-
print('Trees: %d' % n_trees)
221-
print('Scores: %s' % scores)
222-
print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores))))
315+
print 'Trees: %d' % n_trees
316+
print 'Scores: %s' % scores
317+
print 'Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores)))

0 commit comments

Comments
 (0)