机器学习中的许多模型中,对类别型变量,常作的处理是,将它们编码成one-hot。但是对于树模型来说,将类别型变量编码成one-hot,这样作是否有意义呢?像一些机器学习工具包(比如:spark gbm实现),你可以指定为类别型变量,内部自己去做one-hot实现。而像xgboost,则将输入全认为是数值型特征去处理。

一、要不要one-hot?

这在机器学习界也有争论。理论上,树模型如果够深,也能将关键的类别型特型切出来。

关于这个,xgboost的作者tqchen在某个issues有提到过:

I do not know what you mean by vector. xgboost treat every input feature as numerical, with support for missing values and sparsity. The decision is at the user

So if you want ordered variables, you can transform the variables into numerical levels(say age). Or if you prefer treat it as categorical variable, do one hot encoding.

在另一个issues上也提到过(tqchen commented on 8 May 2015):

One-hot encoding could be helpful when the number of categories are small( in level of 10 to 100). In such case one-hot encoding can discover interesting interactions like (gender=male) AND (job = teacher).

While ordering them makes it harder to be discovered(need two split on job). However, indeed there is not a unified way handling categorical features in trees, and usually what tree was really good at was ordered continuous features anyway..

总结起来的结论,大至两条:

  • 1.对于类别有序的类别型变量,比如age等,当成数值型变量处理可以的。对于非类别有序的类别型变量,推荐one-hot。但是one-hot会增加内存开销以及训练时间开销。
  • 2.类别型变量在范围较小时(tqchen给出的是[10,100]范围内)推荐使用

二、one-hot的一致性问题

当你进行one-hot编码时,有些机器学习工具包内置的one-hot编码工具会帮你做这些事,但是真实的情况是,我们有数据集有如下分类:训练集、测试集、预测集(真实数据)等。

这些数据集并不会统一,比如:

训练集上A特征有: a,b,c,d,测试集上A特征有:a,b,d,预测集上可能有b,c,e,f

因此,你需要做的是,将它们统一使用one-hot编码,而非分开做不同的one-hot编码.

参考

部分是翻译,部分是个人的理解.

目录

线性模型

非线性模型

树模型

在推荐系统中,用于测试模型性能,通常会选定随机选定部分用户,观察这些用户在推荐项上的行为。这就是我们常说的分桶测试(bucket tests)。

假定有两个推荐模型:模型A和模型B。我们可以创建两个不相交的样本:基于用户(用户id)的样本选择方式创建、或基于请求(用户访问行为)的样本选择方式创建。接着,对于第一个样本,使用模型A; 对于第二个样本,使用模型B。并持续服务一段时间。这里的每个样本,称为一个桶(bucket)。通常有两种常用的分桶方式:

  • 1.基于用户的分桶(User-based bucket):这样的桶,是一个随机选定用户的集合。一种简单的方式是,使用一个hash函数,为每个user id生成一个hash值,选择一个特定的范围指向一个桶。例如:Ron Rivest设计的md5。

  • 2.基于请求的分桶(Request-based bucket):这样的桶,是一个随机选择的请求的集合。常用的做法是,为每个请求生成一个随机数,然后将对应指定范围的请求随机数指定到某个桶内。注意,在这样的桶中,在实验期间,同一个用户不同的访问,有可能属于不同的分桶。

基于用户的分桶,通常比基于请求的分桶更简洁、更独立。例如,当使用基于请求的分桶时,一个用户使用模型A的响应(Response),可能会影响到模型B。但是,在基于用户的分桶中,这个现象不会发生。另外,任何长期用户行为都可以在基于用户的分桶中进行。然而,如果在基于用户的分桶中使用一个简单模型,该分桶的用户可能会收到不好的结果,这样也会导致较差的用户体验。而基于请求的分桶则对这种模型相对不敏感些,因为一个用户的所有请求不一样分配到相同的bucket中。总之,基于用户的分桶更受欢迎些。

在受控的实验中,分桶的所有设置应该一致,除了为每个分桶分配的模型不同;模型A用于服务分桶1;模型B用于服务分桶2。特别的,对于两个分桶来说,我们要使用相同的选择方式准则。例如,某一个分桶只包含登陆用户,那么另一个分桶也必须一致。

