前言
优化随机森林算法,正确率提高1%~5%(已经有90%+的正确率,再调高会导致过拟合)
论文当然是参考的,毕竟出现早的算法都被人研究烂了,什么优化基本都做过。而人类最高明之处就是懂得利用前人总结的经验和制造的工具(说了这么多就是为偷懒找借口。hhhh)
优化思路
1. 计算传统模型准确率
2. 计算设定树木颗数时最佳树深度,以最佳深度重新生成随机森林
3. 计算新生成森林中每棵树的AUC,选取AUC靠前的一定百分比的树
4. 通过计算各个树的数据相似度,排除相似度超过设定值且AUC较小的树
5. 计算最终的准确率
主要代码粘贴如下 (注释比较详细,就不介绍代码了)
#-*- coding: utf-8 -*- import time from csv import reader from random import randint from random import seed import numpy as np from numpy import mat from group_11 import caculateAUC_1, plotTree # 建立一棵CART树 '''试探分枝''' def data_split(index, value, dataset): left, right = list(), list() for row in dataset: if row[index] < value: left.append(row) else: right.append(row) return left, right '''计算基尼指数''' def calc_gini(groups, class_values): gini = 0.0 total_size = 0 for group in groups: total_size += len(group) for group in groups: size = len(group) if size == 0: continue for class_value in class_values: proportion = [row[-1] for row in group].count(class_value) / float(size) gini += (size / float(total_size)) * (proportion * (1.0 - proportion))# 二分类执行两次,相当于*2 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 # 随机选取特征子集,包含n_features个特征 features = list() while len(features) < n_features: # 随机选取特征 # 特征索引 index = randint(0, len(dataset[0]) - 2) # 往features添加n_features个特征(n_feature等于特征数的根号),特征索引从dataset中随机取 if index not in features: features.append(index) for index in features: # 对每一个特征 # 计算Gini指数 for row in dataset: # 按照每个记录的该特征的取值划分成两个子集,计算对于的Gini(D,A),取最小的 groups = data_split(index, row[index], dataset) gini = calc_gini(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: # left或者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: # 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_one_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'] # 随机森林类 class randomForest: def __init__(self,trees_num, max_depth, leaf_min_size, sample_ratio, feature_ratio): self.trees_num = trees_num # 森林的树的数目 self.max_depth = max_depth # 树深 self.leaf_min_size = leaf_min_size # 建立树时,停止的分枝样本最小数目 self.samples_split_ratio = sample_ratio # 采样,创建子集的比例(行采样) self.feature_ratio = feature_ratio # 特征比例(列采样) self.trees = list() # 森林 '''有放回的采样,创建数据子集''' def sample_split(self, dataset): sample = list() n_sample = round(len(dataset) * self.samples_split_ratio) #每棵树的采样数 while len(sample) < n_sample: index = randint(0, len(dataset) - 2) #随机有放回的采样 sample.append(dataset[index]) return sample ##############***Out-of-Bag***################################ # 进行袋外估计等相关函数的实现,需要注意并不是每个样本都可能出现在随机森林的袋外数据中 # 因此进行oob估计时需要注意估计样本的数量 def OOB(self, oobdata, train, trees): '''输入为:袋外数据dict,训练集,tree_list return oob准确率''' n_rows = [] count = 0 n_trees = len(trees) # 森林中树的棵树 for key, item in oobdata.items(): n_rows.append(item) # print(len(n_rows)) # 所有trees中的oob数据的合集 n_rows_list = sum(n_rows, []) unique_list = [] for l1 in n_rows_list: # 从oob合集中计算独立样本数量 if l1 not in unique_list: unique_list.append(l1) n = len(unique_list) # print(n) # 对训练集中的每个数据,进行遍历,寻找其作为oob数据时的所有trees,并进行多数投票 for row in train: pre = [] for i in range(n_trees): if row not in oobdata[i]: # print('row: ',row) # print('trees[i]: ', trees[i]) pre.append(predict(trees[i], row)) if len(pre) > 0: label = max(set(pre), key=pre.count) if label == row[-1]: count += 1 return (float(count) / n) * 100 '''建立随机森林''' def build_randomforest(self, train): temp_flag = 0 max_depth = self.max_depth # 树深 min_size = self.leaf_min_size # 建立树时,停止的分枝样本最小数目 n_trees = self.trees_num # 森林的树的数目 n_features = int(self.feature_ratio * (len(train[0])-1)) #列采样,从M个feature中,选择m个(m<samerate): # 将对比树置空 newforest[k] = None result_forest = list() for i in range(0, newforest.__len__()): if not newforest[i] == None: result_forest.append(newforest[i]) return result_forest '''auc优化method''' def auc_optimization(auclist,trees_num,trees): # 为auc排序,获取从大到小的与trees相对应的索引列表 b = sorted(enumerate(auclist), key=lambda x: x[1], reverse=True) index_list = [x[0] for x in b] auc_num = int(trees_num * 2 / 3) # 取auc高的前auc_num个 print('auc: ', auc_num, index_list) newTempForest = list() for i in range(auc_num): # myRF.trees.append(tempForest[i]) # newTempForest.append(myRF.trees[index_list[i]]) newTempForest.append(trees[index_list[i]]) return newTempForest '''得到森林中决策树的最佳深度''' def getBestDepth(min_size,sample_ratio,trees_num,feature_ratio,traindata,testdata): max_depth = np.linspace(1, 15, 15, endpoint=True) # max_depth=[5,6,7,8,9,10,11,12,13,14,15] scores_final = [] i=0 for depth in max_depth: # 初始化随机森林 # print('=========>',i,'<=============') myRF_ = randomForest(trees_num, depth, min_size, sample_ratio, feature_ratio) # 生成随机森林 myRF_.build_randomforest(traindata) # 测试评估 acc = myRF_.accuracy_metric(testdata[:-1]) # print('模型准确率:', acc, '%') # scores_final.append(acc.mean()) scores_final.append(acc*0.01) i=i+1 # print('scores_final: ',scores_final) # 找到深度小且准确率高的值 best_depth = 0 temp_score = 0 for i in range(len(scores_final)): if scores_final[i] > temp_score: temp_score = scores_final[i] best_depth = max_depth[i] # print('best_depth:',np.mean(scores_final),best_depth) # plt.plot(max_depth, scores_final, 'r-', lw=2) # # plt.plot(max_depth, list(range(0,max(scores_final))), 'r-', lw=2) # plt.xlabel('max_depth') # plt.ylabel('CV scores') # plt.ylim(bottom=0.0,top=1.0) # plt.grid() # plt.show() return best_depth '''对比不同树个数时的模型正确率''' def getMyRFAcclist(treenum_list): seed(1) # 每一次执行本文件时都能产生同一个随机数 filename = 'DataSet3.csv' #SMOTE处理过的数据 min_size = 1 sample_ratio = 1 feature_ratio = 0.3 # 尽可能小,但是要保证 int(self.feature_ratio * (len(train[0])-1)) 大于1 same_value = 20 # 向量内积的差(小于此值认为相似) same_rate = 0.63 # 树的相似度(大于此值认为相似) # 加载数据 dataset, features = load_csv(filename) traindata, testdata = split_train_test(dataset, feature_ratio) # 森林中不同树个数的对比 # treenum_list = [20, 30, 40, 50, 60] acc_num_list = list() acc_list=list() for trees_num in treenum_list: # 优化1-获取最优深度 max_depth = getBestDepth(min_size, sample_ratio, trees_num, feature_ratio, traindata, testdata) print('max_depth is ', max_depth) # 初始化随机森林 myRF = randomForest(trees_num, max_depth, min_size, sample_ratio, feature_ratio) # 生成随机森林 myRF.build_randomforest(traindata) print('Tree_number: ', myRF.trees.__len__()) # 计算森林中每棵树的AUC auc_list = caculateAUC_1.caculateRFAUC(testdata, myRF.trees) # 选取AUC高的决策数形成新的森林(auc优化) newTempForest = auc_optimization(auc_list,trees_num,myRF.trees) # 相似度优化 myRF.trees = similarity_optimization(newTempForest, same_value, same_rate) # 测试评估 acc = myRF.accuracy_metric(testdata[:-1]) print('myRF1_模型准确率:', acc, '%') acc_num_list.append([myRF.trees.__len__(), acc]) acc_list.append(acc) print('trees_num from 20 to 60: ', acc_num_list) return acc_list if __name__ == '__main__': start = time.clock() seed(1) # 每一次执行本文件时都能产生同一个随机数 filename = 'DataSet3.csv' # 这里是已经利用SMOTE进行过预处理的数据集 max_depth = 15 # 调参(自己修改) #决策树深度不能太深,不然容易导致过拟合 min_size = 1 sample_ratio = 1 trees_num = 20 feature_ratio = 0.3 # 尽可能小,但是要保证 int(self.feature_ratio * (len(train[0])-1)) 大于1 same_value = 20 # 向量内积的差(小于此值认为相似) same_rate = 0.82 # 树的相似度(大于此值认为相似) # 加载数据 dataset,features = load_csv(filename) traindata,testdata = split_train_test(dataset, feature_ratio) # 优化1-获取最优深度 # max_depth = getBestDepth(min_size, sample_ratio, trees_num, feature_ratio, traindata, testdata) # print('max_depth is ',max_depth) # 初始化随机森林 myRF = randomForest(trees_num, max_depth, min_size, sample_ratio, feature_ratio) # 生成随机森林 myRF.build_randomforest(traindata) print('Tree_number: ', myRF.trees.__len__()) acc = myRF.accuracy_metric(testdata[:-1]) print('传统RF模型准确率:',acc,'%') # 画出某棵树用以可视化观察(这里是第一棵树) # plotTree.creatPlot(myRF.trees[0], features) # 计算森林中每棵树的AUC auc_list = caculateAUC_1.caculateRFAUC(testdata,myRF.trees) # 画出每棵树的auc――柱状图 # plotTree.plotAUCbar(auc_list.__len__(),auc_list) # 选取AUC高的决策数形成新的森林(auc优化) newTempForest = auc_optimization(auc_list,trees_num,myRF.trees) # 相似度优化 myRF.trees=similarity_optimization(newTempForest, same_value, same_rate) print('优化后Tree_number: ', myRF.trees.__len__()) # 测试评估 acc = myRF.accuracy_metric(testdata[:-1]) # print('优化后模型准确率:', acc, '%') print('myRF1_模型准确率:', acc, '%') # 画出某棵树用以可视化观察(这里是第一棵树) # plotTree.creatPlot(myRF.trees[0], features) # 计算森林中每棵树的AUC auc_list = caculateAUC_1.caculateRFAUC(testdata, myRF.trees) # 画出每棵树的auc――柱状图 plotTree.plotAUCbar(auc_list.__len__(), auc_list) end = time.clock() print('The end!') print(end-start)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。