機器學習從入門到精通 - 聚類算法大比拼:K-Means、DBSCAN實戰與評估陷阱

機器學習從入門到精通 - 聚類算法大比拼:K-Means、DBSCAN實戰與評估陷阱

開場白:推開無監督學習的大門

朋友們,不知道你們有沒有對著堆積如山、沒有標簽的數據發過愁?想從里面找出點規律,分組什么的,結果發現根本無處下手 —— 這就是聚類算法大顯身手的時候了!它屬于無監督學習的核心領域,目標就是:在不知道正確答案的前提下,把相似的數據點自動歸到一堆兒里。聽起來像魔法對吧?今天咱們就深入兩個明星選手:K-Means 和 DBSCAN。我會手把手帶你們實戰,更重要的是 —— 把那些評估環節里暗藏玄機的陷阱,一個個揪出來。相信我,踩過這些坑,你對聚類的理解絕對能上一個大臺階。準備好筆記本,咱們出發!


一、 為什么要聚類?數據的內在秩序

想象一下,你有一大堆顧客的購買記錄(沒有分類標簽)。你想搞促銷,但不可能給每個人都推一樣的商品吧?聚類幫你把顧客分成不同群體:可能是“母嬰用品愛好者”、“數碼發燒友”、“周末大采購黨”。沒有聚類,你就是大海撈針;有了它,你就能精準投放資源。

或者,天文望遠鏡拍下海量星空圖像,聚類能自動識別出哪些光點屬于同一個星團。聚類的核心價值,就是揭示數據內部隱藏的結構和模式,這是探索性數據分析(EDA)和許多后續任務的基石。多說一句,很多特征工程也依賴它。


二、 K-Means:經典的中心引力戰法

原理核心:物以類聚,人以群分。 K-Means 的想法特別直觀:

  1. 先拍腦袋(或用肘部法則)決定要分成 K 個組(簇)。
  2. 隨機扔 K 個點作為簇中心(Centroid)。
  3. 讓每個數據點都奔向離它最近的中心點,形成初始分組。
  4. 重新計算每個簇的中心點(取所有點的平均值)。
  5. 重復步驟3和4,直到中心點不怎么動了(收斂)。

數學本質:最小化平方誤差

K-Means 的目標函數是最小化簇內平方和(Within-Cluster Sum of Squares, WCSS)

J=∑i=1K∑x∈Ci∣∣x?μi∣∣2J = \sum_{i=1}^{K} \sum_{\mathbf{x} \in C_i} ||\mathbf{x} - \mathbf{\mu}_i||^2J=i=1K?xCi??∣∣x?μi?2

  • K: 我們指定的簇數量。
  • C_i: 第 i 個簇包含的所有數據點的集合。
  • \mathbf{x}: 數據點向量。
  • \mathbf{\mu}_i: 第 i 個簇的中心點(質心)向量。
  • ||\mathbf{x} - \mathbf{\mu}_i||^2: 數據點 \mathbf{x} 到其所在簇中心 \mathbf{\mu}_i 的歐幾里得距離的平方。

目標函數推導(為啥最小化 WCSS 有效?)

  1. 定義距離: 我們使用歐氏距離衡量點到中心的差異:d(\mathbf{x}, \mathbf{\mu}_i) = ||\mathbf{x} - \mathbf{\mu}_i|| = \sqrt{(x_1 - \mu_{i1})^2 + ... + (x_n - \mu_{in})^2}。平方是為了方便計算(可導、凸性更優)并放大較大誤差的影響。
  2. 簇內求和: \sum_{\mathbf{x} \in C_i} ||\mathbf{x} - \mathbf{\mu}_i||^2 計算了簇 C_i 中所有點到其中心的距離平方和。這個值越小,說明這個簇的點越緊密圍繞在中心周圍,簇內相似度越高。
  3. 全局求和: \sum_{i=1}^{K} 把 K 個簇的 WCSS 都加起來,得到全局損失 J
  4. 優化目標: K-Means 的迭代過程(重新分配點 + 重新計算中心)就是在不斷嘗試降低 J。當中心點不再顯著變化(即 J 的下降小于某個閾值)時,算法認為找到了一個(局部)最優解,讓所有簇都盡可能“緊湊”。

