本帖最后由 hero1122 于 2017-7-17 16:32 编辑
1.概述
首先,笔者要先申明,我也是初学机器学习领域的内容,虽然我是从事大数据平台开发的工作,但是工作中确实没有跟spark MLlib打过交道,所以文中如果有描述错误的地方,还请大家指正。机器学习对高数、python的基础都有一定的要求,但是入门我觉得最重要的是理论联系实际,了解机器学习基本概念,然后结合spark MLlib的example代码去入手,亲自跑一个代码尝试一下,这样理解起来更容易一些!本次内容主要学习聚类算法。
2.机器学习的分类
2.1 监督学习
监督是从给定的训练数据集中学习一个函数(模型),当新的数据到来时,可以根据这个函数(模型)预测结果。监督学习的训练集要求包括输入和输出,也可以说是特征和目标。训练集中的目标是由人标注(标量)的。在监督式学习下,输入数据被称为“训练数据”,每组训练数据有一个明确的标识或结果,如对防垃圾邮件系统中“垃圾邮件”、“非垃圾邮件”,对手写数字识别中的“1”、“2”、“3”等。在建立预测模型时,监督式学习建立一个学习过程,将预测结果与“训练数据”的实际结果进行比较,不断调整预测模型,直到模型的预测结果达到一个预期的准确率。常见的监督学习算法包括回归分析和统计分类:
- 二元分类是机器学习要解决的基本问题,将测试数据分成两个类,如垃圾邮件的判别、房贷是否允许等问题的判断。
- 多元分类是二元分类的逻辑延伸。例如,在因特网的流分类的情况下,根据问题的分类,网页可以被归类为体育、新闻、技术等,依此类推。
监督学习常常用于分类,因为目标往往是让计算机去学习我们已经创建好的分类系统。数字识别再一次成为分类学习的常见样本。一般来说,对于那些有用的分类系统和容易判断的分类系统,分类学习都适用。
监督学习是训练神经网络和决策树的最常见技术。神经网络和决策树技术高度依赖于事先确定的分类系统给出的信息。对于神经网络来说,分类系统用于判断网络的错误,然后调整网络去适应它;对于决策树,分类系统用来判断哪些属性提供了最多的信息,如此一来可以用它解决分类系统的问题。
2.2 无监督学习
与监督学习相比,无监督学习的训练集没有人为标注的结果。在非监督式学习中,数据并不被特别标识,学习模型是为了推断出数据的一些内在结构。常见的应用场景包括关联规则的学习以及聚类等。常见算法包括Apriori算法和k-Means算法。这类学习类型的目标不是让效用函数最大化,而是找到训练数据中的近似点。聚类常常能发现那些与假设匹配的相当好的直观分类,例如基于人口统计的聚合个体可能会在一个群体中形成一个富有的聚合,以及其他的贫穷的聚合。
非监督学习看起来非常困难:目标是我们不告诉计算机怎么做,而是让它(计算机)自己去学习怎样做一些事情。非监督学习一般有两种思路:第一种思路是在指导Agent时不为其指定明确的分类,而是在成功时采用某种形式的激励制度。需要注意的是,这类训练通常会置于决策问题的框架里,因为它的目标不是产生一个分类系统,而是做出最大回报的决定。这种思路很好地概括了现实世界,Agent可以对那些正确的行为做出激励,并对其他的行为进行处罚。
因为无监督学习假定没有事先分类的样本,这在一些情况下会非常强大,例如,我们的分类方法可能并非最佳选择。在这方面一个突出的例子是Backgammon(西洋双陆棋)游戏,有一系列计算机程序(例如neuro-gammon和TD-gammon)通过非监督学习自己一遍又一遍地玩这个游戏,变得比最强的人类棋手还要出色。这些程序发现的一些原则甚至令双陆棋专家都感到惊讶,并且它们比那些使用预分类样本训练的双陆棋程序工作得更出色。
2.3 半监督学习
半监督学习(Semi-supervised Learning)是介于监督学习与无监督学习之间一种机器学习方式,是模式识别和机器学习领域研究的重点问题。它主要考虑如何利用少量的标注样本和大量的未标注样本进行训练和分类的问题。半监督学习对于减少标注代价,提高学习机器性能具有非常重大的实际意义。主要算法有五类:基于概率的算法;在现有监督算法基础上进行修改的方法;直接依赖于聚类假设的方法等,在此学习方式下,输入数据部分被标识,部分没有被标识,这种学习模型可以用来进行预测,但是模型首先需要学习数据的内在结构以便合理地组织数据来进行预测。应用场景包括分类和回归,算法包括一些对常用监督式学习算法的延伸,这些算法首先试图对未标识数据进行建模,在此基础上再对标识的数据进行预测,如图论推理算法(Graph Inference)或者拉普拉斯支持向量机(Laplacian SVM)等。
半监督学习分类算法提出的时间比较短,还有许多方面没有更深入的研究。半监督学习从诞生以来,主要用于处理人工合成数据,无噪声干扰的样本数据是当前大部分半监督学习方法使用的数据,而在实际生活中用到的数据却大部分不是无干扰的,通常都比较难以得到纯样本数据。
2.4 强化学习
强化学习通过观察来学习动作的完成,每个动作都会对环境有所影响,学习对象根据观察到的周围环境的反馈来做出判断。在这种学习模式下,输入数据作为对模型的反馈,不像监督模型那样,输入数据仅仅是作为一个检查模型对错的方式,在强化学习下,输入数据直接反馈到模型,模型必须对此立刻做出调整。常见的应用场景包括动态系统以及机器人控制等。常见算法包括Q-Learning 以及时间差学习(Temporal difference learning)。
在企业数据应用的场景下,人们最常用的可能就是监督式学习和非监督式学习的模型。在图像识别等领域,由于存在大量的非标识的数据和少量的可标识数据,目前半监督式学习是一个很热的话题。而强化学习更多地应用在机器人控制及其他需要进行系统控制的领域。
3、Spark MLlib介绍
MLlib目前支持4种常见的机器学习问题: 分类、回归、聚类和协同过滤,MLlib在Spark整个生态系统中的位置如图下图所示。
spark MLlib源码结构如下图所示:
4、Spark MLlib Example(聚类算法)
聚类算法属于非监督式学习,通常被用于探索性的分析,是根据“物以类聚”的原理,将本身没有类别的样本聚集成不同的组,这样的一组数据对象的集合叫做簇,并且对每一个这样的簇进行描述的过程。它的目的是使得属于同一簇的样本之间应该彼此相似,而不同簇的样本应该足够不相似,常见的典型应用场景有客户细分、客户研究、市场细分、价值评估。MLlib 目前支持广泛使用的KMmeans聚类算法。
案例:导入训练数据集,使用 KMeans 对象来将数据聚类到两个类簇当中,所需的类簇个数会被传递到算法中,然后计算集内均方差总和(WSSSE),可以通过增加类簇的个数 k 来减小误差。 实际上,最优的类簇数通常是 1,因为这一点通常是WSSSE图中的 “低谷点”。
[mw_shl_code=scala,true]object Kmeans {
def main(args: Array[String]) {
// 屏蔽不必要的日志显示在终端上
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
// 设置运行环境
val conf = new SparkConf().setAppName("Kmeans").setMaster("local")
val sc = new SparkContext(conf)
// 装载数据集
val data = sc.textFile("file:///export/software/github/spark-2.1.0-bin-hadoop2.6/data/mllib/kmeans_data.txt", 1)
// 解析数据文件
val parsedData = data.map(s => Vectors.dense(s.split(' ').map(_.toDouble)))
// 设置迭代次数、类簇的个数,2个类,20次迭代,进行模型训练形成数据模型
val numClusters = 2
val numIterations = 20
// 进行训练
val model = KMeans.train(parsedData, numClusters, numIterations)
// 打印数据模型的中心点
println("Cluster centers:")
for (c <- model.clusterCenters) {
println(" " + c.toString)
}
// 使用误差平方之和来评估数据模型,统计聚类错误的样本比例
val cost = model.computeCost(parsedData)
println("Within Set Sum of Squared Errors = " + cost)
// 使用模型测试单点数据
println("Vectors 0.2 0.2 0.2 is belongs to clusters:" + model.predict(Vectors.dense("0.2 0.2 0.2".split(' ').map(_.toDouble))))
println("Vectors 0.25 0.25 0.25 is belongs to clusters:" + model.predict(Vectors.dense("0.25 0.25 0.25".split(' ').map(_.toDouble))))
println("Vectors 8 8 8 is belongs to clusters:" + model.predict(Vectors.dense("8 8 8".split(' ').map(_.toDouble))))
// 交叉评估1,只返回结果
val testdata = data.map(s => Vectors.dense(s.split(' ').map(_.toDouble)))
val result1 = model.predict(testdata)
result1.saveAsTextFile("file:///export/software/github/spark-2.1.0-bin-hadoop2.6/data/mllib/result/kmeams1")
// 交叉评估2,返回数据集和结果
val result2 = data.map {
line =>
val linevectore = Vectors.dense(line.split(' ').map(_.toDouble))
val prediction = model.predict(linevectore)
line + " " + prediction
}.saveAsTextFile("file:///export/software/github/spark-2.1.0-bin-hadoop2.6/data/mllib/result/kmeams2")
sc.stop()
}
}[/mw_shl_code]
样本数据如下:
[mw_shl_code=bash,true]cat /export/software/github/spark-2.1.0-bin-hadoop2.6/data/mllib/kmeans_data.txt[/mw_shl_code]
[mw_shl_code=text,true]0.0 0.0 0.0
0.1 0.1 0.1
0.2 0.2 0.2
9.0 9.0 9.0
9.1 9.1 9.1
9.2 9.2 9.2[/mw_shl_code]
结果数据如下: [mw_shl_code=bash,true]cat /export/software/github/spark-2.1.0-bin-hadoop2.6/data/mllib/result/kmeams2/part-00000
[/mw_shl_code] [mw_shl_code=text,true]0.0 0.0 0.0 1
0.1 0.1 0.1 1
0.2 0.2 0.2 1
9.0 9.0 9.0 0
9.1 9.1 9.1 0
9.2 9.2 9.2 0[/mw_shl_code]
从结果可以看出样本数据分为两类,[0.1 0.1 0.1]和[0.2 0.2 0.2]属于一类,[9.0 9.0 9.0]、[9.1 9.1 9.1]、[9.2 9.2 9.2]属于一类。
|