【ML特征工程】第 5 章 :分类变量:机器鸡时代的鸡蛋计数

   🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

编码分类变量

One-Hot 编码

dummy编码

effect编码

分类变量编码的优缺点

处理大型分类变量

特征哈希

Bin Counting

稀有类别呢?

防止数据泄露

计数无界限

概括


顾名思义,分类变量用于表示类别或标签。例如,分类变量可以代表世界上的主要城市、一年中的四个季节或公司的行业(石油、旅游、技术)。在现实世界的数据集中,类别值的数量总是有限的。这些值可以用数字表示。但是,与其他数值变量不同,分类变量的值不能相互排序。(石油作为一种行业既不大于也不小于旅行。)他们被称为非序数

一个简单的问题可以作为试金石来检验某个东西是否应该是一个分类变量:“两个值有多么不同,或者只是它们不同有关系吗?” 500 美元的股票价格是 100 美元的价格的五倍。所以,股票价格应该用一个连续的数值变量来表示。另一方面,公司的行业(石油、旅游、科技等)可能应该是绝对的。

大型分类变量在交易记录中尤为常见。例如,许多 Web 服务使用 ID 跟踪用户,ID 是一个具有数亿至数亿值的分类变量,具体取决于服务的唯一用户数量。互联网交易的 IP 地址是大型分类变量的另一个示例。它们是分类变量,因为即使用户 ID 和 IP 地址是数字,它们的大小通常与手头的任务无关。例如,在对单个交易进行欺诈检测时,IP 地址可能是相关的——一些 IP 地址或子网可能比其他地址或子网产生更多的欺诈交易。但是 164.203.xx 的子网本质上并不比 164.202.xx 更具欺诈性;子网的数值无关紧要。

文档语料库的词汇可以解释为一个大的分类变量,类别是唯一的词。表示这么多不同的类别在计算上可能很昂贵。如果一个类别(例如,单词)在一个数据点(文档)中出现了多次,那么我们可以将其表示为一个计数,并通过它们的计数统计来表示所有的类别。这称为bin 计数。我们从分类变量的常见表示开始这个讨论,并最终蜿蜒曲折地讨论大型分类变量的 bin 计数,这在现代数据集中非常常见。

编码分类变量

分类变量的类别通常不是数字。1例如,眼睛颜色可以是“黑色”、“蓝色”、“棕色”等。因此,需要一种编码方法将这些非数字类别转换为数字。简单地为k个可能的类别中的每一个分配一个整数(比如从 1 到k )是很诱人的——但结果值可以相互排序,这对于类别来说是不允许的。那么,让我们看看一些替代方案。

One-Hot 编码

更好的方法是使用一组位。每一位代表一个可能的类别。如果变量不能同时属于多个类别,则该组中只有一个位可以“打开”。这称为one-hot encoding,它在 scikit-learn 中实现为sklearn.preprocessing.OneHotEncoder. 每个的位是一个功能。因此,具有k个可能类别的分类变量被编码为长度为k的特征向量。表 5-1显示了一个示例。

表 5-1。三城市一类的one-hot编码
 e1e2e3
San Francisco100
New York010
Seattle001

one-hot encoding 很容易理解,但它使用的位比绝对必要的多。如果我们看到k –1 位为 0,则最后一位必须为 1,因为变量必须取k个值之一。在数学上,可以将此约束写为“所有位的总和必须等于 1”:

e1+e2+...+ek=1

因此,我们对我们的手有线性依赖。正如我们在第 4 章中发现的那样,线性相关特征有点烦人,因为它们意味着经过训练的线性模型将不是唯一的。特征的不同线性组合可以做出相同的预测,因此我们需要跳过额外的环节才能理解特征对预测的影响。

dummy编码

问题one-hot encoding是它允许k个自由度,而变量本身只需要k –1。虚拟编码2通过在表示中仅使用k –1 个特征来消除额外的自由度(见表 5-2)。一个特征被抛到总线下并由全零向量表示。这是众所周知的作为参考类别。虚拟编码和单热编码都在熊猫作为pandas.get_dummies

表 5-2。三个城市类别的虚拟编码
 e1e2
San Francisco10
New York01
Seattle00

与单热编码相比,使用虚拟编码建模的结果更易于解释。这在简单的线性回归问题中很容易看出。假设我们有一些关于三个城市的公寓租金价格的数据:旧金山、纽约和西雅图(见表 5-3)。

