【序列推荐】MAN:跨领域顺序推荐的混合注意网络

论文提出MAN,一种混合注意网络,用于处理跨领域顺序推荐,通过局部和全局编码捕获领域特性和共享特征,混合注意层融合项目、序列和群体模式,适用于非同步数据环境。

#论文题目:MAN:Mixed Attention Network for Cross-domain Sequential Recommendation(跨领域顺序推荐的混合注意网络)
#论文地址:https://dl.acm.org/doi/10.1145/3543507.3583278
#论文源码开源地址:https://github.com/Guanyu-Lin/MAN
#论文所属会议:WSDM‘23
#论文所属单位:卡耐基梅隆大学、清华大学、快手科技、合肥工业大学

在这里插入图片描述

一、摘要

本文提出了一种具有局部全局注意模块的混合注意网络(MAN)来提取特定领域和跨领域的信息。首先,提出了一个局部/全局编码层来捕获特定领域/跨领域的序列模式。在此基础上,提出了一个包含项目相似度、序列融合和群体原型的混合注意层,用于捕获局部/全局项目相似度,融合局部/全局项目序列,并分别提取不同领域的用户群体。最后,提出了一个局部/全局预测层,以进一步发展和结合特定领域和跨领域的兴趣。进一步的研究还表明,提出的方法和组件分别是模型无关的和有效的。

二、问题预定义

在跨域顺序推荐问题中,首先用A和B表示两个域,定义IIIAIIIB分别表示定义域a和b中的项目集,更具体的,假设iiitAIIIAiiitBIIIB是第ttt个给定用户交互的在域A和B项目,因此,可以得到(iii1A, iii2A, …, iiitA)和(iii1B, iii2B, …, iiitB)。最终是要判断模型预测的iiit+1Aiiit+1B

具体来说:
Input:对于用户在域A和B中,物品序列(iii1A, iii2A, …, iiitA)和(iii1B, iii2B, …, iiitB)。
Output:跨领域推荐模型估计目标项目iiit+1Aiiit+1B分别在域a和b中,被给定用户与项目序列交互的概率(iii1A,
iii2A, …, iiitA)和(iii1B, iii2B, …, iiitB)。

三、模型结构

总体来说,模型分为三部分:

  • 局部/全局编码层。为项目构建特定领域的本地和跨领域的全局嵌入。进一步分别使用本地和全局编码器对它们进行编码,以捕获特定领域和跨领域的项目序列模式。
  • 混合注意层。提出了项目相似性注意、序列融合注意和群体原型注意来捕获项目、序列和群体水平上的跨领域模式
  • 局部/全局预测层。为了进化兴趣并预测用户在每个领域中与候选人下一个项目交互的概率,提出了一个局部预测层和一个全局预测层。
    在这里插入图片描述

3.1 局部/全局编码层

首先构建本地和全局项目嵌入。然后,进一步查找项目嵌入,并在序列级别用本地和全局编码器对它们进行编码。

3.1.1 本地和全局项目嵌入

为了捕捉不同领域的领域特定模式,创建了两个项目嵌入矩阵MMM𝐴RRR |I𝐴|×𝐷MMMBRRR |IB|×𝐷,其中𝐷表示潜在维度。然后,为了捕捉跨不同领域的共享项目特征,从表征学习的角度,假设存在共享的潜在空间,其中不同领域具有共同的表征;因此,创建一个共享嵌入矩阵MMMRRR|i𝐴∪i𝐵|×𝐷’
之后,通过加入位置编码,得到域A和B项目的局部和全局嵌入:
在这里插入图片描述

3.1.2 序列的局部编码器和全局编码器

在从嵌入层获得EEEAEEEB, EEEAg, EEEBg之后,应用序列编码器来学习序列模式。这里提出如下的局部编码器和全局编码器:

在这里插入图片描述
注意,这个Encoder是序列推荐的骨干模型(如:SASRec、SURGE等)。基于上述操作,得到了SSSA(SSSB), SSSAg( SSSBg)分别代表域A (B)中的局部序列模式和全局序列模式。

3.2 混合注意层

在本节中,首先提出项目相似性注意,以从局部和全局空间中提取相似项目。然后,提出序列融合注意来进一步融合局部和全局项目序列表示,这将结合特定领域和跨领域的序列模式。最后,提出群体原型注意来提取跨领域的群体模式。

3.2.1项目相似性注意

