#Reading Paper# 【序列推荐】Session-based Recommendation with Graph Neural Networks

news2024/11/14 15:10:08

#论文题目:【序列推荐】SR-GNN: Session-based Recommendation with Graph Neural Networks(SR-GNN:基于会话的图神经网络推荐)
#论文地址:https://arxiv.org/abs/1811.00855
#论文源码开源地址: https://github.com/CRIPAC-DIG/SR-GNN
#论文所属会议:AAAI 2019
#论文所属单位:中科院
在这里插入图片描述

1.导读

SR-GNN是中科院提出的一种基于会话序列建模的推荐系统,这里所谓的会话是指用户的交互过(每个会话表示一次用户行为和对应的服务,所以每个用户记录都会构建成一张图),这里说的会话序列应该是专门表示一个用户过往一段时间的交互序列。 基于会话的推荐是现在比较常用的一种推荐方式,比较常用的会话推荐包括 循环神经网络、马尔科夫链。但是这些常用的会话推荐方式有以下的两个缺点

  • 当一个会话中用户的行为数量十分有限时【就是比较少时】,这种方法较难捕获用户的行为表示。比如使用RNN神经网络进行会话方式的推荐建模时,如果前面时序的动作项较少,会导致最后一个输出产生推荐项时,推荐的结果并不会怎么准确。
  • 根据以往的工作发现,物品之前的转移模式在会话推荐中是十分重要的特征,但RNN和马尔科夫过程只对相邻的两个物品的单项转移向量进行 建模,而忽略了会话中其他的物品。【意思是RNN那种方式缺乏整体大局观,只构建了单项的转移向量,对信息的表达能力不够强】

首先在解读论文的时候,需要明白序列召回的输入和输出是什么,一般来说序列召回输入的是用户的行为序列(用户交互过的item id的列表),需要预测的是用户下一个时刻可能点击的top-k个item。那在实际操作的过程中,我们通常把用户的行为序列抽取成一个用户的表征向量,然后和Item的向量通过一些ANN的方法来进行快速的检索,从而筛选出和用户表征向量最相似的top-k个Item。我们下面介绍的SR-GNN就是完成了上述的两个流程

1.1 模型结构

这里我们可以看到,我们对输入的用户的行为序列提取出用户的向量表征进行了如下的处理:

  • 1.将用户的行为序列构造成 Session Graph
  • 2.我们通过GNN来对所得的 Session Graph进行特征提取,得到每一个Item的向量表征
  • 3.在经过GNN提取Session Graph之后,我们需要对所有的Item的向量表征进行融合,以此得到User的向量表征
    在得到了用户的向量表征之后,我们就可以按照序列召回的思路来进行模型训练/模型验证了,下面我们来探讨这三个点如何展开

1.2 构建Session Graph

这里首先需要根据用户的行为序列来进行构图,这里是针对每一条用户的行为序列都需要构建一张图,构图的方法也非常简单,我们这里将其视作是有向图,如果 v 2 v_2 v2 v 1 v_1 v1在用户的行为序列里面是相邻的,并且 v 2 v_2 v2 v 1 v_1 v1之后,则我们连出一条从 v 2 v_2 v2 v 1 v_1 v1的边,按照这个规则我们可以构建出一张图。(下面是论文中给的样例图)

这里为什么将每个用户的行为都包装成一个图,是因为如果将所有用户对商品的交互关系都放在一张图中,会导致对用户独特兴趣捕捉上的混乱,所以需要单独构建,而且这样构建的优势是后续训练时候,每一行样本都可以构建成一张图,还是比较方便的。

在完成构图之后,我们需要使用变量来存储这张图,这里用 A s A_s As来表示构图的结果,这个矩阵是一个 ( d , 2 d ) (d,2d) (d,2d)的矩阵 ,其分为一个 ( d , d ) (d,d) (d,d)的Outing矩阵和一个 ( d , d ) (d,d) (d,d)的Incoming矩阵对于Outing矩阵,直接去数节点向外伸出去的边的条数,如果节点向外伸出的节点数大于1,则进行归一化操作(例如节点 v 2 v_2 v2向外伸出了两个节点 v 3 , v 4 v_3,v_4 v3,v4,则节点 v 2 v_2 v2到节点 v 3 , v 4 v_3,v_4 v3,v4的值都为0.5)。Incoming矩阵同理