表 5-3。三个城市房价的数据集
 CityRent
0SF3999
1SF4000
2SF4001
3NYC3499
4NYC3500
5NYC3501
6Seattle2499
7Seattle2500
8Seattle2501

我们可以训练一个线性回归器来预测仅基于城市特征的租金价格(参见示例 5-1)。

线性回归模型可以写成:

y=w1X1+...+ wnXn

习惯上拟合一个额外的常数项称为截距,因此y可以是非零值x为零时的值:

y=w1X1+...+wnXn+b

示例 5-1。使用单热和虚拟代码对分类变量进行线性回归

>>> import pandas
>>> from sklearn import linear_model

# Define a toy dataset of apartment rental prices in 
# New York, San Francisco, and Seattle
>>> df = pd.DataFrame({
...     'City': ['SF', 'SF', 'SF', 'NYC', 'NYC', 'NYC', 
...              'Seattle', 'Seattle', 'Seattle'],
...     'Rent': [3999, 4000, 4001, 3499, 3500, 3501, 2499, 2500, 2501]
... })
>>> df['Rent'].mean()
3333.3333333333335

# Convert the categorical variables in the DataFrame to one-hot encoding
# and fit a linear regression model
>>> one_hot_df = pd.get_dummies(df, prefix=['city'])
>>> one_hot_df
   Rent  city_NYC  city_SF  city_Seattle
0  3999    0.0       1.0       0.0
1  4000    0.0       1.0       0.0
2  4001    0.0       1.0       0.0
3  3499    1.0       0.0       0.0
4  3500    1.0       0.0       0.0
5  3501    1.0       0.0       0.0
6  2499    0.0       0.0       1.0
7  2500    0.0       0.0       1.0
8  2501    0.0       0.0       1.0

>>> model = linear_regression.LinearRegression()
>>> model.fit(one_hot_df[['city_NYC', 'city_SF', 'city_Seattle']],
...           one_hot_df['Rent'])
>>> model.coef_
array([ 166.66666667,  666.66666667, -833.33333333])
>>> model.intercept_
3333.3333333333335

# Train a linear regression model on dummy code
# Specify the 'drop_first' flag to get dummy coding
>>> dummy_df = pd.get_dummies(df, prefix=['city'], drop_first=True)
>>> dummy_df
   Rent  city_SF  city_Seattle
0  3999    1.0       0.0
1  4000    1.0       0.0
2  4001    1.0       0.0
3  3499    0.0       0.0
4  3500    0.0       0.0
5  3501    0.0       0.0
6  2499    0.0       1.0
7  2500    0.0       1.0
8  2501    0.0       1.0

>>> model.fit(dummy_df[['city_SF', 'city_Seattle']], dummy_df['Rent'])
>>> model.coef_
array([  500., -1000.])
>>> model.intercept_
3500.0

使用 one-hot 编码,截距项表示目标变量的全局均值Rent,并且每个线性系数表示该城市的平均租金与全局均值的差异程度。

使用虚拟编码,偏差系数表示参考类别的响应变量y的平均值,在示例中为纽约市。第i个特征的系数等于第i个类别的平均响应值与参考类别的平均响应值之间的差值。

您可以在表 5-4中非常清楚地看到这些方法如何为线性模型产生非常不同的系数。

表 5-4。线性回归学习系数
 x 1x2 _× 3b
一次性编码166.67666.67–833.333333.33
虚拟编码0500–10003500

effect编码

另一种变体绝对的 多变的编码就是效果编码。效果编码很相似的到虚拟编码,不同之处在于,参考类别现在由所有 –1 的向量表示。

表 5-5。代表三个城市的分类变量的效果编码
 e1e2
San Francisco10
New York01
Seattle–1–1

效应编码与虚拟编码非常相似,但会产生更易于解释的线性回归模型。示例 5-2演示了将效果编码作为输入会发生什么。截距项表示目标变量的全局均值,个体系数表示个体类别的均值与全局均值的差异程度。(这被称为类别或级别的主效应,因此得名“效应编码”。)one-hot 编码实际上得出了相同的截距和系数,但在那种情况下,每个城市都有线性系数。在效果编码中,没有单个特征代表参考类别,因此参考的效果类别需要单独计算为所有其他类别的系数的负和。(有关详细信息,请参阅UCLA IDRE 网站上的“常见问题解答:什么是效果编码?” 。)

