机器执行的每一个步都依赖于我们的指令。它们需要指导去哪里做什么,就像一个不了解周围环境而无法自己做决定的孩子。因此,开发人员会需要为机器编写指令。然而当我们谈论机器学习时,我们谈论的是让机器在没有任何外部指令的情况下学会自己做出决定。这个机器有一个成熟的头脑,可以依据实际情况选择最佳的行动方针。
为了更深入地了解机器学习的基础知识,我建议你阅读这篇介绍文章。
在之前的博客中,我们了解了决策树算法 及其实现。在这个博客中,我们将继续讨论下一个机器学习算法:随机森林算法。决策树算法是随机森林算法的基础,如果不了解请先学习之前的博客。
我们可以说这是“机器学习的其中一种算法”,但是正如我们所知道的,在知识分享的·过程中,解释名词都是必要的。所以让我们深入这个算法。
随机森林算法,顾名思义,是一个森林。而这个森林由树组成,这里提到的树是决策树。所以,我们的完整定义是:随机森林算法由一组随机的决策树组成。因此,这个算法基本上只是决策树算法的一个扩展。
在随机森林算法中,我们创建了多个未剪枝决策树,这是因为随机森林算法不需要对决策树进行剪枝。这里的关键在于我们没有提供给每个决策树所有的训练数据,而是为每个决策树提供了一个随机的训练数据的子集。这个过程被称为bagging,或自助聚合。
Bagging是一个常用的过程,被用于降低方差过高的算法的方差。在这个过程中,为数据集创建子样本,并使用一个子样本来训练我们的决策模型。然后,我们综合每个模型的结果,通过投票(针对分类问题)或通过平均(针对回归问题)产生最后的结果。对于随机森林,我们通常会用三分之二的数据替换(对于其他决策树可以重复数据,所以不需要每棵树都使用唯一的数据)。
在随机森林算法中,每个决策树预测一个训练数据子集的结果,并根据投票决定最终的结果。在分类问题中,大多数决策树所得的结果即是最终的结果。在回归问题中,所有结果的平均值是最终结果。
现在是时候看一看在Scala中如何实现随机森林算法了。我们将像使用决策树时一样使用Smile库。
要使用Smile,请在SBT项目中包含以下依赖项:
libraryDependencies += "com.github.haifengl" %% "smile-scala" % "1.4.0"
对于这个实现我们将使用与决策树相同的数据。我们会得到一个关于二维数组的数组作为训练实例和INT型数组作为返回值。
val weather: AttributeDataset = read.arff("src/main/resources/weather.nominal.arff", 4)
val (trainingInstances,responseVariables) = data.pimpDataset(weather).unzipInt
获取数据后,我们使用smile.operators
包中的randomForest()
方法,这个将返回一个RandomForest
类的实例。
val nTrees = 200
val maxNodes = 4
val rf = randomForest(trainingInstances, responseVariables, weather.attributes(), nTrees, maxNodes)
这里是这个方法的参数列表:
trainingInstances
:Array [Array [Double]](必需参数)responseVariables
:Array [Int] (每个实例的返回值)attributes
:Array [Attribute] (包含所有属性的数组;该参数默认为null)nodeSize
:Int (树中无法拆分的节点中的实例数量;缺省值为1,但对于非常大的数据集则应该不止一个)ntrees
:Int (用于限制树的数量;默认值为500)maxNodes
:Int (每个决策树中叶子节点的最大数目;缺省值 = 属性数目 / nodeSize)mtry
:Int (每个决策树随机选择的属性的数量,默认情况下它的值是平方根*属性的数量)subsample
:Double(如果值为1.0,则用替换样本;如果小于1.0,则不替换样本;默认值为1.0)splitRule
:DecisionTree.SplitRule (为决策树计算信息增益的方法;可以是GINI或ENTROPY;默认情况下是GINI)classWeight
:Array [Int] (每个类包含的实例数量的比例;如果没有提供,算法会自己计算这个值)现在,我们的随机森林已经创建完成了。我们可以使用它的error()
方法来显示我们的随机森林的外包错误。
println(s"OOB error = ${rf.error}")
输出是:
我们可以看到,我们的随机森林中的错误是0.0,这是基于外包错误的统计。我们不需要再用另一个数据集进行测试。
之后我们可以用 RandomForest
类的predict()
方法来预测一些实例的结果。
我们的随机森林已经准备就绪,我们也检查了外包错误。我们知道,每一个预测也会产生一些错误。那么我们如何检查我们刚建立的随机森林的准确性呢?
还好我们有smile.validation
这个包!在这个包中,我们有很多方法可以来测试我们的模型。在这里,我们使用test()
这个方法。这是一个匿名函数,需要几个参数:
val testedRF = test(trainingInstances, responseVariables, testInstances, testResponseVariables)((_, _) => rf)
参数列表如下:
trainingInstances
: Array [Array [Double]]responseValues
: Array [Int]testInstances
: Array [Array [Double]]testResponseValues
: Array [Int]trainer
:一个以trainingInstances
作为输入,并通过responseValues
参数返回分类结果的一个匿名方法。这里testInstances
和 testResponseVaues
是从同一个测试数据集中提取,如下所示:
val weatherTest = read.arff("src/main/resources/weatherRF.nominal.arff", 4)
val (testInstances,testResponseValues) = data.pimpDataset(weatherTest).unzipInt
这是输出:
如上图所示,我们的随机森林的准确性 - 现在是83.33%。
示例代码的链接在这里!