文档章节

Weka开发[9]—KMeans源码介绍

pior
 pior
发布于 2015/10/17 22:44
字数 1080
阅读 236
收藏 4

以前介绍的都是分类的内容,这一次介绍聚类,以最简单的SimpleKMeans源码为例。

       分类中训练一个分类器是用buildClassifier(),在聚类中学习一个Clusterer是用buildCluster()。分类中分类一个样本是用classifyInstance,而在聚类中是用clusterInstance。那我怎么知道这些的呢?(或者说:你怎么知道我是不是在骗你呢?)以ID3为例,你可以看出它继承自Classifier类,进入Classifier类,它有三个比较重要的函数,buildClassifer, classifyInstance, distributionForInstance(这个应该讲过了)。那么如果你在看SimpleKMeans那么可以看它继承自RandomizableCluster,而RandomizableCluster又继承自AbstactCluter,进入AbstactCluster,它也有三个比较重要的函数,buildCluster, clusterInstance, distributionForInstance。关联规则的自己找。但所有的这些最初我是如何知道的呢?同学告诉我的,我也问过他最初如何知道的呢?他神秘地告诉我:源代码。

for (int j = initInstances.numInstances() - 1; j >= 0; j--) {
    instIndex = RandomO.nextInt(j + 1);
    hk = new DecisionTableHashKey(initInstances.instance(instIndex),
           initInstances.numAttributes(), true);
    if (!initC.containsKey(hk)) {
       m_ClusterCentroids.add(initInstances.instance(instIndex));
       initC.put(hk, null);
    }
    initInstances.swap(j, instIndex);
 
    if (m_ClusterCentroids.numInstances() == m_NumClusters) {
       break;
    }
}


以上是随机产生centroid的代码,也没什么特别之处,用RandomO产生一个index,如果这个index所指向的样本不是一个中心点了(用Hash表记录),把这个样本加入m_ClusterCentroids中,m_ClusterCentroids中保存的是所有中心点。最后一个if判断如果产生了用户最初设置的cluster的个数,break。

 

   for (i = 0; i < instances.numInstances(); i++) {
       Instance toCluster = instances.instance(i);
       int newC = clusterProcessedInstance(toCluster, true);
       if (newC != clusterAssignments[i]) {
           converged = false;
       }
       clusterAssignments[i] = newC;
    }


    对每一个样本,用clusterProcessedInstance函数判断它属于哪个cluster,它属于哪个cluster当然就是根据它离哪个centroid近来决定了,再判断新的cluster和以前的cluster是否一样,如果不一样,那么就还没有convergeclusterAssignments[i]是第i个样本属于某个cluster

// update centroids
m_ClusterCentroids = new Instances(instances, m_NumClusters);
for (i = 0; i < m_NumClusters; i++) {
    tempI[i] = new Instances(instances, 0);
}
for (i = 0; i < instances.numInstances(); i++) {
    tempI[clusterAssignments[i]].add(instances.instance(i));
}
for (i = 0; i < m_NumClusters; i++) {
    if (tempI[i].numInstances() == 0) {
    // empty cluster
    emptyClusterCount++;
    } else {
       moveCentroid(i, tempI[i], true);
    }
}


以上代码是更新centroid,TempI[i]中保存的是所以当前属于第i个cluster的所有样本。最后一个for循环,如果tempI[i]中没有样本,那么记录有一个空cluster,如果tempI[i]中有样本,moveCentroid函数移动中心点。moveCentroid这个函数稍稍介绍一下,先看代码前的三句注释,我这里就不翻译了:

// in case of Euclidian distance the centroid is the mean point
// in case of Manhattan distance the centroid is the median point
// in both cases, if the attribute is nominal, the centroid is the mode
if (m_DistanceFunction instanceof EuclideanDistance
                  || members.attribute(j).isNominal()) {
    vals[j] = members.meanOrMode(j);
} else if (m_DistanceFunction instanceof ManhattanDistance) {
    // singleton special case
    if (members.numInstances() == 1) {
       vals[j] = members.instance(0).value(j);
    } else {
       sortedMembers.kthSmallestValue(j, middle + 1);
       vals[j] = sortedMembers.instance(middle).value(j);
       if (dataIsEven) {
           sortedMembers.kthSmallestValue(j, middle + 2);
    vals[j] = (vals[j] + sortedMembers.instance(middle + 
1).value(j)) / 2;
       }
    }
}


    这里有一点我不太明白的是:为什么代码不用ifelse把奇数,偶数分开,而是在偶数样本时计算两次,这种代码实在让我有点无法接受。有点需要解释的是为什么偶数的是时候用的是middle+2,这是因为这个coder在求middle的时候用的是(members.numInstances() - 1) / 2;这样如果是偶数实际求出来的middle就小1,另一点是因为数数是从0数起(讲这个有点污辱人了),所以是+2。这也是我吐血的一点,不就多写两行代码吗?何必把代码写的这么古怪。

