Windows环境下Sklearn凝聚聚类触发MemoryError内存不足问题求助
你的问题根源很明确:AgglomerativeClustering使用ward linkage时,默认会计算全量的两两样本距离矩阵,对于406829个样本来说,这个矩阵的元素数量是n*(n-1)/2≈8.27e10个,每个float64类型占8字节,总内存需求高达617GiB——这显然远远超出了你16GB物理内存的承载能力。修改Jupyter的max_buffer_size没用,因为这个参数管的是Jupyter的缓存,解决不了Python进程本身需要的巨量内存问题。
下面给你几个可行的解决方案,按优先级排序:
1. 用稀疏连接矩阵优化凝聚聚类
sklearn的AgglomerativeClustering支持通过connectivity参数限制只计算样本与邻近样本的距离,避免生成全量距离矩阵,能大幅降低内存占用。具体来说,我们可以用k近邻图来定义样本间的连接关系,只让每个样本和它的k个邻居参与聚类计算:
import pandas as pd import numpy as np from matplotlib import pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.cluster import AgglomerativeClustering from sklearn.neighbors import kneighbors_graph # 先标准化数据(ward linkage对尺度敏感,建议先做标准化) scaler = StandardScaler() data_scaled = scaler.fit_transform(data) # 生成k近邻连接矩阵,k值可以根据你的数据调整,比如先试10 connectivity = kneighbors_graph(data_scaled, n_neighbors=10, include_self=False) # 传入connectivity参数进行聚类 aggloclust = AgglomerativeClustering( affinity='euclidean', linkage='ward', n_clusters=5, connectivity=connectivity ).fit(data_scaled) labels = aggloclust.labels_ plt.scatter(data_scaled[:,0], data_scaled[:,1], c=labels) plt.show()
这个方法能把内存需求从数百GiB降到几百MB级别,同时保留ward linkage的聚类特性。
2. 先降维再聚类
你的数据集是8维,先通过降维算法(比如PCA)把特征维度压缩到2-3维,再进行凝聚聚类。虽然样本数还是40万,但降维后聚类计算的速度会大幅提升,同时如果降维保留了足够多的原始数据信息,聚类结果也不会有太大偏差:
from sklearn.decomposition import PCA # 标准化+PCA降维 scaler = StandardScaler() data_scaled = scaler.fit_transform(data) pca = PCA(n_components=2) # 降到2维,方便后续可视化 data_pca = pca.fit_transform(data_scaled) # 对降维后的数据做聚类 aggloclust = AgglomerativeClustering( affinity='euclidean', linkage='ward', n_clusters=5 ).fit(data_pca) labels = aggloclust.labels_ plt.scatter(data_pca[:,0], data_pca[:,1], c=labels) plt.show()
注意:这个方法本身不会解决全距离矩阵的内存问题,但降维后可以和上面的connectivity方法结合,进一步优化性能。
3. 换用适合大数据集的聚类算法
凝聚聚类(层次聚类)的时间和空间复杂度都是O(n²),天生不适合超大规模数据集。如果可以接受非层次聚类的结果,MiniBatchKMeans是更好的选择——它用小批量样本迭代计算,内存占用极低,速度极快,适合百万级别的数据集:
from sklearn.cluster import MiniBatchKMeans # 标准化数据 scaler = StandardScaler() data_scaled = scaler.fit_transform(data) # MiniBatchKMeans聚类,batch_size可以根据内存调整 mbk = MiniBatchKMeans(n_clusters=5, batch_size=10000, random_state=42) labels = mbk.fit_predict(data_scaled) plt.scatter(data_scaled[:,0], data_scaled[:,1], c=labels) plt.show()
4. 临时方案:扩大虚拟内存(不推荐)
如果你一定要坚持用原始的ward linkage凝聚聚类,可以尝试扩大Windows的虚拟内存(页面文件):
- 右键「此电脑」→「属性」→「高级系统设置」→「高级」→「性能」→「设置」→「高级」→「虚拟内存」→「更改」
- 取消「自动管理所有驱动器的分页文件大小」,然后把系统盘的分页文件设为「自定义大小」,比如初始大小设为32GB,最大值设为64GB或更大
这个方法能让系统把部分数据放到硬盘上,但速度会非常慢(硬盘读写速度远低于内存),可能要运行几个小时甚至更久,只适合紧急场景。
内容的提问来源于stack exchange,提问作者Simran Kaur

