机器学习入门,无监督学习之K-Means聚类算法完全指南:面向Java开发者的Python实现详解

K-Means聚类算法 【sklearn.cluster】 classsklearn.cluster.KMeans(n_clusters=8,*,init='kmeans++',n_init=10,max_iter=300,tol=0.0001, precompute_distances='deprecated',verbose=0,random_state=None,copy_x=True,n_jobs='deprecated',algorithm='auto') 参数: 1、 n_clusters: 聚类类别数量,也是初始聚类中心的... 阅读详情

机器学习入门,无监督学习之K-Means聚类算法完全指南:面向Java开发者的Python实现详解

引言

作为一名Java开发者,你可能对面向对象编程和算法逻辑很熟悉,但Python在数据科学和机器学习领域的简洁语法和丰富库生态使其成为不可或缺的工具。本文将用Java开发者熟悉的视角,详细讲解K-Means聚类算法及其Python实现。

给Java开发者的Python快速参考

Java概念Python对应示例
List<String> list = new ArrayList<>()list = []colors = ['red', 'blue', 'green']
Map<String, Integer> map = new HashMap<>()dict = {}data = {'iteration': 1, 'centroids': [...]}
for (int i=0; i<10; i++)for i in range(10):循环语法
System.out.println()print()输出语句
class MyClass { ... }class MyClass:类定义

K-Means算法核心思想

K-Means是一种无监督学习算法,目标是将数据点自动分组到K个簇中。可以把它想象成:你有一堆混合的彩色球(数据点),要自动把它们按颜色分到K个篮子(簇)里,每个篮子的中心位置(质心)要能最好地代表这个篮子里的所有球。

算法步骤类比

  1. 初始化:随机选择K个球作为初始篮子中心
  2. 分配:把每个球放到离它最近的篮子中心对应的篮子
  3. 更新:根据每个篮子里的球重新计算篮子中心位置
  4. 迭代:重复2-3步,直到篮子中心不再移动

完整代码实现与详细解释

1. 环境准备和导入库

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import matplotlib.animation as animation

Java开发者需要了解的Python库:

  • numpy:相当于Java中的数值计算库,提供高效的数组操作
    • 类似:Java的Arrays + 数学计算库
  • matplotlib:绘图库,相当于Java的JFreeChart或JavaFX图表
  • sklearn:机器学习库,相当于Java的Weka或Smile
  • animation:动画库,用于创建动态可视化

2. 数据生成模块

class KMeansVisualizer:
    """K-Means聚类可视化器"""
    
    def __init__(self, n_clusters=4, max_iter=10, random_state=42):
        # 构造函数,相当于Java的构造方法
        self.n_clusters = n_clusters      # 簇的数量K
        self.max_iter = max_iter          # 最大迭代次数
        self.random_state = random_state  # 随机种子(类似Java的Random种子)
        self.colors = ['red', 'blue', 'green', 'orange', 'purple', 'brown']
    
    def generate_sample_data(self, n_samples=300, centers=4, cluster_std=0.6, random_state=0):
        """生成示例数据"""
        # make_blobs生成模拟的聚类数据
        X, y_true = make_blobs(
            n_samples=n_samples,      # 样本数量
            centers=centers,          # 中心点数量
            cluster_std=cluster_std,  # 簇的标准差(控制分散程度)
            random_state=random_state # 随机种子
        )
        
        # 数据标准化:将数据缩放到均值为0,标准差为1
        # 类似Java中:(value - mean) / std
        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X)
        
        return X_scaled, y_true, scaler

Python语法详解:

  • def __init__(self, ...)::构造函数,self相当于Java的this
  • def method_name(self, ...)::实例方法,必须包含self参数
  • 默认参数:n_samples=300 表示参数默认值,类似Java的方法重载
  • 返回多个值:Python函数可以返回多个值,用逗号分隔

3. K-Means算法核心实现

class VisualKMeans:
    """可记录迭代过程的自定义K-Means实现"""
    
    def __init__(self, n_clusters=4, max_iter=10, random_state=42):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.random_state = random_state
        self.history = []  # 记录每次迭代的状态
        
    def fit(self, X):
        """训练K-Means模型并记录迭代过程"""
        # 设置随机种子,确保结果可重现
        np.random.seed(self.random_state)
        
        # 随机初始化质心:从数据中随机选择K个点作为初始质心
        # np.random.choice 类似:从数组中随机选择不重复的元素
        centroids = X[np.random.choice(len(X), self.n_clusters, replace=False)]
        
        # 开始迭代
        for iteration in range(self.max_iter):
            # 步骤1:计算每个点到质心的距离
            # 这里使用了NumPy的广播机制,避免写循环
            distances = np.sqrt(((X - centroids[:, np.newaxis])**2).sum(axis=2))
            
            # 为每个点分配最近的质心标签
            # np.argmin 返回最小值的索引
            labels = np.argmin(distances, axis=0)
            
            # 记录当前迭代状态(用于可视化)
            self.history.append({
                'iteration': iteration + 1,
                'centroids': centroids.copy(),  # copy()防止引用问题
                'labels': labels.copy()
            })
            
            # 步骤2:更新质心位置
            # 对每个簇,计算其所有点的平均值作为新质心
            new_centroids = np.array([X[labels == i].mean(axis=0) for i in range(self.n_clusters)])
            
            # 检查收敛:如果质心基本不变,提前结束
            if np.allclose(centroids, new_centroids):
                break
                
            centroids = new_centroids  # 更新质心
            
        # 保存最终结果
        self.labels_ = labels
        self.cluster_centers_ = centroids
        return self  # 返回自身,支持链式调用

