4

一次业务实践:基于行为信息的graph embeding

 2 years ago
source link: https://mp.weixin.qq.com/s?__biz=MjM5ODkzMzMwMQ%3D%3D&%3Bmid=2650428889&%3Bidx=3&%3Bsn=e9a3e367c599fe7c577bc1cf05b76dde
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一次业务实践:基于行为信息的graph embeding

AINLP 2022-01-27 14:19

The following article is from 算法让生活更美好 Author BPSk

640?wx_fmt=jpeg
刚刚结束一个小项目,线上收益不错,虽然做法很常规,也很简单,没有什么大的创新点,但是有一些小碎点和一些踩坑个人觉得还是不错的,分享一下,希望对您的业务有所帮助。整个背景大概就是基于用户的历史序列行为构建graph,然后训练,进而离线得到item的embedding,线上使用该emb接入icf,单独作为一路召回。整个逻辑其实是基于阿里的一篇论文EGES,其也被应用在了淘宝推荐策略中,更详细的可以看论文和一些解读博客
https://arxiv.org/pdf/1803.02349.pdf一些解读:https://zhuanlan.zhihu.com/p/69069878参考代码:https://github.com/wangzhegeek/EGES笔者这里简单说一下EGES的思想:主要可以分两块看,一个是构图graph,另一个是训练graph,首先说构图,其实没什么创新就是一个有向图,边的权重是item两两共现的次数;训练graph基本上也是基于游走算法得到训练样本,然后基于skip-gram训练,其最大的亮点是:特征的有权加和,即下图中的a0,a1,...,an,通过这种有权加和的方式使得模型学习到对于不同item有不同的加重权重。
640?wx_fmt=png
说完了原理,那我们在用的时候有哪些点值得思考呢?

有向图 or 无向图?

我们在建立graph的时候,是采用无向图还是有向图呢?这其实是一个很重要的问题,答案是看实际场景,假设你的场景是用户看视频,那其实无向图是不是更合理呢?因为用户先点击A还是先点击B,很多时候取决于用户先看到哪个?(假设用户对两者都感兴趣)。

多跳 or 单跳?

假设有一个用户的行为序列是(A,B,C,D),那么单跳的话就是A->B->C->D,那么是否合理呢?A和C是不是也应该也有关系?还是用户看视频的场景,其看了A后接着看了B,C,D,常识告诉我们,其实B,C,D和A都有一定关系。那么在实际建图的时候,就涉及到所有序列行为都要两两相连吗?这里一般会划出一个时间窗口。

图中有噪声?

在我们建立了图后,所有的边edge都是有权重的,那么在经过了无向和多跳建图后,假设一些item真的有规律,那么连接两者的edge的权重按理来说一定不会低,那么一些权重低的边是不是可以认为是用户的随机行为,极端的话权重是1,即只有一个用户这么做,所以我们可以drop掉这些边,相当于删除了一些graph噪声。这里可能会有一些疑问,就是我们后续会采用一些游走算法来游走路径,大部分游走算法都是基于边权重的,权重低的很难被游走到,对!没错,但是从我们构造图的角度来看,还是希望构造的graph尽可能质量高一点。

图太大了,游走不动?

说一个具体代码实现的事:如果大家在使用开头的给的代码的时候,会发现在游走路径的时候,当节点数太多的时候,直接会报内存错误,游走不动,这时候怎么办呢?我有资源,加大内存,哈哈哈,可以的,假设我们没有怎么办呢?时间换空间呗,读一下代码就会发现,其实使用的node2vec整个代码逻辑是先计算所有节点的转移概率,然后根据转移概率再遍历所有节点,得到每一个节点的游走路径。那么是哪里导致om的呢?那就是先计算了所有节点的转移概率,保存在内存中,后续大家直接调用,虽然很快,但是导致了om,所以我们可以不先计算所有节点的转移概率,我们实时计算,即遍历到当前节点时,先计算转移概率,然后遍历,这样做的坏处是进行了大量的重复计算,但是好处就是可以游走了,只不过慢了很多,关于具体怎么实现,很简单,不再展开叙述,感兴趣的可以一起探讨。

不定个数的tag?

