python机器学习之随机森林(七)


Posted in Python onMarch 26, 2018

机器学习之随机森林,供大家参考,具体内容如下

1、Bootstraping(自助法) 

      名字来自成语“pull up by your own bootstraps”,意思是依靠你自己的资源,称为自助法,它是一种有放回的抽样方法,它是非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法。其核心思想和基本步骤如下:
 (1) 采用重抽样技术从原始样本中抽取一定数量(自己给定)的样本,此过程允许重复抽样。
 (2) 根据抽出的样本计算给定的统计量T。
 (3) 重复上述N次(一般大于1000),得到N个统计量T。
 (4) 计算上述N个统计量T的样本方差,得到统计量的方差。

应该说Bootstrap是现代统计学较为流行的一种统计方法,在小样本时效果很好。通过方差的估计可以构造置信区间等,其运用范围得到进一步延伸。


2、Bagging (套袋法) 

    Bagging即bootstrap aggregating(自举汇聚法)的缩写,其算法过程如下:
    A).从原始样本集中抽取训练集。每轮从原始样本集中使用Bootstraping的方法抽取n个训练样本,意思是从原始集合中随机选择一个样本,然后随机选择一个样本来代替这个样本(在训练集中,有些样本可能被多次抽取到,而有些样本可能一次都没有被抽中)。共进行k轮抽取,得到k个训练集。(k个训练集之间是相互独立的)
    B).每次使用一个训练集得到一个模型,k个训练集共得到k个模型。(注:这里并没有具体的分类算法或回归方法,我们可以根据具体问题采用不同的分类或回归方法,如决策树、感知器等)

3、Boosting(提升法): 

    其主要思想是将弱分类器组装成一个强分类器。在PAC(概率近似正确)学习框架下,则一定可以将弱分类器组装成一个强分类器。关于Boosting的两个核心问题:
    1)在每一轮如何改变训练数据的权值或概率分布?
    通过提高那些在前一轮被弱分类器分错样例的权值,减小前一轮分对样例的权值,来使得分类器对误分的数据有较好的效果。
    2)通过什么方式来组合弱分类器?
    通过加法模型将弱分类器进行线性组合,比如AdaBoost通过加权多数表决的方式,即增大错误率小的分类器的权值,同时减小错误率较大的分类器的权值。而提升树通过拟合残差的方式逐步减小残差,将每一步生成的模型叠加得到最终模型。

4、gradient boosting(梯度提升法): 

    Boosting是一种思想,Gradient Boosting是一种实现Boosting的方法,它主要的思想是,每一次建立模型是在之前建立模型损失函数的梯度下降方向。损失函数(loss function)描述的是模型的不靠谱程度,损失函数越大,则说明模型越容易出错。如果我们的模型能够让损失函数持续的下降,则说明我们的模型在不停的改进,而最好的方式就是让损失函数在其梯度(Gradient)的方向上下降。

5、Bagging,Boosting二者之间的区别:

相同点:
    bagging算法和boosting算法都属于集成学习集成学习(Ensemble Learning)。
不同点:
    1)样本选择上:
    Bagging:训练集是在原始集中有放回选取的,从原始集中选出的各轮训练集之间是独立的。
    Boosting:每一轮的训练集不变,只是训练集中每个样例在分类器中的权重发生变化。而权值是根据上一轮的分类结果进行调整。
    2)样例权重:
    Bagging:使用均匀取样,每个样例的权重相等
    Boosting:根据错误率不断调整样例的权值,错误率越大则权重越大。
    3)预测函数:
    Bagging:所有预测函数的权重相等。
    Boosting:每个弱分类器都有相应的权重,对于分类误差小的分类器会有更大的权重。
    4)并行计算:
    Bagging:各个预测函数可以并行生成
    Boosting:各个预测函数只能顺序生成,因为后一个模型参数需要前一轮模型的结果。

总结: 

    1)Bagging + 决策树 = 随机森林
    2)AdaBoost + 决策树 = 提升树
    3)Gradient Boosting + 决策树 = GBDT

6、Rand forest(随机森林): 

    随机森林是一种重要的基于Bagging的集成学习方法,可以用来做分类、回归等问题。
随机森林优点:
    1)具有极高的准确率
    2)随机性的引入,使得随机森林不容易过拟合
    3)随机性的引入,使得随机森林有很好的抗噪声能力
    4)能处理很高维度的数据,并且不用做特征选择
    5)既能处理离散型数据,也能处理连续型数据,数据集无需规范化
    6)训练速度快,可以得到变量重要性排序
    7)容易实现并行化
