Evalml: 一个热编码器:默认删除一个冗余特征,用于具有两个类别的特征

创建于 2021-03-05  ·  14评论  ·  资料来源: alteryx/evalml

我们的一个热编码器为原始分类特征的每个级别创建一个特征:

from evalml.pipelines import OneHotEncoder
import pandas as pd
df = pd.DataFrame({"category": ["a", "b"], "number": [4,5 ]})
OneHotEncoder().fit_transform(df).to_dataframe()

image

category_acategory_b列是完全共线的,这使得一个冗余。 这可能对估计器拟合产生不利影响。 我认为我们应该默认删除一个。

仅供参考@rpeck

enhancement

最有用的评论

代码第三定律:你不应该做 == 与浮点数的比较

所有14条评论

💯 % 我们应该删除负数列。

如果我们先自己做 OHE,那么 sklearn 希望不会扩展它们。 正如弗雷迪所说,您可以将其视为生成具有完美共线性的两列。

将二进制文件扩展为两列而不是一列时,我看到了两个问题:

  1. 与其他形式的特征共线性一样,它在可解释性方面搞砸了很多事情,因为一个原始源列的效果在两个 OHE 列之间划分。 Freddy 的新 SHAP 汇总显然解决了这个问题,但特征重要性和部分依赖图等问题仍然存在。
  2. 像 Random Forest 和 GBM 这样的树模型会随机采样它们的输入特征。 在这种情况下,源列的随机抽样频率将是实际抽样频率的两倍,因此它会对模型产生巨大的影响。

@freddyaboulton问:上面的 OHE 列数据

@rpeck是的!

@freddyaboulton什么? 那真是怪了。 除了真正的布尔值或 0/1 整数之外,我从未见过任何东西。 我想知道树模型实际上是如何处理这个问题的。 它对我有难闻的气味。

代码第三定律:你不应该做 == 与浮点数的比较

(好的,除非它与Math.NaN

嗯,我以为我们正在这样做!

我同意我们应该。 我认为这只是我们必须在底层实现中设置的标志。

@dsherry @freddyaboulton看起来我们通过我们的drop参数支持它,但只考虑用户输入并且不被我们的 impl 使用,所以这个问题只是跟踪为drop设置默认值除了None之外的东西?

https://github.com/alteryx/evalml/blob/91775ffc26c47205adc0fb255832d828ead6e7c9/evalml/pipelines/components/transformers/encoders/onehot_encoder.py#L28

https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

我们可以选择firstif_binary ,但不确定正确的调用是什么。

@angela97lin你说得对,更改默认值就足够了! 我认为first是要走的路,因为即使类别数> 2,我们也应该避免完全共线的特征。你觉得@rpeck怎么

读了一点,发现这个链接: https :

关键要点:

  • 只有在创建没有正则化的 OLS 模型时才需要删除列(我相信线性回归器属于这一类:https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html,https:// scikit-learn.org/stable/modules/linear_model.html#ordinary-least-squares)
  • 删除 one-hot 编码列会更改线性回归模型的参数和预测,从而影响返回的模型。 然而,我很难确定这是否更好。

RE @rpeck的第一条评论:“像其他形式的特征共线性一样,它在可解释性方面

这对于二进制情况是有意义的,但是在我们有多个类别的情况下,删除一个列仍然会出现这个问题。

也许我们不应该默认这样做,但如果估计器是线性回归器,则应该更新make_pipeline以创建一个以first作为参数的 OHE?

唉,我对做出判断的基础数学没有很强的把握,所以我很想听听你的想法, @ freddyaboulton @rpeck @dsherry

@freddyaboulton @rpeck @dsherry @chukarsten @jeremyliweishih讨论后

  • 我们只会对二进制情况执行此操作。
  • 在二元情况下,“nice-to-have”是使用少数类,否则只需选择两个类别中的一个就足够了。

@angela97lin听起来不错的 RE 默认行为。 另一个不错的选择:通过组件参数覆盖默认行为的能力

@dsherry如果我理解正确,因为我们正在更新drop (一个参数)的默认值,用户将能够通过手动设置组件参数来覆盖它?

四处挖掘,看看实现这一点需要什么。 特别是,我很好奇在二进制情况下总是删除少数类是多么困难。

挖掘的结果是:

  • 使用 scikit-learn,很难选择要删除的类别。 从文档来看,这似乎可以通过drop参数的数组选项(https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html)来实现。 但是,在尝试之后,它要求为每一列指定一个索引值。 因此,以下内容试图删除在索引 0 处为第 0 列指定的类别,并且没有为第 1 列和第 2 列错误删除其他值:
import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder

X = pd.DataFrame({'col_1': ["a", "b", "b", "a", "b"],
                      'col_2': ["a", "b", "a", "c", "b"],
                      'col_3': ["a", "a", "a", "a", "a"]})

indices_to_drop = np.array([0, None, None])

ohe = OneHotEncoder(drop=indices_to_drop)
ohe.fit(X)
ValueError                                Traceback (most recent call last)
<ipython-input-4-a099fa2fc4a7> in <module>
----> 1 ohe.fit(X)

~/Desktop/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py in fit(self, X, y)
    417         self._fit(X, handle_unknown=self.handle_unknown,
    418                   force_all_finite='allow-nan')
--> 419         self.drop_idx_ = self._compute_drop_idx()
    420         return self
    421 

~/Desktop/venv/lib/python3.7/site-packages/sklearn/preprocessing/_encoders.py in _compute_drop_idx(self)
    394                                 ["Category: {}, Feature: {}".format(c, v)
    395                                     for c, v in missing_drops])))
--> 396                 raise ValueError(msg)
    397             return np.array(drop_indices, dtype=object)
    398 

ValueError: The following categories were supposed to be dropped, but were not found in the training data.
Category: 0, Feature: 0
Category: 1, Feature: None
Category: 2, Feature: None

我相信这也是这个问题指出的一半: https :

为了支持这一点,我们可以做的另一种方法是手动跟踪在拟合期间我们想要删除哪些列和哪些值。 将数据传递给 scikit-learn。 然后,修剪掉我们存储并指定要删除的列。 但是,这需要一些逻辑处理来从转换后的列名中确定原始(特征、值)。 (我们在get_feature_names有这个逻辑,但这有助于我们连接列名,假设不应该删除任何内容......)

所有这一切都是说,也许现在只使用默认的 scikit-learn if_binary就足够了,我们可以提交一个单独的问题来始终使用少数类。 老实说,考虑到我们必须围绕它进行大量工作,我们也赞成放弃 scikit-learn 的 OHE 实现。

有用的资源:
OHE 文档: https :
scikit-learn 中的代码导致不灵活: https :
相关问题: https :


对于使用if_binary :scikit-learn 要求handle_unknownerror 。 这不适用于我们的top_n参数,它会丢弃除前 N 个类别之外的所有内容,因为要转换的数据不知道如何处理新类别。 正如 Becca 在https://github.com/alteryx/evalml/pull/830 中指出的那样,我们必须将top_n为 None 才能使这些参数起作用。

考虑到这一点,也许最好只推出我们自己的 impl 🤔

此页面是否有帮助?
0 / 5 - 0 等级