K-均值聚类

K-均值算法试图将一系列样本分割成K个不同的类簇(其中K是模型的输入参数),其形式化的目标函数称为类簇内的方差和(within cluster sum of squared errors,WCSS)。K-均值聚类的目的是最小化所有类簇中的方差之和。标准的K-均值算法初始化K个类中心(为每个类簇中所有样本的平均向量),后面的过程不断重复迭代下面两个步骤。
(1) 将样本分到WCSS最小的类簇中。因为方差之和为欧拉距离的平方,所以最后等价于将每个样本分配到欧拉距离最近的类中心。
(2) 根据第一步类分配情况重新计算每个类簇的类中心。
K-均值迭代算法结束条件为达到最大的迭代次数或者收敛。收敛意味着第一步类分配之后没有改变,因此WCSS的值也没有改变。

数据特征提取

这里我还是会使用之前分类模型的MovieLens数据集

// load movie data
val movies = sc.textFile("/PATH/ml-100k/u.item")
println(movies.first)
// 1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
  • 提取电影的题材标题

在进一步处理之前,我们先从u.genre文件中提取题材的映射关系。

val genres = sc.textFile("/PATH/ml-100k/u.genre")
genres.take(5).foreach(println)
/*
unknown|0
Action|1
Adventure|2
Animation|3
Children's|4
*/
val genreMap = genres.filter(!_.isEmpty).map(line => line.split("\\|")).map(array => (array(1), array(0))).collectAsMap
println(genreMap)
// Map(2 -> Adventure, 5 -> Comedy, 12 -> Musical, 15 -> Sci-Fi, 8 -> Drama, 18 -> Western, ... val titlesAndGenres = movies.map(_.split("\\|")).map { array =>
val genres = array.toSeq.slice(5, array.size)
val genresAssigned = genres.zipWithIndex.filter { case (g, idx) =>
g == "1"
}.map { case (g, idx) =>
genreMap(idx.toString)
}
(array(0).toInt, (array(1), genresAssigned))
}
println(titlesAndGenres.first)
// (1,(Toy Story (1995),ArrayBuffer(Animation, Children's, Comedy)))
// Run ALS model to generate movie and user factors
import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.Rating
val rawData = sc.textFile("/PATH/ml-100k/u.data")
val rawRatings = rawData.map(_.split("\t").take(3))
val ratings = rawRatings.map{ case Array(user, movie, rating) => Rating(user.toInt, movie.toInt, rating.toDouble) }
ratings.cache
val alsModel = ALS.train(ratings, 50, 10, 0.1) // extract factor vectors
import org.apache.spark.mllib.linalg.Vectors
val movieFactors = alsModel.productFeatures.map { case (id, factor) => (id, Vectors.dense(factor)) }
val movieVectors = movieFactors.map(_._2)
val userFactors = alsModel.userFeatures.map { case (id, factor) => (id, Vectors.dense(factor)) }
val userVectors = userFactors.map(_._2)

训练聚类模型

在MLlib中训练K-均值的方法和其他模型类似,只要把包含训练数据的RDD传入KMeans对象的train方法即可。注意,因为聚类不需要标签,所以不用LabeledPoint实例,而是使用特征向量接口,即RDD的Vector数组即可。MLlib的K-均值提供了随机和K-means||两种初始化方法,后者是默认初始化。因为两种方法都是随机选择,所以每次模型训练的结果都不一样。K-均值通常不能收敛到全局最优解,所以实际应用中需要多次训练并选择最优的模型。MLlib提供了完成多次模型训练的方法。经过损失函数的评估,将性能最好的一次训练选定为最终的模型。

代码实现中,首先需要引入必要的模块,设置模型参数:
K(numClusters)、最大迭代次数(numIteration)和训练次数(numRuns)。然后,对电影的系数向量运行K-均值算法。最后,在用户相关因素的特征向量上训练K-均值模型:

// run K-means model on movie factor vectors
import org.apache.spark.mllib.clustering.KMeans
val numClusters = 5
val numIterations = 10
val numRuns = 3
val movieClusterModel = KMeans.train(movieVectors, numClusters, numIterations, numRuns)
/*
...
14/09/02 22:16:45 INFO SparkContext: Job finished: collectAsMap at KMeans.scala:193, took 0.02043 s
14/09/02 22:16:45 INFO KMeans: Iterations took 0.300 seconds.
14/09/02 22:16:45 INFO KMeans: KMeans reached the max number of iterations: 10.
14/09/02 22:16:45 INFO KMeans: The cost for the best run is 2585.6805358546403.
...
movieClusterModel: org.apache.spark.mllib.clustering.KMeansModel = org.apache.spark.mllib.clustering.KMeansModel@2771ccdc
*/
// train user model
val userClusterModel = KMeans.train(userVectors, numClusters, numIterations, numRuns)

使用聚类模型进行预测

K-均值最小化的目标函数是样本到其类中心的欧拉距离之和,我们便可以将“最靠近类中心”定义为最小的欧拉距离。

下面让我们定义这个度量函数,注意引入Breeze库(MLlib的一个依赖库)用于线性代数和向量运算:

// define Euclidean distance function
import breeze.linalg._
import breeze.numerics.pow
def computeDistance(v1: DenseVector[Double], v2: DenseVector[Double]): Double = pow(v1 - v2, 2).sum

利用上面的函数对每个电影计算其特征向量与所属类簇中心向量的距离:

// join titles with the factor vectors, and compute the distance of each vector from the assigned cluster center
val titlesWithFactors = titlesAndGenres.join(movieFactors)
val moviesAssigned = titlesWithFactors.map { case (id, ((title, genres), vector)) => //vector可以理解为该点的坐标向量
val pred = movieClusterModel.predict(vector)//pred为预测出的该点所属的聚点
val clusterCentre = movieClusterModel.clusterCenters(pred)//clusterCentre为该pred聚点的坐标向量
val dist = computeDistance(DenseVector(clusterCentre.toArray), DenseVector(vector.toArray))//求两坐标的距离
(id, title, genres.mkString(" "), pred, dist)
}
val clusterAssignments = moviesAssigned.groupBy { case (id, title, genres, cluster, dist) => cluster }.collectAsMap//根据聚点分组

我们枚举每个类簇并输出距离类中心最近的前20部电影

for ( (k, v) <- clusterAssignments.toSeq.sortBy(_._1)) {
println(s"Cluster $k:")
val m = v.toSeq.sortBy(_._5)
println(m.take(20).map { case (_, title, genres, _, d) => (title, genres, d) }.mkString("\n"))
println("=====\n")
}
  • Cluster 0
    包含了很多20世纪40年代、50年代和60年代的老电影,以及一些近代的戏剧:

  • Cluster 1
    主要是一些恐怖电影:

    这里写图片描述
  • Cluster 2
    有相当一部分是喜剧和戏剧电影:

    这里写图片描述
  • Cluster 3
    和戏剧相关:

    这里写图片描述
  • Cluster 4
    主要是动作片、惊悚片和言情片:

    这里写图片描述

正如你看到的,我们并不能明显看出每个类簇所表示的内容。但是,也有证据表明聚类过程会提取电影之间的属性或者相似之处,这不是单纯基于电影名称和题材容易看出来的(比如外语片的类簇和传统电影的类簇,等等)。如果我们有更多元数据,比如导演、演员等,便有可能从每个类簇中找到更多特征定义的细节

评估聚类模型的性能

与回归、分类和推荐引擎等模型类似,聚类模型也有很多评价方法用于分析模型性能,以及评估模型样本的拟合度。聚类的评估通常分为两部分:内部评估和外部评估。内部评估表示评估过程使用训练模型时使用的训练数据,外部评估则使用训练数据之外的数据。
内部评价指标WCSS(我们之前提过的K-元件的目标函数),是使类簇内部的样本距离尽可能接近,不同类簇的样本相对较远。

MLlib提供的函数computeCost可以方便地计算出给定输入数据RDD [Vector]的WCSS。下面我们使用这个方法计算电影和用户训练数据的性能:

// compute the cost (WCSS) on for movie and user clustering
val movieCost = movieClusterModel.computeCost(movieVectors)
val userCost = userClusterModel.computeCost(userVectors)
println("WCSS for movies: " + movieCost)
println("WCSS for users: " + userCost)
// WCSS for movies: 2586.0777166339426
// WCSS for users: 1403.4137493396831

聚类模型参数调优

不同于以往的模型,K-均值模型只有一个可以调的参数,就是K,即类中心数目。

// cross-validation for movie clusters
val trainTestSplitMovies = movieVectors.randomSplit(Array(0.6, 0.4), 123)
val trainMovies = trainTestSplitMovies(0)
val testMovies = trainTestSplitMovies(1)
val costsMovies = Seq(2, 3, 4, 5, 10, 20).map { k => (k, KMeans.train(trainMovies, numIterations, k, numRuns).computeCost(testMovies)) }
println("Movie clustering cross-validation:")
costsMovies.foreach { case (k, cost) => println(f"WCSS for K=$k id $cost%2.2f") }
/*
Movie clustering cross-validation:
WCSS for K=2 id 942.06
WCSS for K=3 id 942.67
WCSS for K=4 id 950.35
WCSS for K=5 id 948.20
WCSS for K=10 id 943.26
WCSS for K=20 id 947.10
*/ // cross-validation for user clusters
val trainTestSplitUsers = userVectors.randomSplit(Array(0.6, 0.4), 123)
val trainUsers = trainTestSplitUsers(0)
val testUsers = trainTestSplitUsers(1)
val costsUsers = Seq(2, 3, 4, 5, 10, 20).map { k => (k, KMeans.train(trainUsers, numIterations, k, numRuns).computeCost(testUsers)) }
println("User clustering cross-validation:")
costsUsers.foreach { case (k, cost) => println(f"WCSS for K=$k id $cost%2.2f") }
/*
User clustering cross-validation:
WCSS for K=2 id 544.02
WCSS for K=3 id 542.18
WCSS for K=4 id 542.38
WCSS for K=5 id 542.33
WCSS for K=10 id 539.68
WCSS for K=20 id 541.21
*/

从结果可以看出,随着类中心数目增加,WCSS值会出现下降,然后又开始增大。另外一个现象,K-均值在交叉验证的情况,WCSS随着K的增大持续减小,但是达到某个值后,下降的速率突然会变得很平缓。这时的K通常为最优的K值(这称为拐点)。

http://www.jianshu.com/p/d1b4c9f4844f