示例 5-2。带效应编码的线性回归

>>> effect_df = dummy_df.copy()
>>> effect_df.ix[3:5, ['city_SF', 'city_Seattle']] = -1.0
>>> effect_df
   Rent  city_SF  city_Seattle
0  3999    1.0       0.0
1  4000    1.0       0.0
2  4001    1.0       0.0
3  3499   -1.0      -1.0
4  3500   -1.0      -1.0
5  3501   -1.0      -1.0
6  2499    0.0       1.0
7  2500    0.0       1.0
8  2501    0.0       1.0

>>> model.fit(effect_df[['city_SF', 'city_Seattle']], effect_df['Rent'])
>>> model.coef_
array([ 666.66666667, -833.33333333])
>>> model.intercept_
3333.3333333333335

分类变量编码的优缺点

One-hotdummyeffect编码彼此非常相似。他们各有利弊。One-hot 编码是冗余的,它允许针对同一问题使用多个有效模型。非唯一性有时在解释上存在问题,但优点是每个特征清楚地对应一个类别。此外,缺失数据可以编码为全零向量,输出应该是目标变量的总体均值。

虚拟编码和效果编码并不多余。它们产生了独特且可解释的模型。虚拟编码的缺点是它不能轻松处理丢失的数据,因为全零向量已经映射到参考类别。它还对每个类别相对于参考类别的效果进行编码,这可能看起来很奇怪。

效果编码通过为参考类别使用不同的代码避免了这个问题,但是全 1 的向量是一个密集向量,这对于存储和计算来说都是昂贵的。出于这个原因,流行的 ML 软件包如 Pandas 和 scikit-learn 都选择了 dummy coding 或 one-hot encoding 而不是 effect coding。

当类别数量变得非常大时,这三种编码技术都会失效。需要不同的策略来处理非常大的分类变量。 

处理大型分类变量

互联网上的自动数据收集可以生成大分类变量。这在定向广告和欺诈检测等应用中很常见。

在定向广告中,任务是将用户与一组广告相匹配。特征包括用户 ID、广告的网站域、搜索查询、当前页面以及这些特征的所有可能成对连接。(查询是一个文本字符串,可以被切碎并变成通常的文本特征。但是,查询通常很短并且通常由短语组成,所以在这种情况下最好的做法通常是保持它们完整,或者通过散列函数传递它们以使存储和比较更容易。我们稍后将更详细地讨论散列。)这些中的每一个都是一个非常大的分类变量。面临的挑战是找到一种内存效率高的良好特征表示,同时生成可快速训练的准确模型。

现有的解决方案可以这样分类(哈哈):

  1. 不要对编码做任何花哨的事情。使用训练成本低的简单模型。在许多机器上将单热编码输入线性模型(逻辑回归或线性支持向量机)。
  2. 压缩特征。有两种选择:
    1. 特征哈希,流行于线性模型
    2. 二进制计数,在线性模型和树中很受欢迎

使用 vanilla one-hot 编码是一个有效的选择。对于微软的搜索广告引擎,Graepel 等人。(2010) 报告在贝叶斯概率回归模型中使用此类二进制值特征,可以使用简单更新在线训练。同时,其他团体主张压缩方法。雅虎的研究人员!对特征散列发誓(Weinberger 等人,2009 年),尽管 McMahan 等人。(2013) 在谷歌的广告引擎上试验了特征散列,并没有发现显着的改进。然而,Microsoft 的其他人却对 bin counting 的想法产生了兴趣(Bilenko,2015 年)。

正如我们将要看到的,所有这些想法都有利有弊。我们将首先描述解决方案本身,然后讨论它们的权衡。

特征哈希

哈希函数是一个确定性函数,它映射一个潜在的无界整数到有限整数范围 [1, m ]。由于输入域可能大于输出范围,多个数字可能会映射到同一个输出。这就是所谓的碰撞。_ 统一的哈希函数确保大致相同数量的数字被映射到m个容器中的每一个容器中。

在视觉上,我们可以将哈希函数视为一台机器,它接收编号的球(密钥)并将它们路由到m个容器中的一个。编号相同的球总是会被送到同一个箱子(见图 5-1)。这在维护特征空间的同时减少了机器学习训练和评估周期中的存储和处理时间。

可以为任何可以用数字表示的对象构造哈希函数(这适用于可以存储在计算机上的任何数据):数字、字符串、复杂结构等。

