首先更正一点,前篇博客里面说到一个Canopy的测试的例子里面有这样的一句代码:
buildClusters(Configuration conf, Path input, Path output,
DistanceMeasure measure, double t1, double t2, double t3, double t4,
int clusterFilter, boolean runSequential)
我以前认为clusterFilter是分类的数目,其实应该是每个分类中至少含有的(样本数+1)才对,而非分类数,因为Canopy就是要得到分类数目的。
下面分析CanopyMapper:
首先把CanopyMapper改编为可以不依靠hadoop可以跑的代码,即纯java代码,如下:
package mahout.test.canopy.debug;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.mahout.clustering.canopy.Canopy;
import org.apache.mahout.clustering.canopy.CanopyClusterer;
import org.apache.mahout.clustering.canopy.CanopyConfigKeys;
import org.apache.mahout.common.distance.DistanceMeasure;
import org.apache.mahout.common.distance.ManhattanDistanceMeasure;
import org.apache.mahout.math.RandomAccessSparseVector;
import org.apache.mahout.math.Vector;
import org.apache.mahout.math.VectorWritable;
import com.google.common.collect.Lists;
public class CanopyDebug {
/**
* canopy 测试程序
* @author fansy
* time 2013/7/21 22:59
* edited at 2013/7/22 21:38
*/
private static final Collection<Canopy> canopies = Lists.newArrayList();
private static CanopyClusterer canopyClusterer;
private static int clusterFilter;
private static int num=0;
private static Map<Text,VectorWritable> center=new HashMap<Text,VectorWritable>();
public static void main(String[] args) {
// 初始化
DistanceMeasure measure=new ManhattanDistanceMeasure();
double t1=30.2;
double t2=5.3;
int clusterFilter =1;
Configuration conf=init(measure,t1,t2,clusterFilter );
// setup() 函数
setup(conf);
// map()函数
map();
// cleanup() 函数
Map<Text,VectorWritable> result=cleanup();
System.out.println("done..."+result);
}
/**
* 初始化
* @param measure
* @param t1
* @param t2
* @param clusterFileter:每个canopy至少含有的样本数
* @return
*/
public static Configuration init(DistanceMeasure measure,double t1,double t2,int clusterFileter){
Configuration conf=new Configuration();
double t3=t1;
double t4=t2;
conf.set(CanopyConfigKeys.DISTANCE_MEASURE_KEY, measure.getClass()
.getName());
conf.set(CanopyConfigKeys.T1_KEY, String.valueOf(t1));
conf.set(CanopyConfigKeys.T2_KEY, String.valueOf(t2));
conf.set(CanopyConfigKeys.T3_KEY, String.valueOf(t3));
conf.set(CanopyConfigKeys.T4_KEY, String.valueOf(t4));
conf.set(CanopyConfigKeys.CF_KEY, String.valueOf(clusterFilter));
return conf;
}
/**
* map setup() 函数
* @param conf 输入初始参数
*/
public static void setup(Configuration conf){
canopyClusterer = new CanopyClusterer(conf);
clusterFilter = Integer.parseInt(conf.get(
CanopyConfigKeys.CF_KEY));
}
/**
* 获得初始数据 <key,value>--><null,VectorWritable>
* @return
*/
public static List<VectorWritable> makeInData(){
List<VectorWritable> list=new ArrayList<VectorWritable>();
VectorWritable vw=null;
Vector vector=null;
for(int i=0;i<10;i++){
vw=new VectorWritable();
vector=new RandomAccessSparseVector(3);
vector.set(0, i%3*(i%3)*(i%3)*(i%3)+Math.random());
vector.set(1, i%3*(i%3)*(i%3)*(i%3)+Math.random());
vector.set(2, i%3*(i%3)*(i%3)*(i%3)+Math.random());
vw.set(vector);
list.add(vw);
}
return list;
}
/**
* 模仿Mapper的map函数
*/
public static void map(){
List<VectorWritable> vwList=makeInData2();
for(VectorWritable point: vwList){
canopyClusterer.addPointToCanopies(point.get(), canopies);
}
}
/**
* 模仿Mapper的cleanup函数
* @return
*/
public static Map<Text,VectorWritable> cleanup(){
for (Canopy canopy : canopies) {
canopy.computeParameters();
if (canopy.getNumObservations() > clusterFilter) {
center.put(new Text("centroid"+num++), new VectorWritable(canopy
.getCenter()));
}
}
return center;
}
/**
* 固定输入数据,产生输入数据的第二种方式,方便调试
* @return
*/
public static List<VectorWritable> makeInData2(){
List<VectorWritable> list=new ArrayList<VectorWritable>();
VectorWritable vw=null;
Vector vector=null;
for(int i=0;i<10;i++){
vw=new VectorWritable();
vector=new RandomAccessSparseVector(3);
vector.set(0, i%3*(i%3)*(i%3)*(i%3)+1);
vector.set(1, i%3*(i%3)*(i%3)*(i%3)+1);
vector.set(2, i%3*(i%3)*(i%3)*(i%3)+1);
vw.set(vector);
list.add(vw);
}
return list;
}
}
利用上面的代码可以直接进行调试,调试可以看到每一步运行的结果,很方便理解该算法。下面就结合实例进行说明:
输入数据(VectorWritable格式):
[1.0,1.0,1.0]
[2.0,2.0,2.0]
[17.0,17.0,17.0]
[1.0,1.0,1.0]
[2.0,2.0,2.0]
[17.0,17.0,17.0]
[1.0,1.0,1.0]
[2.0,2.0,2.0]
[17.0,17.0,17.0]
[1.0,1.0,1.0]
通过makeData2()函数即可获得上面转换成VectorWritable的数据,然后进入map函数,其中的for循环即是仿造CanopyMapper中的map函数了。map函数里面使用到的函数为:addPointToCanopies,打开CanopyCluster类可以看到这个函数的主要内容如下;
public void addPointToCanopies(Vector point, Collection<Canopy> canopies) {
boolean pointStronglyBound = false;
for (Canopy canopy : canopies) {
double dist = measure.distance(canopy.getCenter().getLengthSquared(), canopy.getCenter(), point);
if (dist < t1) {
if (log.isDebugEnabled()) {
log.debug("Added point: {} to canopy: {}", AbstractCluster.formatVector(point, null), canopy.getIdentifier());
}
canopy.observe(point);
}
pointStronglyBound = pointStronglyBound || dist < t2;
}
if (!pointStronglyBound) {
if (log.isDebugEnabled()) {
log.debug("Created new Canopy:{} at center:{}", nextCanopyId, AbstractCluster.formatVector(point, null));
}
canopies.add(new Canopy(point, nextCanopyId++, measure));
}
}
针对第一行的样本[1.0,1.0,1.0]因为canopies为空,所以直接 执行 canopies.add(new Canopy(point,nextCanopyId++,measure));这样就生成了第一个canopy,先看下Canopy这个类,其父类为DistanceMeasureCluster,父类为AbstractCluster,其属性有:
private int id;
private long numObservations;
private long totalObservations;
private Vector center;
private Vector radius;
// the observation statistics
private double s0;
private Vector s1;
private Vector s2;
在这个函数里面canopy变化的属性只有 s0,s1,s2 ,s0代表这个canopy里面的样本数,s1代表所有样本的对应维度相加,s2代表所有样本所有维度先平方再对应相加;
针对第二行样本[2.0,2.0,2.0],dist=3(曼哈顿距离)<T1=30.3,所以要执行canopy.observe(point)方法,这个方法其实就是设置s0、s1、s2的,设置完成后s0变为2,s1为[3,3,3],s2为[5,5,5]; 因为dist=3<t2=5.2所以不再执行canopies.add()方法了;
针对第三行样本[17,17,17]因为dist=16*3>t1=30.2所以直接执行canopies.add()方法,即又添加了一个canopy;后面的样本以此类推;
在CanopMapper类中最后执行的方法为cleanup函数,这里也仿造了一个cleanup函数,其实cleanup函数的主要作用就是一个过滤和设置值的过程函数对应为canopy.computeParameters(),过滤即是说一个canopy中的样本数必须要>clusterFilter才行,设置值即为设置radius和center值,具体代码参见:
setNumObservations((long) getS0());
setTotalObservations(getTotalObservations() + getNumObservations());
setCenter(getS1().divide(getS0()));
// compute the component stds
if (getS0() > 1) {
setRadius(getS2().times(getS0()).minus(getS1().times(getS1())).assign(new SquareRootFunction()).divide(getS0()));
}
setS0(0);
setS1(center.like());
setS2(center.like());
center的值设置为s1/s0,radius的值:A=[s2*s0-s1.*s1],(前一个是乘以,后面是点乘)然后把A的每一个维度的值开方并除以s0即可得到radius。
分享,快乐,成长
转载请注明出处:http://blog.csdn.net/fansy1990
分享到:
相关推荐
对应于前面的MahoutCanopy.jar文件的源代码,可以参考来看;前面的那个是工具,这个是源码
仿造mahout 的canopy算法编写的canopy算法,只是把其中的输入数据文件的格式改为了text类型,且加入了log信息,方便用户测试算法;
mahoutAlgorithms源码分析 mahout代码解析
Mahout是一个Java的机器学习库。Mahout的完整源代码,基于maven,可以轻易导入工程中
mahout,朴素贝叶斯分类,中文分词,mahout,朴素贝叶斯分类,中文分词,
mahout关联推荐算法的介绍,例如PFPGrowth算法的参数使用介绍以及适用场景
mahout canopy+kmeans测试数据
NULL 博文链接:https://badxy.iteye.com/blog/1356424
mahout-distribution-0.5-src.zip mahout 源码包
mahout聚类算法的介绍,例如:Canopy,KMeans,Fuzzy-KMeans,Spectral Clustering等参数介绍和适用场景介绍
mahout有哪些算法,这个图片上列举了mahout上的算法的名字
mahout中的贝叶斯算法的拓展开发包,提供了相关接口可以供用户调用,直接即可跑出结果,相关运行方式参考blog《mahout贝叶斯算法开发思路(拓展篇)》
mahout中bayesian算法的数据流,可以根据excel中的公式来推断出该算法的数据流
https://github.com/chubbyjiang/MapReduce
Mahout算法解析与案例实战_PDF电子书下载 带书签目录 完整版
svd算法的工具类,直接调用出结果,调用及设置方式参考http://blog.csdn.net/fansy1990 <mahout源码分析之DistributedLanczosSolver(七)>
Mahout推荐算法实战
基于Java+Mahout的协同过滤推荐算法图书推荐系统源码+项目说明.zip 基于协同过滤的书籍推荐系统,图书推荐系统 最新版本,在原先手动计算皮尔逊相似度和评分矩阵的基础上添加了Mahout实现的协同过滤推荐算法。 ...
Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序。Mahout包含许多实现,包括聚类、分类、推荐过滤...