当使用基于用户的分桶时,对于不同的测试,最好使用独立的各不相同的hash函数,以保持正交性。例如,假设我们在一个web页面具有两个推荐模块,每个模块对应两个要测试的模型。两个对应的测试模块:test1和test2。对于每个test i,都有两个对应的推荐模型:Ai和Bi。如果我们在两组test上使用相同的hash函数为用户分配hash值,hash值低于某个阀值的使用模型Ai,剩下的使用模型Bi,这样,模型A1的用户与模型A2的用户相同;模型B1的用户与模型B2的用户相同。由于涉及到与A2和B2的交互,这会导致模型A1与模型B1之间的比较不够合理。解决这种问题的一个方法是,确保分配给A1模型的用户概率与test2中的A2或B2模型相互独立。这很容易实现,如果我们在test1中使用的将user id映射后的hash值与test2中相互统计独立即可。使用独立的hash函数,可以帮助我们控制当前测试与之前测试的独立性。

另一个有用的实践是,使用相同的模型服务两组分桶,并确认两个桶对应的性能指标是否统计上相似。这样的测试通常称为A/A test。它不仅为继承的统计变量提供了一个好的估计,而且还可以帮助在实验阶段发现明显错误。另一个有用的实践是,运行一个分桶测试至少需要一到两周,因为,用户行为通常有一周时间里有时间周期性上的不同。当一个新的推荐模型推荐对应在其它模型上完全不同的item时,由于新奇性效应(novelty effect),用户(user)可能倾向于在初始阶段点击更积极。为了减小因此造成的潜在偏差,当监控测试指标时,通常抛弃开始阶段的测试结果是很有用的。

标准的实验设计方法可以用来决定一个分桶所需要size以达到统计显著性(statistical significance)。拔靴法(Bootstrap sampling)在决定性能指标的方差时很管用,它可以用来帮助计算分桶的抽样size。详见:Montgomery(2012)、Efron和Tibshirani(1993).

参考:

移动端时代的挑战:手机屏更小,输入更不便,信息过载问题更严重。

用户获取信息的方式:浏览 vs. 查询

点击距离(click-distance):

click-distance(i) = selects(i) + scrolls(i) i为item的意思。

1 个性化用户兴趣

两种点击:

  • static hit-table:大众的点击数据,one-size-fits-all
  • user hit-table:个人的点击数据

其中static hit-table如下:

某一个用户的hit-table如下:

然后根据此计算这个用户对每个item的喜好概率. 概率计算:

该用户的喜好排序为:C>F>B>G>D>E

2 个性化调整

ok,计算好了之后。需要对每个用户做menu的调整。调整方式采用的是:垂直提升(vertical promotions)。举个例子,原先如果是三层:根菜单-父菜单-菜单选项。菜单选项提升到父菜单级别,父菜单提升到根菜单级别。别外同级之间的相对位置也会进行调整。

3 指标评测

  • 平均点击距离(是否降低)
  • 平均每个session的平均导航时间(是否降低)
  • 平均内容浏览时间(是否提升)

参考:

1.personalization techniques and recommender systems, Gulden Uchyigit etc.

此处省略开头,回归核心。。。

尽管word2vec提供了高质量的词汇向量,仍然没有有效的方法将它们结合成一个高质量的文档向量。在本篇文章中,受一个随机过程问题(中餐馆订餐过程CRP)的启发,我们讨论了一种可能的探索。基本思路是,使用CRP来驱动聚类过程,并将各种词向量在合适的聚类中合在一起。

假设我们有一个关于鸡肉订单(chicken recipe)的文档。它包含了下面的词汇:”chicken”, “pepper”,“salt”, “cheese”. 它也包含了其它的词汇:“use”, “buy”, “definitely”, “my”, “the”。 word2vec的模型将为每个单词生成一个vector。简单的,我们可以将所有词向量(word vector)合成一个文档向量(doc vector)。这将引入许多噪声。一种降噪方法是使用加权的合并,基于相应的算法,比如:IDF 或者 POS tag.

那么问题来了:当添加词汇时,是否可以更有选择性?回到鸡肉订单文档上,它不应该考虑以下的词汇:“definitely”, “use”, “my” 。基于权重的idf可以有效地减少一些停留词(”the”、”is”等等)的噪声问题。然而,对于这样的词汇:“definitely”, “overwhelming”,那么idf值将不会如你所愿那样的小。

如果我们首先将词汇聚类,像这样的词“chicken”, “pepper”将聚集到同一个类中,而像其它的词类似“junk”则希望聚到另一个类中。如果我们能区别相关的类,那么我们可以将相关类的词相量(word vector)合并,我们就可以得到一个很好的文档(doc vector).

当然,我们可以使用通用的算法:K-means,但是大多数这些算法都需要一个距离公式。word2vec可以通过余弦相似度(cosine)很方便地进行相似判断,不一定需要采用欧氏距离。

