理解FeatureHasher、碰撞和向量大小权衡

我在实现机器学习模型之前预处理数据。一些特征具有高基数,比如国家和语言。

由于将这些特征编码为独热向量会产生稀疏数据,我决定研究哈希技巧,并使用Python的category_encoders如下所示:

from category_encoders.hashing import HashingEncoderce_hash = HashingEncoder(cols = ['country'])encoded = ce_hash.fit_transform(df.country)encoded['country'] = df.countryencoded.head()

查看结果时,我可以看到碰撞

    col_0  col_1  col_2  col_3  col_4  col_5  col_6  col_7 country0       0      0      1      0      0      0      0      0      US <━┓1       0      1      0      0      0      0      0      0      CA.  ┃ US和SE发生碰撞 2       0      0      1      0      0      0      0      0      SE <━┛3       0      0      0      0      0      0      1      0      JP

进一步调查让我找到了这篇Kaggle文章。那里的哈希示例包括既有X也有y

  • y的目的是什么,它是否有助于解决碰撞问题?
  • 我应该向编码器添加更多列并一起编码多个特征(例如国家和语言)吗?

如何使用哈希技巧对这些类别进行编码?

更新:根据@CoMartel的评论,我查看了Sklearn FeatureHasher,并编写了以下代码来对国家列进行哈希:

from sklearn.feature_extraction import FeatureHasherh = FeatureHasher(n_features=10,input_type='string')f = h.transform(df.country)df1 = pd.DataFrame(f.toarray())df1['country'] = df.countrydf1.head()

并得到了以下输出:

     0    1    2    3    4    5    6    7    8    9 country0 -1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0 -1.0  0.0      US1 -1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0 -1.0  0.0      US2 -1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0 -1.0  0.0      US3  0.0 -1.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0      CA4  0.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0 -1.0  0.0      SE5  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0      JP6 -1.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0      AU7 -1.0  0.0  1.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0      AU8 -1.0  0.0  0.0  0.0  0.0  0.0  1.0  0.0  0.0  0.0      DK9  0.0  0.0  0.0  0.0  0.0  0.0  0.0  1.0 -1.0  0.0      SE
  • 这种方法是否可以用来对高类别值进行编码?
  • 为什么有些值是负数?
  • 您将如何选择“正确”的n_features值?
  • 我如何检查碰撞率?

回答:

这种方法是否可以用来对高类别值进行编码?

是的。您的实现没有问题。

您可以将哈希技巧视为“带有小概率碰撞的降维独热编码,如果您能容忍原始特征维度,就不需要使用它”。

这个概念最初是由Kilian Weinberger引入的。您可以在他们的论文中找到对该算法的理论和实际/经验分析。


为什么有些值是负数?

为了避免碰撞,使用了有符号的哈希函数。也就是说,字符串首先使用常规的哈希函数进行哈希(例如,通过将每个字符的ASCII值相加将字符串转换为其对应的数值,然后取模n_feature以获得(0, n_features]中的索引)。然后使用另一个单比特输出的哈希函数。后者按定义产生+1-1,它被添加到第一个哈希函数生成的索引中。

伪代码(看起来像是Python):

def hash_trick(features, n_features):     for f in features:         res = np.zero_like(features)         h = usual_hash_function(f) # 只是常规的哈希         index = h % n_features  # 找到模数以获取res中放置f的索引         if single_bit_hash_function(f) == 1:  # 为了减少碰撞             res[index] += 1         else:             res[index] -= 1 # <--- 这会使值变为负数     return res 

您将如何选择“正确”的n_features值?

作为经验法则,正如您所猜测的,如果我们哈希一个额外的特征(即#n_feature + 1),碰撞肯定会发生。因此,最佳情况是每个特征都被映射到一个唯一的哈希值——希望如此。在这种情况下,从逻辑上讲,n_features应该至少等于实际的特征/类别数量(在您的特定情况下,不同国家的数量)。然而,请记住,这是“最佳”情况,从数学上讲并非如此。因此,当然,越高越好,但有多高?请看下文。


我如何检查碰撞率?

如果我们忽略第二个单比特哈希函数,问题就简化为所谓的“哈希的生日问题”。

这是一个大话题。对于这个问题的全面介绍,我建议您阅读这篇文章,对于一些详细的数学,我推荐这个答案

简而言之,您需要知道的是,没有碰撞的概率是exp(-1/2) = 60.65%,这意味着至少发生一次碰撞的概率大约是39.35%

因此,作为经验法则,如果我们有X个国家,至少发生一次碰撞的概率大约是40%,如果哈希函数的输出范围(即n_feature参数)是X^2。换句话说,如果您示例中的国家数量等于square_root(n_features),那么碰撞的概率是40%。随着n_features的指数增加,碰撞的概率减半。(个人来说,如果不是为了安全目的,而只是将字符串转换为数字,不值得设得太高)。

给好奇的读者的旁注:对于足够大的哈希函数输出大小(例如256位),从安全角度来看,攻击者猜测(或利用)碰撞的几率几乎是不可能的。


关于y参数,正如您已经在评论中得到的,它只是为了兼容性,不使用(scikit-learn有许多其他实现也包括这个)。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注