随机森林的缺点:
    1)当随机森林中的决策树个数很多时,训练时需要的空间和时间会较大
    2)随机森林模型还有许多不好解释的地方,有点算个黑盒模型
随机森林的构建过程:
    1)从原始训练集中使用Bootstraping方法随机有放回采样选出m个样本,共进行n_tree次采样,生成n_tree个训练集
    2)对于n_tree个训练集,我们分别训练n_tree个决策树模型
    3)对于单个决策树模型,假设训练样本特征的个数为n,那么每次分裂时根据信息增益/信息增益比/基尼指数选择最好的特征进行分裂
    4)每棵树都一直这样分裂下去,直到该节点的所有训练样例都属于同一类。在决策树的分裂过程中不需要剪枝
    5)将生成的多棵决策树组成随机森林。对于分类问题,按多棵树分类器投票决定最终分类结果;对于回归问题,由多棵树预测值的均值决定最终预测结果.

7、随机森林的实现: 

    随机森林就是对决策树的集成,但有两点不同:
    (1)采样的差异性:从含m个样本的数据集中有放回的采样,得到含m个样本的采样集,用于训练。这样能保证每个决策树的训练样本不完全一样。
    (2)特征选取的差异性:每个决策树的n个分类特征是在所有特征中随机选择的(n是一个需要我们自己调整的参数)。传统决策树在选择划分特征时在当前节点的特征集合(假定有d个特征)中选择一个最优特征;而在随机森林中,对于决策树的每个节点,先从该节点的特征集合中随机选择一个包含n个特征的子集,然后再从这个子集中选择一个最优特征用于划分。这里的参数n控制了随机性的引入程度:若令n=d,则决策树的构建与传统决策树相同;若令n=1,则是随机选择一个特征进行划分。
    随机森林需要调整的参数有:
    (1)决策树的个数
    (2)每个决策树分类特征的个数
    (3)递归次数(即决策树的深度)

#coding=utf-8
# Random Forest Algorithm on Sonar Dataset
from random import seed
from random import randrange
from csv import reader
from math import sqrt
from math import log

# 加载数据
def load_csv(filename): #导入csv文件
  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

#除了判别列,其他列都转换为float类型 
def str_column_to_float(dataset, column): #将数据集的第column列转换成float形式
  for row in dataset:
    row[column] = float(row[column].strip()) #strip()返回移除字符串头尾指定的字符生成的新字符串。


# 将数据集随机分成n份,方便交叉验证
def cross_validation_split(dataset, n_folds): #将数据集dataset分成n_flods份,每份包含len(dataset) / n_folds个值,每个值由dataset数据集的内容随机产生,每个值被使用一次
  dataset_split = list()
  dataset_copy = list(dataset) #复制一份dataset,防止dataset的内容改变
  fold_size = len(dataset) / n_folds # 每份数据集的大小
  for i in range(n_folds):
    fold = list()  #每次循环fold清零,防止重复导入dataset_split
    while len(fold) < fold_size:  #这里不能用if,if只是在第一次判断时起作用,while执行循环,直到条件不成立
      index = randrange(len(dataset_copy))
      fold.append(dataset_copy.pop(index)) #将对应索引index的内容从dataset_copy中导出,并将该内容从dataset_copy中删除。pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
    dataset_split.append(fold)
  return dataset_split  #由dataset分割出的n_folds个数据构成的列表,为了用于交叉验证

#导入实际值和预测值,计算精确度
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 test_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 gini_index(groups, class_values):  #个人理解:计算代价,分类越准确,则gini越小
  gini = 0.0
  for class_value in class_values: #class_values =[0,1] 
    for group in groups:     #groups=(left,right)
      size = len(group)
      if size == 0:
        continue
      proportion = [row[-1] for row in group].count(class_value) / float(size)
      gini += (proportion * (1.0 - proportion)) #个人理解:计算代价,分类越准确,则gini越小
  return gini

