CIKM2019超大规模推荐之用户兴趣高效检索.pdf

一、赛题


天池CIKM2019超大规模推荐之用户兴趣高效检索是江离于2019年7月决定派代表队参加的天池四赛之一。这里简单地介绍一下我们在这场比赛中所设计的方案。
这个题目的业务场景比较常见,就是利用用户历史的交互商品记录,预测这个用户未来一天的最有可能交互的50件商品;但是这里多了一个要求,就是预测的商品不能是用户交互过的,这题目因而变得有趣起来。
19.12.27 江离CIKM用户兴趣高效检索方案简述 - 图1图1 数据
评价指标也是赛题的一部分。这个赛题的评价指标recall@50,就是求出每个用户前50个商品的召回率,然后对这些召回率求均值。我也搞不清楚如果改成precision@50、F1@50或是recall@200会有怎样的影响。大概我的方案不会有什么本质上的变化。
另外这个赛题还有一个规则。记商品全集的大小为n,要求选手的方案的时间复杂度是O(n)。我认为这实在是一个很多余的规则,因为大部分团队的方案应该都是O(1)的(谁会那么无聊地把商品全集中的每一个商品都预测一遍,大部分团队的方案应该都是和n无关的)。限制时间复杂度不如直接限制运行时间。

二、线下测试

  1. 线下测试应该没有什么疑问。取最后一天的交互数据作为线下测试集,用之前的数据来预测这一天的交互行为。 <br /> 数据挖掘的各种问题都应由客观事实来决定。我遇到很多次「事实与我的判断严重不符」的情况了;每次看一个想法或是一个结论,怎么看怎么想都觉得非常有道理,但验证一下却怎么都不对。我们凭借生活经验以及对数据的分析、对业务的理解等得到的结论,虽然难以保证每一个都去实际验证一下,但也要尽可能根据线下测试的结果来判断。 <br /> (可笑的是,偏有人在有明确的客观成绩的情况下选择相信自己的主观臆测。) <br />

