当前位置:首页 > Java > 正文

Java聚类算法入门指南(手把手教你用Java实现KMeans聚类分析)

在数据科学和机器学习领域,聚类分析是一种无监督学习方法,用于将相似的数据点分组。本文将带你从零开始,使用Java语言实现最经典的聚类算法——KMeans。无论你是编程新手还是有一定经验的开发者,都能轻松上手!

Java聚类算法入门指南(手把手教你用Java实现KMeans聚类分析) Java聚类算法 机器学习聚类 Java实现KMeans 聚类分析教程 第1张

什么是聚类算法?

聚类算法的目标是将一组未标记的数据点划分为若干个“簇”(Cluster),使得同一簇内的数据点彼此相似,而不同簇之间的数据点差异较大。Java聚类算法常用于客户分群、图像分割、异常检测等场景。

其中,KMeans是最简单且广泛使用的聚类算法之一。它的核心思想是:预先设定簇的数量 K,然后通过迭代不断更新簇中心,直到收敛。

KMeans 算法步骤

  1. 随机选择 K 个初始中心点(质心)
  2. 将每个数据点分配给最近的质心,形成 K 个簇
  3. 重新计算每个簇的质心(即该簇所有点的平均值)
  4. 重复步骤 2 和 3,直到质心不再显著变化或达到最大迭代次数

用 Java 实现 KMeans 聚类

下面我们将用纯 Java 编写一个简单的 KMeans 聚类程序。为了简化,我们假设数据是二维的(x, y 坐标)。

1. 定义数据点类

public class Point {    public double x;    public double y;    public Point(double x, double y) {        this.x = x;        this.y = y;    }    // 计算到另一个点的欧氏距离    public double distance(Point other) {        double dx = this.x - other.x;        double dy = this.y - other.y;        return Math.sqrt(dx * dx + dy * dy);    }    @Override    public String toString() {        return "(" + x + ", " + y + ")";    }}

2. 实现 KMeans 聚类主类

import java.util.*;public class KMeans {    private int k; // 簇的数量    private List<Point> points; // 所有数据点    private List<Point> centroids; // 质心列表    private int maxIterations = 100;    public KMeans(int k, List<Point> points) {        this.k = k;        this.points = points;        initializeCentroids();    }    // 随机初始化质心    private void initializeCentroids() {        centroids = new ArrayList<>();        Random random = new Random();        for (int i = 0; i < k; i++) {            int index = random.nextInt(points.size());            centroids.add(new Point(points.get(index).x, points.get(index).y));        }    }    // 执行聚类    public void cluster() {        for (int iter = 0; iter < maxIterations; iter++) {            // 步骤1:为每个点分配最近的质心            Map<Point, List<Point>> clusters = new HashMap<>();            for (Point centroid : centroids) {                clusters.put(centroid, new ArrayList<>());            }            for (Point p : points) {                Point closestCentroid = findClosestCentroid(p);                clusters.get(closestCentroid).add(p);            }            // 步骤2:更新质心            boolean converged = true;            List<Point> newCentroids = new ArrayList<>();            for (Map.Entry<Point, List<Point>> entry : clusters.entrySet()) {                List<Point> clusterPoints = entry.getValue();                if (clusterPoints.isEmpty()) continue;                double sumX = 0, sumY = 0;                for (Point p : clusterPoints) {                    sumX += p.x;                    sumY += p.y;                }                Point newCentroid = new Point(sumX / clusterPoints.size(), sumY / clusterPoints.size());                newCentroids.add(newCentroid);                // 检查是否收敛                if (entry.getKey().distance(newCentroid) > 1e-4) {                    converged = false;                }            }            centroids = newCentroids;            if (converged) {                System.out.println("算法在第 " + (iter + 1) + " 次迭代后收敛。");                break;            }        }    }    // 找到离 point 最近的质心    private Point findClosestCentroid(Point point) {        Point closest = centroids.get(0);        double minDist = point.distance(closest);        for (int i = 1; i < centroids.size(); i++) {            double dist = point.distance(centroids.get(i));            if (dist < minDist) {                minDist = dist;                closest = centroids.get(i);            }        }        return closest;    }    // 打印结果    public void printClusters() {        Map<Point, List<Point>> clusters = new HashMap<>();        for (Point centroid : centroids) {            clusters.put(centroid, new ArrayList<>());        }        for (Point p : points) {            Point c = findClosestCentroid(p);            clusters.get(c).add(p);        }        int clusterId = 1;        for (Map.Entry<Point, List<Point>> entry : clusters.entrySet()) {            System.out.println("\n簇 " + clusterId + " (质心: " + entry.getKey() + "):");            for (Point p : entry.getValue()) {                System.out.println("  " + p);            }            clusterId++;        }    }}

3. 测试代码

import java.util.Arrays;public class Main {    public static void main(String[] args) {        // 创建示例数据点        List<Point> data = Arrays.asList(            new Point(1.0, 1.0),            new Point(1.5, 2.0),            new Point(3.0, 4.0),            new Point(5.0, 7.0),            new Point(3.5, 5.0),            new Point(4.5, 5.0),            new Point(3.5, 4.5)        );        // 设置 K=2,进行聚类        KMeans kmeans = new KMeans(2, data);        kmeans.cluster();        kmeans.printClusters();    }}

运行结果说明

运行上述代码后,程序会将7个点分为2个簇,并输出每个簇包含的点及其质心坐标。你可以尝试修改 K 值或添加更多数据点,观察聚类效果的变化。

总结

通过本教程,你已经掌握了如何用 Java实现KMeans 聚类算法。虽然我们使用的是二维数据,但该算法可轻松扩展到更高维度。对于更复杂的项目,建议使用成熟的机器学习库如 SmileWeka,但理解底层原理对提升你的 机器学习聚类 能力至关重要。

希望这篇 聚类分析教程 对你有所帮助!动手实践是掌握算法的关键,快去试试吧!