为了捕捉局部/全局项目嵌入和目标项目嵌入之间的相似性,首先将来自局部空间(即,EEE𝐴𝑗EEE𝐵𝑗)和全局空间(即,EEE𝐴𝑔𝑗EEE𝐵𝑔𝑗)的项目嵌入融合在一起。具体地,给定域A (B)中的用户,可以如下计算他/她的历史项目和目标项目之间的项目相似性得分FFF𝐴 (FFF𝐵):
在这里插入图片描述
MMMiAMMMiB表示域A (B)的目标项的嵌入,而 || 表示连接操作。基于项目相似性分数,然后可以对相似的历史项目的嵌入进行加权,以如下精炼项目嵌入,
在这里插入图片描述
其中EEE 𝐴𝑖EEE 𝐵𝑖RRR 𝑇×𝐷分别是目标条目的相似历史条目在域a和域b中由FFF𝐴FFF𝐵的相似性分数加权的表示。这里𝐴𝑖和𝐵𝑖分别指𝐴和𝐵领域的项目相似性。

3.2.2 序列融合注意力

在获得SSS𝐴SSS𝐵SSS𝐴𝑔SSS𝐵𝑔之后,然后将它们融合以将特定于域的和跨域的序列模式组合在一起,如下所示(下述部分直接上图了),
在这里插入图片描述

3.2.3 群体原型注意

尽管不能跨域利用重叠的用户id,但是通常存在具有相似偏好的用户组。具体来说,首先汇集每个序列以获得与每个组的相关性。然后,利用多个组原型来聚合项目组,并根据它们的相关性对它们进行加权。

  • 团体利益共享。对于一个用户的一个物品序列,它实际上不仅仅属于一个集团原型。相反,它可以是几个不同重量的原型的混合组合。例如,用户可以同时是青少年和篮球爱好者。因此,提出了一个可学习的软集群分配矩阵,以计算𝑁𝑔集团的重要性。具体来说,每个用户的项目序列首先由一个汇集矩阵WWW𝑃𝐴RRR 𝑁𝑔×𝑇 ( WWW𝑃𝐵RRR 𝑁𝑔×𝑇)汇集,在此基础上,用户与每个组的相关性可以计算如下:
    在这里插入图片描述
  • 群体利益聚合。然后,创建𝑁𝑔集团原型嵌入G ∈ RRR𝑁𝑔×𝐷代表利益集团。这些嵌入然后可以被转换到每个域,聚集典型的相关项目如下,
    在这里插入图片描述
    其中GGG𝐴GGG𝐵RRR 𝑁𝑔×𝐷分别是获得的a域和b域序列的群原型表示。这里CA层类似于方程(8)。然后,可以基于如下相关性分数对所有组原型表示进行加权,
    在这里插入图片描述
  • 群体原型解开。根据定义,每个组原型显然应该是不同的。因此,受解开表征学习进展的启发,提出原型解开正则化如下,并将其加入到总体损失函数的计算过程中:
    在这里插入图片描述

3.3 局部/全局预测层

在这一部分,首先通过相应的预测层发展局部和全局兴趣。然后用每个域的目标函数对它们进行优化。

3.3.1局部和全局预测层

利用提出的混合注意网络(项目相似性注意、序列融合注意和群体原型注意),将输出连接在一起,并将它们馈送到提出的局部预测层以及基于MLP的全局预测层,其可以被公式化如下:
在这里插入图片描述

3.3.2 独立更新的目标函数

然后,利用负对数似然函数进行优化,可以用公式表示如下:
在这里插入图片描述
为了跨两个域进行联合优化,最终的目标函数是等式2中计算的LLL𝐴LLL𝐵LLL𝑔的线性组合,最终的损失函数为:
在这里插入图片描述
其中,θ𝐴和θ𝐵是可学习参数集,𝜆 𝐴和𝜆 𝐵分别作为域a和域b的正则化罚超参数。

讨论。不同于现有的基于桥用户的跨域顺序推荐工作,如PiNet和DASL,提出的MAN模型并不严格要求两个域的项目序列同时作为输入,因为提出的模型的每个域的输出不需要另一个域的输入。也就是说,模型中的每个域都可以独立地更新它的参数。如果没有来自任何域的输入,可以很容易地删除该域的优化目标,例如等式的损失函数(18)如果没有来自域a的输入,将被简化为
在这里插入图片描述
在现实世界的在线推荐中,由于来自两个域的新收集的数据总是不同步的(MAN可以针对每个域迭代优化),所以MAN更实用。

四、实验

在这里插入图片描述
在这里插入图片描述

