基于RDD的聚类算法API

在机器学习中分为监督学习和无监督学习。其中聚类算法就是无监督学习。聚类算法就是根据某种相似性将相似的样本划分为一个类型。比如,最简单的k-mean算法的相似规则就是空间中的位置,两个样本点空间中位置越接近表示越相似。


K-means算法

在Spark mllib中实现了k-mean和k-mean++算法,下面是在saprk mllib中可调的参数:

  • k:分类的数量。注意在Spark mlib所实现的k-mean算法中,实际分出的类型的数量可以小于k。比如,最后结果少于k个不同的类。
  • maxIterations:最大的迭代计算数。
  • initializationModel:指定随机初始化或者通过k-mean++初始化。
  • epsilon:决定k-mean已经收敛的距离闸值。
  • initalModel:用于初始化的一组可选的集群中心。如果提供这个参数,则只会迭代计算一次。
    下面的例子,我们将数据聚合成两个族。然后计算平方误差之和。
package com.kmeanTest


import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}


object Main {
  def main(args: Array[String]): Unit = {
    //TODO 创建环境
    val conf: SparkConf = new SparkConf().setAppName("test").setMaster("local[*]")
    val sc = new SparkContext(conf)
    //TODO 数据操作
    //读取数据
    val path = "src/main/resources/data/mllib/kmeans_data.txt"
    val line: RDD[String] = sc.textFile(path)
    val parsedData: RDD[linalg.Vector] = line.map(s => Vectors.dense(s.split(' ').map(_.toDouble))).cache()

    //设置KMean的参数
    val numClusters = 2   //分成2个簇
    val numIterations = 20
    val clusters: KMeansModel = KMeans.train(parsedData, numClusters, numIterations)

    //计算出误差
    val WSSSE: Double = clusters.computeCost(parsedData)

    println(s"Within Set Sum of Squared Error = $WSSSE")
    //TODO 关闭环境
    sc.stop()
  }
}

高斯混合模型

高斯混合模型是一种复合模型,比如p(x)=π1p1(x)+π2p2(x)+...+πkpk(x)其中i=1kπi=1pi(x)xN(μi,σi2)
在Spark mllib中有以下参数来调整模型:

  • k:需要分簇的数量
  • convergenceTol:表示收敛时,两次计算期望的的差的最大值。
  • maxIterations:表示达不到收敛的时候,可以计算迭代的最大次数。
  • initialModel:一个可选的起点,用来初始化EM算法。(因为EM算法对初始化敏感)如果不设置该参数,会随机生成起始点。
    下面示例中,我们将会将数据分为两个簇,然后输出高斯混合模型的参数。
    部分数据:
 2.59470454e+00 2.12298217e+00
 1.15807024e+00 -1.46498723e-01
 2.46206638e+00 6.19556894e-01

代码:


幂迭代聚类

潜在狄利克雷分配

二分k-means

二分k-means是k-means的一个变形,或者说改进版。k-means的初始质心是随机的。不同的初始质心会导致最后的结果不同。也就是说k-means对初始化比较敏感。而二分k-means就解决了这个问题。二分k-means的算法步骤如下:
假设D是待分簇的数据集,令D={D0,0},初始质心p0,0

  • 步骤1:计算D0,0的所有样本x与该簇所在质心的SSE值,SSEi,j=i=1nj=1mdis(xi,j,pj),其中SSEi,j表示第i次二分的第j个簇,pi表示第j个簇的质心。
  • 步骤2:从{Dn,0,Dn,1...,Dn,n}n<k1选择出SEE最大的簇。
  • 步骤3:假设选出的簇为Dn,s,将其进行d次k为2的普通k-means分簇。将Dn,s分为{B1,B2,..,Bd},Bi=Ci,0,Ci,1。计算出SSE(Bi)=j=01SSE(Ci,j),然后选出最小的SSE(Bi),然后让{Ci,0,Ci,1}代替Dn,s
  • 步骤4:重复步骤2和步骤3,直到n=k-1(因为我是从0开始的)为止。
    二分k-means是一种层次聚类。层次聚类是最常用的聚类分析方法之一,主要目的是建立聚类的层次结构。层次聚类的策略一般分为两类:
  • 聚合式:这是一种“自上而下”的方法,每个观察值都是在自身的簇中计算出的,随着层次的提升,按照某种标准不断的合并簇形成新的簇。
  • 分裂式:这是一种“自下而上”的方法,所有的观察值都是从整体所形成的簇开始的,然后按照某个标准将一个簇分成2个或者多个簇。
    在Spark MLlib中提供了如下的参数来调整模型:
  • k:分簇的数量(默认值为4),注意,模型的结果可以小于k值。
  • maxIterations: 每轮进行2分的k-means的次数。
  • minDivisibleClusterSize:如果是大于1,则规定了可分簇的最小样本数量,比如设置为5,当一个簇的样本为5,而且SSE最大,则算法结束。如果小于1,则规定了可分簇的最小比例。默认值为1,表示簇样本数为1时不可再分。
