……

一、随机森林算法简介:

       在机器学习中,随机森林是一个包含多个决策树的分类器, 并且其输出的类别是由个别树输出的类别的众数而定。 Leo Breiman和Adele Cutler发展出推论出随机森林的算法。而 "Random Forests" 是他们的商标。 这个术语是1995年由贝尔实验室的Tin Kam Ho所提出的随机决策森林(random decision forests)而来的。这个方法则是结合Breimans 的 "Bootstrap aggregating" 想法和 Ho 的"randomsubspace method"以建造决策树的集合。

       根据下列算法而建造每棵树 :

1.   用M来表示训练用例(样本)的个数,N表示特征数目。

2.   输入特征数目n,用于确定决策树上一个节点的决策结果;其中n应远小于N。

3.   从M个训练用例(样本)中以有放回抽样的方式,取样k次,形成一个训练集(即bootstrap取样),并用未抽到的用例(样本)作预测,评估其误差。

4.   对于每一个节点,随机选择n个特征,每棵决策树上每个节点的决定都是基于这些特征确定的。根据这n个特征,计算其最佳的分裂方式。

5.   每棵树都会完整成长而不会剪枝,这有可能在建完一棵正常树状分类器后会被采用。

6.   最后测试数据,根据每棵树,以多胜少方式决定分类。

     在构建随机森林时,需要做到两个方面:数据的随机性选取,以及待选特征的随机选取,来消除过拟合问题。

     首先,从原始的数据集中采取有放回的抽样,构造子数据集,子数据集的数据量是和原始数据集相同的。不同子数据集的元素可以重复,同一个子数据集中的元素也可以重复。第二,利用子数据集来构建子决策树,将这个数据放到每个子决策树中,每个子决策树输出一个结果。最后,如果有了新的数据需要通过随机森林得到分类结果,就可以通过对子决策树的判断结果的投票,得到随机森林的输出结果了。如下图,假设随机森林中有3棵子决策树,2棵子树的分类结果是A类,1棵子树的分类结果是B类,那么随机森林的分类结果就是A类。

     与数据集的随机选取类似,随机森林中的子树的每一个分裂过程并未用到所有的待选特征,而是从所有的待选特征中随机选取一定的特征,之后再在随机选取的特征中选取最优的特征。这样能够使得随机森林中的决策树都能够彼此不同,提升系统的多样性,从而提升分类性能。

     优点:

    随机森林的既可以用于回归也可以用于分类任务,并且很容易查看模型的输入特征的相对重要性。随机森林算法被认为是一种非常方便且易于使用的算法,因为它是默认的超参数通常会产生一个很好的预测结果。超参数的数量也不是那么多,而且它们所代表的含义直观易懂。

    随机森林有足够多的树,分类器就不会产生过度拟合模型。

    缺点:

    由于使用大量的树会使算法变得很慢,并且无法做到实时预测。一般而言,这些算法训练速度很快,预测十分缓慢。越准确的预测需要越多的树,这将导致模型越慢。在大多数现实世界的应用中,随机森林算法已经足够快,但肯定会遇到实时性要求很高的情况,那就只能首选其他方法。当然,随机森林是一种预测性建模工具,而不是一种描述性工具。也就是说,如果您正在寻找关于数据中关系的描述,那建议首选其他方法。

    适用范围:

    随机森林算法可被用于很多不同的领域,如银行,股票市场,医药和电子商务。在银行领域,它通常被用来检测那些比普通人更高频率使用银行服务的客户,并及时偿还他们的债务。同时,它也会被用来检测那些想诈骗银行的客户。在金融领域,它可用于预测未来股票的趋势。在医疗保健领域,它可用于识别药品成分的正确组合,分析患者的病史以识别疾病。除此之外,在电子商务领域中,随机森林可以被用来确定客户是否真的喜欢某个产品。

二、sklearn中随机森林算法应用举例:

    (1)基本步骤:

    ①选择数据:将你的数据分成三组:训练数据、验证数据和测试数据

    ②模型数据:使用训练数据来构建使用相关特征的模型

    ③验证模型:使用你的验证数据接入你的模型

    ④测试模型:使用你的测试数据检查被验证的模型的表现

     ⑤使用模型:使用完全训练好的模型在新数据上做预测

    ⑥调优模型:使用更多数据、不同的特征或调整过的参数来提升算法的性能表现

    为方便大家使用,代码如下:

 

# 随机森林需要调整的参数有:
# (1)    决策树的个数
# (2)    特征属性的个数
# (3)    递归次数(即决策树的深度)

import numpy as np
from numpy import *
import random
from sklearn.model_selection  import train_test_split

