并行化随机森林实现分析

题目大意、训练数据与测试数据
非线性分类问题。数据一共有26种分类(1—26)。每个样本数据有617维的特征属性,属性值已经预处理为-1到1之间的浮点数。训练数据集不算很大,一共有6238条样本数据,测试数据集有1559条数据。
算法思想及数据结构
随机森林是由美国科学家Leo Breiman将其在 1996年提出的Bagging集成学习理论与Ho在1998年提出的随机子空间方法相结合,于2001年发表的一种机器学习算法。随机森林是以决策树为基本分类器的一个集成学习模型,它包含多个由Bagging集成学习技术训练得到的决策树,当输入待分类的样本时, 最终的分类结果由单个决策树的输出结果投票决定。大致来说,随机森林是用随机的方式建立一个森林,森林里面由很多的决策树组成,随机森林的每一棵决策树之间是没有关联的。在得到森林之后当有一个新的输入样本进入的时候,就让森林中的每一棵决策树分别进行判断,判断这个样本属于哪一类。
随机森林是一种有效的分类预测方法,它有很高 的分类精度,对于噪声和异常值有较好的稳健性,且 具有较强的泛化能力。Breiman在论文中提出了随机 森林的数学理论,证明随机森林不会出现决策树的过 拟合问题。
20世纪70年代末期和80年代初期,J.Ross Quinlan提出了ID3决策树算法,Leo Breiman等人提出了CART决策树算法,1993年Quinlan又提出了C4.5决策树算法。这三种算法均采用自上而下的贪婪算法构建一个树状结构,在每个内部节点选取一个 最优的属性进行分裂,每个分枝对应一个属性值,如此递归建树直到满足终止条件,每个叶节点表示沿此路径的样本的所属类别。本次实验将遵循CART决策树的算法思想,并用python实现。
本次实验应用的主要数据结构有:
A. 列表
(1) 二维列表存放训练数据和测试数据,维数分别为(6238,617),和(1559,617)。
(2) 一维列表存放分类标签。
B. 字典
(1) 存放决策树结构
(2) 用于分类标签按个数多少排序
详细思路
A. 决策树
(1)  GINI指数:
  • 是一种不等性度量;
  • 通常用来度量收入不平衡,可以用来度量任何不均匀分布;
  • 是介于0~1之间的数,0-完全相等,1-完全不相等;
  • 总体内包含的类别越杂乱,GINI指数就越大(跟熵的概念很相似)
(2) 在CART算法中, 基尼不纯度表示一个随机选中的样本在子集中被分错的可能性。基尼不纯度为这个样本被选中的概率乘以它被分错的概率。当一个节点中所有样本都是一个类时,基尼不纯度为零。其定义如下:

其中的m仍然表示数据集D中类别C的个数,Pi表示D中任意一个记录属于Ci的概率,计算时Pi=(D中属于Ci类的集合的记录个数/|D|)。如果所有的记录都属于同一个类中,则P1=1,Gini(D)=0,此时不纯度最低。在CART(Classification and Regression Tree)算法中利用基尼指数构造二叉决策树,对每个属性都会枚举其属性的非空真子集,以属性R分裂后的基尼系数为:
D1为D的一个非空真子集,D2为D1在D的补集,即D1+D2=D,对于属性R来说,有多个真子集,即GiniR(D)有多个值,但我们选取最小的那么值作为R的基尼指数。最后:
将Gini(R)增量最大,即GiniR(D)最小的属性作为最佳分裂属性。

算法:根据训练数据记录D生成一棵决策树.
输入:
数据记录D,包含类标的训练数据集; 算法:根据训练数据记录D生成一棵输出:一棵决策树.

(3) 构建决策树具体步骤
过程
1.给定数据集D,和每个节点级划分点列表L,属性列表F。
2.构造一个节点N;
3.如果数据记录D中的所有记录的类标都相同(记为C类):则将节点N作为叶子节点标记为C,并返回结点N;
4.如果属性列表为空:则将节点N作为叶子结点标记为D中类标最多的类,并返回结点N;
5.在属性列表F中选取一个属性f;
6.按顺序在划分点列表L取出划分点lj,f属性值<=vj的分到左子树,f属性值>vj的分到右子树。计算最小的Ginif(D)及其对应的划分点。另外,对于连续属性先进行排序(升序),只有在决策属性(即分类发生了变化)发生改变的地方才需要切开,这可以显著减少运算量。
7.重复步骤5—6直到遍历完属性列表F里面所有属性并找出最小的Gini值所对应的属性和划分点。
8.分裂属性取值是连续的,将数据集按照步骤7得到的属性及其划分将划分成左子集(<=)和右子集(>):,
9.对左子集和右子集重复步骤2—8,直到所有数据满足停止条件(步骤3–4),返回树。