关键算法步骤的Java类比:

距离计算(Python向量化操作 vs Java循环)

Python向量化方式:

distances = np.sqrt(((X - centroids[:, np.newaxis])**2).sum(axis=2))

等价的Java伪代码:

// 假设:X是double[n][2],centroids是double[k][2]
double[][] distances = new double[k][n];
for (int i = 0; i < k; i++) {
    for (int j = 0; j < n; j++) {
        double dx = X[j][0] - centroids[i][0];
        double dy = X[j][1] - centroids[i][1];
        distances[i][j] = Math.sqrt(dx*dx + dy*dy);
    }
}
标签分配

Python方式:

labels = np.argmin(distances, axis=0)

等价的Java伪代码:

int[] labels = new int[n];
for (int j = 0; j < n; j++) {
    int minIndex = 0;
    double minDist = distances[0][j];
    for (int i = 1; i < k; i++) {
        if (distances[i][j] < minDist) {
            minDist = distances[i][j];
            minIndex = i;
        }
    }
    labels[j] = minIndex;
}
更新质心位置

Python方式:

new_centroids = np.array([X[labels == i].mean(axis=0) for i in range(self.n_clusters)])

等价的Java伪代码:

// 假设的数据结构:
// double[][] X;          // 数据矩阵 [n_samples][n_features]
// int[] labels;          // 标签数组 [n_samples]  
// int n_clusters;        // 簇的数量
// int n_samples;         // 样本总数
// int n_features;        // 特征数量

// 创建新质心数组
double[][] newCentroids = new double[n_clusters][n_features];

// 遍历每个簇
for (int clusterId = 0; clusterId < n_clusters; clusterId++) {
    
    // 统计当前簇的样本数量和特征总和
    int clusterSize = 0;
    double[] featureSums = new double[n_features];  // 存储每个特征的总和
    
    // 遍历所有样本,找出属于当前簇的样本
    for (int sampleId = 0; sampleId < n_samples; sampleId++) {
        if (labels[sampleId] == clusterId) {
            clusterSize++;
            // 累加每个特征的值
            for (int featureId = 0; featureId < n_features; featureId++) {
                featureSums[featureId] += X[sampleId][featureId];
            }
        }
    }
    
    // 计算平均值(新质心)
    if (clusterSize > 0) {
        for (int featureId = 0; featureId < n_features; featureId++) {
            newCentroids[clusterId][featureId] = featureSums[featureId] / clusterSize;
        }
    } else {
        // 处理空簇情况(没有样本属于该簇)
        // 策略1:设置为零向量
        for (int featureId = 0; featureId < n_features; featureId++) {
            newCentroids[clusterId][featureId] = 0.0;
        }
        // 策略2:随机选择一个数据点作为新质心
        // int randomIndex = (int)(Math.random() * n_samples);
        // newCentroids[clusterId] = Arrays.copyOf(X[randomIndex], n_features);
    }
}

4. 可视化模块

class KMeansPlotter:
    """K-Means结果绘图器"""
    
    def __init__(self, colors=None):
        # 如果未提供颜色,使用默认颜色列表
        self.colors = colors or ['red', 'blue', 'green', 'orange', 'purple', 'brown']
    
    def plot_iterations_grid(self, history, X, cols=3):
        """绘制迭代过程的网格图"""
        n_iterations = len(history)
        # 计算需要的行数:(总数 + 列数 - 1) // 列数
        rows = (n_iterations + cols - 1) // cols
        
        # 创建子图:plt.subplots 创建多个坐标轴
        fig, axes = plt.subplots(rows, cols, figsize=(15, 5 * rows))
        
        # 如果只有一行,调整axes的形状
        if rows == 1:
            axes = axes.reshape(1, -1)  # reshape改变数组形状
        
        # 遍历所有迭代状态
        for idx, state in enumerate(history):
            # 计算当前子图的位置
            row = idx // cols  # 整除求行
            col = idx % cols   # 取余求列
            
            # 获取对应的坐标轴对象
            ax = axes[row, col] if rows > 1 else axes[col]
            
            # 绘制单次迭代
            self._plot_single_iteration(ax, state, X, history, idx)
        
        # 隐藏多余的子图
        for idx in range(n_iterations, rows * cols):
            row = idx // cols
            col = idx % cols
            ax = axes[row, col] if rows > 1 else axes[col]
            ax.axis('off')  # 关闭坐标轴显示
        
        plt.tight_layout()  # 自动调整子图间距
        plt.show()          # 显示图形
    
    def _plot_single_iteration(self, ax, state, X, history, idx):
        """绘制单次迭代的结果"""
        iteration = state['iteration']
        centroids = state['centroids']
        labels = state['labels']
        
        # 绘制每个簇的数据点
        for cluster_id in range(len(centroids)):
            # 选择属于当前簇的所有点
            cluster_points = X[labels == cluster_id]
            
            # 绘制散点图
            ax.scatter(
                cluster_points[:, 0],   # x坐标
                cluster_points[:, 1],   # y坐标
                c=self.colors[cluster_id],  # 颜色
                alpha=0.6,              # 透明度
                s=50,                   # 点的大小
                label=f'Cluster {cluster_id+1}'  # 图例标签
            )
        
        # 绘制质心(用黑色X标记)
        ax.scatter(
            centroids[:, 0], centroids[:, 1], 
            marker='x',          # 标记形状为X
            s=200,               # 大小
            linewidths=3,        # 线宽
            color='black',       # 颜色
            label='Centroids',   # 图例标签
            zorder=5             # 绘制层级(确保在最上层)
        )
        
        # 绘制质心移动轨迹(从第二次迭代开始)
        if iteration > 1:
            prev_centroids = history[idx-1]['centroids']  # 前一次迭代的质心
            for i in range(len(centroids)):
                # 绘制从前一个位置到当前位置的线段
                ax.plot(
                    [prev_centroids[i, 0], centroids[i, 0]],  # x坐标
                    [prev_centroids[i, 1], centroids[i, 1]],  # y坐标
                    'k--',           # 黑色虚线
                    alpha=0.5,       # 透明度
                    linewidth=1      # 线宽
                )
        
        # 设置子图标题和标签
        ax.set_title(f'Iteration {iteration}')
        ax.set_xlabel('Feature 1')
        ax.set_ylabel('Feature 2')
        ax.grid(True, alpha=0.3)  # 显示网格
        
        # 只在第一个子图显示图例
        if idx == 0:
            ax.legend()