如果我们使用余弦相似度,我们可以很快进行聚类词汇。

回到中餐馆问题,假设你来到一个中餐馆,发现已经有n张桌子,每张桌子有不同的人。另外还有一张空桌子。CRP有一个超参数 r > 0,它表示这张空桌子上可能的人数。你到了(n+1)的桌子中其中之一,桌子上存在不同数目的人(对于空桌子,数目为r)。当你到达其中的一张桌子时,那么整个过程完成。如果你决定坐在空桌子上,餐厅会自动创建一张空桌子。在这个例子中,如果下一个顾客到来时,他会在(n+2)张桌子上做选择(包括新的空桌子)

受CRP的启发,我们尝试了在CRP中,包含相似因子的的以下变量。过程大致相同:我们给定聚类的M个向量。我们去维护两个东西:聚类和(cluster sum,没有中心),聚类中的各个向量(vector)。通过各向量进行迭代。对于当前的向量V,假设我们已经有了n个聚类。现在我们去找到聚类C,它的聚类和与当前的向量相似。我们将这个分数称为 sim(V,C).

  • 变量1: v 创建了一个新的聚类,它的概率为1/(1+n). 否则v就到聚类C中。
  • 变量2:如果sim(V,C) > 1/(1+n),则归到聚类C中。否则概率为1/(1+n),它将创建一个新的聚类,概率为n/(1+n),它将归到C。

在任意两个变量中,如果v归到一个聚类中,我们将更新聚类和,及相应的关系。

对于传统CRP,有一个明显的区别是:如果我们不到空桌子上,我们将决定去往“最相似”的桌子上。

实际上,我们将找到这些创建相似结果的变量。有个不同是,变量1趋向于更多但是单个量级更小的聚类;变量2趋向于少量,但是单个量级更大的聚类。变量2的例子如下所示:

对于chick recipe document,聚类如下:

  • ‘cayenne’, ‘taste’, ‘rating’, ‘blue’, ‘cheese’, ‘raved’, ‘recipe’, ‘powdered’, ‘recipe’, ‘dressing’, ‘blue’, ‘spicier’, ‘spoon’, ‘cup’, ‘cheese’, ‘cheese’, ‘blue’, ‘blue’, ‘dip’, ‘bake’, ‘cheese’, ‘dip’, ‘cup’, ‘blue’, ‘adding’, ‘mix’, ‘crumbled’, ‘pepper’, ‘oven’, ‘temper’, ‘cream’, ‘bleu’, ……
  • ‘the’, ‘a’, ‘that’, ‘in’, ‘a’, ‘use’, ‘this’, ‘if’, ‘scant’, ‘print’, ‘was’, ‘leftovers’, ‘bring’, ‘a’, ‘next’, ‘leftovers’, ‘with’, ‘people’, ‘the’, ‘made’, ‘to’, ‘the’, ‘by’, ‘because’, ‘before’, ‘the’, ‘has’, ‘as’, ‘amount’, ‘is’, ……
  • ‘stars’, ‘big’, ‘super’, ‘page’, ‘oct’, ‘see’, ‘jack’, ‘photos’, ‘extras’, ‘see’, ‘video’, ‘one’, ‘page’, ‘f’, ‘jun’, ‘stars’, ‘night’, ‘jul’, ……

很明显地,第一个聚类最相关。接着,我们获取聚类和向量。下面是python代码,word vector通过c版本将 英文Wiki语料训练得到,它将使用gensim.model.word2vec的python库获取模型文件。 c[0]表示聚类0:

>>> similar(c[0], model[chicken])

0.95703287846549179

>>> similar(c[0], model[recipe] + model[chicken])

0.95602993446153006

>>> similar(c[0], model[recipe] + model[fish])

0.7678791380788017

>>> similar(c[0], model[computer])

0.0069432409372725294

>>> similar(c[0], model[scala])

0.061027248018988116

