大规模文本相似度计算

1,背景

      对于文本相似度计算,背景其实没必要提的,使用得多的地方有推荐和搜索等,所以精通个性化推荐的同学比我熟得多,尽管本人也做过两年半的个性化推荐。而特别写这篇文章的原因在于,在我们当前一个实际的业务需求中,因数据量太大,之前用过的常规content-based算法已无法支撑,除非以大大降低精度与覆盖率为代价(例如,为了单节点能计算,牺牲“热词”)。
      协同过滤方面的经典算法,所谓item-based、user-based、content-based等计算方法基本上是一样的,例如,item-based基本矩阵:
1
其中I表示Item,U表示User,1表示用户行为过的商品,实际中一般替换成一个评分或分值,即用户行为表示成三元组。对于content-based,User换成Word,评分换成权重,即三元组换成。
      说一下我们的具体应用。淘系的商品远超1亿,假设N亿吧。在我们的一个重要产品应用中,挑选了大约共1000万的“精品”,覆盖各类目,主要用于给用户推荐。我们需要做一个普通商品到精品的映射,即全集N亿×1000万,当用户搜到一个值得怀疑的商品时,例如后台判断是假货、劣质、炒信等,我们向用户推荐一个从标题上尽可能一致的精品。这样既能符合用户的查询意图,又能得到高品质的宝贝,可以极大提高用户满意度。为了让表述更直观,展示3个实际映射结果如下(左边是非精品库商品,右边是文本最接近的精品库商品。当前版本用到的关键属性很少,相对标题最终权重低,说明示例只备注标题):
L:正品环保大号桌面化妆品收纳盒抽屉式置物架塑料防水梳妆盒整理箱
R:桌面化妆品收纳盒办公桌上首饰创意收纳箱抽屉式卫生间防水置物架
2
L:2件包邮 日系抹胸纯棉蕾丝 雪纺小碎花小胸聚拢文胸套装可爱少女
R:日系碎花甜美少女可爱蕾丝内衣内裤中厚聚拢文胸套装性感女士胸罩
3
L:意大利经典上色烤漆油画箱多功能手提油 画架原木色工具箱写生便
R:榉木制意大利油画箱便携实木写生油画架多功能手提画箱美术工具箱
4

2,计算量

原始数据量:N亿 × 1000万。按原始数据量直接计算一看就是不行的,假设我们按照1000个计算节点算,则每个节点需要计算的商品相似度pair是N万亿,这个还不算乘上每个商品词的数量。而实际上,在odps上处理过数据倾斜的同学应该比较清楚,单节点join产生的pair超过10亿就已经超慢了以至于几乎不可行,N万亿就不用谈了。
计算量削减一:
对于这种应用中的文本相似度计算,跟Item-based很不一样的地方是:Item-based一般是所有数据一起计算的,当然也可以分行业分类目计算,但就没法得到所谓的“啤酒与尿布”的推荐了。我们这里提到的文本相似度不一样,明显可以分类目做,而且应该分类目做,因为就是为了得到标题尽可能一致的结果,跨类目可以说不是同一样东西了。即分类目计算,或者说带限制类目的文本相似度计算。
计算量削减二:
即使限制类目做文本相似度计算,我们发现,最大类目的笛卡尔积也有约xxxx万×x万,哪怕仅仅只计算这一个类目的商品,按1000个节点,每个节点处理的商品pair也有近120亿,远远超出了可承受范围,更不用说全部类目同时计算了。所以必须要进一步限制,而实际上可以进一步限制。例如都是“连衣裙”类目,一件叫“欧版修身印花连衣裙”,一件叫“韩版镂空针织连衣裙”,就不需要计算了,很明显根本不相似,因为它们仅仅在类目下热词匹配中了。即我们可以将仅仅匹配到了类目下热词的pair去掉
      对于削减二,需要特别说明的是(细节下文中会说):
      常规content-based算法为了计算可行,并不选择需要计算pair,而且直接排除热词,即热词根本不会参与到后面的相似度计算中。如果随便取个类目下阈值,例如500之类的,对淘系商品来说,热词就太多了,直接去掉,必将同时大大降低精准度和覆盖率。本文的做法对于所有选出来的pair,热词也全部参与计算,这样才能保证相似度结果是精准的。这也是与常规content-based过程最大的不同。

3,计算过程

3.1 相似度简要说明

相似度计算有很多方式,欧氏距离、余弦、Jaccard等,这里仅简单提下最常见的两种:
Cosine距离:
5
Jaccard距离:
6

3.2 计算步骤

常规content-based步骤:
      1,分词,词ID化(MR细节方面的考虑,也可以不ID化)
      2,对分词结果,计算词的权重
      3,对词的权重归一化
      4,计算文档对的相似度
      5,对相似度结果排序,输出前top N
本文计算步骤:
      1,分词,词ID化
      2,按cate_id,word对商品计数
      3,用2的结果过滤只在热词上匹配上的i2i对,得到需要计算的i2i池子
      4,对分词结果,计算词的权重
      5,对词的权重归一化
      6,计算文档对的相似度
      7,对相似度结果排序,输出前top N
      其中,2,3步和4,5步可以并行。即
                              7