再说一个具体代码实现的事:试想当前场景item 对应的side information有一个是离散特征,且个数不定,举个例子:爱好的水果,有的人可能喜欢苹果和梨,有的人可能只喜欢橘子,一种能想到的方案是将所有水果mean pooling作为爱好的水果这一个特征的编码,是的,那么代码具体怎么实现呢?其是不定长的哦。笔者这里是通过再传入一个placeholder来告诉模型,其当前喜欢的个数,具体代码如下供借鉴:
input_ids = tf.placeholder(dtype=tf.int32, shape=[None,4])input_len = tf.placeholder(dtype=tf.int32, shape=[None,4])embedding = tf.Variable(np.identity(5, dtype=np.int32))tag_embedding = tf.nn.embedding_lookup(embedding, input_ids)tag_embedding = tf.matmul(tag_embedding, tf.expand_dims(input_len, -1), transpose_a=True)tag_embedding = tf.squeeze(tag_embedding)tag_embedding_s = tf.divide(tag_embedding, tf.expand_dims(tf.reduce_sum(input_len, 1) ,-1))sess = tf.InteractiveSession()sess.run(tf.global_variables_initializer())print(embedding.eval())print("***")my_inputs_id = [[1],[2,3,1,0],[3,2]]tag_input = []tag_len = []max_tag_len = 4for cur_tag in my_inputs_id:    cur_tag_len = len(cur_tag)    pad_tag_len = max_tag_len - cur_tag_len    tag_input.append(cur_tag + [0] * pad_tag_len)    tag_len.append([1]*cur_tag_len + [0]*pad_tag_len)print(my_inputs_id)print(tag_len)print(sess.run(tag_embedding_s, feed_dict={input_ids: tag_input, input_len: tag_len}))
对于没有在graph的item怎么得到其embedding呢?论文中没有细说,我们知道对于在graph中的item,我们可以得到其多个side information的embedding和自身item的embedding以及他们加和的权重(注意:每个item的的加和权重是不同的),对于没有在graph中的item我们后两者是得不到的即item的embedding以及他们加和的权重,那么怎么办呢?这里就是简单将其多个side information的embedding进行mean pooling。说到这里,可能有人想到不能简单的平均加和,应该用所有在graph的item训练的权重取平均得到每个side information的权重,这里笔者实验过,其实权重评价完后差不多,即其实就是平均,当前换到你自己场景可以也试试。

怎么评估?

我们在开发优化的时候,免不了需要一个指标来辅助我们评估,那怎么评估比较合理呢,可以看到论文中是采用线上+线下的方式进行的,这里说一下笔者的一些看法。首先说线上,这个成本其实是挺高的,我们不能盲目的训练了一下就上线,还是要在线下评估一下,效果好了的话再上线,另外当上线的事是别的同事在负责,那么这时候更要小心,不能随便出个结果就给人家,太麻烦人家了,所以线下评估很重要!线下怎么评估呢?那当然是尽可能的模拟线上的业务指标,笔者遇到的问题可能更复杂一些,因为线上那边涉及到很多逻辑,召回,粗排,精排等等,不可能复现出所有逻辑,那怎么办?可以自己设计一些任务,比如论文中的边预测任务,实际实验结果是:没啥用,因为都预测的很好,如下:

       Drop掉1% 的边时候:Hadamard:0.7531、Average:1、L1:1、L2:1

       Drop掉30%的边的时候:基本也是接近1

最后设计了如下任务:

选取1000个用户,先对用户历史item的embedding mean ,然后去候选集中通过余弦相似性召回topk,算准确率。注意topk的选择可以是动态的,即用户在候选集里面就看了2本,那么就召回top_2。

其实在落地任何idea的时候都是这样,开动之前看似非常简单,但是在“本土化”的时候会遇到各种小问题,包括代码实现,适配上面等等,好事多磨,加油!!!。
0?wx_fmt=png
AINLP
一个有趣有AI的自然语言处理公众号:关注AI、NLP、机器学习、推荐系统、计算广告等相关技术。公众号可直接对话双语聊天机器人,尝试自动对联、作诗机、藏头诗生成器,调戏夸夸机器人、彩虹屁生成器,使用中英翻译,查询相似词,测试NLP相关工具包。
342篇原创内容
Official Account
进技术交流群请添加AINLP小助手微信(id: ainlper)
请备注具体方向+所用到的相关技术点
640?wx_fmt=jpeg

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。

640?wx_fmt=jpeg

阅读至此了,分享、点赞、在看三选一吧🙏


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK