作者:http://blog.selfup.cn/ 原文链接:http://blog.selfup.cn/728.html
本文出处:聚类,Cluster analysis,有时也被翻译为簇类,其核心任务是:将一组目标object划分为若干个簇,每个簇之间的object尽可能的相似,簇与簇之间的 object尽可能的相异。聚类算法是机器学习(或者说是数据挖掘更合适)中重要的一部分,除了最为简单的K-Means聚类算法外,较常见的还有:层次 法(CURE、CHAMELEON等)、网格算法(STING、WaveCluster等)等等。
较权威的聚类问题定义:所谓聚类问题,就是给定一个元素集合D,其中每个元素具有n个可观察属性,使用某种算法将D划分成k个子集,要求每个子集内部的元素之间相异度尽可能低,而不同子集的元素相异度尽可能高。其中每个子集叫做一个簇。
与分类不同,分类是示例式学习,要求分类前明确各个类别,并断言每个元素映射到一个类别,而聚类是观察式学习,在聚类前可以不知道类别甚至不给定类别数量,是无监督学习的一种。目前聚类广泛应用于统计学、生物学、数据库技术和市场营销等领域,相应的算法也非常的多。
K-Means属于基于平方误差的迭代重分配聚类算法,其核心思想十分简单:
K-Means算法的结果好坏依赖于对初始聚类中心的选择,容易陷入局部最优解,对K值的选择没有准则可依循,对异常数据较为敏感,只能处理数值属性的数据,聚类结构可能不平衡。
这里有一个K-Means的演示,需要安装Java Applet。
0.0 0.0 0.0 0.1 0.1 0.1 0.2 0.2 0.2 9.0 9.0 9.0 9.1 9.1 9.1 9.2 9.2 9.2 15.1 16.1 17.0 18.0 17.0 19.0 20.0 21.0 22.0
如前文所述,测试数据不用带标签,数据分为3个维度。
private static final Pattern SPACE = Pattern.compile(" "); public static void main(String[] args) { SparkConf sparkConf = new SparkConf() .setAppName("K-Means") .setMaster("local[2]"); JavaSparkContext sc = new JavaSparkContext(sparkConf); JavaRDD<String> data = sc.textFile("/home/yurnom/data/kmeans_data.txt"); JavaRDD<Vector> parsedData = data.map(s -> { double[] values = Arrays.asList(SPACE.split(s)) .stream() .mapToDouble(Double::parseDouble) .toArray(); return Vectors.dense(values); }); int numClusters = 3; //预测分为3个簇类 int numIterations = 20; //迭代20次 int runs = 10; //运行10次,选出最优解 KMeansModel clusters = KMeans.train(parsedData.rdd(), numClusters, numIterations, runs); //计算测试数据分别属于那个簇类 print(parsedData.map(v -> v.toString() + " belong to cluster :" + clusters.predict(v)).collect()); //计算cost double wssse = clusters.computeCost(parsedData.rdd()); System.out.println("Within Set Sum of Squared Errors = " + wssse); //打印出中心点 System.out.println("Cluster centers:"); for (Vector center : clusters.clusterCenters()) { System.out.println(" " + center); } //进行一些预测 System.out.println("Prediction of (1.1, 2.1, 3.1): " + clusters.predict(Vectors.dense(new double[]{1.1, 2.1, 3.1}))); System.out.println("Prediction of (10.1, 9.1, 11.1): " + clusters.predict(Vectors.dense(new double[]{10.1, 9.1, 11.1}))); System.out.println("Prediction of (21.1, 17.1, 16.1): " + clusters.predict(Vectors.dense(new double[]{21.1, 17.1, 16.1}))); } public static <T> void print(Collection<T> c) { for(T t : c) { System.out.println(t.toString()); } }
[0.0,0.0,0.0] belong to cluster :0 [0.1,0.1,0.1] belong to cluster :0 [0.2,0.2,0.2] belong to cluster :0 [9.0,9.0,9.0] belong to cluster :1 [9.1,9.1,9.1] belong to cluster :1 [9.2,9.2,9.2] belong to cluster :1 [15.1,16.1,17.0] belong to cluster :2 [18.0,17.0,19.0] belong to cluster :2 [20.0,21.0,22.0] belong to cluster :2 Within Set Sum of Squared Errors = 38.533333333333815 Cluster centers: [0.10000000000000002,0.10000000000000002,0.10000000000000002] [9.1,9.1,9.1] [17.7,18.033333333333335,19.333333333333332] Prediction of (1.1, 2.1, 3.1): 0 Prediction of (10.1, 9.1, 11.1): 1 Prediction of (21.1, 17.1, 16.1): 2
人为捏造的测试数据所想表现出来的簇类完全被k-means算法体会到了,若是人工将测试数据分成3个簇类,结果也会与上面一样。
K-Means属于无监督学习,最大的特别和优势在于模型的建立不需要训练数据。在日常工作中,很多情况下没有办法事先获取到有效的训练数据,这时 采用K-Means是一个不错的选择。但K-Means需要预先设置有多少个簇类(K值),这对于像计算某省份全部电信用户的交往圈这样的场景就完全的没 办法用K-Means进行。对于可以确定K值不会太大但不明确精确的K值的场景,可以进行迭代运算,然后找出cost最小时所对应的K值,这个值往往能较 好的描述有多少个簇类。