SMOTE特征数超15时报错原因及适配高维特征的过采样方法
SMOTE算法15维特征以上运行报错问题说明
问题现象
使用imblearn.over_sampling模块中的SMOTE算法开展机器学习建模时,观测到明显的运行分界表现:
- 参考Jason Brownlee提供的代码生成15个特征的数据集并调用SMOTE时,程序可正常运行,对应代码如下:
from imblearn.over_sampling import SMOTE from sklearn.datasets import make_classification X, y = make_classification(n_samples=10000, n_features=15, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1) oversample = SMOTE() X, y = oversample.fit_resample(X, y)
- 当数据集特征数设置为16时,调用SMOTE会直接抛出故障,对应代码如下:
from imblearn.over_sampling import SMOTE from sklearn.datasets import make_classification X, y = make_classification(n_samples=10000, n_features=16, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1) oversample = SMOTE() X, y = oversample.fit_resample(X, y)
故障触发时程序抛出的完整错误栈如下:
Traceback (most recent call last): File "\\arete\shared\Los Angeles\Users\Active\bbonifacio\New ADVANCE\untitled1.py", line 13, in <module> X, y = oversample.fit_resample(X, y) File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\imblearn\base.py", line 83, in fit_resample output = self._fit_resample(X, y) File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\imblearn\over_sampling\_smote\base.py", line 324, in _fit_resample nns = self.nn_k_.kneighbors(X_class, return_distance=False)[:, 1:] File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\sklearn\neighbors\_base.py", line 763, in kneighbors results = PairwiseDistancesArgKmin.compute( File "sklearn\metrics\_pairwise_distances_reduction.pyx", line 691, in sklearn.metrics._pairwise_distances_reduction.PairwiseDistancesArgKmin.compute File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\sklearn\utils\fixes.py", line 151, in threadpool_limits return threadpoolctl.threadpool_limits(limits=limits, user_api=user_api) File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 171, in __init__ self._original_info = self._set_threadpool_limits() File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 268, in _set_threadpool_limits modules = _ThreadpoolInfo(prefixes=self._prefixes, File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 340, in __init__ self._load_modules() File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 373, in _load_modules self._find_modules_with_enum_process_module_ex() File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 485, in _find_modules_with_enum_process_module_ex self._make_module_from_path(filepath) File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 515, in _make_module_from_path module = module_class(filepath, prefix, user_api, internal_api) File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 606, in __init__ self.version = self.get_version() File "C:\Users\bbonifacio\Anaconda3\lib\site-packages\threadpoolctl.py", line 646, in get_version config = get_config().split() AttributeError: 'NoneType' object has no attribute 'split'
当前运行环境的依赖包版本如下:
- Sklearn: 1.1.1
- Imblearn: 0.9.1
- Threadpoolctl: 2.1.0
问题根因
该报错和SMOTE算法逻辑、特征维度上限完全无关,属于依赖包版本不兼容问题:
- 报错最终触发点是
threadpoolctl 2.1.0的已知缺陷:该版本在Windows平台枚举进程加载的动态链接库时,无法正确读取部分底层计算库(OpenBLAS、MKL等)的版本信息,返回空值None,后续代码直接对空值调用.split()方法就会抛出属性错误。 - 15维和16维特征的运行差异只是触发路径的巧合:当特征维度达到16时,scikit-learn的近邻计算模块会自动切换至多线程优化的
PairwiseDistancesArgKmin计算逻辑,该逻辑会调用threadpool_limits做线程数管控,刚好触发旧版threadpoolctl的缺陷;特征维度为15时近邻计算走单线程路径,不会触发线程池控制逻辑,因此表现为运行正常。
解决方法
SMOTE本身不存在15维特征的使用限制,所有SMOTE类衍生算法(BorderlineSMOTE、SMOTENC、ADASYN、KMeansSMOTE等)都支持任意维度的特征输入,不需要更换过采样方法,只需要修复依赖兼容性即可:
- 首选方案:直接升级threadpoolctl至修复了该缺陷的版本,执行以下命令:
pip install threadpoolctl>=3.0.0 --upgrade
升级后原代码无论特征维度为16还是更高值,都可正常运行。
- 临时规避方案:如果暂时无法升级依赖,可在代码最开头添加环境变量限制底层计算库的线程数,绕过触发缺陷的代码路径:
import os os.environ["OPENBLAS_NUM_THREADS"] = "1" os.environ["MKL_NUM_THREADS"] = "1"
内容的提问来源于stack exchange,提问作者Brandon Bonifacio
相关产品推荐
相关产品推荐

