推荐系统遇上深度学习(四十二)-使用图神经网络做基于会话的推荐

前两篇,我们介绍了如何使用循环神经网络来做基于会话的推荐,本篇我们更进一步,来看一下如何使用近期比较火热的图网络来做基于会话的推荐。

本文介绍的论文题目为:《Session-based Recommendation with Graph Neural Networks》
论文下载地址为:https://arxiv.org/abs/1811.00855
代码地址为:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-SRGNN-Demo(代码和参考代码一致,写了点注释)
参考代码地址为:https://github.com/CRIPAC-DIG/SR-GNN

好了,开始正题吧,在模型介绍时,我们尽量配合代码,让过程显得更加易懂一些。

1、背景介绍

现有基于会话的推荐,方法主要集中于循环神经网络和马尔可夫链,论文提出了现有方法的两个缺陷:
1)当一个会话中用户的行为数量十分有限时,这些方法难以获取准确的用户行为表示。如当使用RNN模型时,用户行为的表示即最后一个单元的输出,作者认为只有这样并非十分准确。
2)根据先前的工作发现,物品之间的转移模式在会话推荐中是十分重要的特征,但RNN和马尔可夫过程只对相邻的两个物品的单向转移关系进行建模,而忽略了会话中其他的物品。

针对上面的问题,作者提出使用图网络来做基于会话的推荐,其整个模型的框架如下图所示:

接下来,我们就来介绍一下这个流程吧。

2、模型介绍

2.1 符号定义

V={v1,v2,...,vm} 代表所有的物品。s=[vs,1,vs,2,...,vs,n]代表一个session中按照时间先后排序的用户点击序列,我们的目标是预测用户下一个要点击的物品vs,n+1

2.2 子图构建

我们为每一个Session构建一个子图,并获得它对应的出度和入度矩阵。

假设一个点击序列是v1->v2->v4->v3,那么它得到的子图如下图中红色部分所示:

再假设一个点击序列是v1->v2->v3->v2->v4,那么它得到的子图如下:

同时,我们会为每一个子图构建一个出度和入度矩阵,并对出度和入度矩阵的每一行进行归一化,如我们序列v1->v2->v3->v2->v4对应的矩阵如下:

上图中,左边的矩阵是出度矩阵,右边的矩阵是入度矩阵,如果同时考虑出度和入度,模型学习的就不是简单的单向转移关系了,而是更加丰富的双向关系。

2.3 基于Graph学习物品嵌入向量

基于Graph学习物品的嵌入向量,作者借鉴的下面这篇文章《GATED GRAPH SEQUENCE NEURAL NETWORKS》(地址:https://arxiv.org/pdf/1511.05493v3.pdf)的做法,其实就是一个GRU单元,不过在输入时,模型做了一定的改进。

模型的输入计算公式如下:

我们还是使用刚才的序列v1->v2->v3->v2->v4来一点点分析输入的过程。
1)ats,i是t时刻,会话s中第i个点击对应的输入
2)As,i:代表的是一个分块矩阵,它是1*2n的,n代表序列中不同物品的数量,而非序列的长度,此处是4,而非5。举例来说,假设我们当前的i=2,那么其对应的As,2:=[0,0,1/2,1/2 | 1/2,0,1/2,0]。我们可以把As,i:拆解为[As,i:,in,As,i:,out]
3)vt-1i可以理解为序列中第i个物品,在训练过程中对应的嵌入向量,这个向量随着模型的训练不断变化,可以理解为隐藏层的状态,是一个d维向量。
4)H是d*2d的权重向量,也可以看作是一个分块的矩阵,可以理解为H=[Hin|Hout],每一块都是d*d的向量。

那么我们来看看计算过程:
1)[vt-11,...,vt-1n] ,结果是d * n的矩阵,转置之后是n*d的矩阵,计作vt-1
2)As,i:vt-1H相当于[As,i:,invt-1Hin , As,i:,outvt-1Hout],即拆开之后相乘再拼接,因此结果是一个1 * 2d的向量。

上面的过程,相当于分别对一个节点的出度和入度进行处理,再进行合并。该过程是我通过代码慢慢理解的,代码如下:

fin_state = tf.reshape(fin_state, [self.batch_size, -1, self.out_size])
fin_state_in = tf.reshape(tf.matmul(tf.reshape(fin_state, [-1, self.out_size]),
                                    self.W_in) + self.b_in, [self.batch_size, -1, self.out_size])
fin_state_out = tf.reshape(tf.matmul(tf.reshape(fin_state, [-1, self.out_size]),
                                     self.W_out) + self.b_out, [self.batch_size, -1, self.out_size])
av = tf.concat([tf.matmul(self.adj_in, fin_state_in),
                tf.matmul(self.adj_out, fin_state_out)], axis=-1)

上面的代码中,fin_state相当于保存的是我们的vt-1,self.W_in相当于我们的Hin, self.W_out相当于我们的Hout,self.adj_in相当于As,i:,in,self.adj_out相当于As,i:,out

有一丢丢的复杂,上面是我个人的理解的计算过程,大家可以作为参考。

上面的输入,我们充分考虑了图的信息,接下来,就是GRU单元了,这里的GRU单元没有太多变化,公式如下:

由上面的公式,整个学习的过程就是每个物品的向量独自进行循环,但是在每次输入的时候,会充分考虑图中的信息,简单化一下示意图如下:

图中我省略了一些不必要的线,不过我想你能够理解。

2.4 生成Session对应的嵌入向量

好了,经过T轮的图网络,我们得到了一个session中每个点击物品的向量,分别为[v1,v2,...,vn],即下图中红色的部分我们已经获得了:

接下来,我们要讲解的是下图中红色的部分:

我们认为,当前序列中最后一个物品是十分关键的,所以把这个信息单独拎出来,令s1 = vn。但是,我们已不能舍弃其他的信息,所以,模型中使用了一个attention的策略,分别计算前面的物品和最后一个点击物品的相关性,并进行加权,得到sg

最后,将两部分进行横向拼接,并进行线性变换,得到sh:

2.5 给出推荐结果及模型训练

在最后的输出层,使用sh和每个物品的embedding进行内积计算:

并通过一个softmax得到最终每个物品的点击概率:

损失函数是交叉熵损失函数:

3、一点小疑问

在上面进行内积计算的过程中,所使用的vi,应该不是经过GNN中间输出的vi,而是每个物品的初始embedding,这个初始的embedding,即我们GNN的初始的输入v0,如下图:

这个embedding在训练过程中不断的被更新。

个人感觉论文这里符号有点混乱。

4、总结

本文使用图网络进行基于会话的推荐,效果还是不错的,而且图网络逐渐成为现在人工智能领域的一大研究热点。感兴趣的小伙伴们,咱们又有好多知识要学习啦,你行动起来了么?