图 5-1。哈希函数将键映射到容器

当特征非常多时,存储特征向量会占用大量空间。特征哈希通过对特征 ID 应用哈希函数将原始特征向量压缩为m维向量,如示例 5-3所示。例如,如果原始特征是文档中的单词,那么哈希版本将具有固定的词汇量m,无论输入中有多少独特的单词。 

示例 5-3。词特征的特征哈希

def hash_features(word_list, m):
    output = [0] * m
    for word in word_list:
        index = hash_fcn(word) % m
        output[index] += 1
    return output

 特征的另一种变体哈希添加一个符号组件,以便将计数添加到散列 bin 中或从中减去(参见示例 5-4)。从统计学上讲,这确保了散列特征之间的内积在期望上与原始特征的内积相等。

示例 5-4。签名特征散列

def hash_features(word_list, m):
    output = [0] * m
    for word in word_list:
        index = hash_fcn(word) % m
        sign_bit = sign_hash(word) % 2
        if (sign_bit == 0):
            output[index] -= 1
        else:
            output[index] += 1
    return output

哈希后的内积值在欧(1米)的原始内积,因此可以根据可接受的误差来选择哈希表m的大小。在实践中,选择正确的m可能需要反复试验。

特征哈希可用于涉及特征向量和系数内积的模型,例如线性模型和核方法。它已被证明在垃圾邮件过滤任务中是成功的(Weinberger 等人,2009)。在定向广告的情况下,McMahan 等人。(2013) 报告无法将预测误差降低到可接受的水平,除非m约为数十亿,这不足以节省空间。

特征散列的一个缺点是散列特征作为原始特征的集合,不再可解释。

示例 5-5中,我们使用 Yelp 评论数据集来演示存储和可解释性取舍使用 scikit-learn 的FeatureHasher.

示例 5-5。特征散列(又名“散列技巧”)

>>> import pandas as pd
>>> import json

# Load the first 10,000 reviews
>>> f = open('yelp_academic_dataset_review.json')
>>> js = []
>>> for i in range(10000):
...     js.append(json.loads(f.readline()))
>>> f.close()
>>> review_df = pd.DataFrame(js)

# Define m as equal to the unique number of business_ids
>>> m = len(review_df.business_id.unique())
>>> m
528

>>> from sklearn.feature_extraction import FeatureHasher
>>> h = FeatureHasher(n_features=m, input_type='string')
>>> f = h.transform(review_df['business_id'])

# How does this affect feature interpretability?
>>> review_df['business_id'].unique().tolist()[0:5]
['vcNAWiLM4dR7D2nwwJ7nCA',
 'UsFtqoBl7naz8AVUBZMjQQ',
 'cE27W9VPgO88Qxe4ol6y_g',
 'HZdLhv6COCleJMo7nPl-RA',
 'mVHrayjG3uZ_RLHkLj-AMg']

>>> f.toarray()
array([[ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       ...,
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.],
       [ 0.,  0.,  0., ...,  0.,  0.,  0.]])

# Not great. BUT, let's see the storage size of our features.
>>> from sys import getsizeof
>>> print('Our pandas Series, in bytes: ', getsizeof(review_df['business_id']))
>>> print('Our hashed numpy array, in bytes: ', getsizeof(f))
Our pandas Series, in bytes:  790104
Our hashed numpy array, in bytes:  56

我们可以清楚地看到使用特征散列将如何在计算上使我们受益,同时牺牲即时的用户可解释性。当从数据探索和可视化发展到大型数据集的机器学习管道时,这是一个容易接受的权衡。

Bin Counting

计数是常年存在的一种机器学习的重新发现。它已被重新发明并用于各种应用,从广告点击率预测到硬件分支预测(Yeh 和 Patt,1991 年;Lee 等人,1998 年;Chen 等人,2009 年;Li 等人,2010 年) ). 然而,由于它是一种特征工程技术,而不是一种建模或优化方法,因此没有关于该主题的研究论文。可以在 Misha Bilenko(2015 年)的博客文章“Big Learning Made Easy—with Counts!”中找到对该技术的最详细描述。相关的幻灯片

bin 计数的想法非常简单:不是使用分类变量的作为特征,而是使用目标在该值下的条件概率。换句话说,我们不是对分类值的身份进行编码,而是计算该值与我们希望预测的目标之间的关联统计。对于那些熟悉朴素贝叶斯分类器的人来说,这个统计量应该响起,因为它是假设所有特征都是独立的类的条件概率。最好用一个例子来说明(见表 5-6)。