三、基本思路

  1. 我们要预测的是用户将要交互,而且没有交互过的商品。我们掌握的用户信息包括性别,年龄,购买力,以及用户交互过的商品。 <br /> 那么我们要用哪个信息?看起来,性别、年龄、购买力这些信息都不太靠谱,你不能认为所有男生或是所有20多岁的年轻人都会购买相近的商品,这些是比较弱的特征;而用户交互过的商品才是关键。比如某用户交互过**_商品甲_**,我们就可以把这个用户归类为「会交互**_商品甲_**的用户」,然后看看「会交互**_商品甲_**的用户」一般还会交互什么;比如这类用户一般都会交互**_商品乙_**,那么我们就可以把**_商品乙_**作为这个用户的预测。 <br /> 也就是说,我们要找出经常被用户同时交互的商品(注:这里的同时与时间无关,并非表示同一时间)——如上面的**_商品甲_**和**_商品乙_**;我们称这样的商品为关联商品——用它们构造一个关联商品表;然后对于用户交互过的**_商品甲_**,我们要找到和它相关联的**_商品乙_**,并将**_商品乙_**作为预测。也就是![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610678344-38408a5e-c9a2-4e6f-8f09-8a17a6e6de79.svg#clientId=ud87fddca-ae4f-4&from=paste&id=VnENP&originHeight=43&originWidth=276&originalType=url&status=done&style=none&taskId=ud38e4a90-368a-4f46-9675-5a332458590) 这样的**_商品乙_**可能有很多,我们应该选择哪50个呢?我们要根据用户对这件**_商品甲_**的交互情况,及**_商品甲_**和**_商品乙_**的关联程度来给这些**_商品乙_**打分。因此,我们要解决的问题有两个:1. 设计**_商品甲_**和**_商品乙_**的关联打分;2. 设计用户对**_商品甲_**的交互打分。解决这两个问题之后,我们就可以用类似 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610678352-5727d574-a89c-4231-9a36-0b7e5488283b.svg#clientId=ud87fddca-ae4f-4&from=paste&id=qBQSG&originHeight=34&originWidth=676&originalType=url&status=done&style=none&taskId=u075638c2-db16-40e2-a061-c8c72686edf)的形式来计算用户未来交互商品乙的可能性打分。 <br /> (话说相关联可能是因为原本就是相关联的,也有可能是因为经常被展视在一起而相关联的。如果后者的情况占了绝大多数,那么这就是一个很古怪的题目了) <br /> (再进一步地讲,用户交互某个商品,本来就有可能因为看到了被曝光的商品,而被曝光的商品也是利用算法选出的,而不是随机选出的) <br /> 这便是一个基本的思路,即一个简单的基于商品的协同过滤规则。那么基于用户可以吗?当然也可以,只是我还没有来得及尝试。因为根据以往的经验,基于用户的协同过滤规则在这类问题上的效果都比较一般,我通常会优先尝试基于商品。

四、关联打分

  1. 那么我们就来讨论关联打分。

4.1 基础

  1. 一个简单的基线是,我们直接统计两件商品被多少个用户同时交互过。比如**_商品甲_**和**_商品乙_**被10个用户交互过,那么它们之间的关联打分便是10 <br /> 可是仔细想想,如果一个用户交互了1次**_商品甲_**和1次**_商品乙_**,我们把这两件商品的关联打分算作1;另一个用户交互了5次**_商品丙_**和4次**_商品丁_**,我们依然算作1,就不太合理;看起来后者对**_商品丙_**和**_商品丁_**的兴趣是可靠的,而前者说不定就是随便点了一下**_商品甲_**或**_商品乙_**。因此此时**_商品丙_**和**_商品丁_**的关联打分应该大于1才对。那么是多少?5×4吗?5+4吗?或是更泛化的![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610678414-2a9d854a-9b5a-43ed-b75f-557bd5269f66.svg#clientId=ud87fddca-ae4f-4&from=paste&id=uabd9b521&originHeight=26&originWidth=145&originalType=url&status=done&style=none&taskId=u2111deaf-cf61-420e-9b90-00afa13f21e) 次? <br /> 然后如果有 n 个用户,每个用户计算出**_商品甲_**和**_商品乙_**的关联打分分别是 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610678759-ab8ddd74-ec7b-4e1a-98fb-577a6bc4495d.svg#clientId=ud87fddca-ae4f-4&from=paste&id=uaed2bbb7&originHeight=16&originWidth=104&originalType=url&status=done&style=none&taskId=u8179359e-c153-4782-a4e3-e19a0c6d6ab) ,又该如何将他们聚合?直接相加?或者泛化一下,用 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610678905-73194ff1-81d9-43fe-ae45-ad5131c51c9f.svg#clientId=ud87fddca-ae4f-4&from=paste&id=ucec8c68a&originHeight=61&originWidth=133&originalType=url&status=done&style=none&taskId=u8485b819-4d83-46cb-b546-98f826de7d1) ? <br /> 我实在难以用理论来确定这里 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679015-e288e401-26d2-4050-b3b8-71ee4a264632.svg#clientId=ud87fddca-ae4f-4&from=paste&id=uf13d7cc1&originHeight=23&originWidth=37&originalType=url&status=done&style=none&taskId=u2eee49bf-c0c4-4b89-9f84-aa504ca7794) 和![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679235-aadc85df-4787-4441-8c21-9d2411dc66b9.svg#clientId=ud87fddca-ae4f-4&from=paste&id=ub51bdf0a&originHeight=23&originWidth=35&originalType=url&status=done&style=none&taskId=u223d63f6-183e-440b-a9a2-afa7714aea7)的形式,只能大致判断它们都是增函数。在数值不大时,我们可以用一种增函数来拟合另一种增函数;通常我会尝试一些简单而初等的函数,主要包括幂函数、指数函数、对数函数等,并根据线下测试选择其中最优秀的。对于![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679334-576304b4-e251-431f-a679-0902599d08a9.svg#clientId=ud87fddca-ae4f-4&from=paste&id=uaa178599&originHeight=23&originWidth=37&originalType=url&status=done&style=none&taskId=ueb8f852e-a431-472b-b4eb-f281e693140)的形式,我经过测试选择采用 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679333-6a3069b8-7412-49ff-bbe5-e4a6a0664a76.svg#clientId=ud87fddca-ae4f-4&from=paste&id=u40967923&originHeight=23&originWidth=92&originalType=url&status=done&style=none&taskId=u186cf52b-e1d7-41e9-805e-2145982e9b0) (就是前面提到的5×4);对于 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679431-fceed434-aa63-4a37-a029-13a55a31377a.svg#clientId=ud87fddca-ae4f-4&from=paste&id=u3096e62d&originHeight=23&originWidth=35&originalType=url&status=done&style=none&taskId=u18b41d4e-cddf-4ced-ba25-41426fdcffb) 的形式,我在测试![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679639-f6207579-48e2-45e6-9f20-d00f8642aa7f.svg#clientId=ud87fddca-ae4f-4&from=paste&id=u6bc505d7&originHeight=23&originWidth=73&originalType=url&status=done&style=none&taskId=uc9519119-a430-4b8b-a2cf-18b878871bb)、![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679916-e5fcdfd3-28a7-429c-ae61-01495b5e5fd8.svg#clientId=ud87fddca-ae4f-4&from=paste&id=u6fd79a31&originHeight=26&originWidth=82&originalType=url&status=done&style=none&taskId=ue569c4f3-e1d7-4c73-9f1d-c1f0388beac)和![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610679922-8bb1f27d-00e3-425d-953e-4fafc3f77fac.svg#clientId=ud87fddca-ae4f-4&from=paste&id=ue7594579&originHeight=26&originWidth=82&originalType=url&status=done&style=none&taskId=u16032b46-8b34-460d-b479-6770ad3d3bc)等几个形式(一般就是这几个最好)之后,发现它们之间差别不大(差别在误差范围内,分辨不出哪个更好),所以选择最简单的形式![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610680019-0f9ac1a0-f8b7-45d1-9b60-3ecc5e97c025.svg#clientId=ud87fddca-ae4f-4&from=paste&id=u7ce6df38&originHeight=23&originWidth=73&originalType=url&status=done&style=none&taskId=uc80fbd25-414f-4fa9-a40d-ff1f5963a7d)。(所以忙活了半天,最终还是采用了最基本的形式;不过有些情况下,选用不同的形式在效果上会有明显的差别) <br /> 于是如果一个用户交互过5次**_商品甲_**,4次**_商品乙_**,另一个用户交互过3次**_商品甲_**,6次**_商品乙_**,那么用这两个用户计算出的**_商品甲_**和**_商品乙_**的关联打分便是5×4+3×6。![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610680395-fb45794d-d04d-41cf-b77d-110a38e6df8b.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=uc423c8ad&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=u09519093-4962-4776-88a8-5aabd35178a&width=812.5) 图4.1 基础关联打分的一个奇怪的形式(其实是等价的)

4.2 一个小问题

  1. 但是这样做实际上有一个小问题。比如一个用户交互了20件不同的商品,那么就要产生190个关联商品对。这样得到的关联商品对实在是太多了,我的机器不肯接受这样巨大的工作量。因此我们需要想一些办法来简化。 <br /> 我能想到的办法就是要预先过滤一些。大概有这么两个想法:1) 只统计历史交互次数超过n次的商品;2) 只统计同类目(或同店铺、同品牌)的关联商品对。当然,在得到所有的关联商品对之后,也要再过滤一次, <br /> 于是我将这两个想法结合起来。一方面,我分别统计同类目、同店铺、同品牌的关联商品对;另一方面,我先过滤掉交互次数少于32的商品(这个数值当然越小越好,不过实际上即便取大一些也不会有太大影响)然后再统计关联商品对。

