Scikit-learn与Numpy内存泄漏:GaussianMixture调用异常原因咨询
sklearn GaussianMixture 动态样本量下内存持续增长的原因分析
现象复现
- 无内存增长场景:固定使用500000条像素样本循环训练GMM,内存仅呈现正常波动,无持续上涨
import os, psutil from sklearn import mixture import numpy as np import matplotlib.pyplot as plt def get_size(): process = psutil.Process() return process.memory_info().rss / 100000 sizes = [] for _ in range(100): training_pixels = np.random.random((500000,3)) gmm = mixture.GaussianMixture( n_components=5, covariance_type="full", random_state=40 ).fit(training_pixels) size = get_size() sizes.append(size) del gmm, training_pixels plt.plot(sizes)
(对应内存变化:波动但无持续增长)
- 内存持续增长场景:样本量在200000-500000之间随机取值,循环训练后内存持续上涨
import os, psutil from sklearn import mixture import numpy as np import matplotlib.pyplot as plt def get_size(): process = psutil.Process() return process.memory_info().rss / 100000 sizes = [] for _ in range(100): training_pixels = np.random.random((np.random.randint(200000,500000),3)) gmm = mixture.GaussianMixture( n_components=5, covariance_type="full", random_state=40 ).fit(training_pixels) size = get_size() sizes.append(size) del gmm, training_pixels plt.plot(sizes)
(对应内存变化:持续增长)
原因分析
- 底层数值库的内存池复用机制
sklearn的GaussianMixture依赖BLAS/LAPACK等底层数值计算库完成EM算法的矩阵运算,这些库会维护内存池优化重复固定大小的内存分配:
- 样本量固定时,每次训练所需的临时矩阵(如协方差计算、期望最大化的中间变量)大小完全一致,内存池可直接复用之前释放的内存块,不会产生无法回收的内存碎片;
- 样本量动态变化时,每次需要的临时内存大小不同,底层库会分配新的内存块,而释放的小块内存因大小不匹配无法被后续分配复用,逐渐积累成内存碎片——进程的RSS(驻留集大小)持续上涨,看似内存泄漏,实际是碎片化内存无法被系统回收或复用。
Python垃圾回收的局限性
del语句仅删除Python对象的引用,Python垃圾回收器负责回收Python层面的对象,但GaussianMixture底层C扩展分配的内存(如numpy数组的底层内存、BLAS库分配的临时缓存)不受Python GC直接管理。如果底层库在动态大小内存分配后未正确释放或复用内存池,这部分内存会留在进程空间,导致RSS持续增长。EM算法的动态内存分配特性
GMM的EM算法每次迭代会根据样本量分配临时内存,样本量变化时,每次迭代的内存需求不同,底层库无法提前预留固定大小的内存池,只能按需分配新内存,进一步加剧内存碎片的积累。
内容的提问来源于stack exchange,提问作者Adrien Nivaggioli
相关产品推荐
相关产品推荐

