决策树与随机森林及其在SparkMllib中的使用

一.概念

决策树和随机森林:决策树和随机森林都是非线性有监督的分类模型。

决策树是一种树形结构,树内部每个节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶子节点代表一个分类类别。通过训练数据构建决策树,可以对未知数据进行分类。

  • 根节点:最顶层的分类条件
  • 叶节点:代表每一个类别号
  • 中间节点:中间分类条件
  • 分支:代表每一个条件的输出

随机森林是由多个决策树组成,随机森林中每一棵决策树之间没有关联,在得到一个随机森林后,当有新的样本进入的时候,随机森林中的每一棵决策树分别进行判断,分析出该样本属于哪一类,然后最后看哪一类被选择最多,就预测该样本属于这一类。

一个合理的决策树可以描述为:决策树的高度相对低而且树的两边能将数据分类的更彻底。

而想要构建一个最优的决策树,和根节点和每一个中间节点的分类条件的选择密不可分,好的分类条件可以让类别一样的数据在树的一边,从而减少数的高度,因此如何在这些节点上如何选择分类条件成为了一个问题。

决策树的生成原则:

数据不断分裂的递归过程,每一次分裂,尽可能让类别一样的数据在树的一边,当树的叶子节点的数据都是一类的时候,则停止分类。这样分类的数据,每个节点两边的数据不同,将相同的数据分类到树的一侧,能将数据分类的更纯粹。减少树的高度和训练决策树的迭代次数。

注意:训练决策树的数据集要离散化,不然有可能造成训练出来的树有些节点的分支特别多,容易造成过拟合。

二.如何选择分类条件

1.信息熵

信息熵是用来量化信息信息量的指标,信息熵越大,则信息越混乱,信息熵越小,则信息越纯粹。熵的定义如下

信息熵公式

其中,n代表当前类别有多少类别代表当前类别中某一类别的概率。

下面举一个例子看一下信息熵的使用

image.png

由上图可知,最终"是否购买电脑"类别的信息熵如下:


最终"是否购买电脑"的信息熵

构建上图中最优的决策树,首先需要知道那个一个分类条件作为根节点是最优的。

如何选择决策树的根节点分类条件,就是找到某列作为分类条件时,使最终类别分类的更彻底,也就是找到在一个分类条件可以使最终的信息熵相对于没有这个分类条件时,信息熵降低的幅度最大(降低最大,就是熵越低,分类越彻底)。

而这里要使用到条件熵和信息增益。

条件熵:在某个分类条件下某个类别的信息熵叫做条件熵,类似于条件概率,在知道Y的情况下,X的不确定性。条件熵一般使用H(X|Y)表示,代表在Y条件下,X的信息熵。

年龄条件下的信息熵

信息增益:代表熵的变化程度。分类前的信息熵减去分类后的信息熵。

信息增益

因此年龄条件下的信息增益 = 最终"是否购买电脑"的信息熵 - 年龄条件下的信息熵 = 0.94-0.69=0.25

通过对所有分类条件计算信息增益,那么信息增益最大的那个分类条件就是最优的根节点分类条件的选择。

综上所述,在构建决策树时,选择分类分类条件的步骤如下:

  • 1.计算叶节点所对应列的信息熵
  • 2.计算每个已知列的信息熵
  • 3.求得各个条件下的信息增益,选择信息增益大的作为分类条件。选择中间节点时,也以此类推。

在构建决策树时,选择信息增益大的属性作为分类节点的方法也叫 ID3 分类算法。

2.信息增益率

使用信息增益来筛选分类条件,更倾向于选择更混杂的属性。容易出现过拟合问题。可以使用信息增益率来解决这个问题。

信息增益率的公式:


信息增益率的公式

即在某个条件下信息增益除以这个条件的信息熵。

信息增益率消除了当某些属性比较混杂时,使用信息增益来选择分类条件的弊端。使用信息增益率来构建决策树的算法也叫 C4.5 算法。一般相对于信息增益来说,选择信息增益率选择分类条件比较合适。

PS:如果决策树最后一个条件依然没能将数据准确分类,那么在这个节点上就可以使用概率来决定。看看哪些情况出现的多,该情况就是该节点的分类结果。

3.基尼系数

基尼系数也可以表示样本的混乱程度。公式如下:


基尼系数公式

其中,k代表当前类别有K个类别。P代表当前类别中某一类别的概率,1-P 代表不是当前这个类别的概率。

上图中计算“是否购买电脑”这列的基尼系数:


是否购买电脑”这列的基尼系数

基尼系数越小代表信息越纯,类别越少,基尼系数越大,代表信息越混乱,类别越多。基尼增益的计算和信息增益相同。假设某列只有一类值,这列的基尼系数为0。

三.决策树预剪枝和后剪枝

决策树对训练集有很好的分类能力,但是对于未知的测试集未必有好的分类能力,导致模型的泛化能力弱,可能发生过拟合问题,为了防止过拟合问题的出现,可以对决策树进行剪枝。剪枝分为预剪枝和后剪枝。

预剪枝:
就是在构建决策树的时候提前停止。比如指定树的深度最大为3,那么训练出来决策树的高度就是3,预剪枝主要是建立某些规则限制决策树的生长,降低了过拟合的风险,降低了建树的时间,但是有可能带来欠拟合问题。

后剪枝:
后剪枝是一种全局的优化方法,在决策树构建好之后,然后才开始进行剪枝。后剪枝的过程就是删除一些子树,这个叶子节点的标识类别通过大多数原则来确定,即属于这个叶子节点下大多数样本所属的类别就是该叶子节点的标识。选择减掉哪些子树时,可以计算没有减掉子树之前的误差和减掉子树之后的误差,如果相差不大,可以将子树减掉。一般使用后剪枝得到的结果比较好。

