原文:
towardsdatascience.com/no-label-left-behind-alternative-encodings-for-hierarchical-categoricals-d1bcf00afc37
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2ec86c19ba58725a764e5422d8de4b82.png
图片由 Gabriel Tenan 在 Unsplash 提供
在我的数据科学家工作中,我看到了很多标签。数据包含邮政编码标签、性别标签、医疗诊断标签、职位标签、股票代码标签,等等。标签可能是简单的(如 S、M、L 的衬衫尺寸)或复杂的(例如,国际疾病分类系统编码了超过 70,000 种医疗状况,这些编码可以非常幽默具体)。
当它们出现在数据中时,我们称之为分类特征。具有“很多”可能值的分类特征被称为高基数分类特征。高基数分类特征在使用机器学习模型时存在困难。大量的维度使得它们无法直接使用或实际使用(例如,“维度诅咒”)。因此,使用各种编码方案来简化这些特征。
低频或未见的代码也给高基数分类特征带来了挑战。例如,一些邮政编码的人口稀少,而其他地区则拥有数百万人口;我们对某些编码的信心更高。此外,常见的代码集,如医疗诊断代码,会定期更新,导致未见值无法用于训练。未见和低频代码对于成长中的企业或产品,或当训练数据有限时,可能很重要。
编码必须以保留响应信息的同时抵抗过拟合的方式考虑低频和未见值。在基于树的模型中使用的标准目标编码中,这是通过按计数成比例混合总体均值和样本均值来实现的。
编码必须以保留响应信息的同时抵抗过拟合的方式考虑低频和未见值。
一些高基数分类特征可以组织成组。例如,邮政编码可以(大致)聚合到县、州或地区。
组信息可能有助于我们预测低频或未见代码。有可能通过使用高级别组均值的分层混合将此信息纳入目标编码 [1–2]。我在我上次的博客文章中尝试了这种方法,并观察到在未见代码上的性能提高,但某些分组存在过拟合 [3]。
从那时起,我一直想知道如何处理层次分类的替代方法。是更好地构建一个包含尽可能多关于代码系统信息的特征,还是找到一种让模型做这项工作的方法?
在这里,我测试了 XGBoost 模型中层次混合的替代方法。当使用单独的字段用于层次级别而不是将它们混合成一个单一特征时,我观察到一些性能改进。**简单的阈值化,即将低频代码设置为空(或表示不确定性的其他值),似乎特别不容易过拟合。**层次混合对于标准代码层次结构有轻微的优势,但对于某些代码分组则过拟合严重。
Shapley 测试表明,多特征编码允许模型利用分类层次结构更高层次的信息以及其他预测信息。
阈值化就像打开一扇窗户,让其他信息来源的光线透过来。
一个重要的注意事项是,模型需要在低频案例上进行训练,以便泛化到未见案例。这适用于我检查的大多数目标编码变体,除了层次编码。此外,本文中讨论的所有编码方法,如果低频或缺失代码与未见代码不同,可能存在偏差风险。
因此,我想知道在训练数据中随机注入空值(或其他值)是否可以帮助模型学会如何泛化到未知代码。我测试了神经网络中实体嵌入的类似方法,并观察到对于未见代码有显著的性能提升[3]。也许类似的策略可能有助于 XGBoost。如果随机化可以在训练迭代/周期中打乱,那就更好了。
方法
我使用 XGBoost 二分类模型来预测美国小企业管理局(SBA)贷款数据集中的企业贷款违约情况[4–5]。这是一个公开数据集(CC BY 4.0)。方法主要在之前已描述[3]。所有代码均可在 GitHub 上找到[6]。
我编码了 NAICS(行业特征)。NAICS 代码有一个官方的 5 级层次结构,它将企业类型分组到不同粒度的桶中[7]。请参见表 1 中的示例:
<…/Images/8af2bccc79159347300a202a45e1169c.png>
表 1:北美行业分类系统(NAICS)代码的分层组织示例[7]。6 位 NAICS(国家行业)分组是层次结构中最具体的(或“最低”)层级,这些代码被分组到更一般的(或“更高”)层级。右边的三角形表示层次结构较低层级的代码数量更多。图片由作者提供。
与先前方法的一个重要区别是,我有时会使用不同的混合中点值(100)。这样做是为了帮助比较对新方法敏感的中点性能。结果非常相似,但并不完全相同,与[3]中的结果。
探索替代编码
这里是我将要尝试的编码变体:
目标编码(仅 NAICS):仅对 NAICS 特征进行标准目标编码(1 个特征)
分层混合:将更高层级的组均值混合到观察到的比率中,以更好地估计响应(1 个特征)
目标编码(全部):对 NAICS 层次结构的所有层级的标准目标编码(5 个特征)
目标+计数编码:与 4 相同,但包括额外的计数编码特征(10 个特征)
目标-阈值编码:类似于标准目标编码,但不是将低频次/未见值缩小到目标(或任何)均值,而是将低于截止点的值设置为 null。(5 个特征)
对于上述所有内容,最低层 NAICS 特征的编码非常相似;实际上,对于 1、3 和 4 是相同的。对于 2 和 4,变化仅发生在低频次或未见代码中。方法 3-5 涉及编码最低级代码之外的其他特征。
目标编码(仅 NAICS):标准目标编码用给定类别的均值响应(贷款违约率)替换分类特征。均值是在训练数据上计算的,以避免泄露。
对于低频次代码,均值估计显然不太可靠,导致过拟合风险。因此,目标编码通常涉及将整体目标数据的均值混合到编码中,这样非常低频次(或未见)的代码会得到与目标均值相似的价值,而高频次的代码则映射到几乎实际的响应。这里的“低频次”取决于目标比率,因此混合是参数化的。在这里,我使用了一个由混合中点和宽度参数化的 S 形函数(我在一些测试中改变了中点)。
分层混合:分层混合是目标编码的一种变体,其中使用更高层级的 NAICS 组均值来更好地估计低频次或未见代码的均值响应[1–2]。响应与所有可用层级的均值混合,使用相同的 S 形函数来加权每个层级。
目标编码(全部):这种方法与目标编码(仅 NAICS)相同,但你还会对更高层级的代码进行目标编码,例如 NAICS 部门或行业组,共 5 个特征。
一个非线性模型可能“自动”地根据需要结合高级特征,这有些道理。另一方面,对于大多数编码,高级分组特征不会提供额外信息。
目标+计数编码:我想知道 XGBoost 是否能够利用计数信息推断出目标编码特征对于低量级或未见过的编码不可靠。因此,我尝试了一种“目标+计数”编码,它只是将计数编码特征添加到目标编码(所有),总共 10 个特征。我设置计数编码的阈值,这对应于响应分数达到 95%或更多的点(样本均值小于 5%“混合”)。
目标-thresh 编码:如果非线性模型可能使用计数信息来修改其对特征的响应,也许它可以从缺失值或特定值中获取类似信息。如果我只是告诉模型“我不知道”低量级编码的平均值,并让它自己解决,会怎样?目标-thresh 编码对于高量级编码与目标编码相同,但对于低量级或未见过的编码,值设置为 null。
那么,它们的性能如何?
我进行了一个常规的随机训练/验证/测试分割,但还留出 10%的 NAICS 编码样本来评估对未见值的性能(见[3]获取详细信息)。我报告了精确率-召回率曲线下面积(PR-AUC)指标,因为它强调了违约检测(值越高越好)。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/af185dd6cc2cbfc43b94ce310d78f8b4.png
图 1.XGBoost 模型性能与不同 NAICS 编码的关系,以混合中点(或目标-thresh 编码的计数阈值)为函数。左侧面板显示随机测试数据集的 PR-AUC,右侧面板显示保留的 NAICS 测试数据集的 PR-AUC。图片由作者提供。
测试数据
图 1 的左侧面板显示了测试数据集的性能。对于大多数中点,标准目标编码和分层混合非常相似。然而,所有三种多变量编码方法(目标编码(所有)、目标+计数和目标-thresh)都显示出轻微但有意义的表现提升。这三条曲线彼此非常相似。
对于所有编码,在非常高的中点处性能下降,这是预期的,因为关于平均响应的信息正在丢失(注意整体目标率约为 20%)。下降最严重的是标准目标编码,因为它不包括代码层次结构的信息。
如果我只看图 1B 的左侧面板,我可能会得出结论,其中一种多字段编码是最好的。但对于未见过的编码呢?
保留数据
图 1 的右侧面板显示了未见过的 NAICS 代码的性能。值通常低于左侧面板,正如预期的那样,对于未见过的代码,其平均响应不可用于训练。
目标编码(仅限 NAICS),这是唯一一种完全忽略 NAICS 层级的方法,性能最差。现在,层级混合似乎最强,并且对混合中点的敏感性似乎不如多特征方法。多字段编码似乎在 ~100 的中点附近有所改善。我稍后会讨论这个问题。
如果我查看图 1 右侧面板的结果,我可能会得出结论,当未见过的代码很重要时,层级混合可能是首选。然而,在我的上一篇文章 [3] 中,我发现层级混合对于某些代码分组遇到了过拟合问题。因此,我还需要进行更多测试。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/7b629755218fbb71a22443e975cf0046.png
由 Amel Majanovic 在 Unsplash 拍摄的照片
那么不同的代码层级呢?
NAICS 有一个标准的层级结构,这在广泛使用。但是,其他可能具有不同安排的代码呢?也许不同的层级数量或不同的粒度会有所不同。在所有层级测试中,我将混合中点/阈值固定在 100。
NAICS 标准层级变化
为了了解模型可能如何响应层级变化,我使用相同的编码方法,但开始在 NAICS 层级的不同位置。而不是使用所有 5 个层级,我会使用基础 NAICS,然后从第 3 个层级开始进行分组。图 2 展示了在标准 NAICS 层级中改变起始点时的性能。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4e03a38efe73710926a15d1293fc1b09.png
图 2.使用标准 NAICS 代码层级不同部分的 XGBoost 模型性能。混合或特征添加从显示的层级开始或更高。x 轴上的 k 值是每个层级的独特代码数量。左侧面板显示随机测试数据集的 PR-AUC,右侧面板显示保留的 NAICS 测试数据集的 PR-AUC。两个面板上的基线来自标准目标编码。图片由作者提供。
对于随机测试数据集(左侧面板),图 2 的结果与图 1 非常相似,层次混合与标准目标编码相当,但多字段编码方法提高了性能。这种模式无论从哪个级别进行编码都会发生。
图 2 的右侧面板显示,随着层次结构的变化,未见过的代码的性能差异很大。对于层次混合,仅使用最高级组(部门)进行混合实际上比简单的目标编码(此结果之前也曾观察到[3])表现更差。
基于 DGI 特征的分组
除了修改标准的 NAICS 层次结构外,我还会尝试一些完全不同的代码分组。在我的上一篇文章[3]中,我尝试了深度图信息最大化(DGI)加上聚类,根据预测字段创建分组。DGI 分组与模型响应相关,但不含预测字段以上的额外信息。
之前,DGI 分组导致层次混合过拟合,这表明该方法可能存在风险,尤其是在分组风险与其他预测因子重复时[3]。其他方法对 DGI 分组有何反应?
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/223787705e4982e62bd1eacca253e406.png
图 3。基于 DGI 分组代码层次结构的 NAICS 编码的 XGBoost 模型性能。混合或特征创建从或高于显示的级别开始。x 轴上的 k 值是每个级别的不同代码数量(值被选择以匹配此数据集中的标准 NAICS 组)。左侧面板显示随机测试数据集的 PR-AUC,右侧面板显示保留的 NAICS 测试数据集的 PR-AUC。标准目标编码的基线在两个面板上都是水平的灰色线条。图片由作者提供。
图 3 显示,DGI 分组往往会导致性能下降,这是由于过拟合造成的。对于目标编码(所有)和目标+计数编码来说,这是最严重的。层次混合在某些层次结构级别上过拟合,但在其他级别上则没有。目标阈值似乎可以抵抗 DGI 分组的过拟合。
完全随机的分组
由于 DGI 分组存在过拟合,我想知道随机分组会发生什么。
随机分组模拟了一种情况,即代码结构不与问题相关。代码系统通常由政府或行业专家为了自己的目的而维护,这可能与您感兴趣的结果无关。例如,想象一下,如果我们在这个贷款违约模型中使用了一个按字母顺序组织行业的层次结构。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ea1217e0bfe2c9bdc34e5d9ba1c8feb2.png
图 4.使用随机确定的代码层次结构的 NAICS 编码的 XGBoost 模型性能。混合或特征创建从显示的级别开始或更高。x 轴上的 k 值是每个级别中不同代码的数量。左面板显示了随机测试数据集的 PR-AUC,右面板显示了保留的 NAICS 测试数据集的 PR-AUC。两个面板中的基线来自标准目标编码,是水平灰色线条。图片由作者提供。
图 4 显示了完全随机创建的组的结果。大多数编码方法对层次结构没有做任何事情,这是令人放心的。然而,层次结构混合过度拟合得相当严重!
特征重要性
Shapley 测试可以提供一些关于不同编码方法如何影响预测的深入信息。在图 5 中,反映特征对预测贡献的个别 SHAP 值被汇总,以显示特定测试案例的整体响应。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/071a192bc9b3aae05a4e336fb725f106.png
图 5.编码和其他特征的 SHAP 特征重要性。左列的图表显示了测试数据集的结果,右列显示了保留的 NAICS(未见过)数据集的结果。图表被分组到不同的代码层次结构框中(标准目标编码不使用任何层次结构)。对于测试数据集,我从 50,000 个案例的随机样本中获得值。保留的结果是所有 65,052 个案例。图片由作者提供。
图 5 中有很多内容,但先从蓝色条形开始,它们代表“其他”(与 NAICS 无关)的预测因子。与左侧相比,这些条形在右侧的图表中更长。对于未见过的代码,模型增加了对 NAICS 编码之外的特征的依赖。这种影响在多特征方法(目标编码(全部)、目标+计数编码和目标-阈值编码)中最为明显。
橙色条形显示了低级 NAICS 编码编码的重要性。在测试数据中,差异很小,但对于保留数据,层次混合脱颖而出。这种对这一特征的过度依赖与上面看到的过拟合一致。
目标编码只是一个简单的模型,仅基于 NAICS 预测贷款违约;然后这些预测被输入到 XGBoost 中。层次混合是一个更复杂的先验模型。看起来这里存在偏差-方差权衡,并且层次混合过拟合。
承认无知似乎比猜测平均值要好。
最后,绿色条形显示了从层次结构的更高层级(或计数)派生出的特征的重要性;这些特征仅存在于多特征方法中。对于标准的 NAICS 层次结构(图表的第二行),高层级的影响最为明显。标准的层次结构为模型提供了额外的信息,而 DGI 和随机编码则添加了很少或没有价值。
简单的编码方法允许主要特征退居次要位置。在我们信息不完整时,我们让模型寻找替代来源,而不是过度依赖一个特征。
那么,缺失值怎么办?
我认为,在低量级和缺失代码方面,训练数据的组成是至关重要的考虑因素。
目标-阈值编码适用于这个数据集,也许在其他情况下也会很好,但有一些情况我相信它会失败。
我的数据库没有缺失值。原始数据中有空值,主要是针对较老的(2000 年以前的)贷款,所以我删除了这些案例[3]。这些长期贷款的违约率大多较低。
我认为与结果不相关的偶然缺失值可以帮助模型学习如何处理未知数。但在这个数据集中,未知值可能存在偏差,因为它们反映了贷款年龄。那么,如果目标+阈值使用空值来处理未见过的代码,我可能期望性能不佳,特别是在可能存在更多缺失值与低量级代码的情况下。
在训练数据没有缺失值的情况下,目标编码依赖于低频代码来泛化到未见过的标签。记住图 1,其中多字段方法在未见过的代码上的性能在约 100 时上升?我通常认为目标编码的理想中点主要取决于目标率。然而,对于图 1,我认为中点更多地取决于有足够的低频案例进行训练。回到窗口隐喻——那个窗口需要足够地打开。我需要足够的代表不确定率的示例,以便模型能够学习并依赖其他特征。
在训练数据没有缺失值的情况下,目标编码依赖于低频代码来泛化到未见过的标签。
在图 1 中,我还看到,在拥有足够高的中点以泛化到未见过的代码和过高中点导致信息丢失之间存在紧张关系。我有很多数据,NAICS 分布不均,因此存在一个有效范围。这总是如此吗?是否存在没有正确平衡的数据集?
在下面的图表中,我执行了与图 1 完全相同的编码,但在 XGBoost 拟合之前从训练数据中移除了低频代码:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d82402d2dcb5878fccd99a2afbc6c3bd.png
图 6。与图 1 类似,但在训练前移除了低频案例。编码阈值在 x 轴上变化。左侧面板显示随机测试数据集的 PR-AUC,右侧面板显示保留的 NAICS 测试数据集的 PR-AUC。两个面板上的基线来自标准的 target encoding,为水平灰色线条。图片由作者提供。
图 6 显示,当训练数据没有任何缺失值时,target+thresh 的表现不佳。当我使用 target+thresh 并在图 6 的场景中用训练数据的平均值填充时,target+thresh 的表现与 target encoding (all)和 target+count(未展示)非常相似。
图 6 还显示,在拟合之前,训练数据的变化不会影响分层混合的结果。关于分层含义的所有信息都包含在编码特征中。但其他方法依赖于模型学习来补偿低频或缺失的代码。
我对结果如何依赖于训练数据中低频和未见案例的特征持谨慎态度。我认为某种随机化——将训练值设置为缺失或目标均值——可能会有所帮助。在随机缺失案例中,偏差可能会减少,并且我可以确保有足够的相关训练示例。这种随机化的缺点是信息损失,因此最好在训练期间随机化,也许在每次提升轮次中将不同的观测值设置为空值。
其他模型类型如何?
我测试了 XGBoost,并猜测结果可以扩展到其他提升树模型。对于不允许缺失/空值的模型,目标阈值需要使用常数值填充;我对此进行了几次测试,看起来没问题。事实上,图 6 显示,如果训练数据中低频代码很少,常数填充可能更受欢迎。但是,我怀疑在某些情况下可能仍然存在问题,例如,如果某些代码的默认率与整体均值相似。
对于多特征编码,随机森林模型可能会更多地利用高级分组特征(XGBoost 中的列采样可能产生类似效果)。这会影响所有观测值,但对于低频或未见代码可能有所帮助。
目标编码通常用于基于树的模型中,但对于神经网络模型中的实体嵌入也有类似的问题。之前,我发现除非我随机设置用于未见案例的代码作为训练数据的输入,否则神经网络模型会严重过拟合[3]。我建议对一些学生进行随机化,他们也看到了过拟合的大幅减少。因此,对于 N=2 的例子,随机化有助于实体嵌入泛化;这可能是一个有希望的策略。
最后的想法
作为一位以构建依赖于高基数分类特征的模型为生的人,我希望能有一个易于使用的系统,对于已见和未见代码都能提供良好的性能,并且低过拟合风险。我觉得我还没有在测试过的任何方法中达到这个水平,但也许有些方法正在接近。
可能一个方便的系统会涉及目标阈值编码,以及某种拟合来确定最佳阈值(类似于 scikit-learn 的 TargetEncoder [8])。我还需要某种随机的无效化/填充,这样模型可以从更高层次的特征中学习是否有(足够的)低频代码,甚至在预存在的缺失值反映了数据偏差的情况下。为了便于使用,最好在模型训练中内置无效化;洗牌数据也会减少信息损失。
我希望在未来的帖子中更深入地探讨这些想法!我很乐意在评论中听到建议。你如何处理你的分类特征?你遇到过什么问题?
参考文献
[1] 迈克尔·米奇-巴拉卡,扩展目标编码 (2020),数据科学向导.
[2] 迈克尔·米奇-巴拉卡,用于分类和预测问题中高基数分类属性的前处理方案 (2001),ACM SIGKDD 探索 3 (1).
[3] V. 凯里,探索目标编码中的分层混合 (2024),数据科学向导.
[4] 李明,米克尔·阿德里安,泰勒·S,应批准还是拒绝此贷款?:一个带有类别分配指南的大数据集 (2018),统计学教育杂志 26 (1). (CC BY 4.0)
[5] 托克托加拉耶夫·M,应批准还是拒绝此贷款? (2020),Kaggle. (CC BY-SA 4.0)
[6] V. 凯里,GitHub 仓库,github.com/vla6/Blog_gnn_naics.
[7] 美国人口普查局,北美行业分类系统.
[8] Scikit-Learn 文档,目标编码的内部交叉拟合 (2024).