全面了解機器語言之kmeans

深入理解 KMeans 聚類算法:原理、實現與應用

在機器學習領域,聚類算法作為無監督學習的核心技術之一,一直以來都是數據挖掘和模式識別的重要工具。其中,KMeans 算法以其簡潔的原理、高效的計算性能和廣泛的適用性,成為最受歡迎的聚類算法之一。本文將從算法原理、數學推導、實現細節到實際應用,全方位解析 KMeans 算法,并通過完整的代碼案例幫助讀者深入理解其工作機制。

一、聚類算法與 KMeans 的定位

聚類算法的核心目標是將數據集中具有相似特征的樣本自動劃分到同一類別,使得類內相似度最大化類間相似度最小化。與監督學習不同,聚類不需要預先標注的訓練數據,而是通過數據本身的分布特征發現潛在的結構模式。這種特性使其在客戶分群、異常檢測、圖像分割等無標簽數據場景中發揮著不可替代的作用。

在眾多聚類算法中,KMeans 占據著舉足輕重的地位。自 1967 年由 MacQueen 提出以來,經過半個多世紀的發展,其衍生出了 KMeans++、MiniBatchKMeans 等改進版本,但核心思想始終保持一致。據 KDnuggets 2023 年的調研數據顯示,KMeans 在工業界的使用率高達 68%,遠超層次聚類(23%)和 DBSCAN(19%)等其他算法,這與其簡單易懂、計算高效、可擴展性強的特點密不可分。

二、KMeans 算法的核心原理

2.1 基本思想

KMeans 算法的工作流程可以概括為 "初始化 - 分配 - 更新" 的迭代過程:

  1. 確定聚類數量 K:用戶需要預先指定將數據劃分為 K 個類別
  1. 初始化質心:從數據集中隨機選擇 K 個樣本作為初始聚類中心(質心)
  1. 分配樣本:計算每個樣本與 K 個質心的距離,將樣本分配到距離最近的質心所在的簇
  1. 更新質心:計算每個簇內所有樣本的均值,將其作為新的質心
  1. 迭代優化:重復步驟 3 和 4,直到質心位置不再顯著變化或達到最大迭代次數

這種基于原型的聚類方法通過不斷調整質心位置,最終使得各樣本到其所屬質心的距離之和最小化。

2.2 距離度量方式

KMeans 算法的性能很大程度上依賴于距離度量的選擇,常用的距離計算方式包括:

  1. 歐氏距離(Euclidean Distance):最常用的距離度量方式,適用于連續型數據,計算兩個 n 維向量\(X=(x_1,x_2,...,x_n)\)和\(Y=(y_1,y_2,...,y_n)\)之間的直線距離:

\(d(X,Y)=\sqrt{\sum_{i=1}^{n}(x_i-y_i)^2}\)

  1. 曼哈頓距離(Manhattan Distance):適用于高維數據或需要降低計算復雜度的場景,計算各維度差值的絕對值之和:

\(d(X,Y)=\sum_{i=1}^{n}|x_i-y_i|\)

  1. 余弦相似度(Cosine Similarity):常用于文本聚類等特征向量稀疏的場景,通過向量夾角的余弦值衡量相似度:

\(cos\theta=\frac{X\cdot Y}{||X||\cdot||Y||}=\frac{\sum_{i=1}^{n}x_iy_i}{\sqrt{\sum_{i=1}^{n}x_i^2}\sqrt{\sum_{i=1}^{n}y_i^2}}\)

在默認情況下,KMeans 算法采用歐氏距離作為度量標準,這也是大多數場景下的最優選擇。

2.3 目標函數與收斂性

KMeans 算法的優化目標是最小化所有樣本到其所屬質心的距離平方和(Sum of Squared Errors,SSE),目標函數定義為:

\(J=\sum_{k=1}^{K}\sum_{x_i\in C_k}||x_i-\mu_k||^2\)

