简单基于MapReduce实现了下KMeans。
算法思路
KMeans算法作为一种划分式的聚类算法,利用MapReduce进行实现的主要难点在于满足KMeans每次迭代划分过程的中间结果保存。
因此利用HDFS进行中心点的存储,以实现各节点间的数据共享。
基于MapReduce的KMeans算法流程如下:
- 随机分配簇,初始化中心点,存入HDFS。
- Mapper中读取数据文件中的每条数据并与中心点进行距离计算,输出key为最近的中心点序号。
- Reducer中进行归并,计算新的中心点,存入新的中心文件。
- 判断停机条件,不满足则复制新的中心文件到原中心文件,重复2,3步骤。
- 输出聚类结果,包括数据点信息与对应簇序号。
初始化中心点
利用Mapper读取每一个元素的向量信息,随机赋值,在Reducer中计算中心点信息。由于中心点的计算与迭代时的计算相同,与迭代计算共用一个reducer类。
CenterRandomMapper
随机赋值的mapper类。
protected void setup(Context context) throws IOException, InterruptedException {
// 读取k值
Configuration configuration = context.getConfiguration();;
k = configuration.getInt("cluster.k", 3);
}
setup中读取配置的聚类簇数量。
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
// 随机分配簇
int index = (int) (Math.random() * k);
System.out.println(index);
context.write(new Text(Integer.toString(index)), value);
}
map方法中,根据聚类簇数量,对每个元素赋予随机的类簇序号,作为输出的key。value为元素向量,保持不变。
CenterRandomAdapter
初始化中心点的任务配置类,实现一个static方法。
public static void createRandomCenter(String dataPath, String centerPath, int k){
Configuration hadoopConfig = new Configuration();
hadoopConfig.setInt("cluster.k", k);
try {
Job job = Job.getInstance(hadoopConfig, "random center task");
job.setJarByClass(KmeansRun.class);
job.setMapperClass(CenterRandomMapper.class);
job.setReducerClass(KmeansReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
// 输出为新计算得到的center,已存在则删除
Path outPath = new Path(centerPath);
outPath.getFileSystem(hadoopConfig).delete(outPath, true);
//job执行作业时输入和输出文件的路径
FileInputFormat.addInputPath(job, new Path(dataPath));
FileOutputFormat.setOutputPath(job, new Path(centerPath));
//执行job,直到完成
job.waitForCompletion(true);
System.out.println("random center task");
}
}
该方法包含三个参数,分别为数据文件地址,中心点文件地址以及聚类数。首先在配置中设置聚类数,方便mapper中进行读取。
设置对应的mapper和reducer类以及输入输出格式,需要注意的是reducer使用了KmeansReducer类,即正式迭代时计算中心点的reducer。由于HDFS不能直接进行同名文件的覆盖,所以在每次生成新的中心点文件时,需要判断是否已经存在同名文件,存在则删除。
分配元素对应簇,计算中心点
利用Mapper将所有元素与中心点进行对比,分配到最近的簇中。利用Reducer进行求和并计算新的中心点信息。
KmeansMapper
private ArrayList<ArrayList<Double>> centers = null;
@Override
protected void setup(Context context) throws IOExceptio