Spark mlib KMeans聚类算法

1. 聚类
1.1 什么是聚类?


所谓聚类问题,就是给定一个元素集合D,其中每个元素具有n个可观察属性,使用算法将集合D划分成k个子集,要求每个子集内部的元素之间相异度尽可能低,而不同子集的元素相异度尽可能高,其中每个子集叫做一个簇。

K-means算法简介

k-means算法是一种聚类算法,所谓聚类,即根据相似性原则,将具有较高相似度的数据对象划分至同一类簇,将具有较高相异度的数据对象划分至不同类簇。聚类与分类最大的区别在于,聚类过程为无监督过程,即待处理数据对象没有任何先验知识,而分类过程为有监督过程,即存在有先验知识的训练数据集。

K-means算法原理

k-means算法中的k代表类簇个数,means代表类簇内数据对象的均值(这种均值是一种对类簇中心的描述),因此,k-means算法又称为k-均值算法。k-means算法是一种基于划分的聚类算法,以距离作为数据对象间相似性度量的标准,即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。数据对象间距离的计算有很多种,k-means算法通常采用欧氏距离来计算数据对象间的距离

k-means算法优缺点分析

- 优点: 算法简单易实现;

 - 缺点: 需要用户事先指定类簇个数; 聚类结果对初始类簇中心的选取较为敏感; 容易陷入局部最优; 只能发现球形类簇;


1.2 KMeans 聚类算法


K-Means聚类算法主要分为如下几个步骤:

  1. 从D中随机取k个元素,作为k个簇的各自的中心
  2. 分别计算剩下的元素到k个簇中心的相异度,将这些元素分别划归到相异度最低的簇
  3. 根据聚类结果,重新计算k个簇各自的中心,计算方法是取簇中所有元素各自维度的算术平均数
  4. 将D中全部元素按照新的中心重新聚类。 
  5. 重复第4步,直到聚类结果不再变化。 

1.2.1 什么是相异度


设 X={x1,x2.....,xn},Y={y1,y2......yn}其中X,Y是两个元素项,各自具有n个可度量特征属性
X和Y的相异度定义为: d(X,Y)=f(X,Y)->R,其中R为实数域,也就是两个元素的相异度。


1.2.2 相异度的算法


因为每个纬度的数字都是无方向意义的标度变量,可以通过距离来标示相异度
常见的几个距离计算公式:

  1. 欧几里得距离: 
  2. 曼哈顿距离:
  3. 闵可夫斯基距离:

1.2.3 数据的规格化


在计算距离的时候,会发现取值范围大的属性对距离的影响高于取值范围小的属性,为了解决这个问题,一般要对属性值进行规格化。
规格化就是将各个属性值按比例映射到相同的取值区间,这样是为了平衡各个属性对距离的影响。

最典型的规格化就是数据的归一化:将各个属性均映射到[0,1]区间
映射公式为:其中max(ai)和min(ai)表示所有元素项中第i个属性的最大值和最小值


2. Spark Kmeans的实现


2.1 Kmeans 初始化的几个参数

class KMeans private (
    private var k: Int,
    private var maxIterations: Int,
    private var initializationMode: String,
    private var initializationSteps: Int,
    private var epsilon: Double,
    private var seed: Long) extends Serializable 
参数定义
K聚的总类
maxIterations迭代的次数
initializationMode有 random 和 k-means||两种
initializationSteps初始化的步长
epsilon最小中心距离的筏值
seed随机数的种子

 

2.2 步骤1:Kmeans 的初始化中心的选择


Kmeans 在数据集初始化的时候中选K个中心点有两种算法

  • 随机选择:依据给的种子seed,随机生成K个随机中心点
  • k-means||:默认的算法
  1.  随机生成一个中心点,基于这个中心点,找出一批距离这个中心点较远的点作为集合(分布式查找)
  2.  以这些找到的点的集合为新的中心点,依据initializationSteps作为重复查找步骤1,2的次数(分布式查找)
  3.  如果找到的这些点的数量小于k,那么就以这些点为中心点
  4.  不如2步骤找到的这些点大于k,那么将基于这些点作为样本进行k-means++的中心点查找,找到K个中心点。k-means++的查找是在有限的点上查找(driver端的本地权重查找)
if (initializationMode == KMeans.RANDOM) {
          initRandom(data)
        } else {
          initKMeansParallel(data)
        }

2.3  步骤2: 计算每个点的特征向量的norm

// Compute squared norms and cache them.
    val norms = data.map(Vectors.norm(_, 2.0))
    norms.persist()

我们来看一下norm的算法