其中,\(C_k\)表示第 k 個簇,\(\mu_k\)是第 k 個簇的質心,\(||x_i-\mu_k||^2\)是樣本\(x_i\)到質心\(\mu_k\)的歐氏距離平方。

由于目標函數\(J\)是非凸函數,算法可能收斂到局部最優解而非全局最優解。這也是為什么 KMeans 算法通常需要多次運行并選擇 SSE 最小的結果作為最終聚類方案的原因。理論上,隨著迭代次數的增加,\(J\)的值會單調遞減并最終收斂,因為每次更新質心時都會選擇使\(J\)最小化的新質心位置。

三、KMeans 算法的優缺點分析

3.1 優點

  1. 計算效率高:時間復雜度約為\(O(nkt)\),其中 n 是樣本數量,k 是聚類數,t 是迭代次數,通常遠小于 n,適合大規模數據集
  1. 實現簡單:核心邏輯僅包含距離計算和均值更新,易于理解和實現
  1. 可擴展性強:能夠處理百萬級甚至更大規模的數據集,通過 MiniBatch 等變體可進一步提升處理速度
  1. 結果易于解釋:聚類中心具有明確的物理意義(簇內樣本的均值),便于后續分析

3.2 缺點

  1. 需要預先指定 K 值:K 值的選擇對聚類結果影響巨大,而在實際應用中往往難以確定最優 K 值
  1. 對初始質心敏感:不同的初始質心可能導致完全不同的聚類結果,容易陷入局部最優
  1. 對噪聲和異常值敏感:異常值會顯著影響質心的計算,導致聚類偏差
  1. 難以處理非凸形狀簇:對于呈環形、條形等復雜分布的數據,聚類效果較差
  1. 對特征尺度敏感:不同量級的特征會主導距離計算,需要預先進行標準化處理

3.3 改進版本

針對上述缺點,研究者們提出了多種改進算法:

  • KMeans++:通過改進初始質心的選擇方式(使初始質心盡可能遠離),提高了算法收斂到全局最優的概率
  • MiniBatchKMeans:使用隨機抽樣的小批量數據更新質心,大幅提升處理大規模數據的效率
  • ISODATA:能夠自動調整聚類數量,通過合并和分裂操作優化聚類結果
  • Kernel KMeans:通過核函數將數據映射到高維空間,解決非凸形狀簇的聚類問題

四、K 值的確定方法

K 值的選擇是 KMeans 算法應用中的關鍵難題,以下是幾種常用的確定方法:

4.1 肘部法(Elbow Method)

肘部法通過繪制不同 K 值對應的 SSE 曲線,選擇曲線開始趨于平緩的 "肘部" 位置對應的 K 值作為最優解。當 K 值較小時,隨著 K 的增加,SSE 會急劇下降;當 K 值超過某個臨界點后,SSE 的下降速度明顯放緩,此時再增加 K 值對聚類效果的提升有限,該臨界點即為最優 K 值。

4.2 輪廓系數法(Silhouette Score)

輪廓系數綜合考慮了樣本的類內相似度和類間相似度,取值范圍為 [-1,1]。對于每個樣本 i,計算:

  • \(a_i\):樣本 i 與其同簇內其他樣本的平均距離(類內不相似度)
  • \(b_i\):樣本 i 與最近異簇內所有樣本的平均距離(類間不相似度)

樣本 i 的輪廓系數為:\(s_i=\frac{b_i-a_i}{max(a_i,b_i)}\)

整體數據集的輪廓系數為所有樣本輪廓系數的平均值,越接近 1 表示聚類效果越好。通過計算不同 K 值對應的輪廓系數,選擇系數最大的 K 值作為最優解。

4.3 Gap 統計量(Gap Statistic)

Gap 統計量通過比較實際數據的聚類效果與參考數據(通常是隨機生成的均勻分布數據)的聚類效果來確定最優 K 值。定義為:

\(Gap(k)=E[log(D_k)]-log(D_k)\)

其中\(D_k\)是實際數據的聚類分散度,\(E[log(D_k)]\)是參考數據的期望分散度。當 Gap 統計量趨于穩定時的 K 值即為最優解。