流程可視化 (Mermaid)

Yes
No
Start: Choose K
Randomly Initialize K Centroids
Assign Points to Nearest Centroid
Update Centroids: Mean of Points
Converged?
Stop

Python 實戰 & 踩坑記錄

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# 1. 生成模擬數據 - 3個清晰簇
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=0.60, random_state=0)
plt.scatter(X[:, 0], X[:, 1], s=50)
plt.title('Raw Data (Ground Truth Clusters Visible)')
plt.show()# 2. 應用 K-Means - 指定 K=3
kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, random_state=42)
kmeans.fit(X)
y_kmeans = kmeans.labels_# 3. 可視化結果
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X')
plt.title('K-Means Clustering (K=3)')
plt.show()# 4. 評估 - 輪廓系數 (越大越好,范圍[-1, 1])
silhouette_avg = silhouette_score(X, y_kmeans)
print(f"Silhouette Score for K=3: {silhouette_avg:.4f}")# 5. 經典大坑:K 值選錯!
# 嘗試 K=2 和 K=4
for k in [2, 4]:kmeans_wrong = KMeans(n_clusters=k, random_state=42).fit(X)y_wrong = kmeans_wrong.labels_plt.scatter(X[:, 0], X[:, 1], c=y_wrong, s=50, cmap='viridis')plt.title(f'K-Means with K={k} (Probably Wrong)')plt.show()wrong_score = silhouette_score(X, y_wrong)print(f"Silhouette Score for K={k}: {wrong_score:.4f}") # 注意看分數變化!

K-Means 關鍵踩坑點:

  1. K 值選擇: 這是最大的坑!你拍腦袋定的 K 可能完全錯誤。肉眼看著數據猜?不靠譜!用“肘部法則”(Elbow Method)看 WCSS 下降拐點?有時拐點模糊不清。輪廓系數(Silhouette Score)?也不總是可靠(后面評估陷阱會細說)。實踐建議:結合業務理解 + 多種方法嘗試 + 可視化驗證
  2. 初始中心點敏感: 隨機初始化可能導致:
    • 收斂到局部最優解(效果差)。
    • 每次運行結果不同(缺乏穩定性)。
    • 規避策略:使用 k-means++ 初始化(sklearn 默認),它能更聰明地選擇初始中心點,顯著降低隨機性影響。設置 n_init > 1(比如 10)讓算法多次運行選最好結果。
  3. 球形簇假設: K-Means 基于距離(尤其是歐氏距離),它天然偏好發現凸的、球形分布、大小相似的簇。遇到長條形、環形、嵌套形、密度差異大的數據,它就抓瞎了!看圖最直觀:
# 生成非球形數據 (月牙形)
from sklearn.datasets import make_moons
X_moons, _ = make_moons(n_samples=200, noise=0.05, random_state=0)kmeans_moons = KMeans(n_clusters=2, random_state=42).fit(X_moons)
y_kmeans_moons = kmeans_moons.labels_plt.scatter(X_moons[:, 0], X_moons[:, 1], c=y_kmeans_moons, s=50, cmap='viridis')
plt.title('K-Means Fails on Moons Data')
plt.show()
  1. 噪聲點影響: 一個離群點(Noise)能顯著把中心點“拉歪”。K-Means 沒有顯式的噪聲處理機制。

三、 DBSCAN:基于密度的空間探索者

當數據奇形怪狀、有噪音、簇密度還不一樣時,K-Means 就力不從心了。DBSCAN(Density-Based Spatial Clustering of Applications with Noise)登場!它的想法很酷:簇是數據空間中密集的區域,被低密度區域分割開。

