You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Sklearn管道中计算变换后特征的排列重要性

计算预处理后特征的排列重要性

问题背景

我通过Sklearn管道实现了完整的预处理+建模流程,现在需要计算预处理后特征的排列重要性,但默认sklearn.inspection.permutation_importance是针对原始特征计算的。同时我担心单独对测试集做预处理会导致数据泄露,想知道可行的实现技巧。

我的管道代码如下:

import numpy as np
import pandas as pd
import os
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.inspection import permutation_importance

###
# 数据加载部分
###

X_train, X_test, y_train, y_test = train_test_split(all_features, diagnosis, test_size=0.25, random_state=42)

pca_conn = Pipeline(
    steps = [("group_whiten", StandardScaler()),
             ('pca_conn', PCA(n_components = 100)),
            ("pca_whiten", StandardScaler())]
)

pca_grad = Pipeline(
    steps = [("group_whiten", StandardScaler()),
             ('pca_grad', PCA(n_components = 100)),
            ("pca_whiten", StandardScaler())]
)

pca_centroid_disp_pca = Pipeline(
    steps = [("group_whiten", StandardScaler()),
             ('pca_grad', PCA(n_components = 10)),
            ("pca_whiten", StandardScaler())]
)

pca_cortex_disp_pca = Pipeline(
    steps = [("group_whiten", StandardScaler()),
             ('pca_grad', PCA(n_components = 100)),
            ("pca_whiten", StandardScaler())]
)

cat_encoder = Pipeline(
    steps = [("cat_encoder", OneHotEncoder(handle_unknown="ignore"))]
)
whiten = Pipeline(
    steps = [("whiten", StandardScaler())]
)

preprocessor = ColumnTransformer(
    transformers=[
        ("pca_conn", pca_conn, conn_cols),
        ("pca_grad", pca_grad, grad_cols),
        ("pca_centroid_disp_pca", pca_centroid_disp_pca, centroid_disp_cols),
        ("pca_cortex_disp_pca", pca_cortex_disp_pca, cortex_disp_cols),
        ("encode_dataset", cat_encoder, ["dataset"]),
        ("encode_sex", cat_encoder, ["sex"]),
        ("whiten_fd", whiten, ["mean_fd"]),
        ("whiten_age", whiten, ["age"])
    ]
)

lr = LogisticRegression(random_state=42, max_iter = 10000)
clf = Pipeline([('preprocessor', preprocessor),
                ('lr',lr)])

trained_logreg = clf.fit(X_train, y_train)
trained_logreg.score(X_test, y_test)

# 默认计算的是原始特征的重要性,不是我要的
perm_acc = permutation_importance(trained_logreg, X_test, y_test,n_repeats=100, random_state=42, n_jobs = -1)

可行解决方案

直接拆分训练好的管道,用训练阶段拟合完成的预处理步骤转换测试集,再对转换后的特征计算排列重要性,完全不会造成数据泄露:

步骤1:提取预处理后的测试集特征

用训练好的管道里的preprocessor直接转换测试集,这个步骤用的是训练集拟合的参数,没有用到测试集的任何统计信息:

# 获取训练好的预处理模块
preprocessor = trained_logreg.named_steps['preprocessor']
# 转换测试集得到预处理后的特征矩阵
X_test_transformed = preprocessor.transform(X_test)

步骤2:提取训练好的模型

从管道中取出已经训练完成的逻辑回归模型:

model = trained_logreg.named_steps['lr']

步骤3:计算变换后特征的排列重要性

直接对预处理后的特征和测试集标签计算排列重要性:

perm_acc_transformed = permutation_importance(
    model, 
    X_test_transformed, 
    y_test, 
    n_repeats=100, 
    random_state=42, 
    n_jobs=-1
)

补充:给变换后特征命名(方便解读)

变换后的特征没有默认名称,需要手动对应每个预处理模块的输出:

# 手动生成变换后特征的名称列表
transformed_feature_names = []

# 处理每个transformer的输出特征名
for name, transformer, cols in preprocessor.transformers_:
    if name == 'pca_conn':
        # PCA输出100个特征
        transformed_feature_names += [f'pca_conn_{i}' for i in range(100)]
    elif name == 'pca_grad':
        transformed_feature_names += [f'pca_grad_{i}' for i in range(100)]
    elif name == 'pca_centroid_disp_pca':
        transformed_feature_names += [f'pca_centroid_disp_{i}' for i in range(10)]
    elif name == 'pca_cortex_disp_pca':
        transformed_feature_names += [f'pca_cortex_disp_{i}' for i in range(100)]
    elif name == 'encode_dataset':
        # 获取OneHotEncoder的类别名
        cat_names = transformer.named_steps['cat_encoder'].get_feature_names_out(cols)
        transformed_feature_names += list(cat_names)
    elif name == 'encode_sex':
        cat_names = transformer.named_steps['cat_encoder'].get_feature_names_out(cols)
        transformed_feature_names += list(cat_names)
    elif name == 'whiten_fd':
        transformed_feature_names += ['mean_fd_scaled']
    elif name == 'whiten_age':
        transformed_feature_names += ['age_scaled']

# 现在可以把重要性结果和特征名对应起来
importance_df = pd.DataFrame({
    'feature': transformed_feature_names,
    'mean_importance': perm_acc_transformed.importances_mean,
    'std_importance': perm_acc_transformed.importances_std
}).sort_values(by='mean_importance', ascending=False)

为什么不会数据泄露?

  • 预处理模块是用训练集拟合的,转换测试集时没有重新计算任何统计量(比如均值、方差、PCA成分),完全遵循训练/测试数据隔离原则。
  • 排列重要性本身是在测试集上评估特征打乱后的模型性能变化,这一步也不会用到训练集之外的信息。

内容的提问来源于stack exchange,提问作者victoris_93

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:45:24