else if (p == 2) {
      var sum = 0.0
      var i = 0
      while (i < size) {
        sum += values(i) * values(i)
        i += 1
      }
      math.sqrt(sum)

假如:一个点的A(a1,b1) 那么norm的计算就是 sqrt(a1^2+b1^2),这也是向量的L2范数


2.4 步骤3:计算每个点距离其他点的距离


在Spark使用的距离算法是欧式距离算法,我们先来看这个距离算法:对两个点 x(x1,x2....xn)和y(y1,y2....yn)


将方程式解开

sqrt(x1^2+x2^2+x3^2+.....+xn^2 + y1^2+y2^2+...+yn^2 -2(x1y1+x2*y2.....+xn*yn))

对x1^2+x2^2+x3^2+.....+xn^2 这部分可以提前算,但是-2(x1y1+x2*y2.....+xn*yn))这部分的计算是需要时时计算的
 

注意:

在我们前面的文章中,Spark做了一些算法的优化而这些优化是基于欧式距离的,Spark mllib里提供的Kmeans算法不支持其它的距离算法。

3. Kmeans的训练模型


Kmeans本身也提供了训练模型,模型的目的为了对新输入的向量进行判定到哪个类别,聚类的模型最终的目的是为了分类。

@Since("0.8.0")
class KMeansModel @Since("1.1.0") (@Since("1.0.0") val clusterCenters: Array[Vector])
  extends Saveable with Serializable with PMMLExportable {
 
  /**
   * A Java-friendly constructor that takes an Iterable of Vectors.
   */
  @Since("1.4.0")
  def this(centers: java.lang.Iterable[Vector]) = this(centers.asScala.toArray)
 
  /**
   * Total number of clusters.
   */
  @Since("0.8.0")
  def k: Int = clusterCenters.length
 
  /**
   * Returns the cluster index that a given point belongs to.
   */
  @Since("0.8.0")
  def predict(point: Vector): Int = {
    KMeans.findClosest(clusterCentersWithNorm, new VectorWithNorm(point))._1
  }
 
  /**
   * Maps given points to their cluster indices.
   */
  @Since("1.0.0")
  def predict(points: RDD[Vector]): RDD[Int] = {
    val centersWithNorm = clusterCentersWithNorm
    val bcCentersWithNorm = points.context.broadcast(centersWithNorm)
    points.map(p => KMeans.findClosest(bcCentersWithNorm.value, new VectorWithNorm(p))._1)
  }
 
  /**
   * Maps given points to their cluster indices.
   */
  @Since("1.0.0")
  def predict(points: JavaRDD[Vector]): JavaRDD[java.lang.Integer] =
    predict(points.rdd).toJavaRDD().asInstanceOf[JavaRDD[java.lang.Integer]]
 
  /**
   * Return the K-means cost (sum of squared distances of points to their nearest center) for this
   * model on the given data.
   */
  @Since("0.8.0")
  def computeCost(data: RDD[Vector]): Double = {
    val centersWithNorm = clusterCentersWithNorm
    val bcCentersWithNorm = data.context.broadcast(centersWithNorm)
    data.map(p => KMeans.pointCost(bcCentersWithNorm.value, new VectorWithNorm(p))).sum()
  }
 
  private def clusterCentersWithNorm: Iterable[VectorWithNorm] =
    clusterCenters.map(new VectorWithNorm(_))
 
  @Since("1.4.0")
  override def save(sc: SparkContext, path: String): Unit = {
    KMeansModel.SaveLoadV1_0.save(sc, this, path)
  }
 
  override protected def formatVersion: String = "1.0"
}

 

通过KMeansModel的训练模型,predict输入的向量所距离最近的中心点

  @Since("0.8.0")
  def predict(point: Vector): Int = {
    KMeans.findClosest(clusterCentersWithNorm, new VectorWithNorm(point))._1
  }


看了熟悉的函数findClosest,那些中心点是在聚类结束创建中心点

new KMeansModel(centers.map(_.vector))

4. Spark Kmeans的评估
如何评估KMeans的聚类K的效果?可以通过computeCost函数来计算cost

  @Since("0.8.0")
  def computeCost(data: RDD[Vector]): Double = {
    val centersWithNorm = clusterCentersWithNorm
    val bcCentersWithNorm = data.context.broadcast(centersWithNorm)
    data.map(p => KMeans.pointCost(bcCentersWithNorm.value, new VectorWithNorm(p))).sum()
  }


函数的算法:
通过计算所有数据点到其最近的中心点的距离平方和 (a1-c1)^2+(a2-c2)^2 +...... 

使用不同的K,相同的迭代次数,理论上值越小,聚类效果越好,但是这是需要可解释性,如果聚类K等于总数据点,当然聚类效果最好,cost是0,但没有意义。
 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值