核心概念:

  • ε (eps): 鄰域的半徑。想象以每個點為中心畫個圓圈。
  • MinPts: 定義一個點為核心點(Core Point)所需的最小鄰居數(包括自己)。
  • 核心點 (Core Point): 在自身 ε-鄰域內包含至少 MinPts 個點(包括自身)。
  • 邊界點 (Border Point): 自身鄰居數小于 MinPts,但落在某個核心點的 ε-鄰域內。
  • 噪聲點 (Noise Point): 既不是核心點也不是邊界點的點。
  • 直接密度可達 (Directly Density-Reachable):q 在核心點 p 的 ε-鄰域內。
  • 密度可達 (Density-Reachable): 存在一串點 p1, p2, ..., pn,其中 p1 = p, pn = q,且每個 pi+1 都直接密度可達于 pi(所有 pi 都是核心點)。
  • 密度相連 (Density-Connected): 存在一個點 o,使得點 pq 都密度可達于 o

原理流程:

  1. 隨機選一個未訪問點 p
  2. 檢查 p 的 ε-鄰域:
    • 如果鄰居數 >= MinPtsp 是核心點,創建一個新簇 C,把 p 和它鄰域內所有點(包括邊界點)都加入 C
    • 如果鄰居數 < MinPts暫時標記 p 為噪聲(后續可能被其他核心點“拯救”成邊界點)。
  3. 對于剛加入 C每一個未訪問的核心點 q,遞歸地訪問它的 ε-鄰域,把鄰域內所有點也加入 C(這是實現“密度可達”的關鍵)。
  4. 重復 1-3 直到所有點都被訪問。
  5. 最終,所有被標記為噪聲的點就是離群點。

流程可視化 (Mermaid)

Yes
Yes
No
No
Yes
Start
Pick Unvisited Point p
Neighbors of p >= MinPts?
Mark p as Core. Create Cluster C
Add p's Neighbors to C
For each new Core q in C, visit q's neighborhood Recursively
Add q's Neighbors to C
All points in C processed?
Mark all in C as Visited
Mark p as Noise?*
All points visited?
Reclassify Noise: Border if in some Core's Neighbor, else True Noise
End

Python 實戰 & 踩坑記錄

from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler # 重要!# 1. 用之前的月牙形數據
plt.scatter(X_moons[:, 0], X_moons[:, 1], s=50)
plt.title('Moons Data - Ground Truth')
plt.show()# 2. DBSCAN 大展身手
# 參數調優是關鍵坑! eps=0.3, min_samples=5 是常用起點
dbscan = DBSCAN(eps=0.3, min_samples=5)
y_dbscan = dbscan.fit_predict(X_moons)# 3. 可視化 - DBSCAN 完美分離月牙
plt.scatter(X_moons[:, 0], X_moons[:, 1], c=y_dbscan, s=50, cmap='viridis')
plt.title('DBSCAN Clustering on Moons')
plt.show()# 4. 處理噪聲點 (看看標簽 y_dbscan,-1 代表噪聲)
print(f"Number of clusters found: {len(set(y_dbscan)) - (1 if -1 in y_dbscan else 0)}")
print(f"Number of noise points: {list(y_dbscan).count(-1)}")# 5. 另一個坑:數據尺度差異!
# 生成尺度假數據 (一個維度范圍 0-1,另一個 0-100)
X_scale = np.array([[0.1, 10], [0.2, 20], [0.15, 15], [0.9, 90], [0.85, 85], [0.8, 800]]) # 注意最后一個點[0.8, 800] 是噪聲# 錯誤做法:直接扔給 DBSCAN
dbscan_bad = DBSCAN(eps=1, min_samples=2).fit(X_scale)
print("Bad Clustering (without scaling):", dbscan_bad.labels_)# 正確做法:必須標準化!
scaler = StandardScaler()
X_scale_scaled = scaler.fit_transform(X_scale)
dbscan_good = DBSCAN(eps=0.5, min_samples=2).fit(X_scale_scaled) # eps 需要重新調整
print("Good Clustering (with scaling):", dbscan_good.labels_)