#找出分割数据集的最优特征,得到最优的特征index,特征值row[index],以及分割完的数据groups(left,right)
def get_split(dataset, n_features):
  class_values = list(set(row[-1] for row in dataset)) #class_values =[0,1]
  b_index, b_value, b_score, b_groups = 999, 999, 999, None
  features = list()
  #往features添加n_features个特征(n_feature等于特征数的根号),特征索引从dataset中随机取
  while len(features) < n_features:  
    index = randrange(len(dataset[0])-1) 
    if index not in features:
      features.append(index)
  #在n_features个特征中选出最优的特征索引,并没有遍历所有特征,从而保证了每课决策树的差异性    
  for index in features:    
    for row in dataset:
  #groups=(left,right);row[index]遍历每一行index索引下的特征值作为分类值value,找出最优的分类特征和特征值
      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 #最后得到最优的分类特征b_index,分类特征值b_value,分类结果b_groups。b_value为分错的代价成本。
  #print b_score
  return {'index':b_index, 'value':b_value, 'groups':b_groups}

#输出group中出现次数较多的标签
def to_terminal(group):
  outcomes = [row[-1] for row in group]      #max()函数中,当key参数不为空时,就以key的函数对象为判断的标准;
  return max(set(outcomes), key=outcomes.count)  # 输出group中出现次数较多的标签 

#创建子分割器,递归分类,直到分类结束
def split(node, max_depth, min_size, n_features, depth): #max_depth = 10,min_size = 1,n_features = int(sqrt(len(dataset[0])-1)) 
  left, right = node['groups']
  del(node['groups'])
  # check for a no split
  if not left or not right:
    node['left'] = node['right'] = to_terminal(left + right)
    return
  # check for max depth
  if depth >= max_depth:  #max_depth=10表示递归十次,若分类还未结束,则选取数据中分类标签较多的作为结果,使分类提前结束,防止过拟合
    node['left'], node['right'] = to_terminal(left), to_terminal(right)
    return

  # process left child
  if len(left) <= min_size:
    node['left'] = to_terminal(left)
  else:
    node['left'] = get_split(left, n_features) #node['left']是一个字典,形式为{'index':b_index, 'value':b_value, 'groups':b_groups},所以node是一个多层字典
    split(node['left'], max_depth, min_size, n_features, depth+1) #递归,depth+1计算递归层数

  # process right child
  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']:  # index是分割的特征分量,value是特征分量的最优切分点
    if isinstance(node['left'], dict):  #isinstance是Python中的一个内建函数。是用来判断一个对象是否是一个已知的类型。
      return predict(node['left'], row)
    else:
      return node['left']
  else:
    if isinstance(node['right'], dict):
      return predict(node['right'], row)
    else:
      return node['right']

#使用多个决策树trees对测试集test的第row行进行预测,再使用简单投票法判断出该行所属分类
def bagging_predict(trees, row):
  predictions = [predict(tree, row) for tree in trees] 
  return max(set(predictions), key=predictions.count)

#创建数据集的随机子样本
def subsample(dataset, ratio):  
  sample = list()
  n_sample = round(len(dataset) * ratio)  #round() 方法返回浮点数x的四舍五入值。
  while len(sample) < n_sample:
    index = randrange(len(dataset)) #有放回的随机采样,有一些样本被重复采样,从而在训练集中多次出现,有的则从未在训练集中出现,此则自助采样法。从而保证每棵决策树训练集的差异性
    sample.append(dataset[index])
  return sample

# 随机森林算法
def random_forest(train, test, max_depth, min_size, sample_size, n_trees, n_features):
  trees = list()
  for i in range(n_trees):  #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)

#评估算法性能,返回模型得分 
def evaluate_algorithm(dataset, algorithm, n_folds, *args):  
  folds = cross_validation_split(dataset, n_folds)
  scores = list()
  #每次循环从folds从取出一个fold作为测试集,其余作为训练集,遍历整个folds,实现交叉验证
  for fold in folds:  
    train_set = list(folds)
    train_set.remove(fold)
    train_set = sum(train_set, [])  #将多个fold列表组合成一个train_set列表
    test_set = list()
    for row in fold:  #fold表示从原始数据集dataset提取出来的测试集
      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)
  return scores

if __name__ == '__main__':  
  #每一次执行本文件时都能产生同一个随机数  
  seed(1)  
  filename = 'sonar-all-data.csv'
  dataset = load_csv(filename)  
  for i in range(0, len(dataset[0])-1): # 每一列的字符属性转为浮点数
    str_column_to_float(dataset, i)  
  n_folds = 5   #分成5份数据,进行交叉验证  
  max_depth = 20 #调参(自己修改)决策树深度不能太深,不然容易导致过拟合
  min_size = 1  #每个分支下最小的节点个数
  sample_size = 1.0 # 每棵数所用样本子集的大小,这里和训练集相同  
  n_features =15 #调参(自己修改) #准确性与多样性之间的权衡
  for n_trees in [1,10,20]: #理论上树的棵数是越多越好
    scores = evaluate_algorithm(dataset[0:50], 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))))

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