构建决策树具体实现
#构建决策树
def createTree(sampset,defaulVal):
    #print "create"
    if not sampset:
        return defaulVal#,"no sample"
    labelList=[s[-1] for s in sampset]
    if labelList.count(labelList[0])==len(labelList):
        return int(labelList[0])#,"same class"
    newDefault=majorClass(labelList)
    bestFeat,bestFeatVal=findBestFea(sampset)
    #print bestFeat,bestFeatVal
    #当前树节点
    node=(bestFeat,bestFeatVal,len(labelList))
    myTree={node:{}}
    #左子树和右子树
    lset=[s for s in sampset if s[bestFeat]<=bestFeatVal]
    rset=[s for s in sampset if s[bestFeat]>bestFeatVal]
    myTree[node]['left']=createTree(lset,newDefault)
    myTree[node]['right']=createTree(rset,newDefault)
    return myTree
属性划分标准
#寻找最优属性及划分点
def findBestFea(sampset):
    features=range(617)
    feaList=random.sample(features,15)
    bestFea=0
    bestpar=0.0
    mingini=1.0
    for fea in feaList:
        #print fea
        tup=[(s[fea],s[-1]) for s in sampset]
        tup.sort()
        par=[-0.9,-0.5,0,0.5,0.9]
            bar=0
            for t in tup:
                if t[0]>parmem:
                    break
                bar+=1
            nllab=[mem[1] for mem in tup[0:bar]]
            llab=[mem[1] for mem in tup[bar:]]
            nll={}
            for l in nllab:
                if l not in nll.keys():
                    nll[l]=0
                nll[l]+=1
            gini1=1.0
            #print nll
            for k in nll.keys():
                gini1-=pow(float(nll[k])/len(nllab),2)
            ll={}
            for l in llab:
                if l not in ll.keys():
                    ll[l]=0
                ll[l]+=1
            gini2=1.0
            for k in ll.keys():
                gini2-=pow(float(ll[k])/len(llab),2)
            gini=float(len(nllab))/len(tup)*gini1+float(len(llab))/len(tup)*gini2
            #print gini
            if gini < mingini:
                mingini=gini
                bestpar=parmem
                bestFea=fea
            #pdb.set_trace()
    return bestFea,bestpar

决策树分类实现
#分类方法
def classify(inputTree,testList):
    firstnode=inputTree.keys()[0]
    firstFea=inputTree.keys()[0][0]
    firstPar=inputTree.keys()[0][1]
    testFea=testList[firstFea]
    secondDict=inputTree[firstnode]
    if testFea<=firstPar:
        if type(secondDict['left']).__name__=='dict':
            classLabel=classify(secondDict['left'],testList)
        else:
            classLabel=secondDict['left']
    else:
        if type(secondDict['right']).__name__=='dict':
            classLabel=classify(secondDict['right'],testList)
        else:
            classLabel=secondDict['right']
    return classLabel
B. 随机森林
随机森林是以K个决策树 {h(X,θ k ),k=1,2,Ö.,K} 为基本分类器,进行集成学习后得到的一个组合分类器。当输入待分类样本时,随机森林输出的分类结果由每个决策树的分类结果简单投票决定。这里的 k ,k=1,2,Ö.,K} 是一个随机变量序列,它是由随机森林的两大随机化思想决定的:
(1)Bagging思想:从原样本集X中有放回地随机抽取M个与原样本集同样大小的训练样本集{m=1,2,Ö.,M},每个训练样本
(2)集构造一个对应的决策树。
(3)特征子空间思想:在对决策树每个节点进行分裂时,从全部属性中等概率随机抽取一个属性子集(通常取⎣log2(N)+1⎦个属性, N为特征总数),再从这个子集中选择一个最优属性来分裂节点。