4.3 进阶

  1. 再仔细想想,上面的规则也不够合理。例如一个用户交互了**_商品甲_**和**_商品乙_**,我们把**_商品甲_**和**_商品乙_**的打分算作1;另一个用户交互了**_商品丙_**和**_商品丁_**以及另外二十件商品,我们把**_商品丙_**和**_商品丁_**的打分也算作1,这就有问题了。因为前一个用户对**_商品甲_**和**_商品乙_**感兴趣,这两件商品很有可能是关联的;而后一个用户的兴趣太广泛,他可能对什么都感兴趣,那么**_商品丙_**和**_商品丁_**的关联性就不够可靠,因而它们的关联打分应该是小于**_商品甲_**和**_商品乙_**的。因此我们应该对用户交互的次数做一些惩罚,也就是加一个系数,使得交互次数越多时这个系数越小。 <br /> 另一方面,如果一个用户交互了**_商品甲_**和**_商品乙_**,那么它们的关联打分也应该和这两次交互的时间差(或单序差)有关。那么我们又要加一个时间差(或单序差)惩罚,也就是加一个系数,使得交互时间差(或单序差)越大时这个系数越小。 <br /> 对于上面两个系数,我们已经大致确定了它们与标签的相关性。那么一个自然的想法便是 <br /> ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610680597-2cf63d93-3502-43bb-a72e-86101a989c5a.svg#clientId=ud87fddca-ae4f-4&from=paste&id=ubaa412f1&originHeight=69&originWidth=345&originalType=url&status=done&style=none&taskId=u3eb9add7-d2c8-4769-bae5-05c3bdf8b86)<br /> 这里的参数需要经过线下测试确定。显然它们都是正数。这类参数通常不会特别敏感,我们可以从1开始,手动测试一些参数,选择其中最优的。例如这里我测试的过程大概是 <br />参数甲:初始值1→尝试2(变差)→尝试0.5(变好)→尝试0.2(变好)→尝试0.1(变差)→选择0.2<br />参数乙:初始值1→尝试2(变好)→尝试4(变好)→尝试8(变差)→选择4 <br /> 一个非常简单粗暴的方式。 <br /> (然而总有人觉得确定这些参数非常麻烦,觉得只有我可以选出比较好的参数而普通人不能。这是把普通人当成弱智了吗?我本来也可以用一些算法来选择最优参数,可是上面描述的手动过程实在是太简单了,我懒得用那些复杂的算法。) <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610680588-2b0b92fb-092e-45fa-a903-02b3320e44da.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=uc9629ee5&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=u8b8c4db4-668f-4dc1-b0e8-f6f9aabd0b5&width=812.5)图4.3 我们最终采用的关联打分的形式(还是有点奇怪)