<think>嗯,用户想用栈来判断回文字符序列。首先,我得回忆一下栈的基本特性,后进先出,对吧?那回文的特点就是对称,比如“level”或者“madam”。那怎么利用栈来判断呢? 可能的方法是把字符串的前半部分压入栈,然后逐个弹出和后半部分比较。比如,字符串长度是偶数的话,前一半全进栈,然后和后一半逐个比较。如果是奇数的话,中间那个字符可以忽略,只比较前后部分。 但具体怎么操作呢?比如,计算中间位置,然后把前半部分入栈。然后从中间或中间+1的位置开始,依次出栈比较。这时候需要注意索引的处理,特别是当字符串长度为奇数时,中间的那个字符不需要比较。 然后,代码实现的话,可能需要用到Python中的列表模拟栈。比如,将前一半字符压入栈,然后从中间开始遍历剩下的字符,每次和栈顶元素比较,如果不同就返回False。全部匹配的话就是回文。 不过,用户提到的栈的实现是否要考虑其他情况?比如空字符串或者单个字符的情况,这时候应该直接返回True。另外,处理输入的时候可能需要先去除空格或者转为小写,但用户的问题里可能没有提到这些需求,是否需要考虑? 例如,用户给的示例中的引用是关于检查回文子串的,提到偶数位和奇数位的检查。可能用户的问题更偏向于逐个字符检查,但这里的问题是用栈来判断整个字符串是否是回文,所以应该不需要考虑子串的问题,而是整体结构。 那具体的步骤应该是这样的:输入字符串,预处理(比如转小写,去除非字母字符,但用户没说明,可能不需要),然后确定中间位置,将前半部分入栈。然后从后半部分开始,逐个与栈弹出的元素比较。 比如,字符串“abba”,长度4,中间是2。前两个字符a、b入栈。然后从索引2开始,比较栈弹出的元素(b,a)是否与第三个字符b和第四个字符a一致。 对于长度为奇数的字符串,比如“abcba”,中间位置是2(索引从0开始),前两个字符a、b入栈。然后从索引3开始,比较栈弹出的b和a是否与后面的a和a匹配。中间的字符c不需要处理。 那代码的大体结构应该是: 1. 初始化栈。 2. 计算中间位置。 3. 将前半部分字符压入栈。 4. 根据长度奇偶性决定后半部分的起始索引。 5. 逐个弹出栈顶元素与后半部分比较。 需要注意索引的计算是否正确,比如字符串长度为n,中间位置是n//2。比如n=5,中间位置是2,那么前半部分是前2个字符?或者前两个字符加上中间?可能需要更仔细的计算。或者,将前一半入栈,不管长度奇偶,比如n为偶数时,前半是n/2,奇数是n//2。比如n=5,前半是2个字符,中间是第三个,后半从第三个+1开始? 或者,另一个方法是将整个字符串的前半部分入栈,然后依次比较后半部分。例如,对于n=5,入栈前两个字符,然后比较后两个字符是否等于栈弹出的顺序。中间的第三个字符不管。这时候,正确的处理方式应该是: 字符串长度为n,中间索引是n//2。例如: n=5,中间是2,前半部分是0到2-1=1,即前两个字符。然后从中间索引(2)的下一个开始,即3到结尾,比较栈弹出的顺序是否一致。 或者更简单的方式,将前n//2个字符压入栈,然后从n//2开始比较,如果是奇数长度,则从n//2 +1开始。例如n=5,前5//2=2个字符入栈,然后从2开始比较,但需要从中间+1的位置开始?比如原字符串的索引为0-4,中间是2。如果是奇数,后半部分应该从中间+1的位置开始,即3。然后逐个弹出栈中的元素进行比较。 比如,字符串“abcba”,前半是0-1(a,b)入栈。后半从3开始,即字符b和a。栈弹出顺序是b,a,与后半的b和a匹配,所以是回文。 这样处理的话,代码中的索引计算应该是正确的。 那具体到Python代码: def is_palindrome(s): stack = [] mid = len(s) // 2 for i in range(mid): stack.append(s[i]) # 确定后半部分的起始位置 start = mid if len(s) % 2 ==0 else mid +1 for i in range(start, len(s)): if stack.pop() != s[i]: return False return True 这样应该可以处理奇偶的情况。比如,测试“abba”时,mid=2,start=2。后半部分i=2和3,栈弹出的是b和a,对应s[2]是b,s[3]是a,正确。如果是“abcba”,mid=2,start=3,后半部分是i=3和4,栈弹出b和a,对应字符b和a,正确。 那这个代码应该可以正确判断回文。 不过,用户的问题中提到“回文字符序列”,可能是指字符序列,比如输入的可能是列表或者其他结构,但假设输入是字符串。 此外,需要考虑空字符串的情况,比如空字符串是否是回文,或者长度为1的情况。代码中,如果s是空字符串,len(s)是0,mid=0,循环不会执行,然后start=0,for循环也不会执行,返回True。这可能是否符合预期?通常认为空字符串是回文。长度为1的字符串,mid=0,循环不执行,start=0+1=1,但len(s)是1,所以for循环不会执行,返回True。这也是正确的。 所以这个代码应该可以处理这些情况。 另外,用户可能希望看到栈的实现过程,比如如何将字符压入栈,再弹出比较。代码中用列表模拟栈,append和pop方法刚好符合栈的后进先出特性,因为pop()默认弹出最后一个元素,所以这里是否正确? 比如,对于字符串“abba”,压入a,b。然后弹出顺序是b,a。而后半部分是s[2]=b,s[3]=a,所以比较正确。是的。 那这样代码是正确的。 另外,用户提到的引用中的回文检查可能是指不同的情况,比如在构造字符串时检查是否形成回文子串,但这里的问题是关于整个字符串的判断,所以可能不需要考虑子串的情况。不过引用中的内容可能提醒要注意奇偶两种情况,这与代码中的处理方式一致。 总结步骤: 1. 将字符串前一半字符压入栈。 2. 根据字符串长度的奇偶性,确定后半部分的起始位置。 3. 依次弹出栈顶元素与后半部分字符比较,全部匹配则是回文。 这样方法的时间复杂度是O(n),空间复杂度O(n/2),也就是O(n)。 现在,根据用户的要求,需要用中文回答,结构清晰,给出步骤和代码示例,然后生成相关问题。 可能的代码示例是否如上面所写,或者是否需要处理大小写或非字母字符?例如,用户可能输入有大小写混合的情况,比如“AbBa”,这时候需要转为小写。或者是否忽略空格和标点?例如,判断“A man, a plan, a canal: Panama”是否是回文,这时候需要预处理。但用户的问题可能没有提到这些,所以可能不需要处理,但可以在说明中提到预处理的情况。 所以在代码中,可以增加预处理步骤,比如转换为小写,去除非字母数字字符。例如: def is_palindrome(s): # 预处理:去除非字母数字字符并转为小写 s = ''.join(c.lower() for c in s if c.isalnum()) stack = [] mid = len(s) // 2 for i in range(mid): stack.append(s[i]) start = mid if len(s) % 2 == 0 else mid + 1 for i in range(start, len(s)): if stack.pop() != s[i]: return False return True 这样更全面,但用户的问题可能仅要求基本的栈实现,是否需要预处理要看具体需求。不过,可能在示例代码中加入预处理会让回答更完整,并说明预处理的作用。 现在,用户的问题是如何使用栈来判断回文字符序列,所以步骤包括预处理(可选)、入栈、比较。因此,在回答中应分步骤说明,并提供代码示例。 相关问题部分,可能的问题包括栈的其他应用,时间空间复杂度,处理特殊情况,或者与其他方法(如双指针)的比较。</think>### 基于栈的回文字符序列判断方法 #### 实现原理 回文字符序列的特征是**正序和倒序完全相同**。利用栈的**后进先出(LIFO)**特性,可以将前一半字符压入栈后,与后一半字符逐位对比。 #### 实现步骤 1. **预处理字符串**(可选): 移除非字母数字字符并统一为小写,例如 `"A man, a plan"` → `"amanaplan"` 2. **计算中间位置**: 字符串长度 $n$,中间位置为 $mid = \lfloor n/2 \rfloor$ 3. **前一半字符入栈**: 将索引 $[0, mid-1]$ 的字符压入栈 4. **对比后一半字符**: 根据字符串长度奇偶性确定对比起点: - 偶数长度:从 $mid$ 开始对比 - 奇数长度:从 $mid+1$ 开始对比(跳过中间字符) #### 代码实现 ```python def is_palindrome(s): # 预处理:去除非字母数字字符并转为小写 s = ''.join(c.lower() for c in s if c.isalnum()) stack = [] mid = len(s) // 2 # 前一半字符入栈 for i in range(mid): stack.append(s[i]) # 确定对比起始点 start = mid if len(s) % 2 == 0 else mid + 1 # 逐字符对比 for i in range(start, len(s)): if stack.pop() != s[i]: return False return True # 测试示例 print(is_palindrome("abba")) # True print(is_palindrome("abcba")) # True print(is_palindrome("A man a plan"))# True print(is_palindrome("hello")) # False ``` #### 关键点分析 1. **时间复杂度**:$O(n)$,包含预处理、入栈和对比三个线性操作 2. **空间复杂度**:$O(n/2)$,栈存储前一半字符 3. **与双指针法的对比**:栈实现需要额外空间,但能更直观体现"对称匹配"特性[^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值