DBSCAN 關鍵踩坑點:

  1. 參數調優 (epsMinPts): 這是 DBSCAN 最棘手的地方。選小了,把什么都當噪聲;選大了,所有點擠成一團。沒有銀彈規則!強烈建議:
    • 用 KNN 距離圖:計算每個點到其第 MinPts 個最近鄰的距離,排序后畫圖。找拐點(陡升點)作為 eps 的參考值。MinPts 一般從 2 * 維度 開始試。
    • 可視化輔助判斷:在不同參數下可視化結果。
    • 理解數據密度分布。
  2. 數據尺度陷阱: DBSCAN 極度依賴距離!不同特征量綱差異巨大(比如年齡 vs 年薪),必須先標準化(StandardScaler)或歸一化(MinMaxScaler),否則數值大的特征完全主導距離計算,小特征被忽略。這是我見過最常犯的錯誤!
  3. 變密度問題: 如果數據中不同簇的密度差異很大,DBSCAN 可能無法用一個統一的 epsMinPts 同時捕捉到所有簇。可能漏掉低密度簇或把高密度簇切碎。后續的 OPTICS 算法能緩解這個問題。
  4. 高維詛咒: 維度太高時,“距離”概念變得模糊,所有點都顯得差不多遠,DBSCAN 效果會下降。這時可能需要降維(PCA,t-SNE)或嘗試其他算法。

四、 算法大對決:同一數據集,不同表現

用合成數據直觀感受兩者的適用場景:

# 生成多形狀數據:球形 + 環形 + 長條 + 噪聲
from sklearn.datasets import make_circles, make_blobs
import matplotlib.pyplot as plt# 球形簇
centers = [[0, 0], [1.5, 1.5]]
X1, y1 = make_blobs(n_samples=100, centers=centers, cluster_std=0.2, random_state=0)# 環形簇
X2, y2 = make_circles(n_samples=100, factor=0.5, noise=0.05, random_state=0)
X2 = X2 * 0.5 + [0.5, 2]  # 平移縮放# 長條形簇 (手動構造)
np.random.seed(0)
angle = np.pi / 4
rot = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])
X3 = np.dot(np.random.rand(50, 2) * [5, 0.3], rot) + [3, 1]  # 長條# 噪聲點
noise = np.random.rand(20, 2) * [4, 3]  # 大致在 [0,4]x[0,3] 范圍# 合并數據
X_all = np.vstack([X1, X2, X3, noise])
y_true_all = np.hstack([y1, y2 + 2, np.ones(50) * 4, np.ones(20) * -1])  # 生成真實標簽(-1噪聲)# 可視化真值
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.scatter(X_all[:, 0], X_all[:, 1], c=y_true_all, s=50, cmap='viridis')
plt.title('Ground Truth (with Noise)')# K-Means 嘗試 (K=4,大致對應球形+環形+長條?)
kmeans_all = KMeans(n_clusters=4, random_state=42).

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

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

相關文章

AI 重構內容創作:從文案生成到視頻剪輯,創作者該如何與 AI 協同共生?

一、引言&#xff1a;AI 掀起內容創作的 “重構浪潮”?行業現象引入&#xff1a;列舉 AI 在內容創作領域的爆發式應用案例&#xff08;如某平臺 AI 文案工具日生成量破百萬、AI 視頻剪輯軟件用戶增長超 300%&#xff09;?創作者需求變化&#xff1a;通過調研數據說明創作者對…

后端一次性返回十萬條數據時,前端需要采用多種性能優化策略來避免頁面卡頓

當后端一次性返回十萬條數據時&#xff0c;前端需要采用多種性能優化策略來避免頁面卡頓。以下是主要的優化方案&#xff1a; 分頁加載 - 將數據分批次加載顯示虛擬滾動 - 只渲染可視區域內的數據數據懶加載 - 按需加載數據Web Workers - 在后臺線程處理數據時間切片 - 分散渲染…

