随机森林

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容