随机森林构建具体步骤
1.给定样本集T,含有M个样本,记为(x1,y1),(x2,y2),…,(xM,yM),每个样本有N个属性,记为f1,f2,…,fN;给定整数n,用来指示生成树时,选择n种属性作为候选属性;给定整数t,用来指示森林中树的数目。
2.用过有放回的方式,随机在T中抽取M个样本,形成新的样本集T*,(x*1,y*1),(x*2,y*2),…,(x*M,y*M),作为决策树根节点处的样本。
3.随机从N个属性中选择n个属性作为单棵树的候选属性,满足条件m << M。然后从这m个属性中采用某种策略(比如说信息增益)来选择1个属性作为该节点的分裂属性。
4.决策树形成过程中每个节点都要按照步骤2来分裂(很容易理解,如果下一次该节点选出来的那一个属性是刚刚其父节点分裂时用过的属性,则该节点已经达到了叶子节点,无须继续分裂了)。一直到不能够再分裂为止。注意整个决策树形成过程中没有进行剪枝。
5.按照步骤2~3建立t棵决策树,这样就构成了随机森林了。
6.对于输入向量xi,每棵树输出分类结果进行投票。
7.统计投票结果,票数最高的类别就是xi的类别标签。


训练数据集有放回抽样实现
#有放回抽样
def repeatRandomSamp(dataSet):
    samples=[]
    a=len(dataSet)
    for i in xrange(6238):
        samples.append(dataSet[random.randint(0,len(dataSet)-1)])
    return samples

随机抽取特征子集实现
feaList=random.sample(features,15)

效率分析与并行化优化
决策树的构建本来就是很复杂,需要耗费很多时间的,当训练数据增加和特征数、属性划分点个数增加的情况下,树的叶子节点的增长速度是指数级别的。而且随机森林成功所在就是要建立在构建大量决策树的基础上的,当然我们可以通过改用非递归的方式实现决策树,这样会提升1/3的速度,或者减少随机选取的特征数、划分点数目和训练数据的数目,但是这种调整很有可能会影响到随机森林的准确率。所以还可以通过并行化,充分利用计算机的硬件配置。

并行化实现:使用multiprocessing创建进程、应用进程池Pool
都是创建4个进程来创建树,将总的树的数目平均分成4部分给各个进程分担;得到创建后树的列表之后,再创建4个进程,将测试数据的样本数平均分担给这4个进程。当使用multiprocessing时,用multiprocessing的queue来进行主进程与子进程之间的数据传递;用进程池Pool时用它自己的get()来获得子进程的结果。

并行化步骤
1.要创建t棵树,创建4个进程,每个进程负责创建t/4棵决策树,创建好的t/4棵决策树以列表的形式返回到主进程。
2.分别得到4个子进程的决策树列表之后,将4个子列表整合到一个长度为t的决策树列表L。
3.创建4个分类进程,将决策树列表复制4份分别传递到4个分类进程,同时将测试数据分成4份,[0,388]行为第1部分,[389,777]行为第2部分,[778,1166]行为第3部分,[1167,1558]行为第4部分,分别传递到4个分类子进程。
4.第一个子进程以列表的形式返回[0,388]行的分类结果,第二个子进程以列表的形式返回[389,777]行的分类结果,第三个子进程以列表的形式返回[778,1166]行的分类结果,第四个子进程以列表的形式返回[1167,1558]行的分类结果。
5. 分别得到4个子进程的标签列表之后,将4个子列表整合到一个长度为1559的结果标签列表。


1.multiprocessing实现示例
for x in xrange(4):
        process=multiprocessing.Process(target=partcre,args=(ss[x],trees[x]))
        process.start()
        pcs.append(process)
初始化一个线程对象,传入函数target=partcre,及其参数args=(ss[x],trees[x])
 2.进程池multiprocessing.Pool实现示例
pool=multiprocessing.Pool(processes=4)
for x in xrange(4):
result.append(pool.apply_async(partcre,(ss[x],)))
先创建容量为4的进程池,然后将partcre(ss[x])依次传递给

树的数量

随机选取的训练样本数

随机选取的特征数

特征值的切分点个数

是否并行化

时间

分类精度(kaggle测试集)

40

6238

25

5

NO

499

0.93162

40

6238

25

5

Multiprocessing

253

0.9328

40

6238

25

5

pool

292

0.93162


由结果对比可得,两种并行化实现的效果相差不大,都能让随机森林运行时间减少一倍,这里看来multiprocessing更好一点,所以接下来的分析结果运行时都采用multiprocessing进行并行化。

树的数量对分类精度的影响
从对比数据分析,大致符合理论讲的决策树越多,分类精度越高,但是超过150棵之后,再增加树的数量,分类精度增加的幅度也不会很明显了,再考量时间,觉得200棵左右的决策树已经比较合适了。