1.3 通过GNN学习Item的向量表征

这一部分我们主要关注如何从图中学习到Item的向量表征,这里设 v i t v_{i}^{t} vit表示在第t次GNN迭代后的item i的向量表征, A s , i ∈ R 1 × 2 n A_{s,i} \in R^{1 \times 2n} As,iR1×2n表示 A s A_{s} As矩阵中的第 i i i行,即代表着第 i i i个item的相关邻居信息。则我们这里通过公式(1)来对其邻居信息进行聚合,这里主要通过矩阵 A s , i A_{s,i} As,i和用户的序列 [ v 1 t − 1 , . . . , v n t − 1 ] T ∈ R n × d [v_{1}^{t-1},...,v_{n}^{t-1}]^{T} \in R^{n \times d} [v1t1,...,vnt1]TRn×d的乘法进行聚合的,不过要注意这里的公式写的不太严谨,实际情况下两个 R 1 × 2 n 和 R n × d R^{1 \times 2n}和R^{n \times d} R1×2nRn×d的矩阵是无法直接做乘法的,在代码实现中,是将矩阵A分为in和out两个矩阵分别和用户的行为序列进行乘积的

a s , i t = A s , i [ v 1 t − 1 , . . . , v n t − 1 ] T H + b (1) a_{s,i}^{t}=A_{s,i}[v_{1}^{t-1},...,v_{n}^{t-1}]^{T}\textbf{H}+b \tag{1} as,it=As,i[v1t1,...,vnt1]TH+b(1)

'''
A : [batch,n,2n] 图的矩阵
hidden : [batch,n,d] 用户序列的emb
in矩阵:A[:, :, :A.size(1)]
out矩阵:A[:, :, A.size(1):2 * A.size(1)]
inputs : 就是公式1中的 a 
'''
input_in = paddle.matmul(A[:, :, :A.shape[1]], self.linear_edge_in(hidden)) + self.b_iah
input_out = paddle.matmul(A[:, :, A.shape[1]:], self.linear_edge_out(hidden)) + self.b_ioh
# [batch_size, max_session_len, embedding_size * 2]
inputs = paddle.concat([input_in, input_out], 2)

在得到公式(1)中的 a s , i t a_{s,i}^{t} as,it之后,根据公式(2)(3)计算出两个中间变量 z s , i t , r s , i t z_{s,i}^{t},r_{s,i}^{t} zs,it,rs,it可以简单的类比LSTM,认为 z s , i t , r s , i t z_{s,i}^{t},r_{s,i}^{t} zs,it,rs,it分别是遗忘门和更新门

z s , i t = σ ( W z a s , i t + U z v i t − 1 ) ∈ R d (2) z_{s,i}^{t}=\sigma(W_{z}a_{s,i}^{t}+U_{z}v_{i}^{t-1}) \in R^{d} \tag{2} zs,it=σ(Wzas,it+Uzvit1)Rd(2)

r s , i t = σ ( W r a s , i t + U r v i t − 1 ) ∈ R d (3) r_{s,i}^{t}=\sigma(W_{r}a_{s,i}^{t}+U_{r}v_{i}^{t-1}) \in R^{d} \tag{3} rs,it=σ(Wras,it+Urvit1)Rd(3)

这里需要注意,我们在计算 z s , i t , r s , i t z_{s,i}^{t},r_{s,i}^{t} zs,it,rs,it的逻辑是完全一样的,唯一的区别就是用了不同的参数权重而已.
在得到公式(2)(3)的中间变量之后,我们通过公式(4)计算出更新门下一步更新的特征,以及根据公式(5)来得出最终结果

v i t ∼ = t a n h ( W o a s , i t + U o ( r s , i t ⊙ v i t − 1 ) ) ∈ R d (4) {v_{i}^{t}}^{\sim}=tanh(W_{o}a_{s,i}^{t}+U_{o}(r_{s,i}^{t} \odot v_{i}^{t-1})) \in R^{d}\tag{4} vit=tanh(Woas,it+Uo(rs,itvit1))Rd(4)