Python可视化概念解释:

  • plt.subplots():创建图形和子图,返回(figure, axes_array)
  • ax.scatter():在指定坐标轴上绘制散点图
  • ax.plot():绘制线图
  • ax.set_title() / ax.set_xlabel():设置标题和坐标轴标签
  • ax.legend():显示图例
  • plt.tight_layout():自动调整子图间距
  • plt.show():显示图形

5. 动画创建模块

def create_animation(self, history, X, figsize=(10, 8)):
    """创建K-Means聚类过程的动画"""
    # 创建图形和坐标轴
    fig, ax = plt.subplots(figsize=figsize)
    
    def animate(frame):
        """动画的每一帧"""
        ax.clear()  # 清除上一帧
        state = history[frame]  # 获取当前帧对应的状态
        iteration = state['iteration']
        centroids = state['centroids']
        labels = state['labels']
        
        # 绘制数据点(与静态图相同的逻辑)
        for cluster_id in range(len(centroids)):
            cluster_points = X[labels == cluster_id]
            ax.scatter(
                cluster_points[:, 0], cluster_points[:, 1], 
                c=self.colors[cluster_id], alpha=0.6, s=50, 
                label=f'Cluster {cluster_id+1}'
            )
        
        # 绘制质心
        ax.scatter(
            centroids[:, 0], centroids[:, 1], 
            marker='x', s=200, linewidths=3, color='black', 
            label='Centroids', zorder=5
        )
        
        # 绘制质心移动轨迹
        if iteration > 1:
            prev_centroids = history[frame-1]['centroids']
            for i in range(len(centroids)):
                ax.plot(
                    [prev_centroids[i, 0], centroids[i, 0]], 
                    [prev_centroids[i, 1], centroids[i, 1]], 
                    'k--', alpha=0.5, linewidth=2
                )
        
        # 设置标题和标签
        ax.set_title(f'K-Means Clustering - Iteration {iteration}')
        ax.set_xlabel('Feature 1')
        ax.set_ylabel('Feature 2')
        ax.grid(True, alpha=0.3)
        ax.legend()
        
        return ax
    
    # 创建动画对象
    anim = animation.FuncAnimation(
        fig,           # 图形对象
        animate,       # 动画函数
        frames=len(history),  # 总帧数
        interval=1000,        # 帧间隔(毫秒)
        repeat=True           # 循环播放
    )
    plt.close()  # 关闭图形,避免重复显示
    return anim

动画技术说明:

  • FuncAnimation:创建基于函数的动画
  • frames:动画的总帧数,等于迭代次数
  • interval:每帧之间的时间间隔(毫秒)
  • repeat:是否循环播放动画

6. 结果分析模块

class KMeansAnalyzer:
    """K-Means结果分析器"""
    
    @staticmethod
    def evaluate_clustering(X, labels, true_labels=None):
        """评估聚类结果"""
        # 计算轮廓系数:衡量聚类质量的指标
        # 取值范围[-1, 1],值越大表示聚类效果越好
        silhouette = silhouette_score(X, labels)
        
        print("聚类效果评估:")
        print(f"轮廓系数: {silhouette:.3f}")
        
        # 如果有真实标签,计算调整兰德指数
        if true_labels is not None:
            from sklearn.metrics import adjusted_rand_score
            # 调整兰德指数:比较聚类结果与真实标签的相似度
            ari = adjusted_rand_score(true_labels, labels)
            print(f"调整兰德指数: {ari:.3f}")
    
    @staticmethod
    def plot_convergence(history, X):
        """绘制收敛曲线"""
        inertia_history = []
        # 计算每次迭代的簇内平方和
        for state in history:
            centroids = state['centroids']
            labels = state['labels']
            inertia = KMeansAnalyzer._calculate_inertia(X, centroids, labels)
            inertia_history.append(inertia)
        
        # 绘制收敛曲线
        plt.figure(figsize=(10, 4))
        plt.plot(range(1, len(inertia_history)+1), inertia_history, 'go-')
        plt.xlabel('Iteration')
        plt.ylabel('Inertia')
        plt.title('Inertia Convergence')
        plt.grid(True, alpha=0.3)
        plt.show()
    
    @staticmethod
    def _calculate_inertia(X, centroids, labels):
        """计算簇内平方和(每个点到其质心距离的平方和)"""
        inertia = 0
        for i in range(len(centroids)):
            cluster_points = X[labels == i]
            if len(cluster_points) > 0:
                # 计算该簇所有点到质心的距离平方和
                inertia += np.sum((cluster_points - centroids[i])**2)
        return inertia

评估指标说明:

  • 轮廓系数 (Silhouette Score):衡量聚类紧密度和分离度的综合指标
  • 调整兰德指数 (Adjusted Rand Index):比较聚类结果与真实标签的一致性
  • 簇内平方和 (Inertia):每个点到其所属质心距离的平方和,值越小表示聚类越紧密