五、KMeans 算法的實現步驟

5.1 數據預處理

在應用 KMeans 算法前,需要對數據進行必要的預處理:

  1. 缺失值處理:通過均值填充、中位數填充或插值法處理缺失數據
  1. 標準化 / 歸一化:由于 KMeans 對特征尺度敏感,需將數據轉換到相同量級,常用方法包括:
    • 標準化:\(x'=\frac{x-\mu}{\sigma}\)(均值為 0,標準差為 1)
    • 歸一化:\(x'=\frac{x-min}{max-min}\)(映射到 [0,1] 區間)
  1. 異常值檢測:使用 Z-score、IQR 等方法識別并處理異常值,避免其影響聚類中心

5.2 算法實現流程

以下是 KMeans 算法的詳細實現步驟:

  1. 初始化參數:設置聚類數量 K、最大迭代次數、收斂閾值等
  1. 選擇初始質心
    • 隨機選擇 K 個樣本作為初始質心(傳統方法)
    • 采用 KMeans++ 策略選擇初始質心(改進方法)
  1. 分配樣本到最近簇
    • 計算每個樣本與所有質心的距離
    • 將樣本分配到距離最近的質心所在簇
  1. 更新質心位置
    • 計算每個簇內所有樣本的均值
    • 將均值作為新的質心位置
  1. 檢查收斂條件
    • 若質心位置變化小于收斂閾值,或達到最大迭代次數,則停止迭代
    • 否則返回步驟 3 繼續迭代
  1. 輸出結果:返回最終的聚類標簽和質心位置

六、完整代碼案例與解析

下面將通過 Python 實現 KMeans 算法,并使用真實數據集進行聚類分析。我們將分別展示使用自定義實現和 Scikit-learn 庫的兩種方式,以便讀者更好地理解算法細節。

6.1 自定義 KMeans 實現

首先,我們手動實現 KMeans 算法的核心邏輯,包括距離計算、質心更新和迭代優化等步驟:

import numpy as np

import matplotlib.pyplot as plt

from sklearn.datasets import make_blobs

from sklearn.metrics import silhouette_score

from sklearn.preprocessing import StandardScaler

class CustomKMeans:

def __init__(self, n_clusters=2, max_iter=100, tol=1e-4, random_state=None):

"""

初始化KMeans模型

參數:

n_clusters: 聚類數量

max_iter: 最大迭代次數

tol: 收斂閾值,當質心變化小于該值時停止迭代

random_state: 隨機數種子,保證結果可復現

"""

self.n_clusters = n_clusters

self.max_iter = max_iter

self.tol = tol

self.random_state = random_state

self.centroids_ = None # 質心

self.labels_ = None # 聚類標簽

self.inertia_ = None # SSE值

def _euclidean_distance(self, X, centroids):

"""計算樣本與質心之間的歐氏距離"""

distances = []

for centroid in centroids:

# 計算每個樣本到當前質心的距離

dist = np.sqrt(np.sum((X - centroid) ** 2, axis=1))

distances.append(dist)

# 轉換為形狀為(n_samples, n_clusters)的數組

return np.array(distances).T

def _initialize_centroids(self, X):

"""隨機選擇K個樣本作為初始質心"""

np.random.seed(self.random_state)

# 從數據集中隨機選擇K個不同的樣本索引

indices = np.random.choice(X.shape[0], self.n_clusters, replace=False)

return X[indices]

def fit(self, X):

"""訓練KMeans模型"""

# 初始化質心

self.centroids_ = self._initialize_centroids(X)

for _ in range(self.max_iter):

# 計算每個樣本到各質心的距離

distances = self._euclidean_distance(X, self.centroids_)

# 為每個樣本分配最近的簇

self.labels_ = np.argmin(distances, axis=1)

# 計算新的質心

new_centroids = np.array([X[self.labels_ == k].mean(axis=0)

for k in range(self.n_clusters)])

# 計算質心變化量

centroid_shift = np.sum(np.sqrt(np.sum((new_centroids - self.centroids_) **2, axis=1)))

# 更新質心

self.centroids_ = new_centroids

# 檢查是否收斂

if centroid_shift < self.tol:

break

# 計算最終的SSE

self.inertia_ = np.sum([np.sum((X[self.labels_ == k] - self.centroids_[k])** 2)

for k in range(self.n_clusters)])

return self

def predict(self, X):

"""預測新樣本的聚類標簽"""

distances = self._euclidean_distance(X, self.centroids_)

return np.argmin(distances, axis=1)

# 生成測試數據

X, y_true = make_blobs(n_samples=500, centers=4, cluster_std=0.60, random_state=0)

# 數據標準化

scaler = StandardScaler()

X_scaled = scaler.fit_transform(X)

# 測試自定義KMeans

custom_kmeans = CustomKMeans(n_clusters=4, max_iter=100, random_state=42)

custom_kmeans.fit(X_scaled)

custom_labels = custom_kmeans.labels_

custom_centroids = custom_kmeans.centroids_

# 計算輪廓系數

silhouette_avg = silhouette_score(X_scaled, custom_labels)

print(f"自定義KMeans的輪廓系數: {silhouette_avg:.4f}")

print(f"自定義KMeans的SSE值: {custom_kmeans.inertia_:.4f}")

# 可視化聚類結果

plt.figure(figsize=(10, 6))

plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=custom_labels, s=50, cmap='viridis')

plt.scatter(custom_centroids[:, 0], custom_centroids[:, 1], s=200, marker='X',

c='red', label='質心')

plt.title('自定義KMeans聚類結果', fontsize=15)

plt.xlabel('特征1', fontsize=12)

plt.ylabel('特征2', fontsize=12)

plt.legend()

plt.grid(True, linestyle='--', alpha=0.7)

plt.show()

6.2 使用 Scikit-learn 實現 KMeans

Scikit-learn 庫提供了高效的 KMeans 實現,包含了 KMeans++ 初始化等優化特性,實際應用中推薦使用:

from sklearn.cluster import KMeans

from sklearn.datasets import load_iris, make_moons, make_circles

from sklearn.model_selection import train_test_split

from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score

import seaborn as sns

# 1. 測試經典數據集

iris = load_iris()

X_iris = iris.data

y_iris = iris.target

# 數據標準化

X_iris_scaled = StandardScaler().fit_transform(X_iris)

# 使用KMeans++初始化

kmeans_iris = KMeans(n_clusters=3, init='k-means++', n_init=10,

max_iter=300, random_state=42)

kmeans_iris.fit(X_iris_scaled)

iris_labels = kmeans_iris.labels_

# 評估聚類效果(已知真實標簽時)

ari = adjusted_rand_score(y_iris, iris_labels)

nmi = normalized_mutual_info_score(y_iris, iris_labels)

print(f"鳶尾花數據集的調整蘭德指數: {ari:.4f}")

print(f"鳶尾花數據集的標準化互信息: {nmi:.4f}")

# 可視化聚類結果(使用前兩個特征)

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)

