随机森林

随机森林(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

你可能感兴趣的:(随机森林)