7. 完整流程演示

def main():
    """主函数 - 演示完整的K-Means聚类可视化流程"""
    
    # 1. 初始化各个组件
    visualizer = KMeansVisualizer(n_clusters=4, max_iter=10, random_state=42)
    plotter = KMeansPlotter()
    analyzer = KMeansAnalyzer()
    
    # 2. 生成示例数据
    X_scaled, y_true, scaler = visualizer.generate_sample_data()
    print(f"生成数据形状: {X_scaled.shape}")
    
    # 3. 训练K-Means模型
    vkmeans = visualizer.create_custom_kmeans()
    vkmeans.fit(X_scaled)
    
    # 4. 可视化迭代过程
    print("正在生成迭代过程可视化...")
    plotter.plot_iterations_grid(vkmeans.history, X_scaled)
    
    # 5. 创建动画
    print("正在生成动画...")
    anim = plotter.create_animation(vkmeans.history, X_scaled)
    
    # 6. 评估聚类结果
    analyzer.evaluate_clustering(X_scaled, vkmeans.labels_, y_true)
    analyzer.plot_convergence(vkmeans.history, X_scaled)
    
    # 7. 输出摘要信息
    print(f"\n聚类过程摘要:")
    print(f"总共进行了 {len(vkmeans.history)} 次迭代")
    print(f"最终轮廓系数: {silhouette_score(X_scaled, vkmeans.labels_):.3f}")
    
    return vkmeans, anim

# 运行主程序
if __name__ == "__main__":
    kmeans_model, animation_obj = main()

K-Means算法的重要特性

优点(从Java开发者角度)

  1. 算法简单:逻辑清晰,易于理解和实现
  2. 计算高效:时间复杂度为O(n×K×I×d),适合处理大数据集
  3. 可解释性强:聚类结果直观易懂
  4. 广泛应用:客户分群、图像分割、异常检测等

局限性和应对策略

  1. 需要预设K值 → 使用肘部法则选择最佳K值
  2. 对初始值敏感 → 多次运行取最优结果
  3. 对异常值敏感 → 数据预处理时处理异常值
  4. 只能发现球状簇 → 对于复杂形状使用其他算法

如何选择K值(肘部法则)

def find_optimal_k(X, max_k=10):
    """使用肘部法则寻找最佳K值"""
    inertias = []
    k_range = range(1, max_k+1)
    
    for k in k_range:
        kmeans = KMeans(n_clusters=k, random_state=42)
        kmeans.fit(X)
        inertias.append(kmeans.inertia_)  # 簇内平方和
    
    # 绘制肘部图:寻找拐点
    plt.plot(k_range, inertias, 'bo-')
    plt.xlabel('Number of Clusters (K)')
    plt.ylabel('Inertia')
    plt.title('Elbow Method for Optimal K')
    plt.show()

实际应用场景

  1. 客户细分:根据购买行为将客户分组,制定精准营销策略
  2. 文档分类:自动将文档按主题分组
  3. 图像压缩:将相似颜色的像素分组,减少颜色数量
  4. 异常检测:识别与其他数据点显著不同的点
  5. 社交网络分析:发现社区结构

给Java开发者的学习建议

Python与Java的主要差异

  1. 语法简洁:Python使用缩进代替大括号,代码更简洁
  2. 动态类型:变量不需要声明类型
  3. 丰富的科学计算库:NumPy、Pandas、Matplotlib等
  4. 交互式开发:Jupyter Notebook支持即时反馈

下一步学习路径

  1. 掌握NumPy:Python的数值计算基础
  2. 学习Pandas:数据处理和分析
  3. 了解Scikit-learn:机器学习算法库
  4. 实践项目:从简单分类、回归问题开始

总结

通过本文的详细讲解,作为Java开发者的你应该已经:

  1. 理解了K-Means算法的核心原理和实现步骤
  2. 掌握了Python在数据科学中的基本语法和库使用
  3. 学会了如何可视化机器学习算法的迭代过程
  4. 了解了聚类结果的评估方法和质量指标

K-Means算法虽然简单,但它体现了机器学习中的许多重要概念:迭代优化、距离度量、聚类评估等。掌握这个算法为你学习更复杂的机器学习技术奠定了坚实基础。

记住,从Java转向Python最重要的是理解两者的思维差异:Java强调严谨的类型系统和面向对象设计,Python更注重开发效率和简洁表达。在数据科学领域,Python的简洁语法和丰富库生态确实提供了巨大优势。

附:聚类效果

在这里插入图片描述

附:完整的kmeans测试代码示例

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import matplotlib.animation as animation
from IPython.display import HTML


class KMeansVisualizer:
    """K-Means聚类可视化器"""

    def __init__(self, n_clusters=4, max_iter=10, random_state=42):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.random_state = random_state
        self.colors = ['red', 'blue', 'green', 'orange', 'purple', 'brown']

    def generate_sample_data(self, n_samples=300, centers=4, cluster_std=0.6, random_state=0):
        """生成示例数据"""
        X, y_true = make_blobs(
            n_samples=n_samples,
            centers=centers,
            cluster_std=cluster_std,
            random_state=random_state
        )

        # 数据标准化
        scaler = StandardScaler()
        X_scaled = scaler.fit_transform(X)

        return X_scaled, y_true, scaler

    def create_custom_kmeans(self):
        """创建自定义K-Means实例"""
        return VisualKMeans(
            n_clusters=self.n_clusters,
            max_iter=self.max_iter,
            random_state=self.random_state
        )