plt.scatter(X_iris_scaled[:, 0], X_iris_scaled[:, 1], c=y_iris, s=50, cmap='viridis')

plt.title('真實標簽', fontsize=15)

plt.xlabel(iris.feature_names[0], fontsize=12)

plt.ylabel(iris.feature_names[1], fontsize=12)

plt.grid(True, linestyle='--', alpha=0.7)

plt.subplot(1, 2, 2)

plt.scatter(X_iris_scaled[:, 0], X_iris_scaled[:, 1], c=iris_labels, s=50, cmap='viridis')

plt.scatter(kmeans_iris.cluster_centers_[:, 0</doubaocanvas>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92549.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92549.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92549.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

納米陶瓷與光子集成:獵板PCB定義下一代VR硬件的技術藍圖

虛擬現實&#xff08;VR&#xff09;設備正從“視覺沉浸”向“多感官無感交互”演進&#xff0c;其底層PCB技術面臨帶寬、算力密度與動態可靠性的三重挑戰。作為國內高端PCB技術的引領者&#xff0c;??獵板PCB??以材料革新、光電子融合與智能響應為核心&#xff0c;構建了適…

Linux ssh-keygen系列命令與ssh命令的使用

關聯文章 Linux ssh 免密登錄配置&#x1f44d;對日開發 TeraTerm 批量向各臺服務器傳輸文件SSH 教程&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d;&#x1f44d; 目錄一. ssh-keygen相關命令1.1 簡介1.2 生成密鑰1.3 ssh-copy-id 上傳公鑰到指定的服務…

從C++0基礎到C++入門 (第二十五節:指針【所占內存空間】)

目錄 一. 指針所占內存空間 1.1 驗證指針大小的代碼示例 1.2 不同系統架構下的差異 1.3 指針大小與類型無關 1.4 空指針的大小 1.5 多級指針的大小 1.6 實際應用中的注意事項 一. 指針所占內存空間 指針在內存中占用的空間大小取決于系統架構和編譯環境。 32位系統中指…

Windows選擇文件自動刪除及輸入框自動打字的解決辦法

覺得有幫助麻煩您動動發財的小手點贊、收藏、加關注&#xff0c;感謝&#xff01; 運行環境&#xff1a;windows10 現象&#xff1a;鼠標點擊任何文件&#xff0c;上下鍵選擇任何文件都會自動放入回收站并彈警告框&#xff0c;鼠標放入輸入框會自動打一串字符&#xff0c;刪除…

大模型 MCP服務案例詳細講解

大模型與 MCP(Model Context Protocol)服務器的交互是一個雙向、異步、流式的過程,涉及協議解析、函數調用、實時數據交換等關鍵環節。下面我將詳細解釋整個交互流程,結合具體示例和時序圖說明。 ?? 核心交互流程圖 #mermaid-svg-Adxo4FoP4oRzAJdV {font-family:"tr…

MVCC和日志

MVCC是一種并發控制的方法&#xff0c;在數據庫管理系統中&#xff0c;實現對數據庫的并發訪問&#xff0c;在編程語言中實現事務內存主要是為了提高數據庫并發性能&#xff0c;更好的處理讀寫沖突&#xff0c;做到即使有讀寫沖突時&#xff0c;也能做到不加鎖&#xff0c;非阻…

Redis源碼安裝 Failed to configure LOCALE for invalid locale name 報錯解決

源碼安裝之后 報錯 Failed to configure LOCALE for invalid locale name原因是redis 8.0 需要配置字符集 只需要在環境變量中添加 LANGen_US.utf8 即可&#xff0c; 在配置之前先查看當前系統中存在哪些字符集 locale -a然后在 /etc/profile 環境變量中添加配置 LANGen_US.ut…

跑酷小游戲2.0

emm&#xff0c;下面是1.0版本的&#xff0c;我問了下AI&#xff0c;出了第四關&#xff0c;按步驟更新吧。其實是我也搞不懂AI在說啥//跑酷游戲C版 #include<bits/stdc.h> #include<windows.h> #include<stdio.h> #include<conio.h> #include<tim…

相比于傳統的全波分析,特征模分析具有哪些優點

相比傳統的全波分析&#xff08;Full-Wave Analysis&#xff0c;直接求解電場/電流分布&#xff09;&#xff0c;特征模分析&#xff08;Characteristic Mode Analysis&#xff0c;CMA&#xff09;的優點主要體現在物理可解釋性、設計指導性和計算效率三個方面。1. 物理機理更清…

UE材質World Position 和 Object Position

Object Position 是 物體原點在世界坐標系下的位置 World Position 是 物體上的這個點 在世界坐標系下的位置 Actor Position 是 物體軸點位置 WorldPosition - ObjectPosition 是一個從物體原點&#xff08;pivot&#xff09;指向物體上該點的向量&#xff08;方向&#x…

github上傳文件

git remote add origin https://github.com/Ineedstrong/socket-practice.git如果不行的情況下git remote set-url origin gitgithub.com:Ineedstrong/socket-practice.git就以這種方式3. 使用 SSH 替代 HTTPS&#xff08;推薦&#xff09;繞過 HTTPS 的 TLS 問題&#xff1a;生…

【STM32U385RG 測評】基于VSCode的STM32開發環境搭建

【STM32U385RG 測評】搭建基于VSCode的STM32開發環境 文章目錄【STM32U385RG 測評】搭建基于VSCode的STM32開發環境一、安裝軟件1.1 安裝VSCode1.2 安裝STM32CubeMX1.3 安裝STM32CubeCLT1.4 安裝ST-MCU-FINDER-PC二、安裝插件2.1 安裝 STM32Cube for VSCode插件三、創建項目3.1…

設計模式(二)——策略模式

一、基本概念 既然你已經接觸到了設計模式&#xff0c;那你大概率你寫過類似這樣的代碼&#xff1a;根據不同的選擇條件&#xff08;如排序、搜索或路由&#xff09;執行不同的代碼邏輯。通常的解決方案是使用if-else或switch語句&#xff0c;但這些條件判斷有一個最大的問題是…

MySQL基礎知識總結

一、MySQL簡述 數據庫 是一個有組織的集合&#xff0c;用于存儲和管理數據的系統。它是一個軟件系統&#xff0c;被設計用來存儲、檢索和管理數據&#xff0c;并提供數據的快速訪問和處理。數據庫可以被看作是一種特殊的文件系統&#xff0c;但與傳統的文件系統不同的是&#…

數據倉庫命名規范

1. 概述 數據模型是數據管理的分析工具和交流的有力手段&#xff1b;同時&#xff0c;還能夠很好地保證數據的一致性&#xff0c;是實現商務智能&#xff08;Business Intelligence&#xff09;的重要基礎。因此建立、管理一個企業級的數據模型&#xff0c;應該遵循標準的命名…

FlinkSQL Joins全解析

1. Lookup Join用途&#xff1a;用于流表與外部維表&#xff08;靜態或緩慢變化表&#xff09;的關聯&#xff08;如 MySQL、HBase 等&#xff09;。特點&#xff1a;通過 實時查詢外部存儲 獲取維度數據。僅支持 處理時間&#xff08;Processing Time&#xff09;語義&#xf…

【FileZilla】基于 FTP 的 Windows 和 Linux 文件傳輸

在嵌入式開發過程中我們經常需要在 Windows 和 Linux 下進行文件傳輸&#xff0c;本文就介紹一種通過 FTP 實現 Windows 和 Linux 文件傳輸的方法。 Windows 為物理主機&#xff0c;Linux 是在 Vmware 虛擬機中安裝運行的 Ubuntu&#xff0c;版本為 18.04。 Ubuntu 安裝 FTP …

【GPT入門】第42課 ollama安裝與運行llama3模型

【GPT入門】第42課 ollama安裝與運行llama3模型1. 安裝ollama2.運行模型3.測試模型3.1 直接在命令行交互3.2 openai接口1. 安裝ollama https://ollama.com/ 選download, 選linux 執行安裝命令&#xff1a; curl -fsSL https://ollama.com/install.sh | sh2.運行模型 啟動服…

Lua語言元表、協同程序

元表元表的定義允許我們改變table的行為。setmetatable(普通表&#xff0c;元表)-- 元表a {"a","b","c"} -- 普通表 b {} --元表c setmetatable(a,b)print("------------------------")f {}print("f:",f)d setmetatabl…

[已解決]VSCode右鍵菜單消失恢復

前言 莫名其妙,好似VSCode自動更新以后,右鍵菜單就失效了,重裝也無果. 手動搞一個吧 保存下面代碼到桌面修復VSCODE右鍵菜單.reg,雙擊運行即可. Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\Directory\Background\shell\VSCode]"使用 VSCode 打開""…