4.4 选取

  1. 前面提到,我会分别计算同类目、同品牌、同店铺和全体的关联商品。此时我们从这四类中各选取一定的比例。当然这个比例越大越好,但是要考虑我的机器和线上机器的资源限制。我最终选取的数量如下: <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610680694-0d80911b-974f-4287-8a62-f39d67bebe18.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=u7aebdb4a&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=uc36bd75d-4741-48ee-a98c-411c0a03aaa&width=812.5)图4.4 每类关联商品的选取数量 <br /> 这个数量在变大的过程中对效果的影响越来越小。所以其实随便取取效果都差不多。

4.5 合并

  1. 接下来我们要将这四类关联商品合并,方法是直接加权求和。如下图所示![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610681218-132526c5-0dab-4945-9e6a-6b8200bb7532.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=ubd4f02c9&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=u8d146d8e-e4ce-4699-bfd7-af9623eb7ec&width=812.5)<br /> 图4.5 关联商品的合并 <br /> 这里的权重我线下稍微尝试了一下,感觉不太好调整,便都取了1。 <br /> 举个例子来说,**_商品甲_**和**_商品乙_**在同类目关联中计算出的关联打分是0.5,在同品牌中是0.3,而在同店铺和全体中没有出现(按0计算)。那么最终的关联打分是0.5+0.3=0.8。 <br /> (按道理来讲,4.4和4.5的顺序应该调换一下,也就是应该先合并再按比例选取。这里是有些历史原因。简单地说,我在比赛过程中没有注意到这件事,赛后写PPT的时候才反应过来。不过这个对成绩影响不大。) <br />

五、交互打分

  1. 接下来就是交互打分。