v i t = ( 1 − z s , i t ) ⊙ v i t − 1 + z s , i t ⊙ v i t ∼ ∈ R d (5) v_{i}^{t}=(1-z_{s,i}^{t}) \odot v_{i}^{t-1} + z_{s,i}^{t} \odot {v_{i}^{t}}^{\sim} \in R^{d} \tag{5} vit=(1zs,it)vit1+zs,itvitRd(5)

这里我们可以看出,公式(4)实际上是计算了在第t次GNN层的时候的Update部分,也就是 v i t ∼ {v_{i}^{t}}^{\sim} vit,而在公式(5)中通过遗忘门 z s , i t z_{s,i}^{t} zs,it来控制第t次GNN更新时, v i t − 1 v_{i}^{t-1} vit1和${v_{i}{t}}{\sim} $所占的比例。这样就完成了GNN部分的item的表征学习

这里在写代码的时候要注意,对于公式(3)(4)(5),我们仔细观察,对于 a s , i t , v i t − 1 a_{s,i}^{t},v_{i}^{t-1} as,it,vit1这两个变量而言,每个变量都和三个矩阵进行了相乘,这里的计算逻辑相同,所以将 W a , U v Wa,Uv Wa,Uv当作一次计算单元,在公式(3)(4)(5)中,均涉及了一次这样的操作,所以我们可以将这三次操作放在一起做,然后在讲结果切分为3份,还原三个公式,相关代码如下

'''
inputs : 公式(1)中的a
hidden : 用户序列,也就是v^{t-1}
这里的gi就是Wa,gh就是Uv,但是要注意这里不该是gi还是gh都包含了公式3~5的三个部分
'''

# gi.size equals to gh.size, shape of [batch_size, max_session_len, embedding_size * 3]

gi = paddle.matmul(inputs, self.w_ih) + self.b_ih
gh = paddle.matmul(hidden, self.w_hh) + self.b_hh
# (batch_size, max_session_len, embedding_size)
i_r, i_i, i_n = gi.chunk(3, 2)   # 三个W*a
h_r, h_i, h_n = gh.chunk(3, 2)   # 三个U*v
reset_gate = F.sigmoid(i_r + h_r)  #公式(2)
input_gate = F.sigmoid(i_i + h_i)  #公式(3)
new_gate = paddle.tanh(i_n + reset_gate * h_n)  #公式(4)
hy = (1 - input_gate) * hidden + input_gate * new_gate  # 公式(5)

1.4 生成User 向量表征(Generating Session Embedding)

在通过GNN获取了Item的嵌入表征之后,我们的工作就完成一大半了,剩下的就是讲用户序列的多个Item的嵌入表征融合成一个整体的序列的嵌入表征

这里SR-GNN首先利用了Attention机制来获取序列中每一个Item对于序列中最后一个Item v n ( s 1 ) v_{n}(s_1) vn(s1)的attention score,然后将其加权求和,其具体的计算过程如下

a i = q T σ ( W 1 v n + W 2 v i + c ) ∈ R 1 s g = ∑ i = 1 n a i v I ∈ R d (6) a_{i}=\textbf{q}^{T} \sigma(W_{1}v_{n}+W_{2}v_{i}+c) \in R^{1} \tag{6} \\ s_{g}= \sum_{i=1}^{n}a_{i}v_{I}\in R^{d} ai=qTσ(W1vn+W2vi+c)R1sg=i=1naivIRd(6)

在得到 s g s_g sg之后,我们将 s g s_g sg与序列中的最后一个Item信息相结合,得到最终的序列的嵌入表征

s h = W 3 [ s 1 ; s g ] ∈ R d (7) s_h = W_{3}[ s_1 ;s_g] \in R^{d} \tag{7} sh=W3[s1;sg]Rd(7)

