如果你去翻招聘面试的算法题,或者期末考试的复习提纲,“聚类”和“降维”几乎永远是被放在一起点名的两块硬骨头。我第一次真正意义上同时碰这两类算法,是在一个用户分群的项目里:面对三百多列用户特征,光是算距离矩阵就把内存吃得差不多了,更别提把人群画出来给业务方看。后来才琢磨明白,聚类用来“把人分群”,降维用来“把数据看清”,两者天然互补,也是无监督学习里最常配对出场的两个主角。
这篇内容适合这么几类人:刚开始学机器学习、想系统梳理聚类和降维的初学者;期末要考机器学习、正在找复习重点的学生;以及准备面试时想快速过一遍算法体系的同学。我会把算法原理、核心参数、Python实战代码、评估方法和常见的坑一次讲透,尽量让看完的人能直接上手跑通一条“数据预处理 -> 降维 -> 聚类 -> 评估”的完整链路,也能在考场上把高频概念题答清楚。
1. 为什么聚类和降维总被放在一起聊
1.1 两个算法家族的本质关系
聚类是无监督学习的核心任务,它解决的是“没有标签,怎么把样本自动分成有意义的组”这个问题。降维严格来说不算一个独立的学习任务,它更像数据预处理手段,解决的是“特征太多、太冗余、信息密度太低,怎么压缩成更少维度且尽量保留信息”的问题。
这两者经常被放在一起,主要有三个原因:
- 高维数据本身会让聚类失效。距离在高维空间变得不敏感,样本之间的欧氏距离趋向于平均,聚出来的簇没有意义,这就是俗称的维数灾难。所以很多聚类项目会先降维再聚类。
- 降维后可以做可视化,方便人眼判断聚类效果。二维散点图一看就知道分成了几群,比盯着聚类指标要直观得多。
- 两者在数学工具上高度重合。PCA用协方差矩阵的特征分解,谱聚类用拉普拉斯矩阵的特征分解,LDA用类间散布矩阵的广义特征问题——做降维和做聚类的许多底层矩阵运算是一致的。
我在实际项目里的感受是:聚类是目的,降维是手段。当数据维度高到无法直接建模时,降维不是可选操作,而是必经之路。
1.2 这东西学完到底能干什么
从应用角度看,聚类和降维的覆盖面特别广,说几个我实际接触过的场景:
- 用户分群:电商平台根据用户的浏览、购买、收藏行为做聚类,把用户切成价格敏感型、品牌偏好型、低频观望型等几个群,不同群做不同运营策略。特征往往上百维,先PCA压缩到20维再聚类,效果远好于直接跑K-Means。
- 图像聚类:把图片提出特征向量后做聚类,实现无标注的图片整理。
- 异常检测:DBSCAN把密度低的点识别为噪声,和主群分开。反欺诈场景里经常用这个思路。
- 数据可视化:高维数据用t-SNE或UMAP压到二维,看样本分布,排查数据质量、做报告展示都离不开。
对于备考的学生来说,聚类和降维是机器学习课程里最容易出“综合大题”的部分。概念题考K-Means和层次聚类的流程,计算题考PCA特征值或者K-Means一轮迭代,设计题让给出“高维数据聚类”的整体方案。把这套内容吃透了,期末基本不慌。
1.3 一个贯穿全文的示例
为了让后面内容不飘,我先固定一个示例。假设有一批用户数据,每条记录包含年龄、年消费额、月登录次数、平均停留时长等十几个特征。我们要做的是:去掉冗余特征、把用户分群、最后把分群结果画出来给业务方看。这个场景会贯穿全文,用来演示降维和聚类怎么配合。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 聚类算法全景拆解
2.1 聚类流派扫盲
聚类算法大体可以分成四个流派,思想完全不同,适用场景也完全不同:
- 原型聚类:代表是K-Means,核心思想是“每个簇用一个中心点代表”,通过迭代优化让每个点到所属中心点的距离之和最小。特点是快、简单,适合大规模数据和凸形簇,但对离群点敏感,聚类形状也偏球形。
- 密度聚类:代表是DBSCAN,核心思想是“簇是密度相连的点的最大集合”。它能自动发现任意形状的簇,还能把低密度区域标记为噪声,对离群点很稳。缺点是参数(eps和min_samples)不好调,数据密度差异大的时候容易失效。
- 层次聚类:代表是AGNES(自底向上凝聚)和DIANA(自顶向下分裂),核心思想是把样本逐层合并或分裂,形成一棵聚类树。好处是不用预设簇数,可以按树状图任意切分;缺点是时间复杂度高,不适合大数据量。
- 分布聚类:代表是高斯混合模型GMM,假设每个簇服从一个高斯分布,用EM算法估计参数。它能输出“某个样本属于某个簇的概率”,适合簇形状不是球形、或者你想做软聚类的情况。
既然期末和面试最爱考K-Means、层次聚类和DBSCAN这三种,下面重点展开。
2.2 K-Means的完整原理和参数细节
K-Means虽然是聚类里最基础的算法,但真要把它讲清楚、用明白,有几个细节值得深挖。
算法流程其实很简单:
- 随机(或人工指定)选择K个点作为初始质心。
- 计算每个样本到K个质心的距离,把它归到最近的质心所在的簇。
- 对每个簇重新计算质心,取簇内所有样本的均值。
- 重复2和3,直到质心不再变化或达到最大迭代次数。
这里有几个常被忽略的关键点:
一是距离度量。默认用欧氏距离,也就是L2距离。如果特征是量纲不同的混合数据,比如年龄和消费金额直接算欧氏距离,消费金额会完全主导结果。所以跑K-Means前基本必须做标准化,让每个特征在同一尺度上。如果你想用曼哈顿距离或者余弦相似度,那就不叫标准K-Means了,实现上要自己改逻辑。
二是K值怎么选。最常用的方法是手肘法:计算不同K值下的簇内误差平方和SSE,画出来以后找“拐点”。还有一种更严谨的是轮廓系数,计算每个样本的凝聚度和分离度,系数越大说明聚类效果越好,在K=2到K=10之间遍历,取轮廓系数最高的K值。
三是初始化方式。标准K-Means用随机初始化,容易陷入局部最优。工程上一般用K-Means++,它的思路是:第一个质心随机选,后续质心尽量选离已有质心远的点,能在很大程度上规避局部最优问题。sklearn里默认就是K-Means++,这也是为什么用库比自己写要稳的原因。
四是收敛判断。一般是质心移动距离小于阈值,或者SSE变化小于阈值。迭代次数上限也要设置,防止数据不收敛时无限循环。
K-Means的时间复杂度是O(n·k·t),n是样本数,k是簇数,t是迭代次数。样本量大时依然很快,这也是它几十年来都是首选聚类工具的原因。
2.3 层次聚类与DBSCAN的关键区别
层次聚类和K-Means最大的不同在于:它不要求先指定K值,而是输出一棵树。自底向上的凝聚式层次聚类,先是每个样本独立成簇,然后每次找距离最近的两个簇合并,直到所有样本都并到一棵树里。合并过程形成树状图,你可以在任意高度“切一刀”,得到想要的簇数。
这里还有一个考点:簇间距离怎么定义。单链接(最近样本距离)容易形成长条簇,全链接(最远样本距离)偏向紧实球形簇,平均链接是折中方案。很多资料会把沃德法(Ward)单拎出来讲,因为它合并时使簇内方差增加量最小,实际效果通常最好。
DBSCAN则完全换了一个思路。它定义了两个参数:eps是邻域半径,min_samples是一个点被称为核心点所需的最小邻居数。算法从任一未访问点开始,找它的eps邻域内所有点,如果邻居数不小于min_samples,就扩张成一个簇,继续检查簇内其他点;如果邻居数不够,就标记为噪声。这样天然能把稀疏噪声区分出来,不怕数据里有异常点。
DBSCAN最大的优点有两个:一是无需预设簇数,二是簇形状任意。我做过一次地理位置的聚类,用户分布并不是几个正圆,而是沿着城市主干道呈带状,用K-Means怎么分都怪,换DBSCAN一下子就出来了几条带状的簇。但它的短板也明显:如果你数据密度差异大,一个eps值不可能同时适配密集区和稀疏区,聚出来大概率是一边全是一类、另一边全是噪声。这种场景需要改用OPTICS或者做密度分层处理。
2.4 Python实战:三行代码跑通主流聚类
写代码前先说工具。K-Means、层次聚类、DBSCAN这些算法,sklearn已经封装得很好了,不需要自己造轮子。如果你只是想快速跑一个聚类,用sklearn就够了。如果你想研究算法细节,可以看scipy的层次聚类实现,或者直接读sklearn源码。
下面给一个能直接跑的示例,用的数据集是sklearn自带的make_blobs,生成三堆可分的点:
python复制import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score
# 生成三簇模拟数据
X, y_true = make_blobs(n_samples=500, centers=3, cluster_std=0.8, random_state=42)
# 标准化
X_scaled = StandardScaler().fit_transform(X)
# K-Means
kmeans = KMeans(n_clusters=3, init='k-means++', n_init=10, max_iter=300, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
# 轮廓系数评估
print("K-Means 轮廓系数:", silhouette_score(X_scaled, kmeans_labels))
# 层次聚类(沃德法)
agg = AgglomerativeClustering(n_clusters=3, linkage='ward')
agg_labels = agg.fit_predict(X_scaled)
# DBSCAN
db = DBSCAN(eps=0.5, min_samples=5)
db_labels = db.fit_predict(X_scaled)
# 可视化
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, labels, title in zip(axes, [kmeans_labels, agg_labels, db_labels],
['K-Means', 'Agglomerative', 'DBSCAN']):
ax.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='viridis', s=15)
ax.set_title(title)
plt.show()
这份代码里有几个细节值得注意:
- StandardScaler在聚类前几乎必须做,不然特征尺度不一致,距离会被大尺度特征主导。
- KMeans里的n_init=10表示做10次不同初始化,取SSE最小的结果。如果你的数据没有明显簇结构,真正的质心收敛可能很慢,n_init设置得大一点能提高稳定性。
- DBSCAN的eps取值对结果影响巨大。一个经验法则是先算样本两两距离,取距离分布的中位数作为eps初始值,再少量调整。sklearn没有直接提供这个工具,你可以写几行代码自算,这个后面在常见问题部分细说。
2.5 聚类效果怎么评估才靠谱
很多人跑完聚类后喜欢直接用肉眼看图,这没问题,但面试和写报告时总要拿出数字。聚类评估分两类:
内部指标不需要真实标签,只根据聚类结果的紧密度和分离度来评价。最常用的是轮廓系数(Silhouette Coefficient),范围在-1到1之间,越接近1说明样本离自己簇中心近、离其他簇远。还有戴维森堡丁指数(DBI),越小说明簇内越紧、簇间越远。
外部指标需要真实标签,适合在实验环境里评估聚类算法在已知数据上的表现。常用的是调整兰德指数(ARI)和互信息(NMI)。ARI对标签的随机分配做了校正,随机分类的ARI接近0,完全一致的ARI为1。
实战中我的建议是:如果数据有真标签,优先看ARI和NMI,它们能客观反映聚类和真实分布的匹配程度;如果数据没标签,用轮廓系数和一个能说明业务意义的指标组合来判断,比如分群后的消费均值差异是否显著。算法跑出来的群如果业务上解释不通,再漂亮的系数也没用。
3. 降维算法全面拆解
3.1 降维到底在做什么
降维的本质是“用更少的变量表示原始数据的核心结构”。这个“更少”可以是做特征选择,直接从原有特征里挑一部分;也可以是做特征提取,把原有特征线性或非线性组合成新特征。聚类场景里,我们说的降维基本上指后者。
为什么必须降维?除了前面提到的维数灾难,还有几个很现实的原因:
- 特征之间高度相关,信息冗余严重。比如“年消费额”和“月均消费额”本质上就是一个东西,两个都放进去,距离计算时这个维度被重复加权了。
- 噪声特征会掩盖真实结构。真实特征只有两三个,其余二十个全是噪声,聚类时噪声会把真实信号淹没。
- 计算和存储成本。超高维矩阵的运算代价是灾难性的。
降维算法分成两大类:线性降维和非线性降维。线性降维的代表是PCA和LDA,适合数据大致落在线性子空间附近的情况;非线性降维的代表是t-SNE和UMAP,适合流形结构数据,能把卷曲、嵌套的低维结构展开。
3.2 PCA的数学原理和实操要点
PCA是每次面试和期末必考的重头戏,原理并不难,但要能讲清楚每一个步骤为什么这么做。
核心目标一句话:找到一个新坐标系,使得数据在新坐标轴上的方差最大。直觉上说,方差最大的方向就是数据差异最大的方向,保留这个方向等于保留了最多的信息。
算法步骤如下:
- 对原始数据做中心化,也就是每个特征减去均值。这一步让协方差矩阵的计算变得规范。
- 计算特征之间的协方差矩阵。
- 对协方差矩阵做特征值分解,特征值表示该特征向量方向上的方差大小。
- 把特征值从大到小排序,取前d个最大的特征值对应的特征向量作为新坐标轴。
- 用原始数据乘以这些特征向量组成的矩阵,得到降维后的数据。
实际使用中有几个需要注意的点:
一是标准化还是中心化。如果特征量纲差异大,只做中心化会让大方差特征主导主成分,所以通常先做标准化,让每个特征方差为1,再做PCA。但标准化之后PCA找的是相关系数矩阵的主成分,而不仅仅是协方差矩阵的方向,这会让原始方差占比的解释变弱。简单来说:数据量纲一致或接近时,可以直接中心化后做PCA;量纲差异大时,先标准化再做。
二是降维维度怎么定。最常用的是累计解释方差比,选到累计解释方差达到80%或90%的维度数。实际项目中,有时候甚至只取两到三个主成分,纯粹是为了可视化。主成分的解释是个体力活,每个主成分是原始特征的线性组合,权重大的特征往往决定了这个主成分的含义。我会把权重绝对值排序,挑出前几名特征去业务上解释。
三是PCA和聚类的关系。PCA可以滤掉一部分噪声,让后续聚类更稳。但要注意:PCA保留了的是全局方差最大的方向,不等于聚类需要的判别方向。从严格意义上讲,PCA把数据落到主成分方向上再去聚类,是有信息损失的,只是损失的一般是无监督聚类不关心的噪声信息。如果目标就是为了分群,可以优先保留方差大的主成分再聚类,这属于工程经验,不是理论保证。
3.3 从LDA到t-SNE和UMAP
LDA全称线性判别分析,和PCA最大的区别是:PCA是无监督的,不管样本标签;LDA是有监督的,它要找的方向是“让不同类样本之间的距离尽量远、同类样本距离尽量近”的方向。所以LDA在分类场景里非常强大,但聚类场景里没有标签用不上。
t-SNE是另一种常见的非线性降维算法,尤其在可视化场景里几乎成了标配。它的核心思路分两步:先在高维空间里计算样本之间的相似度(用高斯分布建模概率分布),再在低维空间里构造一个相似度分布,用梯度下降让两个分布尽量接近。这样,高维空间里相近的样本在低维空间里也相近,高维空间里远的样本在低维空间里会被推开。
t-SNE有个特别关键的参数叫困惑度,可以理解为“每个点在低维空间里有多少个邻居”,一般取5到50之间,数据量大时取30左右比较稳。困惑度过小,点会散成碎片;过大,簇边界会模糊。
UMAP是近年来更受青睐的非线性降维工具,它的数学基础是黎曼流形和拓扑学,但用法上可以简单地理解为“更快的t-SNE”。UMAP在运行速度、全局结构保留程度上通常优于t-SNE,尤其适合几十万级别的数据可视化。它也有自己的参数,核心是n_neighbors和min_dist,前者控制局部邻居范围,后者控制点在低维空间里能挤多紧。
3.4 实战:高维数据可视化链路
很多人学习PCA时总是拿二维数据举例,但实际场景里我们更常遇到的是高维数据。以手写数字数据集为例,每一张图是64维像素特征,直接PCA降到二维,再用K-Means聚类,最后用t-SNE可视化,这一步是典型的高维聚类展示链路。
python复制from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.cluster import KMeans
# 加载64维手写数字数据
digits = load_digits()
X = digits.data # (1797, 64)
y = digits.target
# PCA降维到二维,做可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
# t-SNE降维到二维
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X)
# 在t-SNE结果上做K-Means聚类
kmeans = KMeans(n_clusters=10, random_state=42)
labels = kmeans.fit_predict(X_tsne)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='tab10', s=5)
plt.title('PCA visualization')
plt.subplot(1, 2, 2)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=labels, cmap='tab10', s=5)
plt.title('t-SNE + K-Means visualization')
plt.show()
这个例子里,PCA的结果和t-SNE的结果差异肉眼可见:PCA保留了全局方差,但不同数字的类别往往重叠在一起;t-SNE让同一类别的点聚成一团,不同类别之间的分离非常清晰。这里有个容易踩的坑:不要在t-SNE降维后的结果上直接做K-Means聚类并期望它一定比在原始数据上聚类更好。因为t-SNE的重要目标是保持局部邻居关系,它会把不同簇“推开”,但簇之间的距离在低维空间里并不保真,所以聚类结果要看指标来验证。
3.5 降维的避坑列表
我用过PCA做过大量实际项目,也踩过不少坑,这里集中总结一下:
- 先标准化再做PCA,这几乎是铁律。不做标准化,量纲大的特征直接主导第一主成分,你得到的不是一个“数据结构”的表示,而是最大尺度特征的投影。
- 主成分方向比较难解释,不要强行给每个主成分起名。业务上你可以说“第一主成分主要由消费金额、消费频次驱动”,但别硬编一个“消费活跃度”,除非经过业务验证。
- 别把PCA当作特征选择。PCA生成的新特征是原始特征的线性组合,它没有“剔除某个原始特征”,只是把它们换了坐标系。当你需要保留特征可解释性时,应该做特征选择而不是PCA。
- 稀有类别在t-SNE里可能被挤压成独立小点,看起来像簇,实际只是个别样本。所以可视化之后还要结合原始数据检查这些“簇”的样本量。
4. 聚类与降维组合使用的完整方案
4.1 一个可复用的流程模板
在实际项目中,我通常把聚类和降维串成一条线来用:
- 数据清洗:处理缺失值、异常值。
- 标准化:连续特征统一Z-score标准化。
- 降维:先看特征数量。特征上百个,先PCA压到几十维;特征几十个,可以先做相关性分析,删掉强相关特征,再决定是否需要PCA。
- 聚类:根据业务目标选择聚类算法。业务要固定数量的群,用K-Means;不知道群数,先用层次聚类看树状图;有噪声和异常趋势,用DBSCAN。
- 评估:有标签用ARI,没标签用轮廓系数,同时结合业务指标判断分群是否可用。
- 可视化:用PCA或t-SNE把结果画到二维,标注每个簇。
4.2 电商用户分群的真实操作案例
回到开头说的用户分群项目。当时的数据有年龄、性别、注册时长、累计消费金额、近30天消费金额、近30天消费次数、近30天登录次数、平均停留时长、浏览页面数等十几个特征。
我的处理顺序是这样的:
首先做相关性分析,发现“累计消费金额”和“近30天消费金额”相关系数0.8以上,二者存在严重共线性,先保留一个。其次标准化。然后做PCA,前5个主成分解释了约82%的方差,于是把特征压缩到5维。再跑K-Means,遍历K=2到8,计算轮廓系数,发现K=4时轮廓系数最高,同时业务意义最清楚:高价值沉睡用户、高活跃低消费用户、稳定中等消费用户、低活跃低消费用户。最后用t-SNE画二维图,四个簇在图上分得清清楚楚,直接拿去做运营分析。
这个案例里最关键的一步其实是“先删相关性高的特征再做PCA”。如果不删,共线性特征会把信息重复计入主成分,PCA虽然还是会压维度,但主成分权重会被重复特征带偏,结果解释起来很别扭。
4.3 先降维后聚类和先聚类后降维
这个问题经常有人问。我的经验是:常规场景先降维后聚类。原因很简单,聚类用距离衡量相似性,高维距离不稳定,先降维能提升聚类的稳定性。
但有一种情况可以考虑先聚类后降维:当你的特征维度不高(比如10到20维),且每个特征都有明确业务含义时,直接用原始特征跑K-Means,再用t-SNE可视化展示聚类结果,这样分群规则的业务解释性好,领导也能看懂。先降维会牺牲掉特征的可解释性,这在业务报告中是个减分项。
4.4 降维不是聚类的万能药
降维能解决很多问题,但不能掩盖数据本身的质量问题。如果原始数据里本身没有簇结构,比如样本均匀分布在一个超球面上,降维和聚类都白搭,强行的聚类只会得到一堆没有意义的边界。所以跑之前最好先用PCA降维到二维或者三维,肉眼看看样本是否有聚集的趋势。如果一片均匀分布,那说明数据集不适合做无监督聚类,要么换特征,要么换任务。
5. 考前突击:聚类与降维高频考点
5.1 高频概念题整理
结合各大高校机器学期末考题和面试题,聚类和降维的高频概念题我整理成了下面这张表,可以直接当复习提纲用:
| 题目 | 答题要点 |
|---|---|
| K-Means算法流程 | 初始化质心、分配样本、更新质心、迭代至收敛 |
| K-Means的优缺点 | 优点:简单快;缺点:需预设K、对噪声敏感、只能凸簇 |
| 如何选择K值 | 手肘法(SSE拐点)、轮廓系数、业务约束 |
| K-Means与KNN的区别 | K-Means是无监督聚类,KNN是有监督分类 |
| PCA的步骤和思想 | 中心化、算协方差、特征分解、取主成分 |
| PCA与LDA区别 | PCA无监督找最大方差方向,LDA有监督找判别方向 |
| 为什么高维不适合直接聚类 | 距离趋于平均、计算量大、噪声淹没信号 |
| DBSCAN核心参数 | eps邻域半径、min_samples最小样本数 |
| 层次聚类和K-Means区别 | 分层结构、不预设K、时间复杂度高 |
| 降维方法分类 | 线性:PCA/LDA;非线性:t-SNE/UMAP/等距映射 |
考试时最怕同学把“K-Means的目标函数”写成“最小化每个点到质心的欧氏距离之和”,这没问题,但要补一句:等价于最小化簇内平方和。
5.2 高频计算题套路
K-Means手算轮迭代几乎是期末必考题型。通常会给你6个点,K=2,初始质心给定,让你计算第一轮聚类结果和新质心。这种题的难点不是算数,而是别算错“距离”。提醒两个易错点:1. 距离要用欧氏距离,记得开根号;2. 计算新质心时要取簇内所有点的均值,不是取中点。
PCA的手算题则一般是这样:给一个2乘2的协方差矩阵,求特征值和特征向量,然后说明主成分对应的方差是多少。这道题本质是解特征方程,|A-λI|=0。算出λ后,把λ代回去解线性方程组得到特征向量。要注意特征向量要归一化,不然后续投影会出错。
如果时间充足,建议把两道题都完整做一遍,考场上几乎可以照葫芦画瓢。
5.3 手撕代码的套路
如果面试要求现场写聚类或降维,大多数公司不会要求你用numpy手写PCA,一般用sklearn就行。但有一个例外:手写K-Means。这个考的是对算法理解是否扎实。给一个简单的模板:
python复制def kmeans(X, k, max_iters=100):
# 随机初始化质心
centroids = X[np.random.choice(len(X), k, replace=False)]
for _ in range(max_iters):
# 分配样本到最近质心
distances = np.linalg.norm(X[:, np.newaxis, :] - centroids, axis=2)
labels = np.argmin(distances, axis=1)
# 更新质心
new_centroids = np.array([X[labels == i].mean(axis=0) for i in range(k)])
if np.allclose(new_centroids, centroids):
break
centroids = new_centroids
return labels, centroids
这段代码不复杂,但重点在于:用索引分配样本、用每个簇均值更新质心、用np.allclose判断收敛。面试官如果追问初始化方式,你说出K-Means++就行。笔试现场能快速写出这个,基本就能拿到分数。
6. 工具选型与常用库速查
6.1 sklearn使用向导
不用重复造轮子,这是工程实践的第一原则。Python里做聚类和降维,最常用的库就是scikit-learn。我把常用的类按功能列出来:
| 功能 | 类名称 | 核心参数 |
|---|---|---|
| K-Means聚类 | sklearn.cluster.KMeans | n_clusters, init, n_init, max_iter |
| 层次聚类 | sklearn.cluster.AgglomerativeClustering | n_clusters, linkage |
| 密度聚类 | sklearn.cluster.DBSCAN | eps, min_samples |
| 高斯混合 | sklearn.mixture.GaussianMixture | n_components, covariance_type |
| PCA降维 | sklearn.decomposition.PCA | n_components |
| t-SNE降维 | sklearn.manifold.TSNE | n_components, perplexity |
| UMAP降维 | umap.UMAP | n_neighbors, min_dist |
| 标准化 | sklearn.preprocessing.StandardScaler | 无 |
| 评估指标 | sklearn.metrics.silhouette_score, adjusted_rand_score | 无 |
另外,scipy.cluster.hierarchy提供了更详细的层次聚类函数,可以画出漂亮的树状图。如果想快速看不同K值下SSE的变化,可以用matplotlib画手肘图,这在选K时非常直观。
6.2 聚类算法怎么选型
我总结了一张选型对照表,基本上可以根据数据情况直接对号入座:
| 数据情况 | 推荐算法 | 原因 |
|---|---|---|
| 大样本、簇近似球形、预设K值 | K-Means | 速度快,内存友好 |
| 不知道K值、想观察层次结构 | 层次聚类 | 树状图直观,可切片选K |
| 有噪声离群点、任意形状簇 | DBSCAN | 能自动识别噪声 |
| 数据密度差异大 | OPTICS或DBSCAN调参 | 避免单eps失效 |
| 簇形状不固定、想要软聚类 | GMM | 每个样本可以属于多个簇的概率 |
| 高维数据聚类 | PCA降维后K-Means | 先降维再聚类更稳 |
6.3 工程上的效率建议
当样本量超过十万甚至百万级时,K-Means还是最好用的算法,因为sklearn的KMeans实现有K-Means++加速,而且能设置n_init并行。如果想要更快,可以用MiniBatchKMeans,它每次随机抽一个小批量数据更新质心,速度比KMeans快一个数量级,代价是聚类结果稍有波动。
DBSCAN在大数据量下会比较吃力,因为它需要计算样本间的邻域关系,复杂度接近O(n^2)。如果样本量大,可以用DBSCAN的近似加速实现,或者先抽样子集来调参,再上全量。
t-SNE在几万条数据上还能跑,几十万条就会非常慢,这种情况下优先考虑UMAP。UMAP在速度上优势明显,而且低维可视化效果不比t-SNE差。在“先用UMAP压二维、再用K-Means分群”这条路上,我在实践里跑过很多次,效果都还不错。
从我个人的经验来看,聚类和降维这类无监督方法,最大的难点从来不在于公式推导和调参,而在于你是否理解数据本身。K-Means跑得好的人,不是因为他知道手肘法,而是他知道自己的数据里大概存在几类人、每类人长什么样。PCA用得好的人,不是因为他会算特征值,而是他知道压缩完维度之后,主成分依然能解释业务上的差异。
最后再分享一个小技巧:新手刚接触高维数据的聚类时,别一头扎进算法细节里,先画图。用PCA或者t-SNE把数据压到二维,用散点图看看分布,能发现很多算法跑完才能发现的线索——比如离群点、类别不均衡、某个特征主导了聚类结果。这套“先降维看结构、再聚类做分群、最后用指标验证”的流程,我用过不下几十次,几乎每次都把我导向正确的方向。希望这篇梳理能帮你少走点弯路,也祝你期末顺利、面试顺利。