表 5-6。计数功能示例(经许可转载自“Big Learning Made Easy—with Counts!”)
UserNumber of clicksNumber of nonclicksProbability of clickQueryHash, AdDomainNumber of clicksNumber of nonclicksProbability of click
Alice51200.04000x598fd4fe, foo.com5,00030,0000.167
Bob202300.08000x50fa3cc0, bar.org1009000.100
     
Joe230.4000x437a45e1, qux.net6180.250

箱计数假定历史数据可用于计算统计数据。表 5-6包含分类变量的每个可能值的汇总历史计数。根据用户“爱丽丝”点击任何广告的次数和她没有点击的次数,我们可以计算出她点击任何广告的概率。同样,我们可以计算任何查询-广告域组合的点击概率。在训练时,每次我们看到“爱丽丝”时,我们都可以使用她的点击概率作为模型的输入特征。这同样适用于QueryHash–AdDomain对,例如“0x437a45e1,qux.net”。

假设有 10,000 个用户。单热编码会生成一个长度为 10,000 的稀疏向量,列中的单个 1 对应于当前数据点的值。bin 计数会将所有 10,000 个二进制列编码为具有 0 到 1 之间实数值的单个特征。

除了历史点击概率之外,我们还可以包括其他特征:原始计数本身(点击次数和非点击次数)、对数优势比或概率的任何其他导数。我们这里的示例用于预测广告点击率,但该技术很容易适用于一般的二元分类。它也可以很容易地扩展到多类分类,使用通常的技术将二元分类器扩展到多类;即,通过一对多的优势比或其他多类标签编码。

箱计数的优势比和对数优势比

比值比通常定义在两个二元变量之间。它通过提问“当X为真时Y为真的可能性有多大?”来考察他们的关联强度。例如,我们可能会问,“与一般人群相比,爱丽丝点击广告的可能性有多大?” 这里,X是二进制变量“Alice 是当前用户”,Y是变量“是否点击广告”。计算使用所谓的双向列联表(基本上,四个数字对应于XY的四种可能组合),如表 5-7所示。

表 5-7。广告点击和用户的列联表
 ClickNonclickTotal
Alice5120125
Not Alice99518,88019,875
Total1,00019,00020,000

给定输入变量X和目标变量Y,优势比定义为:

 在我们的示例中,这转化为“爱丽丝点击广告而不是不点击的可能性有多大”与“其他人点击而不是不点击的可能性有多大”之间的比率。在这种情况下,数字是:

 更简单地说,我们可以只看分子,它检查单个用户 (Alice) 点击广告与不点击的可能性有多大。这适用于具有许多值的大型分类变量,而不仅仅是两个:

 概率比很容易变得非常小或非常大。(例如,有些用户几乎从不点击广告,也许有些用户更频繁地点击广告。)日志转换再次为我们提供了帮助。对数的另一个有用的特性是它将除法变成减法:

 简而言之,bin counting 将分类变量转换为有关值的统计信息。它变成了一个大而稀疏的,二进制表示将分类变量(例如由单热编码生成的变量)转换为非常小的、密集的实数值表示(图 5-2)。

图 5-2。分类变量的单热编码与 bin 计数统计的图示

 在实现方面,分箱计数需要存储每个类别与其关联计数之间的映射。(其余的统计数据可以从原始计数中即时导出。)因此它需要O ( k ) 空间,其中k是分类变量的唯一值的数量。

为了在实践中说明 bin 计数,我们将使用来自 Avazu 主办的 Kaggle 竞赛的数据。这里是关于数据集的一些相关统计数据:

  • 有 24 个变量,包括click,二进制点击/无点击计数器,以及device_id,它跟踪广告显示在哪个设备上。
  • 完整的数据集包含 40,428,967 个观测值,以及 2,686,408 个独特的设备。

Avazu 竞赛的目的是使用广告数据预测点击率,但我们将使用数据集来演示 bin 计数如何大大减少大量流数据的特征空间(参见示例 5-6)。

示例 5-6。计数示例

>>> import pandas as pd

# train_subset data is first 10K rows of 6+GB set
>>> df = pd.read_csv('data/train_subset.csv')

# How many unique features should we have after?
>>> len(df['device_id'].unique())
7201