class VisualKMeans:
    """可记录迭代过程的自定义K-Means实现"""

    def __init__(self, n_clusters=4, max_iter=10, random_state=42):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.random_state = random_state
        self.history = []  # 记录每次迭代的状态

    def fit(self, X):
        """训练K-Means模型并记录迭代过程"""
        np.random.seed(self.random_state)

        # 随机初始化质心
        centroids = X[np.random.choice(len(X), self.n_clusters, replace=False)]

        for iteration in range(self.max_iter):
            # 计算每个点到质心的距离
            distances = np.sqrt(((X - centroids[:, np.newaxis]) ** 2).sum(axis=2))
            labels = np.argmin(distances, axis=0)

            # 记录当前状态
            self.history.append({
                'iteration': iteration + 1,
                'centroids': centroids.copy(),
                'labels': labels.copy()
            })

            # 更新质心
            new_centroids = np.array([X[labels == i].mean(axis=0) for i in range(self.n_clusters)])

            # 检查收敛
            if np.allclose(centroids, new_centroids):
                break

            centroids = new_centroids

        self.labels_ = labels
        self.cluster_centers_ = centroids
        return self


class KMeansPlotter:
    """K-Means结果绘图器"""

    def __init__(self, colors=None):
        self.colors = colors or ['red', 'blue', 'green', 'orange', 'purple', 'brown']

    def plot_iterations_grid(self, history, X, cols=3):
        """绘制迭代过程的网格图"""
        n_iterations = len(history)
        rows = (n_iterations + cols - 1) // cols

        fig, axes = plt.subplots(rows, cols, figsize=(15, 5 * rows))
        if rows == 1:
            axes = axes.reshape(1, -1)

        for idx, state in enumerate(history):
            row = idx // cols
            col = idx % cols

            ax = axes[row, col] if rows > 1 else axes[col]
            self._plot_single_iteration(ax, state, X, history, idx)

        # 隐藏多余的子图
        for idx in range(n_iterations, rows * cols):
            row = idx // cols
            col = idx % cols
            ax = axes[row, col] if rows > 1 else axes[col]
            ax.axis('off')

        plt.tight_layout()
        plt.show()

    def _plot_single_iteration(self, ax, state, X, history, idx):
        """绘制单次迭代的结果"""
        iteration = state['iteration']
        centroids = state['centroids']
        labels = state['labels']

        # 绘制数据点
        for cluster_id in range(len(centroids)):
            cluster_points = X[labels == cluster_id]
            ax.scatter(
                cluster_points[:, 0], cluster_points[:, 1],
                c=self.colors[cluster_id], alpha=0.6, s=50,
                label=f'Cluster {cluster_id + 1}'
            )

        # 绘制质心
        ax.scatter(
            centroids[:, 0], centroids[:, 1],
            marker='x', s=200, linewidths=3, color='black',
            label='Centroids', zorder=5
        )

        # 绘制质心移动轨迹(如果不是第一次迭代)
        if iteration > 1:
            prev_centroids = history[idx - 1]['centroids']
            for i in range(len(centroids)):
                ax.plot(
                    [prev_centroids[i, 0], centroids[i, 0]],
                    [prev_centroids[i, 1], centroids[i, 1]],
                    'k--', alpha=0.5, linewidth=1
                )

        ax.set_title(f'Iteration {iteration}')
        ax.set_xlabel('Feature 1')
        ax.set_ylabel('Feature 2')
        ax.grid(True, alpha=0.3)

        # 只在第一个子图显示图例
        if idx == 0:
            ax.legend()

    def create_animation(self, history, X, figsize=(10, 8)):
        """创建K-Means聚类过程的动画"""
        fig, ax = plt.subplots(figsize=figsize)

        def animate(frame):
            ax.clear()
            state = history[frame]
            iteration = state['iteration']
            centroids = state['centroids']
            labels = state['labels']

            # 绘制数据点
            for cluster_id in range(len(centroids)):
                cluster_points = X[labels == cluster_id]
                ax.scatter(
                    cluster_points[:, 0], cluster_points[:, 1],
                    c=self.colors[cluster_id], alpha=0.6, s=50,
                    label=f'Cluster {cluster_id + 1}'
                )

            # 绘制质心
            ax.scatter(
                centroids[:, 0], centroids[:, 1],
                marker='x', s=200, linewidths=3, color='black',
                label='Centroids', zorder=5
            )

            # 绘制质心移动轨迹
            if iteration > 1:
                prev_centroids = history[frame - 1]['centroids']
                for i in range(len(centroids)):
                    ax.plot(
                        [prev_centroids[i, 0], centroids[i, 0]],
                        [prev_centroids[i, 1], centroids[i, 1]],
                        'k--', alpha=0.5, linewidth=2
                    )

            ax.set_title(f'K-Means Clustering - Iteration {iteration}')
            ax.set_xlabel('Feature 1')
            ax.set_ylabel('Feature 2')
            ax.grid(True, alpha=0.3)
            ax.legend()

            return ax

        anim = animation.FuncAnimation(
            fig, animate, frames=len(history),
            interval=1000, repeat=True
        )
        plt.close()
        return anim