Python 相关文章推荐
python解析中国天气网的天气数据
Mar 21 Python
Python中列表和元组的使用方法和区别详解
Dec 30 Python
Python实现字符串与数组相互转换功能示例
Sep 22 Python
python 读取文本文件的行数据,文件.splitlines()的方法
Jul 12 Python
使用50行Python代码从零开始实现一个AI平衡小游戏
Nov 21 Python
Python2和Python3之间的str处理方式导致乱码的讲解
Jan 03 Python
使用python分析统计自己微信朋友的信息
Jul 19 Python
python根据时间获取周数代码实例
Sep 30 Python
用python爬取历史天气数据的方法示例
Dec 30 Python
windows上彻底删除jupyter notebook的实现
Apr 13 Python
Python配置pip国内镜像源的实现
Aug 20 Python
Anaconda安装pytorch及配置PyCharm 2021环境
Jun 04 Python
Python实现扣除个人税后的工资计算器示例
Mar 26 #Python
python实现决策树、随机森林的简单原理
Mar 26 #Python
python机器学习之贝叶斯分类
Mar 26 #Python
利用python实现微信头像加红色数字功能
Mar 26 #Python
Python扩展内置类型详解
Mar 26 #Python
python函数式编程学习之yield表达式形式详解
Mar 25 #Python
Python实现简单求解给定整数的质因数算法示例
Mar 25 #Python
You might like
PHP下对字符串的递增运算代码
2010/08/21 PHP
php下载excel无法打开的解决方法
2013/12/24 PHP
PHP中使用匿名函数操作数据库的例子
2014/11/17 PHP
使用 PHPStorm 开发 Laravel
2015/03/24 PHP
学习PHP Cookie处理函数
2016/08/09 PHP
Laravel框架用户登陆身份验证实现方法详解
2017/09/14 PHP
Javascript开发包大全整理
2006/12/22 Javascript
Ext对基本类型的扩展 ext,extjs,format
2010/12/25 Javascript
让table变成exls的示例代码
2014/03/24 Javascript
jquery实现类似EasyUI的页面布局可改变左右的宽度
2020/09/12 Javascript
JS动态增删表格行的方法
2016/03/03 Javascript
jQueryUI 拖放排序遇到滚动条时有可能无法执行排序的小bug及解决方案
2016/12/19 Javascript
微信小程序之蓝牙的链接
2017/09/26 Javascript
Angular实现点击按钮后在上方显示输入内容的方法
2017/12/27 Javascript
微信小程序性能优化之checkSession的使用
2019/03/06 Javascript
初试vue-cli使用HBuilderx打包app的坑
2019/07/17 Javascript
Python 编码处理-str与Unicode的区别
2016/09/06 Python
Python去除、替换字符串空格的处理方法
2018/04/01 Python
matplotlib subplots 设置总图的标题方法
2018/05/25 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
2018/07/25 Python
Django结合ajax进行页面实时更新的例子
2019/08/12 Python
使用python检查yaml配置文件是否符合要求
2020/04/09 Python
Python通过kerberos安全认证操作kafka方式
2020/06/06 Python
如何利用python进行时间序列分析
2020/08/04 Python
基于python实现百度语音识别和图灵对话
2020/11/02 Python
css3一款3D字体带阴影效果的实现步骤
2013/03/20 HTML / CSS
带你认识HTML5中的WebSocket
2015/05/22 HTML / CSS
美国在线和移动免费会员制批发零售商:Boxed(移动端的Costco)
2020/01/02 全球购物
如何利用find命令查找文件
2016/11/18 面试题
代理协议书范本
2014/04/22 职场文书
个人考核材料
2014/05/15 职场文书
党的群众路线教育实践活动个人整改措施材料
2014/11/04 职场文书
员工加薪申请报告
2015/05/15 职场文书
python Tkinter的简单入门教程
2021/04/11 Python
关于flex 上下文中自动 margin的问题(完整例子)
2021/05/20 HTML / CSS
Python使用永中文档转换服务
2022/05/06 Python