3.3 计算步骤细节说明

对上述的7个步骤,做不同程度的说明,简单的过程就简单描述了。
步骤一,分词,词ID化:
输入:< item_id,cate_id,title,property_name>
输出:< item_id,cate_id,word,word_type,is_fine>
分词直接采用aliws,分词后得到5元组:< item_id,cate_id,word,word_type,is_fine>。其中,word_type表示词类型,因为我们除了计算title外,还计算了商品的关键属性,title取1,关键属性取2。is_fine是对于我们的具体应用来的,0表示普通商品,1表示精品。
步骤二,对cate_id,word对商品计数:
输入:< item_id,cate_id,word,word_type,is_fine>
输出:< cate_id,word,cnt1,cnt2>
按cate_id, word为组合key来分发,统计cate_id, word下的精品库商品和普通商品数量。得到4元组< cate_id,word,cnt1,cnt2>。其中,cnt1是精品库商品数量,cnt2是普通商品数量。
步骤三,用2的结果过滤只在热词上匹配上的i2i对,得到需要计算的i2i池子:
输入:< item_id,cate_id,word,word_type,is_fine> 和 < cate_id,word,cnt1,cnt2>
输出:< item_id1, item_id2>
其中,< item_id1, item_id2>即实际需要计算的i2i_pair,item_id1是非精品商品id,item_id2精品商品id。
这一步重点说明:

  • 作用:协同过滤里不管是Item-based还是Content-based等都有个特点,即稀疏性,真正需要发生相似度计算的地方是在发生碰撞的地方,例如图1中的I3和I5算相似度时只需要考虑到U1。即后面相似度的全部计算量就是总的碰撞次数,而这一步就是用来控制这个总计算量的。
  • i2i_pair选择:前面已经详述如果不做计算量削减真实计算量是不可行的。重申一下,这个选择做到两点,1)将仅仅匹配到了类目下热词的pair去掉;2)所有选出来的pair,包含的所有词参与计算,包括热词。第1点靠去掉无意义的计算(根本上不相似)保证整体计算可行,第2点保证计算精准性而保障最后排序结果。
  • 总计算量:不做任何限制,分类目计算的总计算量由步骤二步的输出表决定,即8。我们需要限制的就是这一部分,限制太松则不必要的计算增多但提高最终召回率(能找到最佳近似的商品占比);限制太紧则误杀的热词增多会导致最终召回率降低。限制的方式很多,例如cnt1,cnt2分别小于某阈值,cnt1cnt2小于某阈值,或两个条件并用,更合理的是按照类目弹性限制。我们先尝试了限制cnt1cnt2的方式,取一个阈值让总计算量控制在2000亿级别,这样按1000个节点计算,每个节点2亿碰撞,可行。这样最终覆盖率大概在90%。

步骤四,对分词结果,计算词的权重:
输入:< item_id, cate_id, word, word_type, is_fine>
输出:< item_id, cate_id, word, weight, is_fine>
权重计算采用传统TF-IDF,里面需要调的主要有title和属性权重区分、文档频率(DF)过低的词如何处理、DF过高的是否排除等。这些点一般是根据实际结果调整,比我熟的人太多,不细说了。
步骤五,对词的权重归一化:
输入:< item_id, cate_id, word, weight, is_fine>
输出:< item_id, cate_id, word, weight_norm, is_fine>
输出的weight_norm是归一化后的权重。weight_norm=weight / sqrt(sum(weight))。
步骤六,计算文档对的相似度:
输入:< item_id1, item_id2> 和 < item_id, cate_id, word, weight_norm, is_fine>
输出:< item_id1, item_id2, score>
score是最终cosine相似度分数。真正的计算量发生在这一步,耗时约5个小时。
过程简单描述为:
MR1:< item_id1, item_id2>与5元组join,按item_id1为key分发,得< item_id1,item_id2,word1, weight1>
MR2:< item_id1,item_id2,word1,weight1>与5元组join,按item_id2和word组合key分发,得< item_id1,item_id2,weight1,weight2>
MR3:< item_id1,item_id2,weight1,weight2>按item_id1和item_id2组合key分发,得< item_id1,item_id2,sum(weight1*weight2)>
写成sql就是2个join接上一个sum() group by。
步骤七,对相似度结果排序,输出前top N:
输入:< item_id1, item_id2, score>
输出:< item_id1, item_id_list>

4,小结

本文针对实际业务中的一个具体应用(规模N亿×1000万),对文本相似度计算的方法和过程做了分析和自己的改造,在能够完成整体计算的同时,兼顾结果的精准性和覆盖率。对计算过程做了相对详细的说明。当前映射结果基本符合需求,但可以改进的细节也不少,后续主要会就下面几点做优化:
1,对i2i_pair选择上计算量的限制,按类目细化。
2,词的权重优化,例如词类型权重的区分,不同域词权重的区分,文档词频方面的处理等。
3,得到其它相似度计算结果(当前其实也计算了Jaccard相似度,结果上跟cosine区别不是很明显),采用模型融合的方式,优化最终结果。
4,影响最终最终覆盖率的另一个关键因素是精品库本身对类目下商品的覆盖情况,所以将进一步优化精品库。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值