class KMeansAnalyzer:
    """K-Means结果分析器"""

    @staticmethod
    def evaluate_clustering(X, labels, true_labels=None):
        """评估聚类结果"""
        silhouette = silhouette_score(X, labels)

        print("聚类效果评估:")
        print(f"轮廓系数: {silhouette:.3f}")

        if true_labels is not None:
            from sklearn.metrics import adjusted_rand_score
            ari = adjusted_rand_score(true_labels, labels)
            print(f"调整兰德指数: {ari:.3f}")

    @staticmethod
    def plot_convergence(history, X):
        """绘制收敛曲线"""
        inertia_history = []
        for state in history:
            centroids = state['centroids']
            labels = state['labels']
            inertia = KMeansAnalyzer._calculate_inertia(X, centroids, labels)
            inertia_history.append(inertia)

        plt.figure(figsize=(10, 4))
        plt.plot(range(1, len(inertia_history) + 1), inertia_history, 'go-')
        plt.xlabel('Iteration')
        plt.ylabel('Inertia')
        plt.title('Inertia Convergence')
        plt.grid(True, alpha=0.3)
        plt.show()

    @staticmethod
    def _calculate_inertia(X, centroids, labels):
        """计算簇内平方和"""
        inertia = 0
        for i in range(len(centroids)):
            cluster_points = X[labels == i]
            if len(cluster_points) > 0:
                inertia += np.sum((cluster_points - centroids[i]) ** 2)
        return inertia


def main():
    """主函数 - 演示完整的K-Means聚类可视化流程"""

    # 1. 初始化组件
    visualizer = KMeansVisualizer(n_clusters=4, max_iter=10, random_state=42)
    plotter = KMeansPlotter()
    analyzer = KMeansAnalyzer()

    # 2. 生成数据
    X_scaled, y_true, scaler = visualizer.generate_sample_data()
    print(f"生成数据形状: {X_scaled.shape}")

    # 3. 训练K-Means模型
    vkmeans = visualizer.create_custom_kmeans()
    vkmeans.fit(X_scaled)

    # 4. 可视化迭代过程
    print(f"正在生成迭代过程可视化... 迭代次数{len(vkmeans.history)}")
    plotter.plot_iterations_grid(vkmeans.history, X_scaled)

    # 5. 创建动画
    print("正在生成动画...")
    anim = plotter.create_animation(vkmeans.history, X_scaled)

    # 6. 评估结果
    analyzer.evaluate_clustering(X_scaled, vkmeans.labels_, y_true)
    analyzer.plot_convergence(vkmeans.history, X_scaled)

    # 7. 输出摘要
    print(f"\n聚类过程摘要:")
    print(f"总共进行了 {len(vkmeans.history)} 次迭代")
    print(f"最终轮廓系数: {silhouette_score(X_scaled, vkmeans.labels_):.3f}")

    # 可选:保存动画
    anim.save('kmeans_process.gif', writer='pillow', fps=1)
    print("动画已保存为 kmeans_process.gif")

    return vkmeans, anim


if __name__ == "__main__":
    # 运行主程序
    kmeans_model, animation_obj = main()

    # 在Jupyter中显示动画(取消注释以使用)
    html5_video = HTML(animation_obj.to_html5_video())
    print("html5_video \n",html5_video)
K-means算法的python实现及可视化 1.前言最近在实现 K-means 算法的过程中,选用了 python 作为编程语言,由于平常编程用 java 比较多一点,所以对 python 并不是很熟,这就直接导致了后面踩了不少坑。也在网上参考了一些别人的代码,但很多代码都有一些小 bug,不能直接运行。折腾了一天,总算用 python 实现了 K-means 算法,记录下来,希望能给其它初学者提供一个参考,大神请无视。2.K-means算... 阅读详情

相关推荐

机器学习:k-means算法

描述算法接受参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过

程序人生-汤圆甜筒的专栏 1060

kmeans算法_KMeans聚类算法详解

1. 写在前面如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,常见的机器学习算法:监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法Adaboost等无监督算法:聚类,降维,关联规则, PageRank等为了详细的理解这些原理,曾经看过西瓜书,统计学习方法,机器学习实战等书,也听过一些机器学习的课程,但总感觉话语里比较深奥,读起...

weixin_39824223的博客 5204

【机器学习】word2vec词向量,相似词,近义词,k-means文本聚类预处理,python

使用K-means做词聚类需要用到word2vec做词向量化预处理。 #@Author : LinYimeng 代码传送门: # -*- coding: utf-8 -*- #@Author : LinYimeng import multiprocessing import gensim from gensim.test.utils import common_texts, ge...

HelenLee 3405

基于 AutoEncoder 的无监督聚类的实现

原文:How to do Unsupervised Clustering with Keras 作者:Chengwei Zhang 鉴于深度学习出色的非线性表征能力,其被普遍用于进行从输入到给定标签数据集的输出的映射,即:图像分类,需要有人工标注标签的数据集.但是,不管是对 XRay 图像的标注,还是对新闻报道的主题的标注,都依赖于人工进行,尤其是针对大规模数据集,其工作量很大,费时费力. ...

薰衣草PK向日葵的博客 1万+

Java实现k-means文本聚类算法详解

k-means算法是一种广泛应用于聚类分析的无监督学习算法。它将数据集中的样本划分为k个子集(即k个“簇”),使得每个样本点所属的簇能最大程度地减少其与簇中心点的距离之和。向量化方法在文本聚类中扮演着至关重要的角色。词袋模型、TF-IDF和词嵌入各有优劣,而它们的选择依赖于具体的任务需求和上下文。词袋模型的简单性使其在很多情况下仍然非常有用,尽管它会丢失词序信息。TF-IDF弥补了这一缺陷,通过考虑词在文档和整个文档集合中的重要性,提高了模型在文档分析任务中的表现。

weixin_32869687的博客 1063

从零到一:手把手教你用Verilog在FPGA上构建自己的FFT处理器(不依赖IP核)

本文详细介绍了如何从零开始使用Verilog在FPGA上构建256点FFT处理器,不依赖商业IP核。内容涵盖FFT算法基础、蝶形运算单元设计、整体架构实现、时序收敛优化策略以及测试验证方法,重点提升FPGA上算法部署的运行效率与资源利用率。