5.1 基础

  1. 我们此时已经计算出了关联打分。如果不设定交互打分,或者说设定为1,那么大概就是这样 <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610681705-8fc9cf39-bd55-45eb-a745-4a5b7056620b.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=ubd244a58&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=ud26baba0-4313-4087-ab3f-862ae060c7a&width=812.5)<br />图5.1 基础交互打分 <br /> 简单地说,就是沿着 ![](https://cdn.nlark.com/yuque/0/2021/svg/245103/1622610681334-8e8291d0-9724-4f14-bbb2-b785480db93b.svg#clientId=ud87fddca-ae4f-4&from=paste&id=rKeNR&originHeight=43&originWidth=276&originalType=url&status=done&style=none&taskId=u842d6e4c-9575-4511-9eab-2488f887a0a)的路径找到商品乙,并把商品甲和商品乙的关联打分作为未来交互的可能性打分;如果有多条路径可以抵达商品乙,那么就把这些路径上的关联打分直接相加。

5.2 进阶

  1. 和前面类似,这里也存在着交互次数和时间的问题,我们和前面类似地增加了两个系数,这里便不再细说。 <br /> 此外我们还考虑到购买的问题。也就是说,用户在购买了一个类目的商品之后,可能就不会再交互同类目的其它商品。因此我们又增加了一个和这个相关的参数。 <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610681491-5362b6d0-0793-4c93-a867-3e0f3c0eeb74.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=u91b44681&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=ub203f096-cf63-41d5-b6ae-21731fd4b4a&width=812.5)图5.2 我们最终采用的交互打分形式 <br />

六、预测及补充

  1. 预测也没什么好说的,我们的PPT写的足够清楚 <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610681786-7ae315a5-426d-4049-b1f6-4f402735fa07.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=u3dd0ca72&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=uff7bdf06-d68e-42e9-9d12-0dcd34c255f&width=812.5)图6-1 预测 <br /> 以及补充 <br /> ![](https://cdn.nlark.com/yuque/0/2021/png/245103/1622610681893-ef03a8fe-8c9b-43c2-acbe-fb9d283c9732.png#clientId=ud87fddca-ae4f-4&from=paste&height=457&id=u9c695346&margin=%5Bobject%20Object%5D&originHeight=914&originWidth=1625&originalType=url&status=done&style=none&taskId=u0c62113b-b6ba-4d09-8eb2-538b5d1d3a3&width=812.5)<br />图6.2 补充 <br /> (话说我真的有尝试过lightgbm。就是先用上面的方法选出打分较高的用户-商品对作为候选,把上面计算出的打分作为特征,然后再加入年龄、性别、购买力等特征及用户和商品、类目、店铺等的统计特征。这样比我的规则确实有一点提升,但是训练模型要太久了,我最终没有采用) <br />

七、总结

  1. 我在这场比赛中所付出的精力确实要略少一些。比赛的最后一天我用了最后一次机会后,连成绩都来不及看便急匆匆赶去参加另一场比赛的答辩,侥幸得到了还算可以的排名。因此我们的成绩应该还有很大的上升空间(当然官方说的0.07听一听就好,一般情况下官方在这种场合都会吹一吹的;我相信这个问题能做到0.07,我不相信他们能做到0.07)(噢,对了,官方只是说7开头,说不定是0.007)。 <br /> 其实各种数据挖掘问题,我们要做的都是想办法利用好我们所掌握的信息;无论是自行设计打分函数来处理这些数据,还是从数据中提取成特征交给梯度提升决策树,或是处理成特定的形式交给神经网络,都是如此。机器学习算法不过是一些工具,一些帮助我们利用好这些信息的工具。我们需要的时候,可以将这些工具信手拈来;在不需要的时候,也可以直接手动去处理这些信息。人工规则和机器学习模型本来就是没有本质上的区别的。因为一些强行找的借口而使用一些机器学习算法是毫无道理的。评价一些据挖掘方案不应该依据这个方案使用的算法。 <br /> 遗憾的是,目前数据挖掘领域在工业界存在大量的专家,凭借自己的地位来打压异己者。今天XX网络盛行,要求下属使用XX网络的专家就会跳出来称赞使用XX网络的方案而贬低其它方案;明天YY网络盛行,要求下属使用YY网络的专家就会跳出来称赞使用YY网络的方案而贬低其它方案。这实在是十分无奈的事情。