基于-輕量級文檔搜索系統的測試報告

文章目錄一、項目背景二、項目功能三、測試計劃&#xff08;一&#xff09;測試用例設計&#xff08;二&#xff09;測試用例實現1.功能測試2.界面測試3.兼容性測試4.易用性測試5.安全性測試一、項目背景 1.基于輕量級文檔檢索系統采用C技術棧來實現&#xff0c;同時使用了本地…

編輯器vim(Linux)

Linux下開發工具是獨立的寫代碼——編輯器 vim編譯代碼——gcc/g調試——gdb、cgdb構建工具——makefile、make、cmakevim只用來寫代碼注意&#xff1a;直接用vim打開一個不存在的文件并保存退出&#xff0c;就會自動生成該文件vim有多種模式命令模式&#xff08;Normal Mode&a…

GitLab,2025最新如何配置中的SSH key步驟

電腦右鍵先檢查&#xff0c;是否有公鑰 git cat ~/.ssh/id_rsa.pub下面是有&#xff0c;不用生成公鑰&#xff0c;沒有就要生成生成本地電腦公鑰, 建議用第二種 //第一種ssh-keygen -t rsa//第二種------- 1.打開git bash,輸入&#xff1a;ssh-keygen -t rsa -C “你的郵箱”ss…

華為HCIE證書多久續一次費?費用多少?

根據華為官方政策&#xff0c;華為認證HCIE的有效期為3年&#xff0c;有效期自證書正式發放之日起計算&#xff0c;考生可通過華為人才在線官網登錄個人賬號&#xff0c;在“我的證書”欄目中查詢具體有效期起止時間。一、HCIE證書到期后的續證方式 1.重考對應HCIE的認證考試&a…

提升文本到圖像強化學習穩定性:Pref - GRPO算法如何革新圖像生成?

提升文本到圖像強化學習穩定性&#xff1a;Pref - GRPO算法如何革新圖像生成&#xff1f; 在文本到圖像生成領域&#xff0c;強化學習正重塑著模型與人類偏好的對齊方式。本文聚焦于一種創新的基于成對偏好獎勵的GRPO方法&#xff08;Pref - GRPO&#xff09;&#xff0c;它通…

Linux UDisks守護進程曝本地提權漏洞CVE-2025-8067,PoC已發布

漏洞概述安全研究人員在Linux環境中廣泛使用的磁盤管理組件UDisks守護進程中&#xff0c;發現了一個嚴重漏洞&#xff08;編號CVE-2025-8067&#xff0c;CVSS評分8.5&#xff09;。該漏洞已報告給紅帽產品安全團隊&#xff0c;并在UDisks更新版本中得到修復。技術細節該漏洞存在…

uniapp 開發上架 iOS App全流程

操作文檔網址&#xff1a;https://ask.dcloud.net.cn/article/152 操作學習視頻地址&#xff1a;uniapp打包上線微信小程序、安卓、IOS流程_嗶哩嗶哩_bilibili 第一步&#xff1a;注冊蘋果 iOS 個人開發者賬號 費用說明 ?個人開發者賬號?&#xff1a;適用于獨立開發者或小…

Sqlsugar補充自定義模板

DBFirst默認創建所有實體CreateClassFile()的第二個參數為生成實體類命名空間//.net6以下 db.DbFirst.IsCreateAttribute().CreateClassFile("c:\\Demo\\1", "Models"); //.net6以上 string加? db.DbFirst.IsCreateAttribute().StringNullable().CreateCl…

LeetCode 392.判斷子序列

給定字符串 s 和 t &#xff0c;判斷 s 是否為 t 的子序列。 字符串的一個子序列是原始字符串刪除一些&#xff08;也可以不刪除&#xff09;字符而不改變剩余字符相對位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一個子序列&#x…

