kaggle竞赛技巧(其二)——利用null importance筛选有效特征

permutation的问题在于计算量随着特征的增加而线性增加,对于维度很高的数据基本上难以使用。下面介绍一下kaggle 大佬 oliver 发明的 null importance 特征重要性计算方法。

xgboost特征重要性计算算法的问题

  1. 树模型自带的特征重要性评估可能偏向于具有高基数的类别特征或连续特征。这是因为,这些特征在决策树分裂时,因为其多样的分割点,往往能带来更显著的不纯度降低(如基尼不纯度或MSE减少量)。特别是在使用LightGBM和XGBoost这类模型时,哪怕有非常重要的离散特征,模型仍然会倾向于增加连续型特征/高基数特征的分裂次数,导致重要特征无法体现。

  2. 对于xgb和lgb等这类拟合能力超强的模型来说,很多和标签完全无关的特征甚至是随机加入的噪声,都能通过海量的子树与标签建立密切的联系,即只要子树的量够多,哪怕是随机噪声和标签之间都能建立映射。而且在数据集中加入噪声作为特征后,直接输出xgboost给出的特征重要性排序,噪声很有可能是优于正常的特征的,如果我们根据xgboost自身的特征重要性排序来删除特征,则会删除掉很多潜在的有用的信息。

为什么不能用permutation?

之前的文章介绍过permutation,其是处理噪声问题的一种方法,但是该方法的最大问题在于:其计算量与特征维度正成比,面对高维数据计算过慢,而在进行kaggle比赛时,所使用的特征通常定然在千维以上。而null importance则可以较好的解决这样的问题。

null importance的核心思想

Null Importance的核心思想是:一个较好的特征与标签必然具有较高的关联程度,因此,如果将标签随机打乱,好的特征的特征重要性必然出现大幅度的下滑,而差的特征,或者说与标签无关的特征的特征重要性则不会有太大的改变。因此计算两种特征重要性,一种是实际的特征重要性,另一种是打乱标签训练得到的null importance,然后使用后者对前者做一个修正,以得到真实的特征重要性。

如果感觉这段有点难于理解,可以先看具体算法步骤。

具体算法步骤

  1. 计算基准特征重要性:

    • 首先,训练一个标准的树模型,以此模型输出的特征重要度作为基准(实际重要性)。
  2. 生成Null Importance分布:

    • 打乱数据的标签,破坏特征与标签之间的实际关联。
    • 使用与步骤一同样相同的方法重新训练模型,输出特征重要度,称为Null Importance。这代表了模型在不考虑真实标签关联时对特征的随机评价。
    • 由于标签被打乱,导致每次计算的Null Importance可能会有很大的波动,因此需要多次重复上述过程,建立Null Importance的分布,以更准确的评估特征重要性。
  3. 评估特征分数:

    • 对于有用的特征,其实际重要性远高于Null Importance。
    • 对于无用或者不那么重要的特征,其Null Importance很可能接近甚至超过实际重要性。
    • 结合步骤1和步骤2的结果,我们可以通过比较实际重要性和Null Importance来评分每个特征。这种比较可以用多种方法实现,例如差值、比值等。 oliver 使用的评分公式是实际重要性与Null Importance分布的75%分位数的对数比值。
  4. 特征选择与决策:

    • 根据计算出的特征分数,我们可以设定一个阈值,以决定保留哪些特征。
    • 还可以通过观察实际重要性与Null Importance之间的差异,来做出关于特征选择的更直观的决定。

对于第三步:特征分数的计算,oliver给出的衡量方案核心逻辑对应的代码如下:

python 复制代码
feature_scores = []
for _f in actual_imp_df['feature'].unique():
    f_null_imps_gain = null_imp_df.loc[null_imp_df['feature'] == _f, 'importance_gain'].values
    f_act_imps_gain = actual_imp_df.loc[actual_imp_df['feature'] == _f, 'importance_gain'].mean()
    gain_score = np.log(1e-10 + f_act_imps_gain / (1 + np.percentile(f_null_imps_gain, 75)))  # Avoid didvide by zero
    f_null_imps_split = null_imp_df.loc[null_imp_df['feature'] == _f, 'importance_split'].values
    f_act_imps_split = actual_imp_df.loc[actual_imp_df['feature'] == _f, 'importance_split'].mean()
    split_score = np.log(1e-10 + f_act_imps_split / (1 + np.percentile(f_null_imps_split, 75)))  # Avoid didvide by zero
    feature_scores.append((_f, split_score, gain_score))

scores_df = pd.DataFrame(feature_scores, columns=['feature', 'split_score', 'gain_score'])
scores_df = scores_df.sort_values('split_score', ascending=False)

其使用了计算出的null importance的75%分位数与真实重要性的比值,而后取对数,即:

gain_score = np.log(1e-10 + f_act_imps_gain / (1 + np.percentile(f_null_imps_gain, 75)))

笔者在公司的算法赛中使用了null importance 进行了特征重要性筛选,将1600维度压到了500多维,发现线上F1是有少量提升的。

相关推荐
秋饼30 分钟前
Redis 全面解析:从数据结构到主从/哨兵/集群、分布式锁与面试高频考点,代码到原理一次讲透
数据库·redis·分布式·面试·后端开发
考研保研资料分享5 小时前
2022双非保研华科热能工程:三校考核、传热题与报名复盘
面试
weixin_3077791311 小时前
RAG 面试陷阱——混合检索并非无条件优越:一个条件反转实验
人工智能·python·面试·职场和发展
nice_lcj52012 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试
D e re k ZH16 小时前
C++ 并发、未定义行为与工程化难点(No 3)
数据结构·c++·算法·面试
香蕉大香蕉(06敬畏市场)19 小时前
MySQL 单点详解(08)
java·数据库·mysql·面试·八股
Thanks_ks20 小时前
常见 HTTP 状态码的坑:301/302、401/403、500/502 之间差的那一个词
计算机网络·nginx·http·面试·状态码·后端开发·浏览器缓存
怕浪猫20 小时前
Super-agent 在各大模型厂商的应用情况
算法·面试·github
滼1 天前
AI 数据安全面试应答框架:从大框架到小点
大数据·人工智能·安全·面试·安全架构
ZhengEnCi1 天前
面试官为什么总问底层原理:你不是不如他,而是他被「筛选器」困住了
面试·程序员·求职