package com.BisectingKmeansTest

import org.apache.spark.mllib.clustering.{BisectingKMeans, BisectingKMeansModel}
import org.apache.spark.mllib.linalg
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Main {
  def main(args: Array[String]): Unit = {
    //TODO 创建环境
    val conf: SparkConf = new SparkConf().setAppName("test").setMaster("local[*]")
    val sc = new SparkContext(conf)
    //TODO 数据操作
    //读取数据
    val path = "src/main/resources/data/mllib/kmeans_data.txt"
    val line: RDD[String] = sc.textFile(path)
    val data: RDD[linalg.Vector] = line.map(s => {
      Vectors.dense(s.split(' ').map(_.toDouble))
    })

    //设置参数
    val k = 6
    val maxIterations = 10
    val minDivisibleClusterSize = 5
    //创建模型
    val bisectingKmeans: BisectingKMeans = new BisectingKMeans().setK(k)
      .setMaxIterations(maxIterations)
      .setMinDivisibleClusterSize(minDivisibleClusterSize)
    val model: BisectingKMeansModel = bisectingKmeans.run(data)

    //输出簇的质心
    model.clusterCenters.zipWithIndex.foreach(r=>{
      println(s"Cluster Center ${r._2}: ${r._1}")
    })
    //TODO 关闭环境
    sc.stop()
  }
}

流式K-means

在大数据中,常常需要面对的数据是流式数据。在Spark MLlib中实现了伪K-mean的流式处理。流式K-means的关键就是对簇的质心的更新操作,也就是说新的数据到达之后,质心的变化情况。在Spark MLlib是通过以下公式来更新质心的:

ct+1=ctntα+xtmtntα+mt

nt+1=nt+mt

其中ct,xt分别表示原始簇与新添加数据所形成簇的质心。nt,mt分别是原始簇的样本数量和新添簇的样本数量,α是衰变因子。
举个例子就能明白质心是如何被更新的:
假设原始簇只有一个样本(-1,-1),新添加的样本为(1,0),(2,1),(1,2)。则c0=(1,1),n0=1,x0=(1,1),mt=3.

  • α=0时:c1=(1,1)10+(1,1)30+3=(1,1)
  • α=0.5时:c1=(1,1)10.5+(1,1)30.5+3=(5/7,5/7)
  • α=1时:c1=(1,1)11+(1,1)31+3=(1/2,1/2)
    是不是α越大就越靠近原始的簇的质心,其实在大数据中nt往往很大,所以α取小于1的数才比较有价值。
package com.StreamKmeanTest

import org.apache.spark.SparkConf
import org.apache.spark.mllib.clustering.StreamingKMeans
import org.apache.spark.mllib.linalg
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds, StreamingContext}


object Main {
  def main(args: Array[String]): Unit = {
    //TODO 创建环境
    val conf: SparkConf = new SparkConf().setAppName("test").setMaster("local[*]")
    val ssc = new StreamingContext(conf, Seconds(args(2).toLong))
    //TODO 数据操作
    //读取数据
    val trainData: DStream[linalg.Vector] = ssc.textFileStream(args(0)).map(Vectors.parse)
    val testData: DStream[LabeledPoint] = ssc.textFileStream(args(1)).map(LabeledPoint.parse)
    //配置模型
    val k: Int = args(3).toInt
    val alpha = 1.0
    val randomCenters: Int = args(4).toInt
    //训练模型
    val model: StreamingKMeans = new StreamingKMeans().setK(k).setDecayFactor(alpha).setRandomCenters(randomCenters, 0.0)
    model.trainOn(trainData)
    model.predictOnValues(testData.map(r=>(r.label, r.features))).print()
    //TODO 执行程序
    ssc.start()
    ssc.awaitTermination()
  }
}

作者:ALINGMAOMAO

出处:https://www.cnblogs.com/ALINGMAOMAO/p/17118146.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   青山新雨  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示