推荐系统-算法-基于向量的召回

推荐系统-算法-基于向量的召回

FM召回

FM模型在召回阶段是将用户和物品都打包成一个向量,两者的内积就是匹配分数。

这句话里面:

  • 用户向量:用户的基础熟悉+历史行为,比如用户的性别、年龄和之前浏览点击过的物品,把这几个的embedding相组合。
  • 物品向量:物品的自身属性+与其他物品的关联特性,比如该物品所属的分类和价格

Fun-Rec中提供的FM召回定义公式为:
y ^ ( x ) : = w 0 + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n ⟨ v i , v j ⟩ x i x j \hat{y}(x) := w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j y^(x):=w0+i=1nwixi+i=1nj=i+1nvi,vjxixj
翻译过来就是:

总分 = 全局偏置 + 一阶特征加权和 + 二阶特征交叉

​ ⬇ ⬇ ⬇

​ $ w_0 $ $ \sum_{i=1}^{n} w_i x_i $ $ \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle v_i, v_j \rangle x_i x_j $

  1. 首先这个全局偏置,它是一个常数项,不依赖于任何的输入特征,可以被看作为当所有的特征值都为0的时候的预测输出,就像生活中房子的定价一样,这个 $ w_0 $指的就是不考虑房子的位置,大小等特征值时的基础房价。
  2. 其次特征向量w,它反映了不同特征(房子的地理位置、大小、点击率等)的影响,比如在豆瓣的电影推荐中,特征x1表示用户年龄,w1表示用户年龄对评分预测的影响程度。
  3. 然后特征向量x,输入的特征向量x包含了用于描述某个对象或者事件所需的所有特征值。就比如用户的年龄、性别、爱好、购买历史,亦或者是物品的价格、类型、品牌等。
  4. 二阶特征交叉,用于捕获特征间的非线性关系,比如用户的年龄和物品类型两者之间的交互可能比单独考虑这两个特征更能准确的预测用户购买。

针对这个二阶特征交叉项来讲, O ( k n 2 ) O(kn^2) O(kn2)的复杂度过大,通过数学恒等式将二阶项重写: ∑ i < j ⟨ v i , v j ⟩ x i x j = 1 2 [ ( ∑ i = 1 n v i x i ) 2 − ∑ i = 1 n ( v i x i ) 2 ] \sum_{i<j} \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j = \frac{1}{2} \left[ \left( \sum_{i=1}^n \mathbf{v}_i x_i \right)^2 - \sum_{i=1}^n (\mathbf{v}_i x_i)^2 \right] i<jvi,vjxixj=21[(i=1nvixi)2i=1n(vixi)2]展开后复杂度降至 O ( k n ) O(kn) O(kn)

在召回阶段,总分公式拆解为用户向量和物品向量的乘积。
V user = [ 1 ; ∑ u ∈ U v u x u ] \mathbf{V}_{\text{user}} = \left[ 1; \sum_{u \in U} \mathbf{v}_u x_u \right] Vuser=[1;uUvuxu]

  1. 对于用户向量,构造如下:

V user = [ 1 ; ∑ u ∈ U v u x u ] \mathbf{V}_{\text{user}} = \left[ 1; \sum_{u \in U} \mathbf{v}_u x_u \right] Vuser=[1;uUvuxu]

  • 维度1:常数1(保留全局偏置项 w 0 w_0 w0 的可能)
  • 维度2~k+1:用户特征Embedding加权和(如用户ID、历史行为等)
  1. 对于物品向量,构造如下:
    V item = [ ∑ t ∈ I w t x t + 1 2 ∑ f = 1 k ( ( ∑ t ∈ I v t , f x t ) 2 − ∑ t ∈ I v t , f 2 x t 2 ) ; ∑ t ∈ I v t x t ] \mathbf{V}_{\text{item}} = \left[ \sum_{t \in I} w_t x_t + \frac{1}{2} \sum_{f=1}^k \left( (\sum_{t \in I} v_{t,f} x_t)^2 - \sum_{t \in I} v_{t,f}^2 x_t^2 \right); \sum_{t \in I} \mathbf{v}_t x_t \right] Vitem= tIwtxt+21f=1k((tIvt,fxt)2tIvt,f2xt2);tIvtxt

    • 维度1:物品一阶特征加权和 + 物品内部特征交叉(自交互)
    • 维度2~k+1:物品特征Embedding加权和