#生成数据集。数据集包括标签,全包含在返回值的dataset上
def get_Datasets():
    from sklearn.datasets import make_classification
    dataSet,classLabels=make_classification(n_samples=200,n_features=100,n_classes=2)
    #print(dataSet.shape,classLabels.shape)
    return np.concatenate((dataSet,classLabels.reshape((-1,1))),axis=1)

#切分数据集,实现交叉验证。可以利用它来选择决策树个数。但本例没有实现其代码。
#原理如下:
#第一步,将训练集划分为大小相同的K份;
#第二步,我们选择其中的K-1分训练模型,将用余下的那一份计算模型的预测值,
#这一份通常被称为交叉验证集;第三步,我们对所有考虑使用的参数建立模型
#并做出预测,然后使用不同的K值重复这一过程。
#然后是关键,我们利用在不同的K下平均准确率最高所对应的决策树个数
#作为算法决策树个数
def splitDataSet(dataSet,n_folds):
    fold_size=len(dataSet)/n_folds
    data_split=[]
    begin=0
    end=fold_size
    for i in range(n_folds):
        data_split.append(dataSet[begin:end,:])
        begin=end
        end+=fold_size
    return data_split


#构建n个子集
def get_subsamples(dataSet,n):
    subDataSet=[]
    for i in range(n):
        index=[]
        for k in range(len(dataSet)):
            index.append(np.random.randint(len(dataSet)))
        subDataSet.append(dataSet[index,:])
    return subDataSet

#划分数据集
def binSplitDataSet(dataSet,feature,value):
    mat0=dataSet[np.nonzero(dataSet[:,feature]>value)[0],:]
    mat1=dataSet[np.nonzero(dataSet[:,feature]<value)[0],:]
    return mat0,mat1

#计算方差,回归时使用
def regErr(dataSet):
    return np.var(dataSet[:,-1])*shape(dataSet)[0]
#计算平均值,回归时使用
def regLeaf(dataSet):
    return np.mean(dataSet[:,-1])
def MostNumber(dataSet):  #返回多类
    #number=set(dataSet[:,-1])
    len0=len(np.nonzero(dataSet[:,-1]==0)[0])
    len1=len(np.nonzero(dataSet[:,-1]==1)[0])
    if len0>len1:
        return 0
    else:
        return 1
#计算基尼指数
def gini(dataSet):
    corr=0.0
    for i in set(dataSet[:,-1]):
        corr+=(len(np.nonzero(dataSet[:,-1]==i)[0])/len(dataSet))**2
    return 1-corr

#选取任意的m个特征,在这m个特征中,选取分割时的最优特征
def select_best_feature(dataSet,m,alpha="huigui"):
    f=dataSet.shape[1]
    index=[]
    bestS=inf;bestfeature=0;bestValue=0;
    if alpha=="huigui":
        S=regErr(dataSet)
    else:
        S=gini(dataSet)
    for i in range(m):
        index.append(np.random.randint(f))
    for feature in index:
        for splitVal in set(dataSet[:,feature]):
            mat0,mat1=binSplitDataSet(dataSet,feature,splitVal)
            if alpha=="huigui":  newS=regErr(mat0)+regErr(mat1)
            else:
                newS=gini(mat0)+gini(mat1)
            if bestS>newS:
                bestfeature=feature
                bestValue=splitVal
                bestS=newS
    if (S-bestS)<0.001 and alpha=="huigui":    #如果误差不大就退出
        return None,regLeaf(dataSet)
    elif (S-bestS)<0.001:
        #print(S,bestS)
        return None,MostNumber(dataSet)
    #mat0,mat1=binSplitDataSet(dataSet,feature,splitVal)
    return bestfeature,bestValue

def createTree(dataSet,alpha="huigui",m=20,max_level=10):   #实现决策树,使用20个特征,深度为10
    bestfeature,bestValue=select_best_feature(dataSet,m,alpha=alpha)
    if bestfeature==None:
        return bestValue
    retTree={}
    max_level-=1
    if max_level<0:   #控制深度
        return regLeaf(dataSet)
    retTree['bestFeature']=bestfeature
    retTree['bestVal']=bestValue
    lSet,rSet=binSplitDataSet(dataSet,bestfeature,bestValue)
    retTree['right']=createTree(rSet,alpha,m,max_level)
    retTree['left']=createTree(lSet,alpha,m,max_level)
    #print('retTree:',retTree)
    return retTree

def RondomForest(dataSet,n,alpha="huigui"):   #树的个数
    #dataSet=get_Datasets()
    Trees=[]
    for i in range(n):
        X_train, X_test, y_train, y_test = train_test_split(dataSet[:,:-1], dataSet[:,-1], test_size=0.33, random_state=42)
        X_train=np.concatenate((X_train,y_train.reshape((-1,1))),axis=1)
        Trees.append(createTree(X_train,alpha=alpha))
    return Trees

