具有虚拟变量的特征重要性

机器算法验证 分类数据 随机森林 解释 重要性
2022-02-07 23:33:36

我试图了解如何获得已分解为虚拟变量的分类变量的特征重要性。我正在使用 scikit-learn,它不会像 R 或 h2o 那样为您处理分类变量。

如果我将一个分类变量分解为虚拟变量,我会在该变量中获得每个类的单独特征重要性。

我的问题是,通过简单地将这些虚拟变量重要性重新组合成分类变量的重要性值是否有意义?

来自统计学习要素的第 368 页:

的平方相对重要性是在选择它作为分裂变量的所有内部节点上的这种平方改进的总和X

这让我认为,由于重要性值已经通过在选择变量的每个节点处求和一个度量来创建,我应该能够结合虚拟变量的变量重要性值来“恢复”分类变量的重要性。当然,我不希望它完全正确,但无论如何这些值都是非常精确的值,因为它们是通过随机过程找到的。

我编写了以下 python 代码(在 jupyter 中)作为调查:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestClassifier
import re

#%matplotlib inline
from IPython.display import HTML
from IPython.display import set_matplotlib_formats

plt.rcParams['figure.autolayout'] = False
plt.rcParams['figure.figsize'] = 10, 6
plt.rcParams['axes.labelsize'] = 18
plt.rcParams['axes.titlesize'] = 20
plt.rcParams['font.size'] = 14
plt.rcParams['lines.linewidth'] = 2.0
plt.rcParams['lines.markersize'] = 8
plt.rcParams['legend.fontsize'] = 14

# Get some data, I could not easily find a free data set with actual categorical variables, so I just created some from continuous variables
data = load_diabetes()
df = pd.DataFrame(data.data, columns=[data.feature_names])
df = df.assign(target=pd.Series(data.target))

# Functions to plot the variable importances
def autolabel(rects, ax):
    """
    Attach a text label above each bar displaying its height
    """
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x() + rect.get_width()/2.,
                1.05*height,
                f'{round(height,3)}',
                ha='center',
                va='bottom')

def plot_feature_importance(X,y,dummy_prefixes=None, ax=None, feats_to_highlight=None):

    # Find the feature importances by fitting a random forest
    forest = RandomForestClassifier(n_estimators=100)
    forest.fit(X,y)
    importances_dummy = forest.feature_importances_

    # If there are specified dummy variables, combing them into a single categorical 
    # variable by summing the importances. This code assumes the dummy variables were
    # created using pandas get_dummies() method names the dummy variables as
    # featurename_categoryvalue
    if dummy_prefixes is None:
        importances_categorical = importances_dummy
        labels = X.columns
    else:
        dummy_idx = np.repeat(False,len(X.columns))
        importances_categorical = []
        labels = []

        for feat in dummy_prefixes:
            feat_idx = np.array([re.match(f'^{feat}_', col) is not None for col in X.columns])
            importances_categorical = np.append(importances_categorical,
                                                sum(importances_dummy[feat_idx]))
            labels = np.append(labels,feat)
            dummy_idx = dummy_idx | feat_idx
        importances_categorical = np.concatenate((importances_dummy[~dummy_idx],
                                                  importances_categorical))
        labels = np.concatenate((X.columns[~dummy_idx], labels))

    importances_categorical /= max(importances_categorical)
    indices = np.argsort(importances_categorical)[::-1]

    # Plotting

    if ax is None:
        fig, ax = plt.subplots()

    plt.title("Feature importances")
    rects = ax.bar(range(len(importances_categorical)),
                   importances_categorical[indices],
                   tick_label=labels[indices],
                   align="center")
    autolabel(rects, ax)

    if feats_to_highlight is not None:
        highlight = [feat in feats_to_highlight for feat in labels[indices]]
        rects2 = ax.bar(range(len(importances_categorical)),
                       importances_categorical[indices]*highlight,
                       tick_label=labels[indices],
                       color='r',
                       align="center")
        rects = [rects,rects2]
    plt.xlim([-0.6, len(importances_categorical)-0.4])
    ax.set_ylim((0, 1.125))
    return rects

# Create importance plots leaving everything as categorical variables. I'm highlighting bmi and age as I will convert those into categorical variables later
X = df.drop('target',axis=1)
y = df['target'] > 140.5

plot_feature_importance(X,y, feats_to_highlight=['bmi', 'age'])
plt.title('Feature importance with bmi and age left as continuous variables')

#Create an animation of what happens to variable importance when I split bmi and age into n (n equals 2 - 25) different classes
# %%capture

fig, ax = plt.subplots()

def animate(i):
    ax.clear()

    # Split one of the continuous variables up into a categorical variable with i balanced classes
    X_test = X.copy()
    n_categories = i+2
    X_test['bmi'] = pd.cut(X_test['bmi'],
                           np.percentile(X['bmi'], np.linspace(0,100,n_categories+1)),
                           labels=[chr(num+65) for num in range(n_categories)])
    X_test['age'] = pd.cut(X_test['age'],
                           np.percentile(X['age'], np.linspace(0,100,n_categories+1)),
                           labels=[chr(num+65) for num in range(n_categories)])
    X_test = pd.get_dummies(X_test, drop_first=True)

    # Plot the feature importances
    rects = plot_feature_importance(X_test,y,dummy_prefixes=['bmi', 'age'],ax=ax, feats_to_highlight=['bmi', 'age'])
    plt.title(f'Feature importances for {n_categories} bmi and age categories')
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)

    return [rects,]