# 离线物品向量预计算
def precompute_item_vectors(items):
    for item in items:
        # 一阶项 + 自交叉项
        term1 = sum(w[t] * x[t] for t in item.features)
        term2 = 0.5 * sum( (sum(v[t,f] * x[t])**2 - sum(v[t,f]**2 * x[t]**2) 
                          for f in range(k))
        # Embedding和
        emb_sum = sum(v[t] * x[t] for t in item.features)
        item.vector = np.concatenate([ [term1 + term2], emb_sum ])
    faiss_index.add(item.vectors)

# 在线召回
def recall(user):
    user_emb = sum(v[u] * x[u] for u in user.features)
    user_vector = np.concatenate([ [1.0], user_emb ])
    scores, items = faiss_index.search(user_vector, top_k=100)
    return items

item2vec召回

就跟Milvus一样,数据转换为向量,相似的物品有相似的向量值。Word2Vec 主要通过两种模型工作:CBOW 和 Skip-gram。

skip-gram为例,比如有一句话,一只猫坐在床上。我们选择坐这个词,模型试图根据“坐”这个词预测出它的上下文词即“猫”,“在”,和“垫子”。通过这种方式,模型学习到了每个词如何与其它词关联。随着训练的进行,模型不仅学会了直接相邻的词的关系,还学会了整个语料库中词之间的复杂关系。

再比如,还有一句话,一只狗坐在床上,如果此时我们寻找与狗相近的词,就会发现猫~很相似,因为通过embedding后,猫和狗都是坐在床上的,因此有某种程度上的相似性。

在原始的Skip-Gram模型中,为了计算每个上下文位置的损失,需要遍历整个词汇表以计算softmax概率。这对于大型语料库来说是非常耗时的。因此,引入了负采样技术来加速训练过程并提高效率。

skip-gram的负采样

负采样的概念:是一种优化技术,通过仅考虑少量的负样本来代替对整个词汇表的遍历,极大地提高了训练效率,同时保证了词向量的质量。

  • 正样本:指的是真实的上下文单词。
  • 负样本:是从词汇表中随机选择的一些非上下文单词。这些词不是当前中心词的实际上下文词。

实现步骤

  1. 选择正样本:基于实际文本数据,确定中心词及其上下文单词。
  2. 选择负样本:根据一定的概率分布,随机选择若干个负样本。这样做的目的是让罕见词有更高的机会被选为负样本,而常见词则相对较少。
  3. 训练模型:针对每一对(中心词, 上下文词),如果上下文词是正样本,则试图最大化其预测概率;如果是负样本,则尽量减少其预测概率。这可以通过修改后的损失函数实现,如sigmoid函数结合二元交叉熵损失。

还按照上面提到的猫和狗的案例:

如果我们选择“坐”作为中心词,“我”和“狗”就是正样本。如果我们设定负样本的数量为2,则可能会随机选择“猫”和“站”作为负样本。

在这个例子中,模型的目标是:

  • 对于正样本(比如“我”),增加其预测概率。
  • 对于负样本(比如“猫”),降低其预测概率。

这样,模型就能逐渐学会区分哪些词更可能出现在某个特定词的周围。

Airbnb召回

针对Fun-Rec中提到的几个方面来学习

  • 了解 Airbnb 是如何利用 Word2Vec 技术生成房源和用户的Embedding,并做出了哪些改进。
  • 了解 Airbnb 是如何利用 Embedding 解决房源冷启动问题。
  • 了解 Airbnb 是如何衡量生成的 Embedding 的有效性。
  • 了解 Airbnb 是如何利用用户和房源 Embedding 做召回和搜索排序。
  1. 了解 Airbnb 是如何利用 Word2Vec 技术生成房源和用户的Embedding,并做出了哪些改进

    首先是Listing Embedding使用用户的点击会话(session)来训练Embedding。每个session由用户连续点击的房源组成,间隔超过30分钟则视为新session。用Skip-Gram模型,中心词预测上下文,同时负采样优化计算效率。但Airbnb做了两点改进:加入全局上下文的booked listing,以及同区域的负样本。

    • 第一个改进:booked listing作为全局上下文,即在每个滑动窗口中都考虑最终预定的房源作为上下文。这样模型不仅能捕捉点击行为,还能关联到最终的预定结果,可能提升推荐的准确性。比如,用户可能在点击多个房源后最终预定其中一个,这个预定房源应该和之前的点击有更强的关联。
    • 第二个改进是同区域的负样本。因为用户通常在同一区域搜索,随机负样本可能大多来自不同区域,导致模型无法有效学习区域内的差异。所以Airbnb在负采样时加入同区域的样本,这样模型能更好地区分同一区域内不同房源的相关性。
  2. 了解 Airbnb 是如何利用 Embedding 解决房源冷启动问题。

    新房源没有Embedding。Airbnb的做法是根据新房源的属性(位置、价格、类型)找到相似的现有房源,取它们的Embedding均值。

  3. 了解 Airbnb 是如何衡量生成的 Embedding 的有效性。

    有内部工具 Embedding Evaluation Tool

    • 大致原理就是,利用训练好的 Embedding 进行 K 近邻相似度检索。跟Milvus的一模一样
  4. 了解 Airbnb 是如何利用用户和房源 Embedding 做召回和搜索排序。

    召回阶段的目标是从海量房源中快速筛选出与用户兴趣相关的候选集,

    • Airbnb采用了Listing Embedding方法,实时计算当前房源的embedding值,
    • 然后计算它和同一地区的其他房源的余弦相似度,计算方法见我的第一篇文章《推荐系统-概述》
    • 再然后排序,获取前topk个房源(这时候可以进行过滤处理,比如过滤掉哪些不可以预定的日期的房子)

    这里的几个步骤,需要采取分布式计算,不然响应的时间会太久。比如大量的embedding数据,比较查找的速度会很慢,采用分布式计算,有利于加速检索。

    Fun-Rec中还提到了基于 User-type Embedding 的长期兴趣召回,用户在搜索时,结合用户历史预定行为,推荐长期偏好的房子

    • 根据用户属性(如年龄、历史预订类型)生成 User-type Embedding。
    • 计算 User-type Embedding 与所有 Listing-type Embedding 的余弦相似度。
    • 返回相似度高的房源(同样的,这里可以进行自定义的过滤处理)

    搜索排序

    Airbnb将 Embedding 的相似度作为关键特征输入模型。这里面包含用户的短期兴趣特征和长期兴趣特征。

    值得注意的是,Airbnb的embedding是用搜索的Session,即用户行为上下文作为关系训练数据,类似于item2Vec,这样的好处是搜索的结果不单单是关键字的向量匹配,而是更加深层次的通过语义和上下文关系来匹配关联信息。

    短期兴趣特征(基于 Listing Embedding):

    • 用户行为 Embedding 聚合:计算候选房源与用户最近行为的 Embedding 相似度,例如:
      • 点击历史:候选房源与用户过去 2 周点击房源的 Embedding 均值相似度。
      • 收藏/长点击:候选房源与用户最近收藏或长点击房源的相似度。

    长期兴趣特征(基于 User-type & Listing-type Embedding):

    • User-type 与 Listing-type 相似度:直接计算用户类型与候选房源类型的 Embedding 相似度

    数学公式理解不动!!!

YoutubeDNN召回

YouTube的视频数量非常庞大,需要强大的算力和分布式算法的支持,召回模型在线下训练的时候,采用了负采样的思路,线上的时候,采用Hash映射,然后近邻搜索的方式来满足实时性的需求。

这句话中,负采样就是一种加速训练的技术,上文中提到了。

  • 在YouTubeDNN中,正样本是用户实际点击观看的视频,负样本是随机选择的用户没有观看的视频。

近邻搜索:

  • 某个用户兴趣最匹配的物品或视频,这个用户就是给定的点,而其他的视频和物品的寻找就是找到这个点最邻近的若干个点。这种方法是为了避免上文提到的,在大规模数据下比较所有的向量的方法

在线上服务的时候,会将视频的向量嵌入Hash映射中,用于快速定位潜在的相关视频,而不需要遍历整个视频库。这里的hash函数要根据业务进行适当的更改(就是下面的LSH和ANN)。

获得到hash映射的初步筛选结果后,就可以使用LSH(局部敏感哈希)或近似最近邻(ANN)算法进一步缩小范围。找出最相关的几个视频。(待补充)

LSH(局部敏感Hash)

  • 假设我们有两个视频A和B,它们的特征向量如下:
    • 视频A: [0.2, 0.4, 0.6]
    • 视频B: [0.3, 0.5, 0.7]

LSH中,将会使用很多hash函数用于将这些视频进行分桶操作,比如用一条平面向量(比喻)来划分空间,假设平面向量为[1, -1, 0]。

对于上面的视频A和视频B,我们分别计算它们与这条超平面的关系:

  • 视频A: 0.2∗1+0.4∗(−1)+0.6∗0=−0.20.2∗1+0.4∗(−1)+0.6∗0=−0.2
  • 视频B: 0.3∗1+0.5∗(−1)+0.7∗0=−0.20.3∗1+0.5∗(−1)+0.7∗0=−0.2

可以看到这俩的结果都是-0.2,所以,这两个视频将会被分到同一个桶中。当一个新的查询点(用户当前的兴趣向量)出现的时候,就可以再次进行LSH计算,找到它所属于的桶。

之后,我们就可以在这个桶中进行更加精确的距离计算,找到真正的近邻。(Milvus!)

回顾一下之前的推荐系统的漏斗模型:

在这里插入图片描述

  • 召回侧:输入用户的点击历史,还有用户的画像。召回方式根据业务场景(比如热门度、最新发布、用户历史重定向)等规则进行召回处理,但缺乏个性化,只是简单的过滤。

    所以,我们通过监督微调和物品Embedding嵌入的方式进行个性化的召回(开始计算相似度或者进行分类了)。

    这里的YouTubeDNN就是利用神经网络为用户和视频生成向量嵌入,并以此预测用户可能感兴趣的内容。

    一般会采用上文的FM或者RNN,亦或者是基于图和知识图谱,然后利用图神经网络GNN来提取特征。

  • 精排侧:接收召回侧的一小部分候选集(就是召回过滤后的数据PS:粗排后的数据,粗排也要提取特征的,选取topk个再给精排层,粗排会选择线性模型LR(逻辑回归)或者是FM(捕捉特征间的相互作用)再或者找个现成的DNN结构的模型直接用)利用更多的特征信息(如用户画像、视频属性等),提供更加精准的推荐结果。

回到YouTubeDNN上,Fun-Rec中还提到了推荐模型的评估,这里直接引用原文:

论文里面还提到了对模型的评估方面, 线下评估的时候,主要是采用一些常用的评估指标(精确率,召回率, 排序损失或者auc这种), 但是最终看算法和模型的有效性, 是通过A/B实验, 在A/B实验中会观察用户真实行为,比如点击率, 观看时长, 留存率这种, 这些才是我们终极目标, 而有时候, A/B实验的结果和线下我们用的这些指标并不总是相关, 这也是推荐系统这个场景的复杂性。 我们往往也会用一些策略,比如修改模型的优化目标,损失函数这种, 让线下的这个目标尽量的和A/B衡量的这种指标相关性大一些。

接下来,详细的介绍一下召回模型:

引用原文的图片:

在这里插入图片描述

一个典型的DNN结构,首先我们从下到上看:

  1. 最下面俩的embeded是用户特征,包含用户观看历史,搜索历史,用户画像

    这些向量是高维稀疏的向量,首先通过embedding层(word2vec)转换为低维稠密的embedding特征。将用户浏览+搜索历史序列转换为多个embedding的形式,最终得到一个融合后的embedding(通常采用average pooling)。

    原文使用了用户近50次的观看和搜索历史,embedding维度为256维。还可以把item的类别信息也隐射到embedding, 与前面的concat起来。

  2. 这里的ReLU为三层。Fun-Rec中提到,实验的结果是层数越多越好,但提升的效果越来越微弱,训练的困难度直线上升。在这里插入图片描述

下面是通义千问总结的一部分看不太明白的知识:

训练数据的选择与生成

正负样本的选取
  • 正样本:通常由用户的实际行为决定,例如用户完整观看过的视频(这里强调“看完”是为了确保这是一个有效的兴趣信号,而不是误点击)。这意味着只有当用户观看了足够长的时间或完成了整个视频时,该视频才会被视为正样本。
  • 负样本:可以从整个视频库中随机选取,或者从用户曾经接触过但未观看的视频中选择。后者可能更贴近实际情况,因为它考虑到了用户实际接触到但不感兴趣的视频。
注意事项
  1. 样本平衡:为了保证损失函数计算的公平性,每个用户的样本数应当一致。这样做可以防止高度活跃用户对模型训练过程产生不成比例的影响。
  2. 避免信息泄露:特别是在处理时间序列数据时,必须小心不要让模型接触到未来的信息。例如,在预测下一个用户行为时,不应该使用该行为之后的数据作为输入特征。一种解决方法是随机打乱历史搜索关键词顺序,以破坏时间顺序,避免模型学习到不应有的模式。
  3. 滑动窗口技术:对于用户行为较少的情况,可以通过滑动窗口的方式增加训练样本数量。比如,如果一个用户的历史观看记录为"abcdef",则可以生成多个训练样本如"abc -> d", “bcd -> e”, "cde -> f"等。这种方法利用了用户行为之间的连续性和非对称共视概率。
  4. Example Age特征:这是指视频的新鲜度,定义为tmax−ttmax−t,其中tmaxtmax是所有样本中最晚的时间戳,tt是当前样本的时间戳。这个特征有助于捕捉视频流行度随时间的变化趋势,并调整模型预测以反映用户对新内容的偏好。在线上预测时,可以将此值设为0或一个小的负值,使得模型在推理时不依赖于具体上传时间。

Example Age vs Video Age

  • Example Age:侧重于表示相对于训练集结束时间点的新鲜程度,便于归一化处理,并且在线上应用时更容易统一设置。
  • Video Age:指的是从视频上传到成为训练样本之间的时间跨度。虽然两者本质上是互补的(它们之和为常数),但在实际应用中,Example Age更具优势,因为它允许离线计算用户向量而不必依赖具体的视频上传时间,简化了线上预测流程。

关于上线服务,我写了个小的工具,是基于Milvus的,可以看一看M_Milvus

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HalukiSan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值