'''
seq_hidden : 序列中每一个item的emb
ht : 序列中最后一个item的emb,就是公式6~7中的v_n(s_1)
q1 : 公式(6)中的 W_1 v_n
q2 : 公式(6)中的 W_2 v_i 
alpha : 公式(6)中的alpha
a : 公式(6)中的s_g
'''
seq_hidden = paddle.take_along_axis(hidden,alias_inputs,1)
# fetch the last hidden state of last timestamp
item_seq_len = paddle.sum(mask,axis=1)
ht = self.gather_indexes(seq_hidden, item_seq_len - 1)
q1 = self.linear_one(ht).reshape([ht.shape[0], 1, ht.shape[1]])
q2 = self.linear_two(seq_hidden)

alpha = self.linear_three(F.sigmoid(q1 + q2))
a = paddle.sum(alpha * seq_hidden * mask.reshape([mask.shape[0], -1, 1]), 1)
user_emb = self.linear_transform(paddle.concat([a, ht], axis=1))

至此我们就完成了SR-GNN的用户向量生产了,剩下的部分就可以按照传统的序列召回的方法来进行了

参考:https://aistudio.baidu.com/aistudio/projectdetail/5313491

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何设置Windows文件夹背景为黑色?(其实就是“深色模式”)

大家好。我们直接进入正题! 如何把Windows文件夹背景改成黑色?就像下面这样。 第一步:打开 “个性化” 设置界面 这里介绍两种方法:(1)在电脑桌面 右键——》 个性化 ,如下图 (2)点击 开始——》设置 ——》个性化 ,如…

Typora+upic的配置

typora upic 的配置 背景说明 本人使用的是Mac book 的M1芯片电脑,为了方便写博客,整理了图片上传的方式; upic是一款上传图片的工具,主要为了帮助写博客上传工具使用的,方便上传图片; 配置七牛云的图床…

如何在 Kubernetes 部署 PostgreSQL

文章目录1. 简介2. 条件3. helm 部署 posgresql3.1 添加 Helm 存储库3.2 默认安装3.3 选参安装3.4 持久存储安装3.4.1 创建 PersistentVolume3.4.2 创建 PersistentVolumeClaim3.4.3 安装 Helm Chart3.4.5 连接到 PostgreSQL 客户端3.5 自定义配置 value.yaml4. 手动部署 postg…

Anaconda、CUDA、Pytorch安装

文章目录Anaconda、CUDA、Pytorch安装安装Anaconda安装CUDA安装cuDNN安装Pytorch小技巧验证原文链接: Tommy Shang的博客Anaconda、CUDA、Pytorch安装 很久没有更新博客,最近给实验室的机器安装Pytorch环境,顺手也把自己的机器装了一遍。 整…

《凤凰项目》读后感

无极限零部件公司的问题不提变更单导致变更引起很多问题,变更登记系统推行不下去不知道自己团队有多少项目在运行导致无法预估人力,进度怎么样,项目管理系统推行不下去安全部门提出各种安全问题,补丁安装审计部门提出各种审计不合…

Java学习之代码块

目录 一、代码块的基本介绍 二、基本语法 注意事项 三、代码块的好处和案例演示 四、注意事项和使用细节 第一点 第二点 第三点 案例演示 第四点 第五点 第六点 第七点 五、练习题 第一题 第二题 考察知识点 结论 结果 一、代码块的基本介绍 代码化块又称为初…

【论文导读】Deep Stable Learning for Out-Of-Distribution Generalization