K-均值聚类——电影类型的更多相关文章

  1. 5-Spark高级数据分析-第五章 基于K均值聚类的网络流量异常检测

    据我们所知,有‘已知的已知’,有些事,我们知道我们知道:我们也知道,有 ‘已知的未知’,也就是说,有些事,我们现在知道我们不知道.但是,同样存在‘不知的不知’——有些事,我们不知道我们不知道. 上一章 ...

  2. 探索sklearn | K均值聚类

    1 K均值聚类 K均值聚类是一种非监督机器学习算法,只需要输入样本的特征 ,而无需标记. K均值聚类首先需要随机初始化K个聚类中心,然后遍历每一个样本,将样本归类到最近的一个聚类中,一个聚类中样本特征 ...

  3. k均值聚类算法原理和(TensorFlow)实现

    顾名思义,k均值聚类是一种对数据进行聚类的技术,即将数据分割成指定数量的几个类,揭示数据的内在性质及规律. 我们知道,在机器学习中,有三种不同的学习模式:监督学习.无监督学习和强化学习: 监督学习,也 ...

  4. 【转】算法杂货铺——k均值聚类(K-means)

    k均值聚类(K-means) 4.1.摘要 在前面的文章中,介绍了三种常见的分类算法.分类作为一种监督学习方法,要求必须事先明确知道各个类别的信息,并且断言所有待分类项都有一个类别与之对应.但是很多时 ...

  5. 机器学习实战5:k-means聚类:二分k均值聚类+地理位置聚簇实例

    k-均值聚类是非监督学习的一种,输入必须指定聚簇中心个数k.k均值是基于相似度的聚类,为没有标签的一簇实例分为一类. 一 经典的k-均值聚类 思路: 1 随机创建k个质心(k必须指定,二维的很容易确定 ...

  6. Python实现kMeans(k均值聚类)

    Python实现kMeans(k均值聚类) 运行环境 Pyhton3 numpy(科学计算包) matplotlib(画图所需,不画图可不必) 计算过程 st=>start: 开始 e=> ...

  7. 机器学习理论与实战(十)K均值聚类和二分K均值聚类

    接下来就要说下无监督机器学习方法,所谓无监督机器学习前面也说过,就是没有标签的情况,对样本数据进行聚类分析.关联性分析等.主要包括K均值聚类(K-means clustering)和关联分析,这两大类 ...

  8. 第十篇:K均值聚类(KMeans)

    前言 本文讲解如何使用R语言进行 KMeans 均值聚类分析,并以一个关于人口出生率死亡率的实例演示具体分析步骤. 聚类分析总体流程 1. 载入并了解数据集:2. 调用聚类函数进行聚类:3. 查看聚类 ...

  9. K均值聚类的失效性分析

    K均值聚类是一种应用广泛的聚类技术,特别是它不依赖于任何对数据所做的假设,比如说,给定一个数据集合及对应的类数目,就可以运用K均值方法,通过最小化均方误差,来进行聚类分析. 因此,K均值实际上是一个最 ...

  10. K均值聚类

    聚类(cluster)与分类的不同之处在于, 分类算法训练过程中样本所属的分类是已知的属监督学习. 而聚类算法不需要带有分类的训练数据,而是根据样本特征的相似性将其分为几类,又称为无监督分类. K均值 ...

随机推荐

  1. appium(二)简单的demo

     转自http://blog.csdn.net/Yejianyun1/article/details/55517418     启动appium服务,连接手机,将测试用例demo存放到.py文件中 # ...

  2. Struts2_HelloWorld_6

    为 eclipse 在编写 xml配置文件时提供提示,需要加上dtd或xls的标签定义文件的路径,具体操作: 1.Window——Preferences——XML Catalog 2.添加 dtd 文 ...

  3. Azure 5 月新公布(二)

    Azure 5 月新发布(二):CDN 图片处理功能, CDN Restful API, 新版 CDN 管理门户, 计量名称变更延期  Azure CDN 图片处理功能开放公共预览版 Azure CD ...

  4. Hadoop常用操作

    Hadoop常用操作 1.Hadoop安装 略 2.Hadoop配置 略 3.Hadoop多目录配置 namenode和datanode节点下都可以进行多个目录的配置,但是意义不同.namenode的 ...

  5. Jmeter入门7 测试中使用到的几个定时器和逻辑控制器

    1 测试中提交数据有延时1min,所以查询数据是否提交成功要设置定时器. 固定定时器页面:单位是毫秒 [dinghanhua] 2 集合点.Synchronizing Timer 集合点编辑:集合用户 ...

  6. 【JavaScript 封装库】BETA 1.0 测试版发布!

    /* 源码作者: 石不易(Louis Shi) 联系方式: http://www.shibuyi.net =============================================== ...

  7. 前端高质量知识(二)-JS执行上下文(执行环境)详细图解Script

    先随便放张图 我们在JS学习初期或者面试的时候常常会遇到考核变量提升的思考题.比如先来一个简单一点的. console.log(a); // 这里会打印出什么? var a = 20; PS: 变量提 ...

  8. 2017.10.5 Java图形化界面设计——布局管理器

    1.BorderLayout(边界布局) 边界布局管理器把容器的的布局分为五个位置:CENTER.EAST.WEST.NORTH.SOUTH.依次对应为:上北(NORTH).下南(SOUTH).左西( ...

  9. Bean的初始化和销毁

    在我们实际开发的时候,经常会遇到在Bean在使用之前或者之后做些必要的操作,Spring对Bean的生命周期的操作提供了支持.在使用Java配置和注解配置下提供如下两种方式.    1.Java配置方 ...

  10. Can Microsoft’s exFAT file system bridge the gap between OSes?

    转自:http://arstechnica.com/information-technology/2013/06/review-is-microsofts-new-data-sharing-syste ...