树的数量

随机选取的训练样本数

随机选取的特征数

特征值的切分点个数

是否并行化

时间

分类精度(kaggle测试集)

20

6238

25

5

Multiprocessing

133

0.92735

40

6238

25

5

Multiprocessing

238

0.93162

80

6238

25

5

Multiprocessing

536

0.93504

120

6238

25

5

Multiprocessing

766

0.94017

160

6238

25

5

Multiprocessing

1040

0.94188

200

6238

25

5

Multiprocessing

1246

0.94017

400

6238

25

5

Multiprocessing

2757

0.94274

500

6238

25

5

Multiprocessing

3782

0.93932


随机选取的训练样本数对分类精度的影响
我实现的时候是按照随机森林的算法,有放回的抽取跟原本训练数据集的大小相同的随机样本集,这里由于好奇训练数据集大小对结果的影响,就做了以下对比。可以发现在1000—3000训练样本的时候分类精度是随样本数量增加而提高的,但是在3000—5000这个范围即使训练样本数量增加也不会提高随机森林的分类精读了。神奇的是到了6238的时候,分类精度又变高了,果然是理论的力量.

树的数量

随机选取的训练样本数

随机选取的特征数

特征值的切分点个数

是否并行化

时间

分类精度(kaggle测试集)

40

1000

25

5

Multiprocessing

37

0.91624

40

2000

25

5

Multiprocessing

79

0.91880

40

3000

25

5

Multiprocessing

114

0.92821

40

4000

25

5

Multiprocessing

174

0.92735

40

5000

25

5

Multiprocessing

196

0.92821

40

6238

25

5

Multiprocessing

253

0.9328



随机选取的特征数对分类精度的影响
从理论和实验对比结果综合分析,在构建 RF 的过程中,为了保证随机性,在每棵决策树的分裂节点,需要从原始特征集合中随机地抽取一个特征子集,再从候选特征中选择最优特征作为该节点的分裂特征。 决策树通过这种多样化的分裂标准使得RF具有随机性。但是对于RF 而言,过于随机化,即在每个节点选择过少的相关特征,所建的树的分类精度就会降低;相对的,大量选择相关特征,降低了分裂节点处的随机性。 随机特征子集越小,随机化越强,分类精度就会相应降低,随机特征子集越大,RF的多样性就会降低,分类精度也会相应降低。因此 随机特征子集可以用来协调 RF 中分类性能和多样性之间的平衡。目前的研究中通常取 ⎣log 2 (N)+1⎦ 个属性, N 为特征总数。看对比数据,15个特征数的效果已经比较让人满意了。

树的数量

随机选取的训练样本数

随机选取的特征数

特征值的切分点个数

是否并行化

时间

分类精度(kaggle测试集)

40

6238

5

5

Multiprocessing

61

0.90085

40

6238

10

5

Multiprocessing

129

0.92479

40

6238

15

5

Multiprocessing

165

0.93932

40

6238

20

5

Multiprocessing

198

0.92650

40

6238

25

5

Multiprocessing

253

0.9328

40

6238

50

5

Multiprocessing

501

0.93333

40

6238

100

5

Multiprocessing

963

0.92393


特征的切分点个数对分类精度的影响
训练数据集的属性值都是在[-1,1]的范围内,为了节省时间,我不采用随机森林里将连续值转换为离散值的算法了,而是在[-1,1]划分一定的划分点。1个划分点的列表是[0];3个划分点的列表是[-0.5,0,0.5];5个划分点的列表是[-0.9,-0.5,0,0.5,0.9];11个划分点的列表是[-0.9,-0.7,-0.5,-0.3,-0.1,0,0.1,0.3,0.5,0.7,0.9];19个划分点的列表是[-0.9,-0.8,-0.7,-0.6,-0.5,-0.4,-0.3,-0.2,-0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]。结果显示5个划分点进度可以接受而且时间较短,没有必要继续增加划分点了。

树的数量

随机选取的训练样本数

随机选取的特征数

特征值的切分点个数

是否并行化

时间

分类精度(kaggle测试集)

40

6238

15

1

Multiprocessing

92

0.88120

40

6238

15

3

Multiprocessing

154

0.93248

40

6238

15

5

Multiprocessing

165

0.93932

40

6238

15

11

Multiprocessing

304

0.93504

40

6238

15

19

Multiprocessing

480

0.93419

输出:一棵决策树.
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值