Shl*_*rtz 11 python machine-learning data-science
在实施机器学习模型之前,我正在预处理我的数据。某些特征具有高基数,例如国家/地区和语言。
由于将这些特征编码为 one-hot-vector 可以产生稀疏数据,我决定研究散列技巧并使用 python 的 category_encoders 像这样:
from category_encoders.hashing import HashingEncoder
ce_hash = HashingEncoder(cols = ['country'])
encoded = ce_hash.fit_transform(df.country)
encoded['country'] = df.country
encoded.head()
Run Code Online (Sandbox Code Playgroud)
查看结果时,我可以看到碰撞
col_0 col_1 col_2 col_3 col_4 col_5 col_6 col_7 country
0 0 0 1 0 0 0 0 0 US <??
1 0 1 0 0 0 0 0 0 CA. ? US and SE collides
2 0 0 1 0 0 0 0 0 SE <??
3 0 0 0 0 0 0 1 0 JP
Run Code Online (Sandbox Code Playgroud)
进一步调查使我找到了这篇 Kaggle 文章。那里的哈希示例包括X 和 y。
将欣赏如何使用散列技巧对此类类别进行编码的解释。
更新: 根据我从@CoMartel 得到的评论,我查看了Sklearn FeatureHasher并编写了以下代码来对国家/地区列进行哈希处理:
from sklearn.feature_extraction import FeatureHasher
h = FeatureHasher(n_features=10,input_type='string')
f = h.transform(df.country)
df1 = pd.DataFrame(f.toarray())
df1['country'] = df.country
df1.head()
Run Code Online (Sandbox Code Playgroud)
并得到以下输出:
0 1 2 3 4 5 6 7 8 9 country
0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 US
1 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 US
2 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 US
3 0.0 -1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 CA
4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 -1.0 0.0 SE
5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 JP
6 -1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 AU
7 -1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 AU
8 -1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 DK
9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 -1.0 0.0 SE
Run Code Online (Sandbox Code Playgroud)
n_features值?这是使用库来编码高分类值的方法吗?
是的。您的实施没有任何问题。
您可以将哈希技巧视为一种“具有较小冲突风险的减小尺寸的独热编码,如果您可以容忍原始特征维度,则不需要使用它”。
这个想法是由Kilian Weinberger首先提出的。您可以在他们的论文中找到对该算法的理论和实践/经验的整体分析。
为什么有些值是负值?
为了避免冲突,使用签名哈希函数。也就是说,首先使用通常的哈希函数对字符串进行哈希处理(例如,通过将每个字符的 ASCII 值相加,将字符串转换为相应的数值,然后取模n_feature以获得 (0, ]) 中的索引n_features。然后再进行另一个单使用位输出+1哈希函数。后者根据定义生成or -1,将其添加到第一个哈希函数产生的索引中。
伪代码(不过看起来像 Python):
def hash_trick(features, n_features):
for f in features:
res = np.zero_like(features)
h = usual_hash_function(f) # just the usual hashing
index = h % n_features # find the modulo to get index to place f in res
if single_bit_hash_function(f) == 1: # to reduce collision
res[index] += 1
else:
res[index] -= 1 # <--- this will make values to become negative
return res
Run Code Online (Sandbox Code Playgroud)
您将如何选择“正确的” n_features 值?
根据经验,您可以猜到,如果我们散列一个额外的功能(即 # n_feature + 1),则肯定会发生冲突。因此,最好的情况是每个特征都映射到唯一的哈希值——希望如此。在这种情况下,从逻辑上讲,n_features应该至少等于功能/类别的实际数量(在您的特定情况下,不同国家/地区的数量)。尽管如此,请记住,这是“最好”的情况,但“从数学角度来说”并非如此。因此,当然越高越好,但是多高呢?参见下一篇。
如何检查碰撞率?
如果我们忽略第二个单位哈希函数,问题就会简化为“哈希的生日问题”。
这是一个很大的话题。对于这个问题的全面介绍,我建议你阅读这篇文章,对于一些详细的数学,我推荐这个答案。
简而言之,您需要知道的是,没有碰撞的概率为exp(-1/2) = 60.65%,这意味着至少有39.35%可能发生一次碰撞。
因此,根据经验,如果我们有X国家,40%如果散列函数输出范围(即n_feature参数)为,则至少有一次碰撞的机会X^2。换句话说,40%如果示例中的国家/地区数量 = ,则有可能发生冲突square_root(n_features)。当你n_features成倍增加时,碰撞的机会就会减少一半。(就个人而言,如果不是出于安全目的,而只是从字符串到数字的简单转换,则不值得走得太高)。
好奇读者的旁注:对于足够大的哈希函数输出大小(例如 256 位),攻击者猜测(或利用)冲突的机会几乎是不可能的(从安全角度来看)。
关于y参数,正如您在评论中已经得到的那样,它只是出于兼容性目的,未使用(scikit-learn在许多其他实现中都有此参数)。