k-means算法是聚类分析中使用最广泛的算法之一。它把n个对象根据它们的属性分为k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。
k-means算法的基本过程如下所示:
任意选择k个初始中心 c 1 , c 2 , … , c k c{1},c{2},…,c_{k} c1,c2,…,ck 。
计算X中的每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;
重新计算每个中心对象 C i C_{i} Ci的值
计算标准测度函数,当满足一定条件,如函数收敛时,则算法终止;如果条件不满足则重复步骤(2),(3)。
k-means算法虽然简单快速,但是存在下面的缺点:
k-means++算法选择初始聚类中心的基本原则是:初始的聚类中心之间的相互距离要尽可能的远
他的步骤是先随机选出第一个点,然后找出距离第一个点最远的点,再找出距离前两个点最远的点,以此类推。
虽然k-means++算法可以确定地初始化聚类中心,但是从可扩展性来看,它存在一个缺点,那就是它内在的有序性特性:下一个中心点的选择依赖于已经选择的中心点。
针对这种缺陷,k-means||算法提供了解决方法。
k-means||算法是在k-means++算法的基础上做的改进:
第1步随机初始化一个中心点,第2-6步计算出满足概率条件的多个候选中心点C,候选中心点的个数可能大于k个,所以通过第7-8步来处理。第7步给C中所有点赋予一个权重值 ,这个权重值表示距离x点最近的点的个数。
第8步使用本地k-means++算法聚类出这些候选点的k个聚类中心。在spark的源码中,迭代次数是人为设定的,默认是5。
该算法与k-means++算法不同的地方是它每次迭代都会抽样出多个中心点而不是一个中心点,且每次迭代不互相依赖,这样我们可以并行的处理这个迭代过程。由于该过程产生出来的中心点的数量远远小于输入数据点的数量,所以第8步可以通过本地k-means++算法很快的找出k个初始化中心点。
高斯混合模型 代表一个复合分布,由此点是从一个绘制ķ高斯子分布,每个具有其自己的概率。该spark.ml实现使用 期望最大化 算法在给定一组样本的情况下得出最大似然模型。
例子:
import org.apache.spark.ml.clustering.GaussianMixture
// Loads data
val dataset = spark.read.format("libsvm").load("data/mllib/sample_kmeans_data.txt")
// Trains Gaussian Mixture Model
val gmm = new GaussianMixture()
.setK(2)
val model = gmm.fit(dataset)
// output parameters of mixture model model
for (i <- 0 until model.getK) {
println(s"Gaussian $i:\nweight=${model.weights(i)}\n" +
s"mu=${model.gaussians(i).mean}\nsigma=\n${model.gaussians(i).cov}\n")
}
功率迭代聚类(PIC)是Lin和Cohen开发的可伸缩图聚类算法。从摘要中:PIC使用截断幂迭代在数据的标准化成对相似矩阵上发现了非常低维的数据集嵌入。
spark.ml的PowerIterationClustering实现采用以下参数:
在快速迭代算法中,我们构造另外一个矩阵 ,同第一个做比对,我们可以知道W的最大特征向量就是拉普拉斯矩阵L的最小特征向量。
我们知道拉普拉斯矩阵有一个特性:第二小特征向量(即第二小特征值对应的特征向量)定义了图最佳划分的一个解,它可以近似最大化划分准则。更一般的,k个最小的特征向量所定义的子空间很适合去划分图。
因此拉普拉斯矩阵第二小、第三小直到第k小的特征向量可以很好的将图W划分为k个部分。
例子:
import org.apache.spark.ml.clustering.PowerIterationClustering
val dataset = spark.createDataFrame(Seq(
(0L, 1L, 1.0),
(0L, 2L, 1.0),
(1L, 2L, 1.0),
(3L, 4L, 1.0),
(4L, 0L, 0.1)
)).toDF("src", "dst", "weight")
val model = new PowerIterationClustering().
setK(2).
setMaxIter(20).
setInitMode("degree").
setWeightCol("weight")
val prediction = model.assignClusters(dataset).select("id", "cluster")
// Shows the cluster assignment
prediction.show(false)
LDA是一种概率主题模型:隐式狄利克雷分布(Latent Dirichlet Allocation,简称LDA)。LDA是2003年提出的一种主题模型,它可以将文档集中每篇文档的主题以概率分布的形式给出。
通过分析一些文档,我们可以抽取出它们的主题(分布),根据主题(分布)进行主题聚类或文本分类。同时,它是一种典型的词袋模型,即一篇文档是由一组词构成,词与词之间没有先后顺序的关系。一篇文档可以包含多个主题,文档中每一个词都由其中的一个主题生成。
例子:
import org.apache.spark.ml.clustering.LDA
// Loads data.
val dataset = spark.read.format("libsvm")
.load("data/mllib/sample_lda_libsvm_data.txt")
// Trains a LDA model.
val lda = new LDA().setK(10).setMaxIter(10)
val model = lda.fit(dataset)
val ll = model.logLikelihood(dataset)
val lp = model.logPerplexity(dataset)
println(s"The lower bound on the log likelihood of the entire corpus: $ll")
println(s"The upper bound on perplexity: $lp")
// Describe topics.
val topics = model.describeTopics(3)
println("The topics described by their top-weighted terms:")
topics.show(false)
// Shows the result.
val transformed = model.transform(dataset)
transformed.show(false)