我在实现机器学习模型之前预处理数据。一些特征具有高基数,比如国家和语言。
由于将这些特征编码为独热向量会产生稀疏数据,我决定研究哈希技巧,并使用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
有许多其他实现也包括这个)。