聚类是一个无监督的学习问题,我们旨在基于相似性的概念将实体的子集彼此分组。聚类通常用于探索性分析和/或作为分级监督学习管道的组成部分(在该学习管道中,针对每个聚类训练不同的分类器或回归模型)。
K均值是最常用的聚类算法之一,它将数据点聚集成预定数量的聚类。 spark.mllib实现包括k-means ++方法的并行变体,称为kmeans ||。 spark.mllib中的实现具有以下参数:
示例代码
以下代码段可以在spark-shell中执行。
在下面的示例中,在加载和解析数据之后,我们使用KMeans对象将数据分为两个集群。所需聚类的数量传递给算法。然后,我们计算平方误差的集合和内(WSSSE)。您可以通过增加k来减少此错误度量。实际上,最优k通常是WSSSE图中存在“肘”的那个。
有关API的详细信息,请参考KMeans Scala文档和KMeansModel Scala文档。
import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg.Vectors
// Load and parse the data
val data = sc.textFile("data/mllib/kmeans_data.txt")
val parsedData = data.map(s => Vectors.dense(s.split(' ').map(_.toDouble))).cache()
// Cluster the data into two classes using KMeans
val numClusters = 2
val numIterations = 20
val clusters = KMeans.train(parsedData, numClusters, numIterations)
// Evaluate clustering by computing Within Set Sum of Squared Errors
val WSSSE = clusters.computeCost(parsedData)
println(s"Within Set Sum of Squared Errors = $WSSSE")
// Save and load model
clusters.save(sc, "target/org/apache/spark/KMeansExample/KMeansModel")
val sameModel = KMeansModel.load(sc, "target/org/apache/spark/KMeansExample/KMeansModel")
高斯混合模型表示一种复合分布,其中从k个高斯子分布之一中抽取点,每个子分布都有自己的概率。 spark.mllib实现使用期望最大化算法在给定一组样本的情况下得出最大似然模型。该实现具有以下参数:
示例代码
import org.apache.spark.mllib.clustering.{GaussianMixture, GaussianMixtureModel}
import org.apache.spark.mllib.linalg.Vectors
// Load and parse the data
val data = sc.textFile("data/mllib/gmm_data.txt")
val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble))).cache()
// Cluster the data into two classes using GaussianMixture
val gmm = new GaussianMixture().setK(2).run(parsedData)
// Save and load model
gmm.save(sc, "target/org/apache/spark/GaussianMixtureExample/GaussianMixtureModel")
val sameModel = GaussianMixtureModel.load(sc,
"target/org/apache/spark/GaussianMixtureExample/GaussianMixtureModel")
// output parameters of max-likelihood model
for (i <- 0 until gmm.k) {
println("weight=%f\nmu=%s\nsigma=\n%s\n" format
(gmm.weights(i), gmm.gaussians(i).mu, gmm.gaussians(i).sigma))
}
幂迭代聚类(PIC)是一种可扩展且高效的算法,用于对图形的顶点进行聚类,并赋予成对相似性作为边缘属性,如Lin and Cohen的Power Iteration Clustering中所述。 它通过幂迭代计算图的归一化亲和矩阵的伪特征向量,并将其用于对顶点进行聚类。 spark.mllib包括使用GraphX作为后端的PIC实现。它采用(srcId,dstId,相似性)元组的RDD,并输出具有聚类分配的模型。相似性必须是非负的。 PIC假设相似性度量是对称的。一对(srcId,dstId)与顺序无关,应该在输入数据中最多出现一次。如果输入中缺少一对,则将它们的相似性视为零。 spark.mllib的PIC实现采用以下(超级)参数:
PowerIterationClustering实现PIC算法。它采用表示亲和力矩阵的(srcId:Long,dstId:Long,相似性:Double)元组的RDD。调用PowerIterationClustering.run返回PowerIterationClusteringModel,其中包含计算出的群集分配。
有关API的详细信息,请参阅PowerIterationClustering Scala文档和PowerIterationClusteringModel Scala文档。
import org.apache.spark.mllib.clustering.PowerIterationClustering
val circlesRdd = generateCirclesRdd(sc, params.k, params.numPoints)
val model = new PowerIterationClustering()
.setK(params.k)
.setMaxIterations(params.maxIterations)
.setInitializationMode("degree")
.run(circlesRdd)
val clusters = model.assignments.collect().groupBy(_.cluster).mapValues(_.map(_.id))
val assignments = clusters.toList.sortBy { case (k, v) => v.length }
val assignmentsStr = assignments
.map { case (k, v) =>
s"$k -> ${v.sorted.mkString("[", ",", "]")}"
}.mkString(", ")
val sizesStr = assignments.map {
_._2.length
}.sorted.mkString("(", ",", ")")
println(s"Cluster assignments: $assignmentsStr\ncluster sizes: $sizesStr")
潜在狄利克雷分配(LDA)是一种主题模型,可以从文本文档集合中推断出主题。可以将LDA视为聚类算法,如下所示:
LDA通过setOptimizer函数支持不同的推理算法。 EMLDA Optimizer使用对似然函数的期望最大化学习聚类并产生综合结果,而OnlineLDAOptimizer使用迭代小批量采样进行在线变异推断,并且通常对内存友好。
LDA接收文档集合作为单词计数和以下参数的向量(使用构建器模式设置):
spark.mllib的所有LDA模型均支持:
注意:LDA仍处于积极开发中的实验功能。结果,某些功能仅在优化器生成的两个优化器/模型之一中可用。当前,可以将分布式模型转换为本地模型,但反之则不能。
以下讨论将分别描述每个优化器/模型对。
期望最大化
在EMLDAOptimizer和DistributedLDAModel中实现。
对于提供给LDA的参数:
Note: 进行足够的迭代很重要。在早期迭代中,EM通常没有用的主题,但是在进行更多迭代之后,这些主题会显着改善。根据您的数据集,通常至少合理使用20次甚至50-100次迭代。EMLDAOptimizer生成一个DistributedLDAModel,该模型不仅存储推断出的主题,还存储完整的训练语料库和训练语料库中每个文档的主题分布。 DistributedLDAModel支持:
在线变分贝叶斯
在OnlineLDAOptimizer和LocalLDAModel中实现。
对于提供给LDA的参数:
此外,OnlineLDAOptimizer接受以下参数:
OnlineLDAOptimizer生成一个LocalLDAModel,该模型仅存储推断出的主题。 LocalLDAModel支持:
示例代码
import org.apache.spark.mllib.clustering.{DistributedLDAModel, LDA}
import org.apache.spark.mllib.linalg.Vectors
// Load and parse the data
val data = sc.textFile("data/mllib/sample_lda_data.txt")
val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble)))
// Index documents with unique IDs
val corpus = parsedData.zipWithIndex.map(_.swap).cache()
// Cluster the documents into three topics using LDA
val ldaModel = new LDA().setK(3).run(corpus)
// Output topics. Each is a distribution over words (matching word count vectors)
println(s"Learned topics (as distributions over vocab of ${ldaModel.vocabSize} words):")
val topics = ldaModel.topicsMatrix
for (topic <- Range(0, 3)) {
print(s"Topic $topic :")
for (word <- Range(0, ldaModel.vocabSize)) {
print(s"${topics(word, topic)}")
}
println()
}
// Save and load model.
ldaModel.save(sc, "target/org/apache/spark/LatentDirichletAllocationExample/LDAModel")
val sameModel = DistributedLDAModel.load(sc,
"target/org/apache/spark/LatentDirichletAllocationExample/LDAModel")
平分K均值通常会比常规K均值快得多,但通常会产生不同的聚类。
均分k均值是一种层次聚类。分层聚类是寻求建立聚类层次的最常用聚类分析方法之一。层次集群的策略通常分为两种:
二等分k均值算法是一种分裂算法。 MLlib中的实现具有以下参数:
import org.apache.spark.mllib.clustering.BisectingKMeans
import org.apache.spark.mllib.linalg.{Vector, Vectors}
// Loads and parses data
def parse(line: String): Vector = Vectors.dense(line.split(" ").map(_.toDouble))
val data = sc.textFile("data/mllib/kmeans_data.txt").map(parse).cache()
// Clustering the data into 6 clusters by BisectingKMeans.
val bkm = new BisectingKMeans().setK(6)
val model = bkm.run(data)
// Show the compute cost and the cluster centers
println(s"Compute Cost: ${model.computeCost(data)}")
model.clusterCenters.zipWithIndex.foreach { case (center, idx) =>
println(s"Cluster Center ${idx}: ${center}")
}
当数据到达流中时,我们可能希望动态估计群集,并在新数据到达时对其进行更新。 spark.mllib提供对流式k均值聚类的支持,并带有控制估计值衰减(或“健忘”)的参数。该算法使用了小批量k均值更新规则的概括。对于每批数据,我们将所有点分配给它们最近的聚类,计算新的聚类中心,然后使用以下方法更新每个聚类:
其中ct是群集的先前中心,nt是到目前为止分配给群集的点数,xt是当前批次中新的群集中心,mt是在当前批次中添加到群集的点数。衰减因子α可用于忽略过去:α= 1时,将从头开始使用所有数据;当α= 0时,将仅使用最新数据。这类似于指数加权移动平均值。
可以使用halfLife参数指定衰减,该参数确定正确的衰减因子a,以便对于在时间t采集的数据,其在时间t + halfLife的贡献将降至0.5。可以将时间单位指定为批次或点,并且将相应地调整更新规则。
示例代码
import org.apache.spark.mllib.clustering.StreamingKMeans
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.streaming.{Seconds, StreamingContext}
val conf = new SparkConf().setAppName("StreamingKMeansExample")
val ssc = new StreamingContext(conf, Seconds(args(2).toLong))
val trainingData = ssc.textFileStream(args(0)).map(Vectors.parse)
val testData = ssc.textFileStream(args(1)).map(LabeledPoint.parse)
val model = new StreamingKMeans()
.setK(args(3).toInt)
.setDecayFactor(1.0)
.setRandomCenters(args(4).toInt, 0.0)
model.trainOn(trainingData)
model.predictOnValues(testData.map(lp => (lp.label, lp.features))).print()
ssc.start()
ssc.awaitTermination()
在添加带有数据的新文本文件时,群集中心将更新。每个训练点的格式应为[x1,x2,x3],每个测试数据点的格式应为(y,[x1,x2,x3]),其中y是一些有用的标签或标识符(例如,真实的类别分配) )。任何时候将文本文件放在/ training / data / dir中,模型都会更新。任何时候将文本文件放在/ testing / data / dir中,您都会看到预测。有了新数据,集群中心将发生变化!