anim = animation.FuncAnimation(fig, animate, frames=24, interval=1000)

HTML(anim.to_html5_video())

以下是一些结果:

在此处输入图像描述

在此处输入图像描述

我们可以观察到变量重要性主要取决于类别的数量,这导致我质疑这些图表的实用性。尤其是age 达到比其连续对应物更高的价值的重要性。

最后,如果我将它们保留为虚拟变量(仅 bmi)的示例:

# Split one of the continuous variables up into a categorical variable with i balanced classes
X_test = X.copy()
n_categories = 5
X_test['bmi'] = pd.cut(X_test['bmi'],
                       np.percentile(X['bmi'], np.linspace(0,100,n_categories+1)),
                       labels=[chr(num+65) for num in range(n_categories)])
X_test = pd.get_dummies(X_test, drop_first=True)

# Plot the feature importances
rects = plot_feature_importance(X_test,y, feats_to_highlight=['bmi_B','bmi_C','bmi_D', 'bmi_E'])
plt.title(f"Feature importances for {n_categories} bmi categories")

在此处输入图像描述

3个回答

通常在处理“特征重要性”时,记住在大多数情况下,正则化方法通常是一个很好的选择。它会自动为手头的问题“选择最重要的特征”。现在,如果我们不想遵循正则化的概念(通常在回归的上下文中),随机森林分类器和置换测试的概念自然会为变量组的特征重要性提供解决方案。这实际上已经在这里问过:“ R 中随机森林分类中一组预测变量的相对重要性”几年前。更严格的方法,如 Gregorutti 等人的:“使用随机森林分组变量重要性和在多元函数数据分析中的应用“。Chakraborty 和 Pal 的Selecting Useful Groups of Features in a Connectionist Framework在多层感知器的背景下研究了这项任务。回到 Gregorutti 等人的论文,他们的方法直接适用于任何类型的分类/回归算法简而言之,我们在训练期间使用的每个袋外样本中使用随机排列的版本。

如上所述,虽然置换测试最终是一种启发式方法,但过去准确解决的是在正则化回归的背景下对虚拟变量的惩罚。该问题的答案是Group-LASSOGroup-LARSGroup-Garotte该工作的开创性论文是 Yuan 和 Lin 的:“ Model selection andestimate in regression with grouped variables ”(2006 年)和 Meier 等人的:“ The group lasso for Logistic regression ”(2008 年)。这种方法使我们能够在以下情况下工作:“每个因素可能有多个级别,并且可以通过一组虚拟变量来表示”(Y&L 2006)。效果是这样的“组套索鼓励因子水平的稀疏性。” (Y&L 2006)。基本思想是标准惩罚被正定矩阵 ,的范数取代,其中是我们检查的组数。CV 有一些关于 Group-Lasso 的好线程hereherehere ,如果你想进一步研究的话。[因为我们特别提到了 Python:我没有使用 Python 的包,但它似乎包括grouped lasso正则化。]l1Kjj={1,,J}Jpyglmnet

总而言之,简单地从单个虚拟变量中“累加”变量重要性是没有意义的,因为它不会捕捉它们之间的关联,也不会导致潜在的无意义结果。也就是说,组惩罚方法和置换变量重要性方法都提供了一个连贯且(尤其是在置换重要性程序的情况下)普遍适用的框架来做到这一点。

最后要说明一点:不要对连续数据进行分箱这是不好的做法,这里(和这里有一个很好的主题。我们在连续变量离散化后观察到虚假结果这一事实age并不奇怪。Frank Harrell 还撰写了大量关于对连续变量进行分类所引起的问题

您可以在 scikit-learn 中采用的一种方法是在permutation_importance包含 one-hot 编码的管道上使用该函数。如果您这样做,那么该permutation_importance方法将在分类列被一次性编码之前对它们进行置换。这种方法可以在scikit-learn 网页上的这个例子中看到。编码前排列的结果显示在第二个和第三个图中,您可以看到每个分类变量都报告了一个重要性。

问题是:

通过简单地求和这些虚拟变量的重要性,将它们重新组合成分类变量的重要性值是否有意义?

简短的回答:

简单回答是不。根据教科书(第368页), and 因此 总之,你必须先取平方根。

Importance(Xl)=I
(I)2=t=1J1i2I(v(t)=)
I=t=1J1i2I(v(t)=)

更长,更实用的答案..

您不能简单地将虚拟变量的各个变量重要性值相加,因为您有风险

与它们高度相关的其他变量掩盖了重要变量。(第 368 页)

可能的多重共线性等问题会扭曲变量重要性值和排名。

了解变量重要性如何受到多重共线性等问题的影响实际上是一个非常有趣的问题。论文Determining Predictor Importance In Multiple Regression Under Varried Correlational and Distributional Conditions讨论了计算变量重要性的各种方法,并比较了违反典型统计假设的数据的性能。作者发现,

尽管多重共线性确实会影响相对重要性方法的性能,但多元非正态性不会。(惠特克 p366)