# For each category, we want to calculate:
# Theta = [counts, p(click), p(no click), p(click)/p(no click)]

>>> def click_counting(x, bin_column):
...     clicks = pd.Series(x[x['click'] > 0][bin_column].value_counts(), 
...                        name='clicks')
...     no_clicks = pd.Series(x[x['click'] < 1][bin_column].value_counts(), 
...                           name='no_clicks')
    
...     counts = pd.DataFrame([clicks,no_clicks]).T.fillna('0')
...     counts['total_clicks'] = counts['clicks'].astype('int64') +
...                              counts['no_clicks'].astype('int64')
...     return counts

>>> def bin_counting(counts):
...     counts['N+'] = counts['clicks']
...                    .astype('int64')
...                    .divide(counts['total_clicks'].astype('int64'))
...     counts['N-'] = counts['no_clicks']
...                    .astype('int64')
...                    .divide(counts['total_clicks'].astype('int64'))
...     counts['log_N+'] = counts['N+'].divide(counts['N-'])
...     # If we wanted to only return bin-counting properties, 
...     # we would filter here
...     bin_counts = counts.filter(items= ['N+', 'N-', 'log_N+'])
...     return counts, bin_counts

# Bin counts example: device_id
>>> bin_column = 'device_id'
>>> device_clicks = click_counting(df.filter(items=[bin_column, 'click']), 
...                                bin_column)
>>> device_all, device_bin_counts = bin_counting(device_clicks)

# Check to make sure we have all the devices
>>> len(device_bin_counts)
7201

>>> device_all.sort_values(by = 'total_clicks', ascending=False).head(4)

         clicks  no_clicks  total  N+        N-        log_N+
a99f214a 15729   71206      86935  0.180928  0.819072  0.220894
c357dbff 33      134        167    0.197605  0.802395  0.246269
31da1bd0 0       62         62     0.000000  1.000000  0.000000
936e92fb 5       54         59     0.084746  0.915254  0.092593

稀有类别呢?

就像稀有的话,稀有类别需要特殊处理。想一想每年登录一次的用户:几乎没有数据可以可靠地估计该用户的广告点击率。此外,稀有类别会浪费计数表中的空间。

单程解决这个问题的方法是通过回退,这是一种简单的技术,可以将所有稀有类别的计数累积到一个特殊的容器中(见图 5-3)。如果计数大于某个阈值,则该类别将获得自己的计数统计信息。否则,我们使用来自 back-off bin 的统计数据。这实质上将单个稀有类别的统计数据还原为对所有稀有类别计算的统计数据。使用退避方法时,还可以添加一个二进制指示符来指示统计信息是否来自退避仓。

图 5-3。如果稀有类别获得计数,它可以移动到回退箱的阈值之上,使用自己的计数统计进行建模

还有一种处理方法这个问题,称为count-min sketch(Cormode 和 Muthukrishnan,2005)。在这种方法中,所有类别,无论是稀有类别还是常见类别,都通过输出范围m远小于类别数量k的多个哈希函数进行映射。检索统计信息时,重新计算该类别的所有哈希值并返回最小的统计信息。拥有多个散列函数可以降低单个散列函数内发生冲突的可能性。该方案之所以有效,是因为散列函数的数量乘以m,即散列表的大小,可以小于k,类别的数量,并且仍然保持较低的整体冲突概率。

图 5-4说明了这一点。每个项目 i 都映射到计数数组每一行中的一个单元格。当 c t对项目 i t的更新到达时,c t被添加到这些单元格中的每一个,并使用函数 h 1 … h d进行散列。

图 5-4。计数分钟草图

防止数据泄露

由于 bin counting 依赖于历史数据来生成必要的统计数据,因此它需要等待一个数据收集期,从而导致学习管道中的轻微延迟。此外,当数据分布发生变化时,需要更新计数。数据变化越快,需要重新计算的次数就越频繁。这对于定向广告等应用尤为重要,在这些应用中,用户偏好和热门查询变化非常快,无法适应当前的分布可能意味着广告平台的巨大损失。

有人可能会问,为什么不使用相同的数据集来计算相关统计数据并训练模型呢?这个想法似乎很天真。这里的大问题是统计数据涉及目标变量,这是模型试图预测的。使用输出计算输入特征会导致称为泄漏的有害问题。简而言之,泄漏意味着信息被透露给模型,使其具有做出更好预测的不切实际的优势。当测试数据泄露到训练集中,或者未来的数据泄露到过去时,可能会发生这种情况。当模型在生产中进行实时预测时,只要模型获得了不应访问的信息,就会出现泄漏。Kaggle 的维基给出了更多泄漏示例以及为什么它对机器学习应用程序不利。

