如何将递归VIF特征消除功能集成到scikit-learn的Pipeline流水线中
核心错误点
- 错误使用scaler.fit的返回值:scikit-learn中转换器的
fit方法返回自身实例而非转换后的数据,原代码直接将self.scaler.fit(X)赋值给X,导致后续VIF计算接收的是StandardScaler对象而非数据集,触发属性错误。 - transform阶段维度不匹配:原代码先对全量输入特征做标准化,再强制将列名设置为筛选后的特征列表,此时标准化后的数据集列数与筛选后的列数不一致,触发维度或列名错误。
- 流程逻辑错误:VIF计算需要基于标准化后的特征值,原代码未完成标准化转换就传入VIF计算逻辑,结果不符合预期。
修复后代码
1. 自定义VIF转换器
import pandas as pd from sklearn import base, preprocessing from statsmodels.stats.outliers_influence import variance_inflation_factor class ReduceVIF(base.BaseEstimator, base.TransformerMixin): def __init__(self, thresh=10.0): # VIF值5-10为可接受范围,高于阈值则移除特征 self.thresh = thresh self.scaler = preprocessing.StandardScaler() # 存储fit阶段筛选出的保留特征名 self.predictors = None def fit(self, X, y=None): # 要求输入为带列名的pandas DataFrame if not isinstance(X, pd.DataFrame): raise TypeError("输入X必须为pandas DataFrame类型") # 先拟合标准化器,再转换训练集 X_scaled = pd.DataFrame( self.scaler.fit_transform(X), columns=X.columns ) # 基于标准化后的训练集计算VIF,筛选特征 X_selected = self.calculate_vif(X_scaled, self.thresh) # 存储保留的特征名,供transform阶段使用 self.predictors = X_selected.columns.tolist() return self def transform(self, X, y=None): if not isinstance(X, pd.DataFrame): raise TypeError("输入X必须为pandas DataFrame类型") # 先对全量输入特征做标准化 X_scaled = pd.DataFrame( self.scaler.transform(X), columns=X.columns ) # 只返回fit阶段筛选出的特征列 return X_scaled[self.predictors] @staticmethod def calculate_vif(X, thresh=10.0): dropped = True count = 0 # 最多迭代15次避免死循环 while dropped and count <= 15: dropped = False vif_list = [ variance_inflation_factor(X.values, X.columns.get_loc(var)) for var in X.columns ] max_vif = max(vif_list) if max_vif > thresh: max_loc = vif_list.index(max_vif) drop_col = X.columns[max_loc] print(f"移除特征{drop_col},VIF值为{max_vif:.2f}") X = X.drop(drop_col, axis=1) dropped = True count += 1 return X
2. 流水线构造
from sklearn import pipeline def make_finetuning_pipeline(model): steps = [ ('remove_multicollinearity', ReduceVIF(thresh=10)), ('model', model) ] return pipeline.Pipeline(steps)
使用注意事项
- 传入流水线的特征数据集必须是带列名的pandas DataFrame,否则会触发类型错误。
- 交叉验证时每折的ReduceVIF实例会独立拟合、筛选特征,不会出现各折特征列不一致的问题,也避免了全量数据预筛选导致的数据泄露。
内容的提问来源于stack exchange,提问作者ilovewt
相关产品推荐
相关产品推荐

