
无监督学习这个主题很多人第一次接触的时候会觉得它比监督学习虚——没有标签怎么知道学得对不对我刚开始学的时候也有这个困惑后来做几个实际项目才慢慢体会到无监督学习解决的是另一类问题当你手里只有一堆数据但完全不知道它们该怎么分类、有哪些规律时它能帮你把隐藏的结构挖出来。这篇内容适合已经了解机器学习基本概念、想系统搞懂无监督学习的朋友也适合正在做数据分析、用户画像、异常检测相关工作的从业者参考。我会从实际应用的角度出发把聚类、降维、关联规则这几大块讲透配上可运行的代码和踩坑经验尽量让你看完就能上手。1. 无监督学习到底在解决什么问题1.1 从没有答案说起监督学习的场景很好理解你有一堆邮件每封都标好了垃圾或正常模型学完之后就能判断新邮件。但现实中更多的情况是你拿到一批用户行为数据根本不知道这些用户应该分成几类也没有人来给你标注。这时候监督学习那套思路就走不通了因为你连正确答案都没有。无监督学习的核心思路是让算法自己去发现数据中的结构。它不需要标签只需要数据本身。算法会尝试找出数据点之间的相似性、密度分布、内在维度等特征然后把这些信息组织成有意义的形式。我经常用一个类比来解释监督学习像是老师拿着标准答案教你做题无监督学习像是把你扔进一个陌生的城市让你自己摸索出这个城市的功能分区——哪里是商业区、哪里是住宅区、哪里是工业区。没有人告诉你答案但你通过观察建筑密度、人流方向、店铺类型慢慢就能画出这个城市的结构图。1.2 三大典型任务类型无监督学习的任务类型很多但最核心的可以归为三类聚类Clustering把相似的数据点分到同一组。比如把用户按消费行为分成几群把新闻按主题归堆把图像按视觉特征分组。聚类的关键问题是相似怎么定义以及分多少组合适。降维Dimensionality Reduction把高维数据压缩到低维空间同时尽量保留重要信息。比如把1000维的用户特征压缩到20维方便可视化和后续建模。降维的核心矛盾是压缩率和信息保留之间的平衡。关联规则学习Association Rule Learning发现数据项之间的共现关系。最经典的就是购物篮分析——买了啤酒的人往往也会买尿布。这类方法在推荐系统、交叉销售里用得很多。这三类任务在实际项目中经常组合使用。比如先降维再做聚类或者先聚类再在每个簇内挖关联规则。后面我会分别展开讲。1.3 无监督学习的评估困境这是无监督学习最让人头疼的地方没有标签怎么知道结果好不好监督学习可以直接算准确率、召回率、F1一目了然。无监督学习不行你聚类分完组没有一个客观的正确答案来对比。这就导致两个问题一是算法选择困难K-Means和DBSCAN跑出来的结果可能完全不同你不知道该信哪个二是参数调优没有明确方向K-Means的K值、DBSCAN的eps和min_samples调起来很靠经验。实践中常用的评估手段有这么几种轮廓系数Silhouette Score衡量簇内紧密度和簇间分离度Calinski-Harabasz指数看簇间离散度和簇内离散度的比值Davies-Bouldin指数衡量簇间相似度。但这些指标都只是参考最终还是要结合业务来判断。我做过一个用户分群的项目轮廓系数最高的方案把用户分成了极其均匀的8群但业务方看了之后说这8群在我们眼里就是同一类人反而是一个轮廓系数稍低的3群方案更有业务解释性。提示无监督学习的结果评估业务可解释性的优先级往往高于数学指标。先跑指标筛选候选方案再拿给业务方看让他们判断哪个分组更有意义。2. 聚类算法从K-Means到DBSCAN的选型逻辑2.1 K-Means最常用但不一定最合适K-Means几乎是无监督学习的代名词很多人第一个学的聚类算法就是它。原理很简单先随机选K个中心点然后把每个数据点分配给最近的中心再重新计算每个簇的中心反复迭代直到中心不再移动。它的优点很突出速度快、原理直观、容易实现。但缺点同样明显需要预先指定K值这是最大的痛点。你往往不知道数据应该分几类。对初始中心敏感不同的初始化可能得到不同结果。sklearn默认用k-means初始化来缓解这个问题。假设簇是球形的K-Means用欧氏距离隐含假设每个簇是各向同性的球形分布。如果实际簇是长条形或不规则形状效果会很差。对异常值敏感均值受极端值影响大一个远离群体的点可能把中心拉偏。我一般会这样判断是否用K-Means如果数据量在百万级以内、簇的形状大致均匀、且我能接受必须指定K值这个约束那K-Means是首选。否则就要考虑其他算法。K值怎么选常用的方法有两种。手肘法Elbow Method是画出不同K值对应的簇内平方和inertia找曲线拐点。但拐点有时候不明显我一般会结合轮廓系数一起看。另一个经验是如果业务上能给出合理的分组数量范围优先用业务约束来定K数学指标只做辅助验证。from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import numpy as np inertias [] silhouettes [] K_range range(2, 11) for k in K_range: kmeans KMeans(n_clustersk, initk-means, n_init10, random_state42) labels kmeans.fit_predict(X_scaled) inertias.append(kmeans.inertia_) silhouettes.append(silhouette_score(X_scaled, labels)) # 找轮廓系数最高的K同时观察inertia的拐点 best_k K_range[np.argmax(silhouettes)] print(f轮廓系数最优K值: {best_k})2.2 DBSCAN基于密度的聚类思路DBSCANDensity-Based Spatial Clustering of Applications with Noise走的是完全不同的路线。它不预设簇的数量而是根据数据点的密度来形成簇如果一个点周围一定半径eps内有足够多的邻居min_samples它就被认为是核心点核心点之间密度可达的区域就连成一个簇。不属于任何簇的点被标记为噪声。这个特性让DBSCAN在几个场景下特别有用不知道簇的数量不需要指定K算法自己决定。存在噪声和异常值能自动识别并排除噪声点这在异常检测里非常实用。簇形状不规则能发现任意形状的簇不限于球形。但DBSCAN也有自己的问题。最大的挑战是eps和min_samples这两个参数不好调。eps太小大部分点都变成噪声eps太大所有点都连成一个簇。min_samples的选择跟数据维度和密度有关维度越高min_samples应该越大。我的经验是先用k-距离图来辅助选eps。具体做法是计算每个点到第k个最近邻的距离排序后画曲线找拐点位置作为eps的参考值。min_samples一般从维度数1开始试然后根据结果调整。from sklearn.cluster import DBSCAN from sklearn.neighbors import NearestNeighbors import matplotlib.pyplot as plt # 用k-距离图辅助选择eps k 5 neighbors NearestNeighbors(n_neighborsk) neighbors_fit neighbors.fit(X_scaled) distances, indices neighbors_fit.kneighbors(X_scaled) distances np.sort(distances[:, k-1], axis0) plt.plot(distances) plt.xlabel(数据点按距离排序) plt.ylabel(f第{k}近邻距离) plt.title(k-距离图拐点处即为eps参考值) plt.show() # 根据拐点选择eps dbscan DBSCAN(eps0.5, min_samples5) labels dbscan.fit_predict(X_scaled) n_clusters len(set(labels)) - (1 if -1 in labels else 0) n_noise list(labels).count(-1) print(f簇数量: {n_clusters}, 噪声点数量: {n_noise})2.3 层次聚类与高斯混合模型除了K-Means和DBSCAN还有两类算法值得了解。层次聚类Hierarchical Clustering分两种方向自底向上凝聚型和自顶向下分裂型。凝聚型更常用思路是先把每个点当成一个簇然后不断合并最近的簇直到满足停止条件。它的优势是能生成一个树状结构树状图你可以通过切割树状图得到不同数量的簇相当于一次性探索了多种分组方案。缺点是计算复杂度高O(n²)或O(n³)大数据集上跑不动。高斯混合模型GMM可以看作是K-Means的软聚类版本。K-Means把每个点硬性分配给一个簇GMM则给出每个点属于各个簇的概率。它假设数据由多个高斯分布混合生成通过EM算法估计每个高斯分布的参数。GMM适合簇之间有重叠、需要概率输出的场景比如用户可能同时属于多个兴趣群体。选型的时候我会这样考虑数据量小且需要探索多种分组方案用层次聚类需要概率输出或簇有重叠用GMM数据量大且簇形状规则用K-Means有噪声且簇形状不规则用DBSCAN。算法需要指定簇数簇形状假设噪声处理适用场景K-Means是球形敏感大数据集、规则簇DBSCAN否任意自动识别异常检测、不规则簇层次聚类否可切割任意不处理小数据集、探索性分析GMM是椭圆形概率处理重叠簇、概率输出3. 降维PCA、t-SNE与UMAP的实战取舍3.1 PCA线性降维的基准线主成分分析PCA是最经典的线性降维方法。它的核心思想是找到数据方差最大的方向作为第一主成分然后在这个方向的垂直平面上找方差第二大的方向作为第二主成分以此类推。通过保留前几个主成分就能在尽量不丢失信息的前提下降低维度。PCA的数学本质是对协方差矩阵做特征值分解或对数据矩阵做SVD。特征值大的方向就是数据变化最剧烈的方向也就是信息量最大的方向。保留前k个主成分相当于保留了数据中最大的k个方差方向。实际使用中有几个关键决策点要不要标准化几乎总是要。PCA对特征的尺度非常敏感。如果一列是身高厘米范围150-200另一列是年收入元范围50000-500000不标准化的话年收入会主导整个分析。用StandardScaler做Z-score标准化是标准做法。保留多少个主成分常用累计方差贡献率来判断。一般保留累计贡献率达到85%或95%的主成分。但这不是铁律如果降维是为了可视化直接降到2维或3维如果是为了后续建模可以看下游任务的表现在微调。PCA的局限性它只能捕捉线性关系。如果数据分布在一个弯曲的流形上PCA会强行用直线去拟合效果不好。这时候就需要非线性降维方法。from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) pca PCA(n_components0.95) # 保留95%方差 X_pca pca.fit_transform(X_scaled) print(f原始维度: {X.shape[1]}) print(f降维后维度: {X_pca.shape[1]}) print(f各主成分方差贡献率: {pca.explained_variance_ratio_}) print(f累计方差贡献率: {pca.explained_variance_ratio_.cumsum()})3.2 t-SNE可视化利器但别乱用t-SNEt-Distributed Stochastic Neighbor Embedding在可视化领域几乎是标配。它能把高维数据映射到2维或3维同时保留局部邻域结构出来的图往往能清晰展示数据的聚类结构。但t-SNE有几个非常容易踩的坑我必须重点说第一t-SNE的距离没有全局意义。两个簇在图上离得远不代表它们在原始空间中真的远。t-SNE只保证局部邻域关系全局结构可能被扭曲。我见过有人拿t-SNE图说这两个簇很相似因为它们离得近这是错误的解读方式。第二困惑度perplexity参数影响巨大。perplexity大致可以理解为每个点考虑多少个邻居典型值在5到50之间。小的perplexity关注非常局部的结构大的perplexity关注更全局的结构。同一个数据集用不同perplexity跑出来的图可能看起来完全不同。我的习惯是跑多个perplexity值比如5、15、30、50看哪些结构是稳定的。第三t-SNE计算慢。原始t-SNE是O(n²)的复杂度大数据集上跑不动。Barnes-Hut优化版本降到O(n log n)但数据量超过十万级别还是吃力。第四t-SNE不适合作为特征提取器。它没有transform方法不能把新数据映射到已有空间。所以t-SNE只适合做可视化不适合做预处理。from sklearn.manifold import TSNE # 先用PCA降到50维再跑t-SNE这是常见做法 pca_50 PCA(n_components50, random_state42) X_pca50 pca_50.fit_transform(X_scaled) tsne TSNE(n_components2, perplexity30, n_iter1000, random_state42) X_tsne tsne.fit_transform(X_pca50) plt.scatter(X_tsne[:, 0], X_tsne[:, 1], clabels, cmapviridis, s5) plt.title(t-SNE可视化 (perplexity30)) plt.show()注意跑t-SNE之前先用PCA降到50维左右既能加速计算又能去除一些噪声。这是van der Maatent-SNE作者本人推荐的做法。3.3 UMAP速度与结构的平衡UMAPUniform Manifold Approximation and Projection是近几年流行起来的降维方法在很多场景下可以替代t-SNE。它的优势在于速度快比t-SNE快很多大数据集上优势明显。保留更多全局结构虽然也以局部结构为主但相比t-SNE全局结构保留得更好。支持transform可以把新数据映射到已有空间这意味着它可以用作特征提取。参数更直观n_neighbors控制局部与全局的平衡min_dist控制点的聚集程度。UMAP的数学基础是黎曼几何和代数拓扑理解起来比较复杂但使用上并不难。n_neighbors越大越关注全局结构越小越关注局部细节。min_dist越小点越聚集。我的经验是如果只是做可视化t-SNE和UMAP都可以UMAP更快如果需要把降维结果用于后续建模UMAP更合适因为它支持transform如果数据量特别大百万级以上UMAP几乎是唯一选择。import umap reducer umap.UMAP(n_neighbors15, min_dist0.1, n_components2, random_state42) X_umap reducer.fit_transform(X_scaled) plt.scatter(X_umap[:, 0], X_umap[:, 1], clabels, cmapviridis, s5) plt.title(UMAP可视化 (n_neighbors15, min_dist0.1)) plt.show()3.4 降维方法的选择框架面对一个具体问题怎么选降维方法我一般按这个流程走先问目的是什么。如果是为了可视化优先考虑UMAP或t-SNE如果是为了后续建模做特征压缩优先考虑PCA或UMAP如果是为了去噪PCA是标准选择。再看数据规模和维度。数据量小万级以下什么方法都能跑数据量大十万级以上优先UMAP或PCA维度极高上千维先用PCA降到几百维再用其他方法。最后看是否需要解释性。PCA的主成分有明确的方差解释可以分析每个主成分受哪些原始特征影响t-SNE和UMAP的维度没有可解释性纯粹是嵌入空间。4. 关联规则与异常检测中的无监督思路4.1 购物篮分析Apriori与FP-Growth关联规则学习最经典的应用就是购物篮分析。核心概念有三个支持度Support某个项集在所有交易中出现的频率。比如啤酒和尿布同时出现的支持度是2%意味着2%的交易里这两样都买了。置信度Confidence在买了A的条件下买B的概率。规则A→B的置信度 P(A∩B) / P(A)。提升度Lift置信度除以B的基础概率。提升度大于1说明A的出现确实提升了B的出现概率两者正相关等于1说明独立小于1说明负相关。Apriori算法是最经典的关联规则挖掘算法思路是先找出频繁项集支持度超过阈值的项集再从频繁项集中生成置信度超过阈值的规则。它的缺点是候选集生成过程中需要多次扫描数据库数据量大时效率低。FP-Growth用FP树频繁模式树来压缩数据只需要扫描数据库两次效率比Apriori高很多。实际项目中我基本都用FP-Growth。from mlxtend.frequent_patterns import fpgrowth, association_rules # 数据格式每行是一笔交易每列是一个商品值为True/False frequent_itemsets fpgrowth(df, min_support0.01, use_colnamesTrue) rules association_rules(frequent_itemsets, metriclift, min_threshold1.2) # 按提升度排序看最强的关联 rules_sorted rules.sort_values(lift, ascendingFalse) print(rules_sorted[[antecedents, consequents, support, confidence, lift]].head(10))实际使用中有几个经验min_support的设置很关键太高会漏掉有意义的规则太低会产生大量无意义的规则。我一般会先看商品的出现频率分布把min_support设在能覆盖大部分商品但又不至于产生爆炸性组合的水平。另外提升度比置信度更可靠因为置信度高可能只是因为后件本身就很常见。4.2 孤立森林与LOF无监督异常检测异常检测是无监督学习的一个重要应用方向。核心假设是异常点是少数且与正常数据有显著差异的。孤立森林Isolation Forest的思路很巧妙异常点因为少而不同更容易被随机切分隔离出来。算法随机选择特征和切分点构建多棵孤立树异常点通常在更浅的叶节点就被隔离。它的优势是速度快、不需要假设数据分布、对高维数据也有效。局部离群因子LOF从密度的角度定义异常一个点的局部密度如果显著低于其邻居的局部密度就被认为是异常。LOF适合密度不均匀的数据集因为它是局部比较而非全局比较。from sklearn.ensemble import IsolationForest from sklearn.neighbors import LocalOutlierFactor # 孤立森林 iso_forest IsolationForest(contamination0.05, random_state42) outlier_labels iso_forest.fit_predict(X_scaled) # -1表示异常1表示正常 # LOF lof LocalOutlierFactor(n_neighbors20, contamination0.05) outlier_labels_lof lof.fit_predict(X_scaled)contamination参数表示预期的异常比例这个值需要根据业务经验来设。如果不确定可以先设一个保守的值比如0.01然后人工检查被标记的异常点是否合理再调整。4.3 自编码器用重构误差做异常检测自编码器Autoencoder是神经网络版的降维方法。它由编码器和解码器组成编码器把输入压缩成低维表示解码器尝试从低维表示重构原始输入。训练目标是让重构误差最小。用自编码器做异常检测的逻辑是正常数据在训练集中常见自编码器学得很好重构误差小异常数据没见过重构误差大。所以重构误差可以作为异常分数。这个方法在图像、时序数据上效果不错但需要注意几点训练集要尽量干净如果混入了太多异常自编码器会把异常也学好瓶颈层的维度要合适太小会丢失正常数据的细节太大会让异常也能被重构好阈值的选择需要根据验证集或业务经验来定。5. 无监督学习的完整项目流程与踩坑记录5.1 从原始数据到可用结果的完整链路一个完整的无监督学习项目我通常会走这么几步第一步数据探索与清洗。先看数据的基本统计量、缺失值分布、异常值情况。无监督学习对数据质量特别敏感因为没有标签来纠偏。缺失值处理要谨慎删除还是填充取决于缺失机制。异常值不要急着删它们可能正是你要找的东西。第二步特征工程。无监督学习的效果很大程度上取决于特征的质量。类别特征需要编码One-Hot、Target Encoding等数值特征需要标准化或归一化。如果特征维度很高考虑先做一轮特征选择。第三步降维可选。如果特征维度超过50我一般会先做PCA降维既能加速后续计算又能去除噪声。但要注意降维后的特征失去了原始含义如果业务需要解释性这一步要慎重。第四步聚类/异常检测/关联规则。根据任务类型选择算法。我通常会跑多个算法对比结果看哪些结构是稳定的。第五步结果评估与业务验证。用内部指标轮廓系数等做初步筛选然后拿给业务方看让他们判断分组是否有意义。这一步往往需要反复迭代。第六步结果应用。聚类结果可以用于用户分群、精准营销异常检测结果可以用于风控、故障预警关联规则可以用于推荐、交叉销售。5.2 那些文档里不会写的坑坑一标准化做在了错误的位置。我见过有人在train/test split之前就对整个数据集做了标准化导致数据泄露。正确做法是先在训练集上fit scaler然后transform训练集和测试集。虽然无监督学习通常没有严格的train/test划分但如果你后续要用聚类结果做分类任务这个坑就很重要。坑二把聚类标签当成真实类别。聚类出来的簇编号是任意的簇1不代表比簇2好也不代表任何顺序关系。我见过有人把聚类标签直接当成有序变量去做回归这是完全错误的。坑三忽略特征的业务含义。纯数学上的最优聚类业务上可能毫无意义。比如把用户按年龄和收入聚类数学上分得很开但业务方说我们关心的是消费频次不是年龄。特征选择必须结合业务目标。坑四t-SNE图上的簇间距离被过度解读。前面说过t-SNE的距离没有全局意义。两个簇在图上离得近不代表它们相似。我见过有人在t-SNE图上量距离来做定量分析这是误用。坑五DBSCAN的eps用默认值。DBSCAN的默认eps0.5这个值在大多数真实数据集上都不合适。必须用k-距离图来辅助选择。坑六关联规则的min_support设得太高。很多人第一次跑Apriorimin_support设0.1结果什么都没挖出来。真实零售数据中单个商品的购买频率可能只有百分之几组合就更低了。min_support要从0.01甚至0.001开始试。5.3 评估指标的实际使用心得轮廓系数的取值范围是-1到1越接近1越好。但实际使用中轮廓系数高不一定代表结果好。我遇到过轮廓系数0.7的聚类方案业务方完全不认可也遇到过轮廓系数0.3的方案业务方觉得很有洞察。所以我的做法是轮廓系数只用来排除明显差的方案比如低于0.2的在剩下的方案中由业务来选。Calinski-Harabasz指数越大越好它计算的是簇间离散度与簇内离散度的比值。这个指标在比较不同K值时比较有用但同样不能作为唯一标准。Davies-Bouldin指数越小越好它衡量的是簇内散度与簇间距离的比值。和上面两个指标一样只做参考。提示无监督学习的评估我的经验是数学指标筛候选业务验证定终选。不要指望某个指标能直接告诉你最优方案。5.4 一个完整的用户分群案例最后分享一个我做过的用户分群项目把上面的流程串起来。背景是某电商平台想对用户做精细化运营但不知道用户应该怎么分。数据包括用户的浏览行为、购买行为、互动行为等共200多个特征。第一步数据清洗。删除了注册不满7天的用户行为数据不足处理了缺失值用中位数填充数值特征用众数填充分类特征。第二步特征工程。从200多个原始特征中筛选出30个与消费行为相关的特征包括最近一次购买间隔、购买频次、平均客单价、浏览转化率等。对偏态分布的特征做了对数变换然后全部标准化。第三步降维。用PCA把30维降到12维累计方差贡献率约88%。第四步聚类。同时跑了K-MeansK3到8、DBSCAN和GMM。K-Means在K5时轮廓系数最高0.52DBSCAN识别出4个簇加一部分噪声GMM的BIC在5个分量时最小。第五步业务验证。把K-Means的5群结果拿给运营团队看他们根据每群的特征均值给每群起了名字高价值活跃用户价格敏感型用户低频高客单用户新用户流失预警用户。运营团队认为这个分组很有业务意义决定采用。第六步应用。针对不同群体制定了不同的运营策略高价值用户给VIP权益价格敏感用户推优惠券流失预警用户发召回短信。上线三个月后整体复购率提升了12%。这个项目让我最深的体会是无监督学习的技术门槛其实不高难的是把技术结果翻译成业务语言。算法工程师和业务方之间的沟通往往比调参更花时间。