本文目录导读:

在Java中实现分布式KMeans聚类,通常有两种主要方式:
使用Apache Spark MLlib(推荐)
Spark提供了开箱即用的分布式KMeans实现:
import org.apache.spark.ml.clustering.KMeans;
import org.apache.spark.ml.clustering.KMeansModel;
import org.apache.spark.ml.evaluation.ClusteringEvaluator;
import org.apache.spark.ml.linalg.Vectors;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
public class SparkKMeansExample {
public static void main(String[] args) {
// 1. 创建SparkSession
SparkSession spark = SparkSession.builder()
.appName("KMeansExample")
.master("local[*]") // 分布式部署时改为yarn/mesos等
.getOrCreate();
// 2. 准备数据(示例数据)
Dataset<Row> dataset = spark.createDataFrame(
Arrays.asList(
RowFactory.create(Vectors.dense(0.0, 0.0)),
RowFactory.create(Vectors.dense(1.0, 1.0)),
RowFactory.create(Vectors.dense(9.0, 8.0)),
RowFactory.create(Vectors.dense(8.0, 9.0)),
RowFactory.create(Vectors.dense(5.0, 5.0))
),
new StructType()
.add("features", new VectorUDT(), false)
);
// 3. 训练KMeans模型
KMeans kmeans = new KMeans()
.setK(2)
.setSeed(1L)
.setMaxIter(20)
.setFeaturesCol("features")
.setPredictionCol("prediction");
KMeansModel model = kmeans.fit(dataset);
// 4. 评估模型(轮廓系数)
Dataset<Row> predictions = model.transform(dataset);
ClusteringEvaluator evaluator = new ClusteringEvaluator();
double silhouette = evaluator.evaluate(predictions);
System.out.println("轮廓系数: " + silhouette);
// 5. 获取聚类中心
Vector[] centers = model.clusterCenters();
System.out.println("聚类中心:");
for (Vector center : centers) {
System.out.println(center);
}
// 6. 预测新数据
Vector newPoint = Vectors.dense(0.5, 0.5);
int cluster = model.predict(newPoint);
System.out.println("点 " + newPoint + " 属于簇: " + cluster);
spark.stop();
}
}
Maven依赖:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.12</artifactId>
<version>3.4.0</version>
</dependency>
使用Apache Mahout(早已成熟)
Mahout与Hadoop集成,适合大规模数据:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.mahout.clustering.kmeans.KMeansDriver;
import org.apache.mahout.clustering.kmeans.Kluster;
import org.apache.mahout.common.distance.EuclideanDistanceMeasure;
import org.apache.mahout.math.VectorWritable;
public class MahoutKMeansExample {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
// 设置输入输出路径
Path input = new Path("hdfs://localhost:9000/input/data");
Path clusters = new Path("hdfs://localhost:9000/clusters");
Path output = new Path("hdfs://localhost:9000/output");
// 运行KMeans
KMeansDriver.run(conf, input, clusters, output,
new EuclideanDistanceMeasure(), // 距离度量
0.001, // 收敛阈值
10, // 最大迭代次数
true, // 是否聚类
0.0, // 聚类中心阈值
false); // 是否运行在本地
}
}
自定义分布式KMeans(基于MapReduce)
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.*;
import org.apache.hadoop.mapreduce.lib.output.*;
// Mapper类
public class KMeansMapper extends Mapper<LongWritable, Text, IntWritable, Text> {
private List<Vector> centers = new ArrayList<>();
@Override
protected void setup(Context context) {
// 从分布式缓存读取聚类中心
// 初始化centers列表
}
@Override
protected void map(LongWritable key, Text value, Context context) {
// 1. 解析数据点
Vector point = parsePoint(value.toString());
// 2. 找到最近的聚类中心
int nearestCenter = findNearestCenter(point, centers);
// 3. 输出:中心索引和点向量
context.write(new IntWritable(nearestCenter),
new Text(point.toString()));
}
}
// Reducer类
public class KMeansReducer extends Reducer<IntWritable, Text, IntWritable, Text> {
@Override
protected void reduce(IntWritable key, Iterable<Text> values, Context context) {
// 1. 计算新的聚类中心(所有点的平均值)
Vector sum = new DenseVector();
int count = 0;
for (Text value : values) {
Vector point = parsePoint(value.toString());
sum = sum.plus(point);
count++;
}
Vector newCenter = sum.divide(count);
// 2. 输出新的聚类中心
context.write(key, new Text(newCenter.toString()));
}
}
// Driver类
public class DistributedKMeansDriver {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "Distributed KMeans");
job.setJarByClass(DistributedKMeansDriver.class);
job.setMapperClass(KMeansMapper.class);
job.setReducerClass(KMeansReducer.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 迭代执行直到收敛
int iteration = 0;
do {
System.exit(job.waitForCompletion(true) ? 0 : 1);
// 更新聚类中心
// 检查是否收敛
iteration++;
} while (iteration < MAX_ITERATIONS && !converged);
}
}
性能优化建议
- 数据分区:合理设置分区数(通常是集群CPU核心数的2-3倍)
// Spark分区优化 dataset.repartition(100); // 显式设置分区数
-
缓存中间结果:
Dataset<Row> cachedData = dataset.cache(); // 或 dataset.persist(StorageLevel.MEMORY_AND_DISK());
-
选择合适的K值:
// 使用肘部法则选择K double[] costs = new double[10]; for (int k = 2; k <= 10; k++) { KMeans kmeans = new KMeans().setK(k); KMeansModel model = kmeans.fit(dataset); costs[k-2] = model.computeCost(dataset); } -
并行度设置:
spark.conf().set("spark.sql.shuffle.partitions", "200");
适用场景对比
| 方案 | 适合场景 | 优势 | 劣势 |
|---|---|---|---|
| Spark MLlib | 大数据(GB-TB级) | 内存计算,速度快,易用 | 需要Spark集群 |
| Mahout | 超大数据(TB+) | 成熟稳定,支持Hadoop | 配置复杂,较慢 |
| 自定义MR | 学习/定制化需求 | 完全可控 | 开发工作量大 |
对于大多数生产环境,推荐使用Spark MLlib,它提供了最平衡的性能、易用性和功能完整性。