.. _user_interests: 深化用户兴趣表示 ================ 传统的向量召回方法,如双塔模型,倾向于将用户所有的历史行为“压扁”成一个单一的静态向量。这种“平均化”的处理方式虽然高效,但在两个关键方面存在明显局限:首先,它无法表达用户兴趣的多样性,例如一个用户可能既是需要购买专业书籍的程序员,又是一位需要选购婴儿用品的新手父亲,单一向量很难同时兼顾这两种截然不同的需求;其次,它忽略了兴趣的时效性,无法区分用户长期稳定的爱好(如对摄影的持续关注)和临时的即时需求(如今天突然搜索“感冒药”),而后者往往更能预示下一次的交互行为。 为了构建一个更丰富、更立体的用户画像以实现更精准的召回,研究者们沿着“深化用户兴趣表示”的路径进行了持续探索。本章将介绍其中的两个代表性模型:MIND 和 SDM。我们将首先探讨如何使用多个向量来表示用户的多元兴趣,然后在此基础上,进一步融入时间维度,学习如何动态地捕捉用户兴趣的演化。 MIND:用多个向量捕捉用户的多元兴趣 ---------------------------------- MIND (Multi-Interest Network with Dynamic Routing) :cite:`li2019multi` 模型直接应对了用户兴趣多样性的挑战。其核心思想非常直观:不应该用一个向量来概括用户的复杂性,而应该用一组向量,其中每个向量代表一个独立的兴趣维度。MIND的核心创新在于引入胶囊网络的动态路由机制,自动将用户行为聚类到不同的兴趣簇中,每个簇生成一个专门的兴趣向量。这种多向量表示不仅提高了用户兴趣建模的精度,还为个性化推荐提供了更细粒度的控制能力。 .. _mind-model-architecture: .. figure:: ../../img/mind_model_architecture.png MIND模型整体结构 从整体架构来看,除了常规的Embedding层,MIND模型还包含了两个重要的组件:多兴趣提取层和Label-aware注意力层。 **多兴趣提取** MIND模型的多兴趣提取技术源于对胶囊网络动态路由机制的创新性改进。胶囊网络 :cite:`sabour2017dynamic` 最初在计算机视觉领域被提出,其核心思想是用向量而非标量来表示特征,向量的方向编码属性信息,长度表示存在概率。动态路由则是确定不同层级胶囊之间连接强度的算法,它通过迭代优化的方式实现输入特征的软聚类。这种软聚类机制的优势在于,它不需要预先定义聚类数量或类别边界,而是让数据自然地分组,这正好契合了用户兴趣发现的需求。MIND模型引入了这一思想并提出了行为到兴趣(Behavior to Interest,B2I)动态路由:将用户的历史行为视为行为胶囊,将用户的多重兴趣视为兴趣胶囊,通过动态路由算法将相关的行为聚合到对应的兴趣维度中。MIND模型针对推荐系统的特点对原始动态路由算法进行了三个关键改进: 1. **共享变换矩阵**\ 。与原始胶囊网络为每对胶囊使用独立变换矩阵不同,MIND采用共享的双线性映射矩阵 :math:`S \in \mathbb{R}^{d \times d}`\ 。这种设计有两个重要考虑:首先,用户行为序列长度变化很大,从几十到几百不等,共享矩阵确保了算法的通用性 ;其次,共享变换保证所有兴趣向量位于同一表示空间,便于后续的相似度计算和检索操作。路由连接强度的计算公式为: .. math:: b_{ij} = \boldsymbol{u}_j^T \boldsymbol{\textrm{S}} \boldsymbol{e}_i 其中 :math:`\boldsymbol{e}_i` 表示用户历史行为 :math:`i` 的物品向量,\ :math:`\boldsymbol{u}_j` 表示第 :math:`j` 个兴趣胶囊的向量,\ :math:`b_{ij}` 衡量行为 :math:`i` 与兴趣 :math:`j` 的关联程度。 2. **随机初始化策略**\ 。为避免所有兴趣胶囊收敛到相同状态,算法采用高斯分布随机初始化路由系数 :math:`b_{ij}`\ 。这一策略类似于K-Means聚类中的随机中心初始化,确保不同兴趣胶囊能够捕捉用户兴趣的不同方面。 3. **适应兴趣数量**\ 。考虑到不同用户的兴趣复杂度差异很大,MIND引入了动态兴趣数量机制: .. math:: K_u' = \max(1, \min(K, \log_2 (|\mathcal{I}_u|))) 其中 :math:`|\mathcal{I}_u|` 表示用户 :math:`u` 的历史行为数量,\ :math:`K` 是预设的最大兴趣数。这种设计为行为较少的用户节省计算资源,同时为活跃用户提供更丰富的兴趣表示。 改进后的动态路由过程通过迭代方式执行。\ :math:`b_{ij}`\ 在第一轮迭代时,初始化为0,在每轮迭代中更新路由系数和兴趣胶囊向量,直到收敛。 公式 (2.3.1)描述了路由系数 :math:`b_{ij}` 的更新,但关键的兴趣胶囊向量 :math:`\boldsymbol{u}_j` 是通过以下步骤计算的,这本质上是一个软聚类算法: 1. **计算路由权重 :** 对于每一个历史行为(低层胶囊 :math:`i`\ ),其分配到各个兴趣(高层胶囊 :math:`j`\ )的权重 :math:`w_{ij}` 通过对路由系数 :math:`b_{ij}` 进行Softmax操作得到。 .. math:: w_{ij} = \frac{\exp{b_{ij}}}{\sum_{k=1}^{K_u'} \exp{b_{ik}}} 这里的 :math:`w_{ij}` 可以理解为行为 :math:`i` 属于兴趣 :math:`j` 的“软分配”概率。 2. **聚合行为以形成兴趣向量:** 每一个兴趣胶囊的初步向量 :math:`\boldsymbol{z}_j` 是通过对所有行为向量 :math:`\boldsymbol{e}_i` 进行加权求和得到的。每个行为向量在求和前会先经过共享变换矩阵 :math:`\boldsymbol{S}` 的转换。 .. math:: \boldsymbol{z}_j = \sum_{i\in \mathcal{I}_u} w_{ij} \boldsymbol{S} \boldsymbol{e}_i 这一步是聚类的核心:根据刚刚算出的权重,将相关的用户行为聚合起来,形成代表特定兴趣的向量。 3. **非线性压缩 :** 为了将向量的模长(长度)约束在 [0, 1) 区间内,同时不改变其方向,模型使用了一个非线性的“squash”函数,从而得到本轮迭代的最终兴趣胶囊向量 :math:`\boldsymbol{u}_j`\ 。向量的长度可以被解释为该兴趣存在的概率,而其方向则编码了兴趣的具体属性。 .. math:: \boldsymbol{u}_j = \text{squash}(\boldsymbol{z}_j) = \frac{\left\lVert \boldsymbol{z}_j \right\rVert ^ 2}{1 + \left\lVert \boldsymbol{z}_j \right\rVert ^ 2} \frac{\boldsymbol{z}_j}{\left\lVert \boldsymbol{z}_j \right\rVert} 4. **更新路由系数 (Updating Routing Logits):** 最后,根据新生成的兴趣胶囊 :math:`\boldsymbol{u}_j` 和行为向量 :math:`\boldsymbol{e}_i` 之间的一致性(通过点积衡量),来更新下一轮迭代的路由系数 :math:`b_{ij}`\ 。 .. math:: b_{ij} \leftarrow b_{ij} + \boldsymbol{u}_j^T \boldsymbol{S} \boldsymbol{e}_i 以上四个步骤会重复进行固定的次数(通常为3次),最终输出收敛后的兴趣胶囊向量集合 :math:`\{\boldsymbol{u}_j, j=1,...,K_{u}^\prime\}` 作为该用户的多兴趣表示。 **标签感知的注意力机制** 多兴趣提取层生成了用户的多个兴趣向量,但在训练阶段,我们需要确定哪个兴趣向量与当前的目标商品最相关。因为在训练时,我们拥有‘正确答案’(即用户实际点击的下一个商品),所以可以利用这个‘标签’信息,来反向监督模型,让模型学会在多个兴趣向量中,挑出与正确答案最相关的那一个。这相当于在训练阶段给模型一个明确的指引。MIND模型设计了标签感知注意力层来解决这个问题。 该注意力机制以目标商品向量作为查询,以用户的多个兴趣向量作为键和值。具体计算过程如下: .. math:: v_u = V_u \cdot \text{softmax}(\text{pow}(V_u^T e_i, p)) 其中\ :math:`V_u = (v_u^1, \ldots, v_u^K)`\ 表示用户的兴趣胶囊矩阵,通过将兴趣胶囊向量\ :math:`\boldsymbol{u}`\ 与用户画像embedding进行拼接,再经过多层ReLU变换得到 (见 :numref:`mind-model-architecture` )。\ :math:`e_i`\ 是目标商品\ :math:`i`\ 的embedding向量,\ :math:`p`\ 是控制注意力集中度的超参数。 参数\ :math:`p`\ 控制注意力分布:当\ :math:`p`\ 接近0时,所有兴趣向量获得均等关注;随着\ :math:`p`\ 增大,注意力逐渐集中于与目标商品最相似的兴趣向量;当\ :math:`p`\ 趋于无穷时,机制退化为硬注意力,只选择相似度最高的兴趣向量。实验表明,使用较大的\ :math:`p`\ 值能够加快模型收敛速度。 通过标签感知得到用户向量\ :math:`v_u`\ 后,MIND模型会通过最大化用户与其实际交互商品的概率来训练模型。它的训练方式和YoutubeDNN类似,都是使用sampled softmax损失函数,通过负采样技术来近似计算归一化项。 **代码实践** .. raw:: latex \diilbookstyleinputcell .. code:: python import sys import funrec from funrec.utils import build_metrics_table config = funrec.load_config('mind') # 加载数据 train_data, test_data = funrec.load_data(config.data) # 准备特征 feature_columns, processed_data = funrec.prepare_features(config.features, train_data, test_data) # 训练模型 models = funrec.train_model(config.training, feature_columns, processed_data) # 评估模型 metrics = funrec.evaluate_model(models, processed_data, config.evaluation, feature_columns) print(build_metrics_table(metrics)) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output +---------------+--------------+-----------+----------+----------------+---------------+ | hit_rate@10 | hit_rate@5 | ndcg@10 | ndcg@5 | precision@10 | precision@5 | +===============+==============+===========+==========+================+===============+ | 0.0318 | 0.023 | 0.0229 | 0.0201 | 0.0032 | 0.0046 | +---------------+--------------+-----------+----------+----------------+---------------+ SDM:融合长短期兴趣,捕捉动态变化 --------------------------------- MIND解决了兴趣“广度”的问题,但新的问题随之而来:时间。 用户兴趣不仅是多元的,还是动态演化的。一个用户在一次购物会话(Session)中的行为,往往比他一个月前的行为更能预示他下一刻的需求。MIND虽然能捕捉多个兴趣,但并未在结构上显式地区分它们的时效性。序列深度匹配模型(SDM) :cite:`lv2019sdm` 正是为了解决这一问题而提出的。SDM模型 的核心思想是分别建模用户的短期即时兴趣和长期稳定偏好,然后智能地融合它们。 .. _sdm-model-architecture: .. figure:: ../../img/sdm_model_architecture.png SDM模型结构 **捕捉短期兴趣** 为了精准捕捉短期兴趣,SDM设计了一个三层结构来处理用户的当前会话序列(:numref:`sdm-model-architecture` 左下角) 。 首先,将短期会话中的商品序列输入LSTM网络,学习序列间的时序依赖关系。LSTM的标准计算过程为: .. math:: \begin{aligned} \boldsymbol{i} \boldsymbol{n}_{t}^{u} &=\sigma\left(\boldsymbol{W}_{i n}^{1} \boldsymbol{e}_{i_{t}^{u}}+\boldsymbol{W}_{i n}^{2} \boldsymbol{h}_{t-1}^{u}+b_{i n}\right) \\ \boldsymbol{f}_{t}^{u} &=\sigma\left(\boldsymbol{W}_{f}^{1} \boldsymbol{e}_{i_{t}^{u}}+\boldsymbol{W}_{f}^{2} \boldsymbol{h}_{t-1}^{u}+b_{f}\right) \\ \boldsymbol{o}_{t}^{u} &=\sigma\left(\boldsymbol{W}_{o}^{1} \boldsymbol{e}_{i}^{u}+\boldsymbol{W}_{o}^{2} \boldsymbol{h}_{t-1}^{u}+b_{o}\right) \\ \boldsymbol{c}_{t}^{u} &=\boldsymbol{f}_{t}^{u} \boldsymbol{c}_{t-1}^{u}+\boldsymbol{i} \boldsymbol{n}_{t}^{u} \tanh \left(\boldsymbol{W}_{c}^{1} \boldsymbol{e}_{i_{t}^{u}}+\boldsymbol{W}_{c}^{2} \boldsymbol{h}_{t-1}^{u}+b_{c}\right) \\ \boldsymbol{h}_{t}^{u} &=\boldsymbol{o}_{t}^{u} \tanh \left(\boldsymbol{c}_{t}^{u}\right) \end{aligned} 这里\ :math:`\boldsymbol{e}_{i_{t}^{u}}`\ 表示第\ :math:`t`\ 个时间步的商品embedding,\ :math:`\sigma`\ 表示sigmoid激活函数,\ :math:`\boldsymbol{W}`\ 表示权重矩阵,\ :math:`b`\ 表示偏置向量。LSTM采用多输入多输出模式,每个时间步都输出隐藏状态\ :math:`\boldsymbol{h}_{t}^{u} \in \mathbb{R}^{d \times 1}`\ ,最终得到序列表示\ :math:`\boldsymbol{X}^{u} = [\boldsymbol{h}_{1}^{u}, \ldots, \boldsymbol{h}_{t}^{u}]`\ 。 LSTM的引入主要是为了处理在线购物中的一个常见问题:用户往往会产生一些随机点击,这些不相关的行为会干扰序列表示。通过LSTM的门控机制,模型能够更好地捕捉序列中的有效信息。 然后,SDM采用多头自注意力机制来捕捉用户兴趣的多样性。多头自注意力机制。 .. math:: \text{head}_{i}^{u}=\operatorname{Attention}\left(\boldsymbol{W}_{i}^{Q} \boldsymbol{X}^{u}, \boldsymbol{W}_{i}^{K} \boldsymbol{X}^{u}, \boldsymbol{W}_{i}^{V} \boldsymbol{X}^{u}\right) 具体计算过程为: .. math:: \begin{aligned} &f\left(Q_{i}^{u}, K_{i}^{u}\right)=Q_{i}^{u T} K_{i}^{u} \\ &A_{i}^{u}=\operatorname{softmax}\left(f\left(Q_{i}^{u}, K_{i}^{u}\right)\right) \\ &\operatorname{head}_{i}^{u}=V_{i}^{u} A_{i}^{u T} \end{aligned} 其中\ :math:`Q_{i}^{u}`\ 、\ :math:`K_{i}^{u}`\ 、\ :math:`V_{i}^{u}`\ 分别表示第\ :math:`i`\ 个头的查询、键、值矩阵,\ :math:`\boldsymbol{W}_{i}^{Q}`\ 、\ :math:`\boldsymbol{W}_{i}^{K}`\ 、\ :math:`\boldsymbol{W}_{i}^{V}`\ 是对应的权重矩阵。 多头注意力的最终输出为: .. math:: \hat{X}^{u}=\text{MultiHead}\left(X^{u}\right)=W^{O} \text{concat}\left(\text{head}_{1}^{u}, \ldots, \text{head}_{h}^{u}\right) 其中\ :math:`h`\ 是头的数量,\ :math:`W^{O}`\ 是输出权重矩阵。每个头可以专注于不同的兴趣维度,通过多头机制实现对用户多重兴趣的并行建模。 最后,SDM加入个性化注意力层,使用用户画像向量\ :math:`\boldsymbol{e}_u`\ 作为查询,对多头注意力输出进行加权: .. math:: \begin{aligned} \alpha_{k} &=\frac{\exp\left(\hat{\boldsymbol{h}}_{k}^{u T} \boldsymbol{e}_{u}\right)}{\sum_{k=1}^{t} \exp\left(\hat{\boldsymbol{h}}_{k}^{u T} \boldsymbol{e}_{u}\right)} \\ \boldsymbol{s}_{t}^{u} &=\sum_{k=1}^{t} \alpha_{k} \hat{\boldsymbol{h}}_{k}^{u} \end{aligned} 这里\ :math:`\hat{\boldsymbol{h}}_{k}^{u}`\ 是多头注意力输出\ :math:`\hat{X}^{u}`\ 中第\ :math:`k`\ 个位置的隐藏状态,\ :math:`\alpha_{k}`\ 是对应的注意力权重。最终得到融合个性化信息的短期兴趣表示\ :math:`\boldsymbol{s}_{t}^{u} \in \mathbb{R}^{d \times 1}`\ 。 **捕捉长期兴趣** 长期行为包含丰富的用户偏好信息,但与短期行为的建模方式不同。SDM从特征维度对长期行为进行聚合,将历史行为按不同特征分成多个子集 (:numref:`sdm-model-architecture` 左上角): .. math:: \mathcal{L}^{u}=\left\{\mathcal{L}_{f}^{u} \mid f \in \mathcal{F}\right\} 具体包括:交互过的商品ID集合 :math:`\mathcal{L}^{u}_{id}`\ ,叶子类别集合 :math:`\mathcal{L}^{u}_{leaf}`\ ,一级类别集合 :math:`\mathcal{L}^{u}_{cate}`\ ,访问过的商店集合 :math:`\mathcal{L}^{u}_{shop}`\ ,交互过的品牌集合 :math:`\mathcal{L}^{u}_{brand}`\ 。这种特征维度的分离使模型能够从不同角度理解用户的长期偏好模式。 对每个特征子集,模型使用注意力机制计算用户在该维度上的偏好。将特征实体\ :math:`f^{u}_{k} \in \mathcal{L}^{u}_{f}`\ 通过嵌入矩阵转换为向量\ :math:`\boldsymbol{g}^{u}_{k}`\ ,然后使用用户画像\ :math:`\boldsymbol{e}_u`\ 计算注意力权重: .. math:: \begin{aligned} \alpha_{k} &=\frac{\exp \left(\boldsymbol{g}_{k}^{u T} \boldsymbol{e}_{u}\right)}{\sum_{k=1}^{\left|\mathcal{L}_{f}^{u}\right|} \exp \left(\boldsymbol{g}_{k}^{u T} \boldsymbol{e}_{u}\right)} \\ \boldsymbol{z}_{f}^{u} &=\sum_{k=1}^{\left|\mathcal{L}_{f}^{u}\right|} \alpha_{k} \boldsymbol{g}_{k}^{u} \end{aligned} 其中\ :math:`\left|\mathcal{L}_{f}^{u}\right|`\ 表示特征子集的大小。 最终将各特征维度的表示拼接,通过全连接网络得到长期兴趣表示: .. math:: \begin{aligned} \boldsymbol{z}^{u} &=\operatorname{concat}\left(\left\{\boldsymbol{z}_{f}^{u} \mid f \in \mathcal{F}\right\}\right) \\ \boldsymbol{p}^{u} &=\tanh \left(\boldsymbol{W}^{p} \boldsymbol{z}^{u}+\boldsymbol{b}\right) \end{aligned} 其中\ :math:`\boldsymbol{W}^{p}`\ 是权重矩阵,\ :math:`\boldsymbol{b}`\ 是偏置向量。 **长短期兴趣融合** 有了长短期兴趣表示后,关键问题是如何有效融合这两部分信息。用户的长期行为虽然丰富,但通常只有一小部分与当前决策相关。简单的拼接或加权求和难以准确提取相关信息。 SDM创新性地设计了门控融合机制,类似LSTM中的门控思想 (:numref:`sdm-model-architecture` 中间部分): .. math:: \begin{aligned} \boldsymbol{G}_{t}^{u} &= \operatorname{sigmoid}\left(\boldsymbol{W}^{1} \boldsymbol{e}_{u}+\boldsymbol{W}^{2} \boldsymbol{s}_{t}^{u}+\boldsymbol{W}^{3} \boldsymbol{p}^{u}+\boldsymbol{b}\right) \\ \boldsymbol{o}_{t}^{u} &= \left(1-\boldsymbol{G}_{t}^{u}\right) \odot \boldsymbol{p}^{u}+\boldsymbol{G}_{t}^{u} \odot \boldsymbol{s}_{t}^{u} \end{aligned} 这里\ :math:`\boldsymbol{G}_{t}^{u} \in \mathbb{R}^{d \times 1}`\ 是门控向量,\ :math:`\odot`\ 表示逐元素乘法,\ :math:`\boldsymbol{W}^{1}`\ 、\ :math:`\boldsymbol{W}^{2}`\ 、\ :math:`\boldsymbol{W}^{3}`\ 是权重矩阵。 门控网络接收三个输入:用户画像\ :math:`\boldsymbol{e}_{u}`\ 、短期兴趣\ :math:`\boldsymbol{s}_{t}^{u}`\ 和长期兴趣\ :math:`\boldsymbol{p}^{u}`\ ,输出的门控向量每个元素值介于0到1之间,决定了对应维度上长短期兴趣的贡献比例。这让模型能够在不同兴趣维度上分别选择保留长期或短期信息,避免简单平均可能带来的信息损失,使模型能够精确捕捉长期行为中与当前兴趣最相关的部分。 SDM模型的训练方式同MIND类似。 **代码实践** .. raw:: latex \diilbookstyleinputcell .. code:: python config = funrec.load_config('sdm') # 加载数据 train_data, test_data = funrec.load_data(config.data) # 准备特征 feature_columns, processed_data = funrec.prepare_features(config.features, train_data, test_data) # 训练模型 models = funrec.train_model(config.training, feature_columns, processed_data) # 评估模型 metrics = funrec.evaluate_model(models, processed_data, config.evaluation, feature_columns) print(build_metrics_table(metrics)) .. raw:: latex \diilbookstyleoutputcell .. parsed-literal:: :class: output +---------------+--------------+-----------+----------+----------------+---------------+ | hit_rate@10 | hit_rate@5 | ndcg@10 | ndcg@5 | precision@10 | precision@5 | +===============+==============+===========+==========+================+===============+ | 0.0084 | 0.0028 | 0.0029 | 0.0011 | 0.0008 | 0.0006 | +---------------+--------------+-----------+----------+----------------+---------------+