面试机器学习相关岗位的时候,Xgboost 算法是一个必问项,其中关于特征重要性的问题又是重中之重,一般来说大家仅仅知道两种特征重要性的计算方式,如果你能说出来第三种的话,面试官一定会对你刮目相看。
本次带你复习和掌握在 Python 中从 Xgboost 模型中获取特征重要性的 3 种方式以备日后面试使用。
- 内置特征重要性
- 基于排列的重要性
- 使用 SHAP 值计算的重要性
Xgboost 内置功能的重要性
import numpy as np
import pandas as pd
import shap
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.inspection import permutation_importance
from matplotlib import pyplot as plt
import seaborn as sns
from xgboost import XGBRegressor
加载 boston 数据集并将其拆分为训练和测试子集。75% 的数据将用于训练,其余用于测试。
boston = load_boston()
X = pd.DataFrame(boston.data, columns=boston.feature_names)
y = boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=12)
安装 Xgboost 回归器很简单,只需 2 行代码
xgb = XGBRegressor(n_estimators=100)
xgb.fit(X_train, y_train)
在 Xgboost 中使用了默认的超参数,并且只设置了模型中的树数 ( n_estimators=100)。
从 Xgboost 模型中使用 feature_importances_ 获取特征重要性:
xgb.feature_importances_
array([0.01690426, 0.00777439, 0.0084541 , 0.04072201, 0.04373369,
0.20451033, 0.01512331, 0.04763542, 0.01018296, 0.02332482,
0.04085794, 0.01299683, 0.52778 ], dtype=float32)
与 scikit-learn 模型的 API 接口相同,例如在随机森林中可是使用同样的方法获取特征重要性。
使用条形图方式观察特征重要性更加直观
plt.barh(boston.feature_names, xgb.feature_importances_)
根据重要性值对特征进行排序获取更优的观察方式:
sorted_idx = xgb.feature_importances_.argsort()
plt.barh(boston.feature_names[sorted_idx], xgb.feature_importances_[sorted_idx])
plt.xlabel("Xgboost Feature Importance")
Xgboost 内置功能
- Xgboost 中计算特征重要性类型。默认类型是 gain 如果您使用 scikit-learn 类似 API ( docs ) 构建模型。当访问 Booster对象 并使用方法获取重要性时 get_score,默认为 weight 。可以使用 xgb.importance_type 计算特征的类型。
- 该 gain类型 显示了使用特征的所有拆分的平均增益。
- 显示 weight特征 用于拆分数据的次数。
- 还有 cover、total_gain、total_cover 类型的重要性。
基于排列的特征重要性 (with scikit-learn)
可以在 Xgboost 上使用 permutation_importance ,Xgboost 实现了 scikit-learn(0.22版本) 接口 API。
这种排列方法将随机打乱每个特征并计算模型性能的变化,对性能影响最大的特性是最重要的特性,而且可以很容易地计算出来并可视化:
perm_importance = permutation_importance(xgb, X_test, y_test)
sorted_idx = perm_importance.importances_mean.argsort()
plt.barh(boston.feature_names[sorted_idx], perm_importance.importances_mean[sorted_idx])
plt.xlabel("Permutation Importance")
基于排列的重要性比较复杂,检查数据集中的相关性:
def correlation_heatmap(train):
correlations = train.corr()
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(correlations, vmax=1.0, center=0, fmt='.2f', cmap="YlGnBu",
square=True, linewidths=.5, annot=True, cbar_kws={"shrink": .70}
)
plt.show();
correlation_heatmap(X_train[boston.feature_names[sorted_idx]])
根据上述结果,理论上会剔除ZN、CHAS、AGE、INDUS。它们基于排列的重要性非常低,并且与其他特征的相关性不高**(abs(corr) < 0.8)**。
在 AutoML 包 mljar-supervised 中,做了一个特征选择技巧:将随机特征插入到训练数据中,并检查哪些特征的重要性低于随机特征。我将这些从进一步的培训中删除,与 Boruta算法 中使用的技巧非常相似。
使用 SHAP 值计算的特征重要性
在 Xgboost 中计算特征重要性的第三种方法是使用 SHAP包。它与模型无关,并使用博弈论中的 Shapley 值来估计每个特征对预测的贡献。
explainer = shap.TreeExplainer(xgb)
shap_values = explainer.shap_values(X_test)
为了可视化特征重要性,我们需要使用 summary_plot 方法:
shap.summary_plot(shap_values, X_test, plot_type="bar")
可以绘制更多的解释图:
shap.summary_plot(shap_values, X_test)
shap.dependence_plot("LSTAT", shap_values, X_test)
但是使用 SHAP 计算特征重要性的计算成本可能很高。