侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

python 随机森林算法及其优化详解

Python  /  管理员 发布于 7年前   311

前言

优化随机森林算法,正确率提高1%~5%(已经有90%+的正确率,再调高会导致过拟合)

论文当然是参考的,毕竟出现早的算法都被人研究烂了,什么优化基本都做过。而人类最高明之处就是懂得利用前人总结的经验和制造的工具(说了这么多就是为偷懒找借口。hhhh)

优化思路

1. 计算传统模型准确率

2. 计算设定树木颗数时最佳树深度,以最佳深度重新生成随机森林

3. 计算新生成森林中每棵树的AUC,选取AUC靠前的一定百分比的树

4. 通过计算各个树的数据相似度,排除相似度超过设定值且AUC较小的树

5. 计算最终的准确率

主要代码粘贴如下(注释比较详细,就不介绍代码了)

#-*- coding: utf-8 -*-import timefrom csv import readerfrom random import randintfrom random import seedimport numpy as npfrom numpy import matfrom 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<<M)  # print('特征值为 : ',n_features)  oobs = {} # ----------------------  for i in range(n_trees):   # 建立n_trees棵决策树   sample = self.sample_split(train)  # 有放回的采样,创建数据子集   oobs[i] = sample # ----------------   tree = build_one_tree(sample, max_depth, min_size, n_features) # 建立决策树   self.trees.append(tree)   temp_flag += 1   # print(i,tree)  oob_score = self.OOB(oobs, train, self.trees) # oob准确率---------  print("oob_score is ", oob_score) # 打印oob准确率---------  return self.trees '''随机森林预测的多数表决''' def bagging_predict(self, onetestdata):  predictions = [predict(tree, onetestdata) for tree in self.trees]  return max(set(predictions), key=predictions.count) '''计算建立的森林的精确度''' def accuracy_metric(self, testdata):  correct = 0  for i in range(len(testdata)):   predicted = self.bagging_predict(testdata[i])   if testdata[i][-1] == predicted:    correct += 1  return correct / float(len(testdata)) * 100.0# 数据处理'''导入数据'''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)   dataset.append(row[:-1]) # return dataset return dataset[1:], dataset[0]'''划分训练数据与测试数据'''def split_train_test(dataset, ratio=0.3): #ratio = 0.2 # 取百分之二十的数据当做测试数据 num = len(dataset) train_num = int((1-ratio) * num) dataset_copy = list(dataset) traindata = list() while len(traindata) < train_num:  index = randint(0,len(dataset_copy)-1)  traindata.append(dataset_copy.pop(index)) testdata = dataset_copy return traindata, testdata'''分析树,将向量内积写入list'''def analyListTree(node, tag, result): # 叶子节点的父节点 if (isinstance(node['left'], dict)):  # 计算node与node[tag]的内积  tag="left"  re = Inner_product(node, tag)  result.append(re)  analyListTree(node['left'], 'left', result)  return elif (isinstance(node['right'], dict)):  # 计算node与node[tag]的内积  tag = "right"  re = Inner_product(node, tag)  result.append(re)  analyListTree(node['right'], 'right', result)  return else:  return'''求向量内积'''# 计算node与node[tag]的内积def Inner_product(node ,tag): a = mat([[float(node['index'])], [float(node['value'])]]) b = mat([[float(node[tag]['index'])], [float(node[tag]['value'])]]) return (a.T * b)[0,0]'''相似度优化'''''' same_value = 20  # 向量内积的差(小于此值认为相似) same_rate = 0.63  # 树的相似度(大于此值认为相似) 返回新的森林(已去掉相似度高的树)'''def similarity_optimization(newforest, samevalue, samerate): res = list()    # 存储森林的内积 result = list()    # 存储某棵树的内积 i = 1 for tree in newforest:  # 分析树,将向量内积写入list  # result 存储tree的内积  analyListTree(tree, None, result)  res.append(result)  # print('第',i,'棵树:',len(result),result)  result = [] # print('res = ',len(res),res) # 取一棵树的单个向量内积与其他树的单个向量内积做完全对比(相似度) # 遍历列表的列 for i in range(0, len(res) - 1):  # 保证此列未被置空、  if not newforest[i] == None:   # 遍历做对比的树的列   for k in range(i + 1, len(res)):    if not newforest[k] == None:     # time用于统计相似的次数,在每次更换对比树时重置为0     time = 0     # 遍历列表的当前行     for j in range(0, len(res[i])):      # 当前两颗树对比次数      all_contrast = (res[ i].__len__() * res[k].__len__())      # 遍历做对比的树的行      for l in range(0, len(res[k])):       # 如果向量的内积相等,计数器加一       if res[i][j] - res[k][l] < samevalue:        time = time + 1      # 如果相似度大于设定值     real_same_rate = time / all_contrast     if (real_same_rate > 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_listif __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)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    Python实现最大子序和的方法示例
    下一条:
    python中将两组数据放在一起按照某一固定顺序shuffle的实例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客