DWR用到复杂数据集中。 看了一半发现一个博客将的也不错 放在这里Deep Stable Learning for Out-Of-Distribution Generalization_六点先生的博客-CSDN博客_随机傅里叶特征 目标任务: 一般假设(训练数据的已知异质性(如领域标签&#xff0…

FGH75T65SHD-F155 场截止沟槽 IGBT单管 应用于太阳能逆变器、UPS等多种应用

FGH75T65SHD-F155采用新型场截止 IGBT 技术,为太阳能逆变器、UPS、焊接机、电信、ESS 和 PFC 等低导通和开关损耗至关重要的应用提供最佳性能。 ONsemi安森美IGBT单管系列: FGH40N60SMD FGH60N60SMD FGH75T65SHD-F155 NGTB40N120FL2WG 特性&#x…

架构设计---用户加密处理

前言: 在互联网各种安全问题中,最能引发话题,刺激大众神经的就是用户的泄密问题,数据库被拖库导致所有的数据泄露,这种系统安全问题涉及的因素可能有很多,大部分和开发软件的程序员没有关系,但…

【PyTorch深度学习实践】03_反向传播

文章目录1.计算图2.反向传播2.1 链式求导法则2.2 反向传播过程3.Pytorch中前馈和反馈的计算3.1 Tensor3.2 代码演示对于简单的模型,梯度变换可以用解析式表达进行手算,但是复杂模型(很多w,b)的损失函数需要挨个写解析式…

【黑马】瑞吉外卖-Day03、04笔记

瑞吉外卖Day03、04 公共字段自动填充 使用MybatisPlus实现 问题分析 代码实现 Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。 实现步骤…

【学习】life long learning

文章目录life long learningLLL的难点评估二、LLL的三个解法1、Selective Synaptic Plasticity选择性突触可塑性为什么会有灾难性遗忘呢?GEM2、Additional Neural Resource Allocation额外的神经资源分配packNet&CPG3、memory replyCurriculum Learninglife lon…

SAP 字段仍作为视图字段在视图中使用 | 更改表结构重新生成 CDS View「实例」

错误信息 Field ZPDAUSER-ZUSERID is still being used as a view field in view ZV_PDA_USER视图 ZPDAUSER-ZUSERID 仍作为视图字段在视图 ZV_PDA_USER 使用 错误原因 当前表被 CDS View 引用,由 CDS View 生成的「视图」已占用当前表的相关字段然而生成的视图又…

实战5:基于 Pytorch 搭建 Faster-R-CNN 实现飞机目标检测(代码+数据)

任务描述: 通过一个飞机检测的案例来对目标检测的基本概念进行介绍并且实现一个简单的目标检测方法。数据集:使用从COCO数据集抽取的飞机数据集mini-airplane,数据集中的数据均为正常的图片。https://download.csdn.net/download/qq_38735017/87374251运行环境:操作系统:l…

Day4 基于XML的Spring应用

总结java依赖注入的方式set方法注入List、map和properties的注入通过构造方法注入ref是reference的缩写,需要引用其他bean的id,value用于注入普通属性值。自定义标签和其他标签的引用自定义标签beansbeanimportalias其他标签用于引用其他命名空间1 bean的…

sqli-labs 第八关 多命通关攻略(Python3 自动化实现布尔盲注)

sqli-labs 第八关 多命通关攻略(Python3 自动化实现布尔盲注)描述判断注入类型正常输入不正常输入错误输入爆破方式的可行性铺垫函数 IF()关于 MySQL 数据类型之间转换的小小礼物(仅部分)函数 ASCII()ASCII 表(可显示字…

火山引擎 DataTester:5 个优化思路,构建高性能 A/B 实验平台

导读:DataTester 是由火山引擎推出的 A/B 测试平台,覆盖推荐、广告、搜索、UI、产品功能等业务应用场景,提供从 A/B 实验设计、实验创建、指标计算、统计分析到最终评估上线等贯穿整个 A/B 实验生命周期的服务。DataTester 经过了字节跳动业务…

vivo 故障定位平台的探索与实践

作者:vivo 互联网服务器团队- Liu Xin、Yu Dan 本文基于故障定位项目的实践,围绕根因定位算法的原理进行展开介绍。鉴于算法有一定的复杂度,本文通过图文的方式进行说明,希望即使是不懂技术的同学也能理解。 一、背景介绍 1.1 程…

2023最新连锁店软件排名,国内十大连锁店管理软件新鲜出炉!

普通的数据工具、人工管理难以满足连锁店老板们的需求,正所谓“有需求就有市场”,随着连锁店、加盟店如雨后春笋般在城市里出现,连锁店软件也越来越多。究竟哪一款连锁店管理软件,才能满足老板们的需求?小编收集了国内…

9/365 java 数组 内存

1.数组 声明: int[] a;//首选 int a[];//一般不用 创建: int[] a new int[10]; // 需指定数组大小 初始化: 静态初始化: int[] a {8,9,10}; String[] s {new String("hello"), new String("world")…