邏輯回歸:從原理到實戰的完整指南

在機器學習中&#xff0c;分類任務是最常見的應用場景之一。而邏輯回歸&#xff08;Logistic Regression&#xff09;&#xff0c;盡管名字中有“回歸”&#xff0c;實際上是一種非常強大且廣泛應用的二分類模型。它簡單、高效、可解釋性強&#xff0c;是數據科學初學者入門分類…

鴻蒙搭配前端開發:應用端與WEB端交互

鴻蒙系統&#xff08;HarmonyOS&#xff09;是華為開發的一款面向全場景的分布式操作系統&#xff0c;其設計初衷是為了適應物聯網時代的需求&#xff0c;旨在構建一個統一的操作系統&#xff0c;支持多種設備的無縫協同工作。其分布式開發的一些主要優勢&#xff1a; 跨設備協…

配置sscms時被sql server處處刁難

今天要記下來的是一個小例子。接前面&#xff0c;當我終于完成sql server的安裝時&#xff0c;才發現要填寫sscms的兩個空是有多么艱難。首先安裝sql server2016出現了太多環境不兼容的問題&#xff0c;讓我只好退而安裝sql server2012。安裝sql server2012時其實是可以避坑的&…

【Flink】DataStream API:源算子、數據類型

目錄源算子&#xff08;Source&#xff09;從集合中讀取數據從文件讀取數據從Socket讀取數據從Kafka讀取數據從數據生成器讀取數據Flink支持的數據類型Flink的類型系統Flink支持的數據類型類型提示&#xff08;Type Hints&#xff09;源算子&#xff08;Source&#xff09; Fli…

Linux 安裝docker-compose安裝方法(安裝docker compose安裝)

文章目錄**方法一&#xff1a;通過 curl 下載二進制文件&#xff08;推薦&#xff09;**1. 安裝前準備- **確保已安裝 Docker**- **檢查 Docker 是否安裝成功**2. 下載并安裝 Docker Compose- **下載最新版本的 Docker Compose 二進制文件**- **國內加速下載&#xff08;解決 G…

OCR 發票識別與驗真接口:助力電子化發票新時代

自 2025 年 10 月 1 日起&#xff0c;紙質火車票徹底告別歷史舞臺&#xff0c;全面數字化的電子發票取而代之&#xff0c;這一變革標志著票務領域的重大革新&#xff0c;也讓電子化發票處理的需求呈井噴式增長。在此背景下&#xff0c;OCR 發票識別和發票驗真接口技術挺身而出&…

設計模式:抽象工廠模式(Abstract Factory Pattern)

文章目錄一、概念二、實例分析三、完整示例一、概念 抽象工廠模式是一種創建型設計模式。 提供一個接口用于創建一系列相關或相互依賴的對象&#xff0c;而無需指定它們的具體類。 相比于工廠方法模式&#xff0c;抽象工廠模式不僅僅是創建單一產品&#xff0c;而是一族產品&am…

輕量級注意力模型HOTSPOT-YOLO:無人機光伏熱異常檢測新SOTA,mAP高達90.8%

【導讀】 無人機光伏巡檢如何更智能、更高效&#xff1f;HOTSPOT-YOLO模型給出了亮眼答案&#xff01;給AI裝上“熱成像鷹眼”&#xff0c;能精準鎖定光伏板上的細微熱斑缺陷。它不僅將檢測精度&#xff08;mAP&#xff09;提升至90.8%&#xff0c;更在保持實時性的前提下大幅…

CHT共軛傳熱: 導熱系數差異如何影響矩陣系數

文章目錄 一、導熱系數差異如何影響矩陣系數&#xff1f;二、如何處理系數差異以加速收斂&#xff1f;1. **變量重縮放&#xff08;Scaling of Variables&#xff09;**2. **使用物理型預條件子&#xff08;Physics-based Preconditioning&#xff09;**3. **區域分解法&#x…