如果 bin-counting 程序使用当前数据点的标签来计算部分输入统计量,那将构成直接泄漏。防止这种情况的一种方法是在计数收集(用于计算 bin-count 统计数据)和训练之间建立严格的分离,如图 5-5所示——即,使用较早一批的数据点进行计数,使用当前数据点进行计数训练(将分类变量映射到我们刚刚收集的历史统计数据),并使用未来的数据点进行测试。这解决了泄漏问题,但引入了上述延迟(输入统计数据和模型将落后于当前数据)。

图 5-5。使用时间窗可以防止在 bin 计数期间的数据泄漏

事实证明,还有另一种解决方案,基于差分隐私。如果统计数据的分布在有或没有任何一个数据点的情况下保持大致相同,则该统计数据几乎是防泄漏的。实际上,添加分布为 Laplace(0,1) 的小随机噪声足以掩盖来自单个数据点的任何潜在泄漏。这个想法可以与留一法相结合,对当前数据进行统计(Zhang,2015)。

计数无界限

如果根据越来越多的历史数据不断更新统计数据,原始计数将无限增长。这可能是模型的问题。训练有素的模型“知道”输入数据直至观察到的规模。训练有素的决策树可能会说:“当x大于 3 时,预测 1。” 训练有素的线性模型可能会说,“将x乘以0.7,看看结果是否大于全球平均水平。” 当x介于 0 和 5 之间时,这些可能是正确的决定。但除此之外会发生什么?没人知道。

当输入计数增加时,将需要重新训练模型以适应当前规模。如果计数累积得比较慢,那么有效尺度就不会变化太快,模型也不需要太频繁地重新训练。但是当计数增加得非常快时,频繁的再训练会很麻烦。

出于这个原因,通常最好使用保证在已知区间内有界的归一化计数。例如,估计的点击概率在 [0, 1] 之间。另一种方法是对数变换,它有一个严格的界限,但是当计数很大时,增加的速度会很慢。

这两种方法都无法防止输入分布的变化(例如,去年的芭比娃娃现在已经过时了,人们将不再点击这些广告)。该模型将需要重新训练以适应输入数据分布中这些更根本的变化,或者整个管道将需要转移到模型连续不断的在线学习环境适应到输入。

概括

本章详述的每种方法都有其优点和缺点。这是一个纲要取舍。

普通的one-hot编码
空间要求O ( n ) 使用稀疏向量格式,其中n是数据点的数量
计算要求O ( nk ) 在线性模型下,其中k是类别数
优点
  • 最容易实施
  • 可能最准确
  • 适合在线学习
缺点
  • 计算效率低下
  • 不适应不断增长的品类
  • 对于线性模型以外的任何东西都不可行
  • 需要对真正的大数据集进行大规模分布式优化
特征散列
空间要求O ( n ) 使用稀疏矩阵格式,其中n是数据的数量积分
计算要求O ( nm ) 在线性或内核模型下,其中m是哈希箱的数量
优点
  • 易于实施
  • 使模型训练更便宜
  • 轻松适应新类别
  • 轻松处理稀有类别
  • 适合在线学习
缺点
  • 仅适用于线性或核化模型
  • 散列特征不可解释
  • 准确性的混合报告
Bin-counting
空间要求O ( n+k ) 用于每个数据点的小而密集的表示,加上必须的计数统计为每个类别保留
计算要求O ( n ) 对于线性模型;也可用于非线性模型,例如树
优点
  • 训练时的最小计算负担
  • 启用基于树的模型
  • 相对容易适应新品类
  • 使用回退或计数最小草图处理稀有类别
  • 可解释的
缺点
  • 需要历史数据
  • 需要延迟更新,不完全适合在线学习
  • 更高的泄漏可能性

正如我们所见,没有一种方法是完美的。使用哪一个取决于所需的模型。线性模型的训练成本更低,因此可以处理非压缩表示,例如单热编码。另一方面,基于树的模型需要对所有特征进行重复搜索以获得正确的分割,因此仅限于小的表示,例如 bin 计数。特征散列介于这两个极端之间,但关于结果准确性的报告好坏参半。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sonhhxg_柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值