对于每个属性,对于不同的距离公式,对于离散/连续属性,选择不同确定中心的方式。

    判断聚类是否结束,第一种是如果每一个样本(也就是第二段代码)都在上一次产生的cluster中,也就是converged,另一种是用户设计的一个m_MaxIterations,如果达到最大迭代次数,也结束。

再看一下clusterInstance函数,请注意,它最后调用的clusterProcessedInstance, 刚才提了一下这个函数,这里把核心代码列出来:

for (int i = 0; i < m_NumClusters; i++) {
    double dist = m_DistanceFunction.distance(instance,
       m_ClusterCentroids.instance(i));
    if (dist < minDist) {
       minDist = dist;
       bestCluster = i;
    }
}


讲这种代码,实在没什么意思,就是比较m_NumClusters个中心点,看instance与哪一个中心点近,作为bestCluster返回。


本文转载自:

共有 人打赏支持
pior
粉丝 26
博文 151
码字总数 22496
作品 0
济南
高级程序员
私信 提问
Weka开发[6]-参数设置

这一次介绍的非常简单,会用传命令行参数的人就不用浪费时间看这一篇了,这一篇介绍weka中一些类参数传递的问题。 首先要传递参数当然要知道参数有哪些,有什么作用,要知道这些,建议用Wek...

pior
2015/10/17
79
0
数据挖掘和R包(转)

下面列出了可用于数据挖掘的R包和函数的集合。其中一些不是专门为了数据挖掘而开发,但数据挖掘过程中这些包能帮我们不少忙,所以也包含进来。 1、聚类 2、分类 3、关联规则与频繁项集 4、序...

MtrS
2016/04/26
45
0
Python3机器学习实践:Kmeans++聚类【实例:啤酒聚类】

下面介绍Kmeans以及Kmeans++算法理论以及算法步骤: 根据样本特征选择不同的距离公式,程序实例中采用欧几里得距离。下面分别给出Kmeans以及Kmeans++算法的步骤。 Kmeans聚类算法的结果会因为...

AiFan
2018/07/02
0
0
与数据挖掘有关或有帮助的R包和函数的集合

与数据挖掘有关或者有帮助的R包和函数的集合。 1、聚类 常用的包:fpc,cluster,pvclust,mclust 基于划分的方法:kmeans,pam,pamk,clara 基于层次的方法:hclust,pvclust,agnes,diana 基于模...

dongzhumao
2015/01/28
0
0
Spark MLlib 机器学习算法与源码解析(网络课程—第一期)

《Spark MLlib 机器学习算法与源码解析》 spark是一个开源集群运算框架,最初是由加州大学柏克利分校AMPLab所开发。Spark使用了内存内运算技术,在内存上的运算速度比Hadoop MapReduce的运算...

sunbow0
2016/05/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Quartz监听器Listerner

概述 Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。Quartz监听器主要有JobListener、TriggerListener、SchedulerListener三种,顾名思义,分别表示任务、...

大笨象会跳舞吧
23分钟前
3
0
Call exception, tries=10, retries=35, started=38348 ms ago, cancelled=false, msg=pc-node1 row

写hbase的问题,2019-01-18 23:23:28,082 | INFO | [hconnection-0x6431d54d-shared--pool2-t5] | Call exception, tries=10, retries=35, started=38348 ms ago, cancelled=false, msg=p......

stys35
26分钟前
2
0
docker 安装portainer、gogs、redis、mongodb、es、rabbitmq、mysql、jenkins、harbor

1、准备三台虚拟机ip如下 编号 Ip 1 192.168.100.101 2 192.168.100.102 3 192.168.100.103 2、镜像应用编排 192.168.100.101 主要安装系统运维相关服务 192.168.100.102 主要安装mysql、mon...

北岩
36分钟前
5
0
storm 提交任务报SocketException错误及解决办法

提交任务爆错: org.apache.storm.thrift.transport.TTransportException: java.net.SocketException: Broken pipe (Write failed) ..... Caused by: org.apache.storm.thrift.transport.TTr......

jingshishengxu
40分钟前
1
0
值得收藏:一份非常完整的MySQL规范

一、数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命...

Java干货分享
50分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部