Sklearn Pipeline处理加州住房数据集:训练拟合正常但测试数据转换报错问题求助
根据你的代码描述和训练/测试阶段的行为差异,我梳理了几个最可能导致测试阶段transform报错的原因,以及对应的修复方案:
1. 硬编码衍生特征列名引发的列匹配问题
你的get_columns()函数里硬编码了AddComboFeatures生成的衍生特征(比如population_per_room),虽然训练时add_features步骤确实会生成这些列,但这种耦合方式有两个隐患:
- 要是后续修改衍生特征的生成逻辑(比如新增/删除列),
get_columns()不会自动同步,直接导致ColumnTransformer找不到目标列; - 部分sklearn版本中,ColumnTransformer会提前校验列名,哪怕这些列是Pipeline后续步骤生成的,也可能在测试transform阶段触发"列不存在"的错误。
修复方案:动态获取衍生特征列名
不要硬编码衍生列,而是通过自定义Transformer的get_feature_names_out方法动态获取:
def get_columns(): # 定义原始特征列 original_numerical = [ "longitude", "latitude", "housing_median_age", "total_rooms", "total_bedrooms", "population", "households", "median_income" ] original_categorical = ["ocean_proximity"] # 从AddComboFeatures中动态获取衍生列名 combo_transformer = AddComboFeatures() all_combo_features = combo_transformer.get_feature_names_out(original_numerical) derived_features = [col for col in all_combo_features if col not in original_numerical] # 定义需要对数转换的列(原始列+指定衍生列) log_like_columns = [ "total_rooms", "total_bedrooms", "population", "households", "median_income", "population_per_room", "income_per_house", "income_per_population" ] # 剩余数值列(原始列+未被对数转换的衍生列) numerical_columns = original_numerical + derived_features remaining_columns = [col for col in numerical_columns if col not in log_like_columns] return log_like_columns, remaining_columns, original_categorical
2. Pipeline输出格式不统一导致的类型错误
你的preprocess_data函数里调用了pipeline.set_output(transform="pandas")确保输出是DataFrame,但测试代码里直接用features.get_pipeline()生成的Pipeline没有设置这个参数,默认输出是numpy数组。如果后续代码期望处理DataFrame,就会触发类型错误。
修复方案:统一设置Pipeline输出为DataFrame
修改get_pipeline()函数,在返回前统一配置输出格式:
def get_pipeline(): log_like_columns, remaining_columns, categorical_columns = get_columns() log_transformer = FunctionTransformer( log_transform, inverse_func=inv_log_transform, validate=False ) log_and_scale = Pipeline( [("log_transform", log_transformer), ("scale_transform", StandardScaler())] ) full_pipeline = Pipeline( [ ( "add_features", Pipeline( [ ("add_censored_cols", AddCensoredFeatures()), ("add_combo_cols", AddComboFeatures()), ], ), ), ( "preprocessing", ColumnTransformer( [ ("log_and_scale_lognormal", log_and_scale, log_like_columns), ("scale_normal", StandardScaler(), remaining_columns), ( "categorical_encoder", OneHotEncoder(sparse_output=False), categorical_columns, ), ], remainder="passthrough", verbose_feature_names_out=False # 顺便禁用列名前缀,避免后续手动处理 ), ), ] ) # 全局设置输出为DataFrame full_pipeline.set_output(transform="pandas") return full_pipeline
3. 自定义train_test_split函数的列缺失问题
你的测试代码用了自定义的train_test_split(df_housing),如果这个函数没有正确保留所有特征列(比如误删了AddComboFeatures依赖的total_rooms等列),就会在transform阶段触发ValueError: The following columns are missing。
修复方案:验证测试数据的列完整性
在测试代码里添加列检查逻辑:
train_X_raw, train_labels, test_X_raw, test_labels = train_test_split(df_housing) # 检查测试数据是否包含所有必填列 required_cols = ["total_rooms", "total_bedrooms", "population", "median_income", "households"] missing_cols = [col for col in required_cols if col not in test_X_raw.columns] if missing_cols: raise ValueError(f"测试数据缺失必填列: {missing_cols}")
如果确实存在列缺失,需要修复自定义的train_test_split函数,确保分割时保留所有特征列。
4. 手动修改列名引发的Pipeline复用问题
你的preprocess_data函数里手动去除了ColumnTransformer生成的列名前缀(比如log_and_scale_lognormal__total_rooms改成total_rooms),但这个操作是在fit_transform之后做的,并没有修改Pipeline内部的列名映射。如果后续复用Pipeline或直接用pipeline.transform,会导致训练数据和测试数据的列名不一致,引发错误。
修复方案:禁用ColumnTransformer的列名前缀
上面的get_pipeline()修改中已经添加了verbose_feature_names_out=False参数,这个参数会让ColumnTransformer直接输出原始列名,无需再手动分割处理,彻底避免列名不一致的问题。
内容的提问来源于stack exchange,提问作者Caitlin Kubina

