美文网首页
随机森林

随机森林

作者: 点点渔火 | 来源:发表于2017-07-14 11:53 被阅读0次

    随机森林(RandomForest), 可用于分类或者回归, 相比较决策树的算法, 随机森林是由多棵CART(Classification And Regression Tree)构成的。优点很明显:

    • 决策树过拟合问题严重, RandomForest不存在,所以也避免了剪枝的问题;抗噪性好(两个随机采样)

    • 速度快,模型简单,精度高

    • 能高效应对特征缺失的情况, 标量特征和连续特征通吃,无需归一化

    • GINI系数评估变量属性重要性,并给出连续变量的分界值

    • 在不平衡的数据集中, 它含有平衡误差的方法。

    • 可生成一个Proximities=(pij)矩阵,用于度量样本之间的相似性: pij=aij/N, aij表示样本i和j出现在随机森林中同一个叶子结点的次数,N随机森林中树的颗数

    • 计算样本实例之间的Proximities,可以用来聚类分析、异常分析、或者数据的其他有趣的视图。

    缺点:

    • 递归过深, 吃内存, 容易堆栈溢出

    思想:
    两个随机采样:

    • 样本采样: 假设原始样本数为N,用bootstrap方法从N样本中获取构造每棵决策树的训练集。 有放回采样

    • 特征采样: 无放回抽样

    随机森林: 分类用投票vote, 回归用回归树的平均值

    森林中单颗树的分类强度(Strength):每颗树的分类强度越大,则随机森林的分类性能越好。
    森林中树之间的相关度(Correlation):树之间的相关度越大,则随机森林的分类性能越差。
    随机选取训练样本集:使用Bagging方法形成每颗树的训练集

    随机选取分裂属性集:假设共有p个属性,指定一个属性数p≤m,在每个内部结点,从M个属性中随机抽取F个属性作分裂属性集,以这p个属性上最好的分裂方式对结点进行分裂(在整个森林的生长过程中, F的值一般维持不变)

    细节:
    用作分类时,m默认取 sqrt(p),最小取1;
    用作回归时,m默认取 p / 3,最小取5

    代码实现:

    # !/usr/bin/pytho
    # encoding=utf-8
    from __future__ import division
    import numpy as np
    import math
    import copy
    
    """
    http://blog.csdn.net/ljblog2014/article/details/38875137
    随机森林: 分类用投票vote, 回归用回归树的平均值
    森林中单颗树的分类强度(Strength):每颗树的分类强度越大,则随机森林的分类性能越好。
    森林中树之间的相关度(Correlation):树之间的相关度越大,则随机森林的分类性能越差。
    随机选取训练样本集:使用Bagging方法形成每颗树的训练集
    
    随机选取分裂属性集:假设共有p个属性,指定一个属性数p≤m,在每个内部结点,从M个属性中随机抽取F个属性作分裂属性集,以这p个属性上最好的分裂方式对结点进行分裂(在整个森林的生长过程中, F的值一般维持不变)
    
    细节:
    用作分类时,m默认取 sqrt(p),最小取1;
    用作回归时,m默认取 p / 3,最小取5
    m是一个可调的参数
    RandomForestClassifier(
                n_estimators=10,   //树的棵树
                criterion='gini',      //分类标准
                max_depth=None,  //最大深度
                 min_samples_split=2,  //最少分裂几个子节点
                 min_weight_fraction_leaf=0.0,
                max_leaf_nodes=None,
                 bootstrap=True,
                n_jobs=1,    //指定并行使用的进程数
                random_state=None,
                verbose=0,
                 warm_start=False,
                 class_weight=None  //类别权重,样本不均衡时很重要
    )
    
    这个代码每棵树采用由放回的随机采样, feature用的全部的feature
    """
    
    
    class node(object):
        def __init__(self, col=-1, value=None, results=None, trueBranch=None, falseBranch=None):
            self.col = col
            self.value = value
            self.results = results
            self.trueBranch = trueBranch
            self.falseBranch = falseBranch
    
        def getLabel(self):
            """
            如果不限制树的深度的话, 叶子节点就是单一类别
            如果限制树的深度的话, 叶子节点取多数样本的类别
            :return:
            """
            label = None
            if self.results == None:
                return None
            else:
                max_counts = 0
                for key in self.results.keys():
                    if self.results[key] > max_counts:
                        label = key
                        max_counts = self.results[key]
            return label
    
    
    class RandomForestsClassifier:
        def __init__(self, n_bootstrapSamples=20):
            self.n_bootstrapSamples = n_bootstrapSamples  # 决策树的数量
            self.list_tree = []
    
        def divideSet(self, samples, column, value):
            splitFunction = None
            if isinstance(value,int) or isinstance(value,float):
                splitFunction = lambda row: row[column] >= value
            else:
                splitFunction = lambda row: row[column] == value
            set1 = [row for row in samples if splitFunction(row)]
            set2 = [row for row in samples if not splitFunction(row)]
            return (set1,set2)
    
        def uniqueCounts(self, samples):
            # 统计每个类别下的样本数
            results = {}
            for row in samples:
                r = row[len(row)-1]
                if r not in results:
                    results[r] = 0
                results[r] = results[r]+1
            return results
    
        def giniEstimate(self, samples):
            if len(samples) == 0: return 0
            total = len(samples)
            counts = self.uniqueCounts(samples)
            gini = 0
            for target in counts:
                gini = gini + pow(counts[target],2)
            gini = 1 - gini / pow(total,2)
            return gini
    
        def buildTree(self, samples): # 构造CART决策树
            if len(samples) == 0:
                return node()
            currentGini = self.giniEstimate(samples)
            bestGain = 0
            bestCriteria = None
            bestSets = None
            colCount = len(samples[0]) - 1
            colRange = range(0,colCount)
            np.random.shuffle(colRange)
            for col in colRange[0:int(math.ceil(math.sqrt(colCount)))]:
                colValues = {}
                for row in samples:
                    colValues[row[col]] = 1
                for value in colValues.keys():
                    (set1,set2) = self.divideSet(samples, col, value)
                    gain = currentGini - (len(set1)*self.giniEstimate(set1) + len(set2)*self.giniEstimate(set2)) / len(samples)
                    if gain > bestGain and len(set1) > 0 and len(set2) > 0:  # 最合适的gain系数
                        bestGain = gain
                        bestCriteria = (col, value)
                        bestSets = (set1, set2)
            if bestGain > 0:
                trueBranch = self.buildTree(bestSets[0])
                falseBranch = self.buildTree(bestSets[1])
                return node(col=bestCriteria[0], value=bestCriteria[1],
                            trueBranch=trueBranch, falseBranch=falseBranch)
            else:
                return node(results=self.uniqueCounts(samples))
    
        def printTree(self, tree,indent='  '):  # 以文本形式显示决策树
            if tree.results != None:
                print str(tree.results)
            else:
                print str(tree.col)+':'+str(tree.value)+'?'
                print indent+'T->',
                self.printTree(tree.trueBranch,indent+'  ')
                print indent+'F->',
                self.printTree(tree.falseBranch,indent+'  ')
    
        def predict_tree(self, observation, tree):  # 利用决策树进行分类
            if tree.results != None:
                return tree.getLabel()
            else:
                v = observation[tree.col]
                branch = None
                if isinstance(v,int) or isinstance(v,float):
                    if v >= tree.value: branch = tree.trueBranch
                    else: branch = tree.falseBranch
                else:
                    if v == tree.value: branch = tree.trueBranch
                    else: branch = tree.falseBranch
                return self.predict_tree(observation,branch)
    
        def generateBootstrapSamples(self, data): # 构造bootstrap样本
            samples = []
            # 增加特征的随机选取过程: 特征随机无放回
            features = len(data[0][:-1])
            randRange = range(features)
            np.random.shuffle(range(features))
            # 特征取sqart数量
            sample_features = randRange[:max(1, int(math.ceil(math.sqrt(features))))]
    
            # 这个是有放回的样本采样
            for i in range(len(data)):
                # np.random.randint产生0~len(data)的随机数, 有放回的随机采样, 即samples存在重复数据
                temp = data[np.random.randint(len(data))]
                tp = []
                for j in range(len(temp[:-1])):
                    if j in sample_features:
                        tp.append(temp[j])
                tp.append(temp[-1])
                samples.append(data[np.random.randint(len(data))])
            return samples
    
        def fit(self, data): # 构造随机森林
            for i in range(self.n_bootstrapSamples):
                # 构造self.n_bootstrapSamples 棵CART树
                samples = self.generateBootstrapSamples(data)
                currentTree = self.buildTree(samples)
                self.list_tree.append(currentTree)
    
        def predict_randomForests(self, observation): # 利用随机森林对给定观测数据进行分类
            results = {}
            for i in range(len(self.list_tree)):
                # 单决策树预测
                currentResult = self.predict_tree(observation, self.list_tree[i])
                # 投票表决
                if currentResult not in results:
                    results[currentResult] = 0
                results[currentResult] = results[currentResult] + 1
            max_counts = 0
            finalResult = None
            # 投票
            for key in results.keys():
                if results[key] > max_counts:
                    finalResult = key
                    max_counts = results[key]
            return finalResult
    
    
    from sklearn.datasets import load_iris
    
    iris = load_iris()
    X = iris.data
    #pp = lambda x: x > 2 and 1 or -1
    #X = np.array([[t[0], t[1], t[2], pp(t[-1])] for t in X.tolist()])
    #print X
    y = iris.target
    temp_data = np.concatenate([X, y.reshape((len(y), 1))], axis=1)
    # 由于上述代码要求输入的观测数据存储在二维列表中,需将numpy二维数组转换成列表
    
    data = []
    for i in range(temp_data.shape[0]):
        temp = []
        for j in range(temp_data.shape[1]):
            temp.append(temp_data[i][j])
        data.append(temp)
    
    rowRange = range(150)
    np.random.shuffle(rowRange)    # shuffle随机排序
    
    #从鸢尾花数据集(容量为150)按照随机均匀抽样的原则选取70%的数据作为训练数据
    training_data = [data[i] for i in rowRange[0:105]]
    #按照随机均匀抽样的原则选取30%的数据作为检验数据
    testing_data = [data[i] for i in rowRange[105:150]]
    classifier = RandomForestsClassifier(n_bootstrapSamples=20) # 初始化随机森林
    classifier.fit(training_data)#利用训练数据进行拟合
    finalResults = []
    for row in testing_data:
        finalResult = classifier.predict_randomForests(row[:-1])#对检验数据集进行分类
        finalResults.append(finalResult)
    errorVector = np.zeros((len(testing_data),1))   # np.zeros 错误率
    errorVector[np.array(finalResults) != (np.array(testing_data))[:,4]] =1
    print errorVector.sum() / len(testing_data)#计算错判率
    
    
    import sklearn.ensemble as ens
    
    iris = load_iris()
    clf = ens.RandomForestClassifier(n_estimators=20) #初始化
    
    # RandomForestClassifier 不允许 字符串变量
    clf = clf.fit([t[:-1] for t in training_data], [t[-1] for t in training_data]) #训练决策树
    print clf.feature_importances_
    print clf.n_classes_
    print clf.estimators_
    print clf.predict_log_proba([3.2, 2.5, 2.1, 2.0])
    print clf.score((np.array(testing_data))[:,0:-1], (np.array(testing_data))[:,-1])
    
    finalResults = []
    for row in testing_data:
        finalResult = clf.predict(row[:-1])[0]#对检验数据集进行分类
        finalResults.append(finalResult)
    errorVector = np.zeros((len(testing_data),1))   # np.zeros 错误率
    errorVector[np.array(finalResults) != (np.array(testing_data))[:,4]] =1
    print errorVector.sum() / len(testing_data)#计算错判率
    
    

    sklearn可视化:

    参考:
    http://blog.csdn.net/a819825294/article/details/51177435
    http://blog.csdn.net/ljblog2014/article/details/38875137

    相关文章

      网友评论

          本文标题:随机森林

          本文链接:https://www.haomeiwen.com/subject/kdvghxtx.html