#预测单个数据样本
def treeForecast(tree,data,alpha="huigui"):
    if alpha=="huigui":
        if not isinstance(tree,dict):
            return float(tree)
        if data[tree['bestFeature']]>tree['bestVal']:
            if type(tree['left'])=='float':
                return tree['left']
            else:
                return treeForecast(tree['left'],data,alpha)
        else:
            if type(tree['right'])=='float':
                return tree['right']
            else:
                return treeForecast(tree['right'],data,alpha)
    else:
        if not isinstance(tree,dict):
            return int(tree)
        if data[tree['bestFeature']]>tree['bestVal']:
            if type(tree['left'])=='int':
                return tree['left']
            else:
                return treeForecast(tree['left'],data,alpha)
        else:
            if type(tree['right'])=='int':
                return tree['right']
            else:
                return treeForecast(tree['right'],data,alpha)
#单棵树预测测试集
def createForeCast(tree,dataSet,alpha="huigui"):
    m=len(dataSet)
    yhat=np.mat(zeros((m,1)))
    for i in range(m):
        yhat[i,0]=treeForecast(tree,dataSet[i,:],alpha)
    return yhat

#随机森林预测
def predictTree(Trees,dataSet,alpha="huigui"):
    m=len(dataSet)
    yhat=np.mat(zeros((m,1)))
    for tree in Trees:
        yhat+=createForeCast(tree,dataSet,alpha)
    if alpha=="huigui": yhat/=len(Trees)
    else:
        for i in range(len(yhat)):
            if yhat[i,0]>len(Trees)/2:
                yhat[i,0]=1
            else:
                yhat[i,0]=0
    return yhat

if __name__ == '__main__' :
    dataSet=get_Datasets()  #得到数据集和标签
    print(dataSet[:,-1].T)   #打印标签,与后面预测值对比
    RomdomTrees=RondomForest(dataSet,4,alpha="fenlei")   #4棵树,分类。
    print("---------------------RomdomTrees------------------------")
    #print(RomdomTrees[0])
    yhat=predictTree(RomdomTrees,dataSet,alpha="fenlei")
    print(yhat.T)
#get_Datasets()

执行结果:

C:\Anaconda3\python.exe "C:\Program Files\JetBrains\PyCharm 2019.1.1\helpers\pydev\pydevconsole.py" --mode=client --port=56305
import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['C:\\app\\PycharmProjects', 'C:/app/PycharmProjects'])
Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
PyDev console: using IPython 7.12.0
Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)] on win32
runfile('C:/app/PycharmProjects/ArtificialIntelligence/test.py', wdir='C:/app/PycharmProjects/ArtificialIntelligence')
[1. 1. 0. 0. 0. 0. 0. 0. 1. 1. 1. 0. 1. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 1.
 1. 1. 1. 1. 1. 1. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.
 0. 1. 0. 1. 1. 1. 0. 0. 1. 1. 0. 1. 0. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1.
 0. 0. 0. 1. 0. 1. 1. 1. 1. 1. 1. 0. 0. 1. 0. 1. 1. 0. 0. 1. 0. 0. 0. 0.
 1. 1. 0. 1. 0. 1. 1. 0. 0. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 1. 1. 0. 0.
 1. 1. 1. 0. 0. 1. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 0. 0. 0. 0. 1. 1. 1. 0.
 0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 1. 1. 1. 0. 0. 0. 0. 1. 0. 0. 1. 1.
 0. 1. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 1. 1. 0. 1. 1. 0. 1. 0. 1. 0. 0.
 1. 1. 1. 1. 0. 0. 1. 0.]
---------------------RomdomTrees------------------------
[[1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 0. 0. 1. 1. 0. 1. 0. 0. 1.
  1. 1. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.
  0. 1. 0. 1. 1. 1. 0. 0. 0. 1. 0. 1. 0. 1. 1. 0. 1. 1. 1. 0. 1. 0. 1. 1.
  0. 0. 0. 1. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 0. 0. 1. 0. 0. 0. 0.
  1. 1. 0. 1. 0. 1. 1. 0. 0. 0. 0. 1. 0. 0. 1. 1. 0. 0. 0. 0. 0. 1. 0. 0.
  1. 1. 1. 0. 0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 1. 1. 1. 0.
  0. 0. 0. 1. 0. 1. 0. 1. 0. 0. 1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 1. 1.
  0. 1. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 1. 1. 0. 1. 1. 0. 1. 0. 1. 0. 0.
  1. 1. 1. 1. 0. 0. 1. 0.]]

 

 posted on 2020-06-18 09:50  大码王  阅读(1717)  评论(0编辑  收藏  举报
复制代码