vv45678的博客 823

KMeans算法( 聚类分析)

数据集在文末链接。也可留邮箱,看到会及时以邮件形式发送. 1 聚类分析相关概念 1.1 聚类与分类 分类其实是从特定的数据中挖掘模式,作出判断的过程。比如Gmail邮箱里有垃圾邮件分类器,一开始的时候可能什么都不过滤,在日常使用过程中,我人工对于每一封邮件点选“垃圾”或“不是垃圾”,过一段时间,Gmail就体现出一定的智能,能够自动过滤掉一些垃圾邮件了。这是因为在点选的过程中,...

郑德帅 2万+

警惕!数字签名伪造的攻防演练:用SigThief克隆证书的真实测试(Windows环境)

本文深入探讨了Windows环境下数字签名伪造的攻防技术,详细介绍了使用SigThief工具进行签名提取与克隆的实战步骤。通过真实测试案例,揭示了数字签名安全机制的潜在漏洞,并提供了有效的防御策略和检测技术,帮助安全人员提升系统防护能力。

weixin_42530732的博客 410

机器学习入门,无监督学习之DBSCAN聚类算法完全指南:面向Java开发者的Python实现详解

本文介绍了DBSCAN聚类算法的核心概念、实现原理及Python实现。DBSCAN是一种基于密度的聚类算法,相比K-Means具有无需预设簇数、能识别任意形状簇和噪声点等优势。文章通过Java类比解释了核心概念(如核心点、边界点等),详细讲解了算法步骤,并提供了完整的Python实现代码,包括自定义DBSCAN类和可视化示例。该实现采用广度优先搜索策略扩展簇,适合Java开发者理解Python实现方式。

明月专栏 778

2025-08-19利用opencv检测图片中文字及图片的坐标

该代码实现了图片中中文文本的检测与位置计算功能。主要使用CnOcr库进行OCR识别,结合OpenCV和PIL库处理图像。功能包括:1)检测图片中的中文文本及位置坐标;2)计算每个文本的像素宽度和高度;3)在原图上绘制文本边界框并标注识别结果。代码通过put_chinese_text函数解决中文显示问题,最终输出各文本内容、坐标位置及尺寸信息。适用于需要获取图片中文字精确位置的应用场景。

m0_58149406的博客 397

ESP32-P4 GPIO双域架构:HP/LP分离、寄存器级控制与低功耗工程实践

GPIO(通用输入输出)是嵌入式系统中最基础且高频使用的硬件接口,其性能、灵活性与功耗特性直接影响产品实时性与续航能力。现代高性能MCU如ESP32-P4通过HP/LP双电源域分离、可编程IO复用(IO MUX)、信号交换矩阵及硬件自动化机制(如ETM事件、Sigma-Delta调制),将传统GPIO升级为兼具模拟能力、亚微秒响应与微安级待机的智能外设子系统。其核心价值在于实现引脚功能动态重构、中断零延迟处理、Deep-sleep期间自主唤醒与多核安全操作。典型应用场景涵盖工业PLC信号采集、电池供电传感器

weixin_35949153的博客 101

