44'''
55Created 2017-04-25
66Random 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'''
913from 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)
8387def 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
134138def 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
156174def 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
162190def 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
172212def 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