预测问题记住一点:最垃圾的预测就是使用平均值,若是你的预测连比直接给出平均值效果都要差,那就省省吧!
统计学诞生一个多世纪以后,随着如今机器学习和数据科学的产生,咱们依旧使用回归的思想来进行预测,尽管回归 就是用平均值向后不断回滚来预测。回归的技术和分类的技术紧密相关。一般状况下,当目标变量是连续数值时指的是回归,例如预测 身高和体重。当预测的目标变量是名义或者说是类别变量时,指的就是分类,例如预测邮件是不是垃圾邮件。
不管是分类仍是回归,都须要给定已知信息去预测未知信息,因此它们都须要从输入输出来学习。它们须要包括问题和答案。这类算法所以也称为监督学习的方法。
回归和分类是使用年代最近研究的最充分的预测分析技术。不少算法开源包都包含通用的这些方法。好比:支持向量机,逻辑回归,朴素贝叶斯,神经网络和深度学习。
本文的重点是讨论:决策树和它的扩展随机森林。决策树是通用并且灵活的分类回归算法。html
注意:特别当心分类变量(尤为是那些用数字表示的分类变量,不要随便放到算法中去训练)和数值变量
注意:不是全部的算法都能处理分类变量,或者都能处理回归分类问题,可是放心决策树均可以linux
算法名字 | 决策树,DT,Decision Trees |
算法描述 | 适合任何数据类型的分类和回归算法 关键概念:树、信息增益、中止条件、过拟合 |
算法原理 | 根据已有样本训练获得一颗树,树的叶子节点就是预测的值,新的样本将按照样本的值遍历树的相对应节点最后到达叶子节点,叶子节点的值就是新样本的预测值。<br/> 决策树原理很是简单,以下图:<br/> ![]() |
使用场景 | 强大的算法,适合回归和预测的不少场合 |
算法优缺点 | 优势: 1. 可以天然处理分类和数值特征 2. 容易并行计算 3. 对异常值具备很好的鲁棒性(意味着小量的异常值或者错误数据不影响分类结果) 4. 能够处理不一样类型和不一样维度的数据不须要数据预处理或者预先正则化数据 5. 结果容易理解和解释<br /> 缺点: 1. 容易过拟合(不过它的变种随机森林已经改进了这个问题) |
数据类型 | 数值型和标称型数据 |
参考资料 | 1. 算法原理 机器学习实战 <br /> 2. MLlib实现 DesisionTree<br /> 3. MLlib实现 RandomForest |
本文将使用著名的 Covtype 数据集合,能够在 http://bit.ly/1KiJRfg 这里下载。下载以后是一个压缩的 csv 文件, linux 用户能够用命令:tar -xzvf 解压缩,windows用户可使用 .7-zip 解压缩,同时下载数据集的描述文件 covtype.info 数据集记录的是美国 Colorado 植被覆盖类型数据,也是惟一一个关心真实森林的数据。每条记录都包含不少指标描述每一块土地。 例如:高度、坡度、到水的距离、树荫下的面积、土壤的类型等等。森林的覆盖类型是须要根据其余54个特征进行预测的特征。 这是一个有趣的数据集,它包含分类和数值特征。总共有581012条记录。每条记录有55列,其中一列是土壤的类型,其余54列是输入特征。 虽然这个数据集还不能算得上真正的大数据,可是也能说明不少问题。很幸运,这个数据集已是csv文件,因此不须要太多的数据清洗或者其余的准备就能够给 Spark Mllib 使用。 数据集可上传到 HDFS,固然也能够先放到本地进行这个有趣的测试。无论哪一种方式,Spark 都只须要改变一两个参数。 这里不得再也不次提醒一个问题,分类变量如何编码,下面是编码的方式:算法
当算法中把编码看成数字的时候只能使用第一种编码,第二种编码会得出滑稽的结果。具体缘由是没有大小的东西被强制成有大小之分。
Covtype 数据集中有不少类型的特征,不过很幸运,它已经帮咱们转换成 one-hot 形势,具体来讲:apache
这个数据集每一列的变量单位都不必定相同,有的表示距离,有的表示度数等等windows
下面给出一个初步的利用Spark MLlib 实验的决策树模型,具体的意图,代码都有详细的注释:微信
//本地测试 val rootDir = "your sample data directory" def main(args: Array[String]) { val conf = new SparkConf().setAppName("SparkInAction").setMaster("local[4]") val sc = new SparkContext(conf) val covTypeData = sc.textFile(rootDir + "/covtype.data") val data = dataPrepare(covTypeData) //选择测试集合和训练集合 val Array(train, cvData, test) = data.randomSplit(Array(0.8, 0.1, 0.1)) train.cache() cvData.cache() test.cache() val model = buildDecisionTree(train, cvData) } /** * Spark MLlib 表示特征向量的对象是 LabeledPoint * 这个对象由表示特征的向量和目标变量组成 */ def dataPrepare(data: RDD[String]) = { val sample = data.map{ line => //所有数据转化为 double val array = line.split(",").map(_.toDouble) //前面54列是特征向量,后面一列是目标变量 label val featureVector = Vectors.dense(array.init) //决策树目标变量必须从0开始,按照1递增 LabeledPoint(array.last - 1, featureVector) } sample } /** * 决策树模型创建,这里很容易改进为十择交叉验证。 * 对每一份数据创建模型时,都须要随机选出部分数据来调整模型参数到最优。 * 经过交叉验证的方式调整参数。 * @param train * @param cvData */ def buildDecisionTree(train: RDD[LabeledPoint], cvData: RDD[LabeledPoint]) = { def getMetrics(model: DecisionTreeModel, data: RDD[LabeledPoint]) = { val predictionsAndLabels = data.map { example => (model.predict(example.features), example.label) } new MulticlassMetrics(predictionsAndLabels) } val model = DecisionTree.trainClassifier( train, 7, Map[Int, Int](), "gini", 4, 100 ) val matrics = getMetrics(model, cvData) println(matrics.confusionMatrix) (0 until 7).map( cat => (matrics.precision(cat), matrics.recall(cat)) ).foreach(println) }
这个是初步的运行结果:网络
#总体的准确率和召回率 (0.7012384971978136,0.7012384971978136) #每个类别的准确率和召回率 (0.685108051158916,0.6668097486526446) (0.7255299659774928,0.7930627570177007) (0.6194455768446799,0.8685338668190912) (0.3771043771043771,0.39436619718309857) (0.55,0.011727078891257996) (0.0,0.0) (0.7174657534246576,0.4134188455846078)
70%的准确率和召回率彷佛效果还不错,可是咱们如今不能盲目的认为咱们的效果就真的不错了,有时候瞎猜效果也会不错。 例如:70%的数据属于类别1,每次都猜想类别是1,那么效果也能达到70%的准确率,下面咱们肯定一下瞎猜的准确率: 回答瞎猜猜对的几率,这个问题也不是简单的,回到几率论课堂上,在训练样本每类几率已知的状况下,测试样本瞎猜对的几率有多大呢? 随机给出一个样本:猜想类A的几率是由训练样本决定的,同时猜对的几率是由测试样本决定的,因此瞎猜猜对的几率是训练样本每类的几率分别 乘以测试样本对应类的几率之和dom
/** * 获取模型瞎猜的几率 * @param train 测试数据集 * @param cvData 验证数据集 */ def guessProb(train: RDD[LabeledPoint], cvData: RDD[LabeledPoint]) { /** * 返回数据集合中,每个类别的几率 * @param data 训练数据集 */ def labelProb(data: RDD[LabeledPoint]): Array[Double] = { val labelCnt = data.map(_.label).countByValue() val labelProb = labelCnt.toArray.sortBy(_._1).map(_._2) labelProb.map(_.toDouble/labelProb.sum) } val trainProb = labelProb(train) val cvProb = labelProb(cvData) val prob = trainProb.zip(cvProb).map { case (a, b) => a * b }.sum println(prob) }
能够看到瞎猜的结果只有:0.3771281350885772 的准确率。说明70%的准确率效果确实不错,可是请注意,咱们尚未优化参数, 说明咱们的模型还有优化的空间。机器学习
主要的参数有下面几个:学习
/** * 模型评估 * @param trainData 训练数据 * @param cvData 交叉验证数据 */ def evaluate(trainData: RDD[LabeledPoint], cvData: RDD[LabeledPoint]): Unit = { val evaluations = for (impurity <- Array("gini", "entropy"); depth <- Array(1, 20); bins <- Array(10, 300)) yield { val model = DecisionTree.trainClassifier( trainData, 7, Map[Int,Int](), impurity, depth, bins) val predictionsAndLabels = cvData.map(example => (model.predict(example.features), example.label) ) val accuracy = new MulticlassMetrics(predictionsAndLabels).precision ((impurity, depth, bins), accuracy) } evaluations.sortBy(_._2).reverse.foreach(println) }
欢迎关注本人微信公众号,会定时发送关于大数据、机器学习、Java、Linux 等技术的学习文章,并且是一个系列一个系列的发布,无任何广告,纯属我的兴趣。