看上去语义信息保存完好。我们使用doc向量是可信服的。 菜单文档看起来很简单。我们可以尝试更多的挑战,比如一篇新闻文章。新闻本身是叙事型的,包含很少的“主题词”。我们尝试在这篇文章标题为:“Signals on Radar Puzzle Officials in Hunt for Malaysian Jet”的文章进行聚类。我们可以得到4个聚类:

  • ‘have’, ‘when’, ‘time’, ‘at’, ‘when’, ‘part’, ‘from’, ‘from’, ‘in’, ‘show’, ‘may’, ‘or’, ‘now’, ‘on’, ‘in’, ‘back’, ‘be’, ‘turned’, ‘for’, ‘on’, ‘location’, ‘mainly’, ‘which’, ‘to’,, ‘also’, ‘from’, ‘including’, ‘as’, ‘to’, ‘had’, ‘was’ ……
  • ‘radar’, ‘northwest’, ‘radar’, ‘sends’, ‘signal’, ‘signals’, ‘aircraft’, ‘data’, ‘plane’, ‘search’, ‘radar’, ‘saturated’, ‘handles’, ‘search’, ‘controlled’, ‘detection’, ‘data’, ‘nautical’, ‘patrol’, ‘detection’, ‘detected’, ‘floating’, ‘blips’, ‘plane’, ‘objects’, ‘jets’, ‘kinds’, ‘signals’, ‘air’, ‘plane’, ‘aircraft’, ‘radar’, ‘passengers’, ‘signal’, ‘plane’, ‘unidentified’, ‘aviation’, ‘pilots’, ‘ships’, ‘signals’, ‘satellite’, ‘radar’, ‘blip’, ‘signals’, ‘radar’, ‘signals’ ……
  • ‘of’, ‘the’, ‘of’, ‘of’, ‘of’, ‘the’, ‘a’, ‘the’, ‘senior’, ‘the’, ‘the’, ‘the’, ‘the’, ‘the’, ‘the’, ‘a’, ‘the’, ‘the’, ‘the’, ‘the’, ‘the’, ‘of’, ‘the’, ‘of’, ‘a’, ‘the’, ‘the’, ‘the’, ‘the’, ‘the’, ‘the’, ‘its’, ……
  • ‘we’, ‘authorities’, ‘prompted’, ‘reason’, ‘local’, ‘local’, ‘increasing’, ‘military’, ‘inaccurate’, ‘military’, ‘identifying’, ‘force’, ‘mistaken’, ‘expanded’, ‘significance’, ‘military’, ‘vastly’, ‘significance’, ‘force’, ‘surfaced’, ‘military’, ‘quoted’, ‘showed’, ‘military’, ‘fueled’, ‘repeatedly’, ‘acknowledged’, ‘declined’, ‘authorities’, ‘emerged’, ‘heavily’, ‘statements’, ‘announced’, ‘authorities’, ‘chief’, ‘stopped’, ‘expanding’, ‘failing’, ‘expanded’, ‘progress’, ‘recent’, ……

看起来挺不错的。注意,这是个 输入为1的聚类过程,并且我们不必去指定聚类数目。这对于对延迟很敏感的服务来说很有帮助。

缺失了一环:如何找出相关的聚类?我们在这部分不必做扩展实验。可以考虑:

  • idf权值
  • POS tag。我们不必在文档中标记每个词。根据经验,word2vec趋向于在语法构成上聚在一起。我们对每个簇都抽取出一些tag。
  • 计算聚类和总向量,与标题向量

当然,还有其它问题需要考虑:

  • 1) 如何合并簇?基于向量间的相似度?或者簇成员间的平均相似度
  • 2)词的最小集合,可以重构簇和向量?可以使用关键词抽取方法。

结构:google的word2vec提供了强大的词向量。我们可以以有效的方式,来使用这些vector来生成高质量的文档向量。我们尝试了一个基于CRP变种的策略,并取得了结果。当然,还有很多问题需要研究,BalabalaBala…

代码如下:

# vecs: an array of real vectors
def crp(vecs):
    clusterVec = []         # tracks sum of vectors in a cluster
    clusterIdx = []         # array of index arrays. e.g. [[1, 3, 5], [2, 4, 6]]
    ncluster = 0
    # probablity to create a new table if new customer
    # is not strongly "similar" to any existing table
    pnew = 1.0/ (1 + ncluster)
    N = len(vecs)
    rands = random.rand(N)         # N rand variables sampled from U(0, 1)

    for i in range(N):
        maxSim = -Inf
        maxIdx = 0
        v = vecs[i]
        for j in range(ncluster):
            sim = cosine_similarity(v, clusterVec[j])
            if sim < maxSim:
                maxIdx = j
                maxSim = sim
            if maxSim < pnew:
                if rands(i) < pnew:
                    clusterVec[ncluster] = v
                    clusterIdx[ncluster] = [i]
                    ncluster += 1
                    pnew = 1.0 / (1 + ncluster)
                continue
        clusterVec[maxIdx] = clusterVec[maxIdx] + v
        clusterIdx[maxIdx].append(i)

    return clusterIdx

本文译自:http://eng.kifi.com/from-word2vec-to-doc2vec-an-approach-driven-by-chinese-restaurant-process/