四.随机森林

随机森林是由多个决策树组成。是用随机的方式建立一个森林,里面由很多决策树组成。随机森林中每一棵决策树之间都是没有关联的。得到随机森林之后,对于一个样本输入时,森林中的每一棵决策树都进行判断,看看这个样本属于哪一类,最终哪一类得到的结果最多,该输入的预测值就是哪一类。

随机森林中的决策树生成过程是对样本数据进行多次行采样和列采样,可以指定随机森林中的树的个数和属性个数,这样当训练集很大的时候,随机选取数据集的一部分,生成一棵树,重复上面过程,可以生成一堆形态各异的树,这些决策树构成随机森林。

随机森林的优点:
随机森林中的每个决策树可以分布式的训练,解决了单棵决策树在数据量大的情况下预算量大的问题。当训练样本中出现异常数据时,决策树的抗干扰能力差,对于随机森林来说也解决了模型的抗干扰能力。

五.SparkMlLib中决策树和随机森林的使用

1.决策树
/**
 * 决策树
 */
object ClassificationDecisionTree {

  def main(args: Array[String]): Unit = {
      val conf = new SparkConf()
              conf.setAppName("analysItem")
              conf.setMaster("local[3]")
        val sc = new SparkContext(conf)
    val data: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "汽车数据样本.txt")
    // Split the data into training and test sets (30% held out for testing)
    val splits = data.randomSplit(Array(0.7, 0.3))
    val (trainingData, testData) = (splits(0), splits(1))
    //指明分类的类别
    val numClasses=2
    //指定离散变量,未指明的都当作连续变量处理
    //某列下有1,2,3类别 处理时候要自定为4类,虽然没有0,但是程序默认从0开始分类
    //这里天气维度有3类,但是要指明4,这里是个坑,后面以此类推
    val categoricalFeaturesInfo=Map[Int,Int](0->4,1->4,2->3,3->3)
    //设定评判标准  "gini"/"entropy"
    val impurity="entropy"
    //树的最大深度,太深运算量大也没有必要  剪枝   防止模型的过拟合!!!
    val maxDepth=3
    //设置离散化程度,连续数据需要离散化,分成32个区间,默认其实就是32,分割的区间保证数量差不多  这个参数也可以进行剪枝
    val maxBins=32
    //生成模型
    val model =DecisionTree.trainClassifier(trainingData,numClasses,categoricalFeaturesInfo,
                    impurity,maxDepth,maxBins)
    val labelAndPreds: RDD[(Double, Double)] = testData.map { point =>
      val prediction = model.predict(point.features)
      (point.label, prediction)
    }
    //测试
    val testErr = labelAndPreds.filter(r => r._1 != r._2).count().toDouble / testData.count()
    println("Test Error = " + testErr)
    println("Learned classification tree model:\n" + model.toDebugString)

  }
}

样本数据示例如下:

1 1:2 2:1 3:1 4:1 5:80 
1 1:3 2:2 3:1 4:1 5:77 
1 1:3 2:2 3:1 4:1 5:77 
1 1:2 2:1 3:1 4:1 5:77 
1 1:2 2:1 3:1 4:1 5:72 
1 1:3 2:2 3:1 4:1 5:40 
1 1:2 2:2 3:1 4:1 5:61 
1 1:2 2:1 3:1 4:1 5:69 
1 1:2 2:1 3:1 4:1 5:71 
2.随机森林
/**
 * 随机森林
 * 
 */
object ClassificationRandomForest {
  def main(args: Array[String]): Unit = {
      val conf = new SparkConf()
              conf.setAppName("analysItem")
              conf.setMaster("local[3]")
        val sc = new SparkContext(conf)
    //读取数据
    val data =  MLUtils.loadLibSVMFile(sc,"汽车数据样本.txt")
    //将样本按7:3的比例分成
    val splits = data.randomSplit(Array(0.7, 0.3))
    val (trainingData, testData) = (splits(0), splits(1))
    //分类数
    val numClasses = 2
    // categoricalFeaturesInfo 为空,意味着所有的特征为连续型变量
    val categoricalFeaturesInfo =Map[Int, Int](0->4,1->4,2->3,3->3)
    //树的个数
    val numTrees = 3 
    //特征子集采样策略,auto 表示算法自主选取
    //"auto"根据特征数量在4个中进行选择
    // 1:all 全部特征 。2:sqrt 把特征数量开根号后随机选择的 。 3:log2 取对数个。 4:onethird 三分之一
    val featureSubsetStrategy = "auto"
    //纯度计算  "gini"/"entropy"
    val impurity = "entropy"
    //树的最大层次
    val maxDepth = 3
    //特征最大装箱数,即连续数据离散化的区间
    val maxBins = 32
    //训练随机森林分类器,trainClassifier 返回的是 RandomForestModel 对象
    val model = RandomForest.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo,
        numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins)
//    //打印模型
//    println(model.toDebugString)
    //保存模型
   //model.save(sc,"汽车保险")
    //在测试集上进行测试
    val count = testData.map { point =>
        val prediction = model.predict(point.features)
    //    Math.abs(prediction-point.label)
        (prediction,point.label)
     }.filter(r => r._1 != r._2).count()
    println("Test Error = " + count.toDouble/testData.count().toDouble)
    println("model "+model.toDebugString)
  }
}

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