【机器学习】聚类算法——K-Means算法(理论+图解)

  简 介:下面是我在学习时候的记录并加上自己的理解。本文意在记录自己近期学习过程中的所学所得,如有错误,欢迎大家指正。   关键词:Python、机器学习、K-Means聚类 一、K-Means聚类 其实它是一种基于质心的聚类,为什么这么说呢?因为它的设计思想就是从总样本中找到几个标志性的数据,将其定为每个簇的数据中心,然后分别判断每个数据的距离状况,然后进行更新每个簇内的质心。 对于样本集 D={x1,x2...xn}D=\{x_1,x_2...x_n\}D={x1​,x2​..

优快云 精品推荐 1万+

适合小白的超详细yolov8环境配置+实例运行教程,从零开始教你如何使用yolov8训练自己的数据集(Windows+conda+pycharm)

yolov8源码下载地址:GitHub - ultralytics/ultralytics: NEW - YOLOv8 🚀 in PyTorch > ONNX > OpenVINO > CoreML > TFLiteyolov8使用文档: Home - Ultralytics YOLOv8 Docs教学视频参考: 包会!YOLOv8训练自己的数据集_哔哩哔哩_bilibili先用conda prompt创建一个虚拟环境,我的叫yolov8, python版本用3.10 源码地址:GitHub - ul

CV在读 25万+

【机器学习】简单易懂的聚类算法K-Means

K-Means 是一种广泛使用的聚类算法,其目的是将数据集分成 K 个聚类,其中每个数据点都属于最近的均值(质心)所代表的聚类。K-Means 算法的执行过程简单明了,但也有一些局限性。

wendao76的专栏 2342

基于Zynq的便携式γ能谱仪设计与实现

数字多道分析器(DMCA)是核辐射能谱测量的核心设备,其本质是将探测器输出的模拟脉冲按幅度数字化分道,构建能量-计数分布曲线,从而支撑放射性核素识别与定量分析。其技术原理依赖高速采样、数字滤波成形、基线恢复与峰值提取等关键信号处理环节,具备高分辨率、优线性度与强抗干扰能力。在环境监测、核安全巡检与野外地质勘探等场景中,对低功耗、小型化、实时性提出严苛要求。本方案采用Xilinx Zynq-7020异构SoC架构,融合FPGA实时数字信号处理与ARM嵌入式控制,实现软硬协同优化,兼顾算法精度与工程落地性,为国

weixin_33173126的博客 192

Cadence Virtuoso里搞定斩波运放AC仿真:PSS+PAC保姆级设置避坑指南

然而,当工程师们尝试在Cadence Virtuoso环境中对这种特殊结构进行AC特性分析时,往往会遇到传统AC仿真方法失效的困境——这正是PSS(Periodic Steady-State)与PAC(Periodic AC)仿真组合大显身手的场景。实际项目中我们常采用"先快后精"的两步法:先用快速设置确认大体趋势,再针对关键频段进行精细仿真。例如某次设计迭代中,将harmonics从10增加到20后,发现在1MHz处的相位裕度估算误差达15°,这解释了之前样机测试时的振荡问题。

weixin_33508411的博客 453

深入剖析机器学习K-means聚类算法:原理、应用场景与优化技巧详解

K-means 聚类算法是机器学习中的基本算法之一,具有简单、高效和易于实现的特点。其数学基础主要包括距离度量、聚类中心更新以及目标函数优化。通过选择合适的 K 值、优化初始化方法、并行化处理和内存优化,可以显著提高 K-means 算法的性能和效果。本文详细介绍了 K-means 聚类算法的原理、应用、优化方法,并通过具体的示例代码帮助初学者理解和实现这一算法。希望本文能帮助你掌握 K-means 聚类算法,并应用到实际的数据分析和机器学习任务中。

科技改变人类,技术成就未来 2727

Python实现多元线性回归(Multiple Linear Regression)

多元线性回归是一种用于建立多个自变量与因变量之间关系的统计学方法。在多元线性回归中,我们可以通过多个自变量来预测一个因变量的值。每个自变量对因变量的影响可以用回归系数来表示。

优快云 精品推荐 4333

【机器学习篇】K-Means 算法详解:从理论到实践的全面解析

K-Means 是一种广泛应用的聚类算法,用于将数据集分组为 K 个簇。由于其简单易懂和高效的计算性能,K-Means 被广泛应用于各种数据分析任务中。本文将详细讲解 K-Means 的基本算法原理,使用步骤,并通过代码示例展示其在 Python 中的实现,帮助读者更好地理解和应用该算法。K-Means 算法是一个简单且高效的聚类方法,适用于各种数据分析任务。通过本文的讲解与示例,读者应该能够掌握 K-Means 的基本原理,并通过 Python 实现聚类分析。

记录从移动端、鸿蒙到物联网与AI的完整实战与思考。专注于分享可复用的工程方案与产品思维,尤其深入鸿蒙生态与全栈开发。以十年工程与管理沉淀,为你提供清晰的技术纵深路径。坚信技术的价值在于“贯通”与“落地”。 1762

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

K-Means和Fuzzy C-Means聚类算法原理以及python代码实现

K-Means和Fuzzy C-Means聚类算法原理以及python代码实现1.K-Means聚类1、原理2、python实现2.Fuzzy C-Means聚类1、原理2、python实现 1.K-Means聚类 1、原理 K-Means算法原理       网上有很多关于K-Means算法的原理,当然通过阅读《统计学习方法》也可以知道K-Means聚类的原理,我这个比较懒,在这里就不打出详细的原理了,具体的可以参考上面的链接,写的很详细。   &nbsp

weixin_44424296的博客 1882

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【机器学习】利用numpy实现K-Means聚类算法(Python代码)

  简 介:下面是我在学习时候的记录并加上自己的理解。本文意在记录自己近期学习过程中的所学所得,如有错误,欢迎大家指正。   关键词:Python、机器学习、K-Means聚类 """ * Created with PyCharm * 作者: 阿光 * 日期: 2021/7/18 * 时间: 14:19 * 描述: 利用numpy自己实现K-Means算法 """ import random import warnings import numpy as np imp..

优快云 精品推荐 1632

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

自然语言处理系列五十四》文本聚类算法》K-means文本聚类算法原理

【配套新书教材】《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】新书特色:本书从自然语言处理基础开始,逐步深入各种NLP热点前沿技术,使用了Java和Python两门语言精心编排了大量代码实例,契合公司实际工作场景技能,侧重实战。

weixin_52610848的博客 1684

python租房大数据分析系统 聚类算法 分类算法 K-means聚类算法 线性回归预测算法 机器学习 链家租房网 Django框架 scrapy 爬虫(源码)✅

python租房大数据分析系统 聚类算法 分类算法 K-means聚类算法 线性回归预测算法 机器学习 链家租房网 Django框架 scrapy 爬虫(源码)✅

vx_biyesheji0001的博客 808

用java实现K-均值聚类(k-means)

首先大家了解一下什么是K-均值聚类,如下: K均值聚类算法是先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚...

luohualiushui1的专栏 1523

无监督学习与生成式人工智能(MEAP)(一)

世界上只有模式,模式之上的模式,影响其他模式的模式。被模式隐藏的模式。模式中的模式。” - 查克·帕拉尼克我们喜欢模式。无论是我们的业务还是我们的生活,我们都会发现模式,并且(通常)倾向于坚持它们。我们有自己喜欢购买的杂货,使用的电信运营商和通话套餐,关注的新闻文章,电影类型和音轨 - 这些都是我们喜好模式的例子。我们热爱模式,而且比模式更喜欢的是发现它们,整理它们,也许习惯于它们!有一句俗语流传开来 - “数据是新的电力”。数据确实宝贵;这一点无人能否认。但纯粹的数据将毫无用处。

龙哥盟 1510
上一篇: AIGC入门,手搓大模型客户端与MCP交互第2集
下一篇: 机器学习入门,无监督学习之DBSCAN聚类算法完全指南:面向Java开发者的Python实现详解
明月(Alioo)
博客等级 码龄18年 267粉丝 207原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月(Alioo)

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值