推荐系统-用户塔-embeding矩阵训练


这是一个非常经典且贴近实际业务的场景!在股票交易系统的推荐(比如给用户推荐可能感兴趣的股票、基金或理财产品)中,双塔模型(Two-Tower Model)是粗排(召回阶段)的绝对主力。

针对你提到的四个用户特征(性别、投资偏好、用户等级、风险偏好),我们来看看在双塔模型的 User 塔中,它们的 Embedding 矩阵是如何被一步步训练出来的。

我们可以把整个过程分为 特征定义 -> 前向传播 -> 损失计算 -> 反向更新 四个步骤来拆解。


第一步:为每个特征准备独立的 Embedding 矩阵

在模型初始化时,这四个特征并不是共享同一个矩阵的,而是各自拥有一个独立的 Embedding 矩阵。矩阵的大小由该特征的词表大小(Vocabulary Size, $V$)设定的维度(Dimension, $D$)决定。

  1. 性别 (Gender)
    • 词表:男、女、未知($V=3$)。
    • 矩阵:非常小,比如 $3 \times 4$ 的矩阵(维度设为 4 足够了)。
  2. 用户等级 (User Level)
    • 词表:比如青铜到王者,或者 V1 到 V10($V=10$)。
    • 矩阵:比如 $10 \times 8$ 的矩阵。
  3. 风险偏好 (Risk Tolerance)
    • 词表:保守型、稳健型、激进型等($V=5$)。
    • 矩阵:比如 $5 \times 8$ 的矩阵。
  4. 投资偏好 (Investment Preference)
    • 注意:这是一个特殊特征! 因为它通常是多值(Multi-value)的。比如一个用户可能同时偏好“白酒”、“新能源”、“短线操作”。
    • 词表:所有可能的偏好标签集合(假设有 $V=500$ 种标签)。
    • 矩阵:比如 $500 \times 16$ 的矩阵。

此时,这些矩阵里的数字全是随机产生的,它们还不知道“激进型”和“保守型”在炒股上有什么区别。


第二步:User 塔的前向传播(把特征变成向量)

假设现在有一个真实用户 A 来到了系统:

  • 用户 A 的特征是:[男, V5, 激进型, (新能源, 半导体)]

User 塔会这样处理这个用户的数据:

  1. 查表(Lookup)
    • 去性别矩阵抽出“男”对应的 4 维向量 $e_{gender}$。
    • 去等级矩阵抽出“V5”对应的 8 维向量 $e_{level}$。
    • 去风险矩阵抽出“激进型”对应的 8 维向量 $e_{risk}$。
  2. 多值特征的 Pooling(池化)
    • 去偏好矩阵抽出“新能源”和“半导体”两个 16 维向量。因为是多值的,DNN 层没法处理变长的输入,所以通常会把这两个向量做一个平均(Mean Pooling)求和(Sum Pooling),融合成一个单一的 16 维向量 $e_{pref}$。
  3. 拼接(Concat)
    • 把这 4 个向量拼在一起:[4维 + 8维 + 8维 + 16维] = 一个 36 维的长向量。
  4. 过 DNN(多层感知机)
    • 把这个 36 维的向量输入到 User 塔的 DNN 层(比如经过 128 -> 64 -> 32 的全连接层)。
    • 最终输出一个 32 维的 User 向量(User Representation)

(与此同时,Item 塔也把某只股票 B 的特征,经过类似操作,变成了一个 32 维的 Item 向量


第三步:计算损失(Loss)—— 训练的指挥棒

在双塔模型中,怎么判断推荐得好不好呢?

  • 计算相似度:把 User 向量和 Item 向量做一个内积(Dot Product)余弦相似度。得分越高,代表模型认为该用户越喜欢这只股票。
  • 对比真实标签(Ground Truth)
    • 如果用户 A 实际上买入/点击了股票 B,真实标签就是 1(正样本)。
    • 如果系统推荐了股票 C,但用户 A 无视了它,真实标签就是 0(负样本)。
  • 计算 Loss:模型预测得分和真实标签之间的差距,通常使用二元交叉熵损失(LogLoss / BCE)对比损失(InfoNCE)来计算出当前误差。

第四步:反向传播,更新 Embedding(核心!)

这是真正“训练”发生的地方。假设模型预测用户 A 不会买股票 B(相似度得分很低),但实际上用户 A 买了(产生了很大的 Loss)。

  1. 梯度回传:Loss 会产生梯度,告诉模型:“你应该让 User 向量和股票 B 的 Item 向量靠得更近一点!”
  2. 穿过 DNN:梯度从 User 向量反向穿过 User 塔的层层 DNN,传导到了那个拼接好的 36 维向量上。
  3. 分配给各个 Embedding:梯度被拆解,分别回传给刚才查表用到的具体位置:
    • 性别矩阵中“男”的那一行向量被更新。
    • 等级矩阵中“V5”的那一行被更新。
    • 风险矩阵中“激进型”的那一行被更新。
    • 偏好矩阵中“新能源”“半导体”这两行被更新。

关键点:

  • 在这一轮更新中,性别矩阵里的“女”、等级矩阵里的“V1”、偏好里的“白酒”,因为没有出现在用户 A 身上,梯度为 0,不发生变化
  • 只有被触发的那些特征的 Embedding 向量,顺着梯度的方向稍微移动了一点,使得下一次遇到类似的“激进型、爱新能源的 V5 男性”时,模型能给出更高的分数。

总结与工程经验建议

在你的股票交易系统中,这四个 Embedding 矩阵就是通过海量的用户交易/点击日志(正负样本),不断重复上述四步,硬生生“逼”出来的。

训练成熟后,你会发现一些有趣的现象(可以通过计算向量余弦相似度发现):

  • “激进型”和“稳健型”的 Embedding 距离可能比较远。
  • “新能源”和“光伏”在投资偏好矩阵中的 Embedding 向量会靠得非常近(因为它们经常在同一个用户的行为中共同出现)。

给你的两个实战小建议:

  1. 维度设计:不要给所有特征相同的 Embedding 维度。像“性别”这种词表极小的,维度给 4 甚至 2 就够了;像“投资偏好”这种信息量大、词表大的,给 16 或 32。维度太大容易过拟合且浪费内存。
  2. 冷启动/未知值:一定要在每个特征的词表里预留一个 <UNK>(Unknown)的位置。因为总会有没填风险偏好的新用户,或者系统新增了一个之前没有的等级,这时候就把它们映射到 <UNK> 的 Embedding 上参与计算。

矩阵的维度如何确定

刚才非常抱歉,网络似乎开了一点小差,回答中断了。我们接着聊!

你问到了一个极其核心的工程问题:“性别为什么是 4 维?用户等级为什么是 8 维?这些数字是怎么来的?”

简单直接的答案是:4 和 8 不是通过严密的数学公式算出来的绝对真理,它们是算法工程师人为设定的“超参数(Hyperparameters)”。

虽然是人为设定的,但并不是瞎猜的。在工业界,设定 Embedding 维度(Dimension, $D$)主要遵循一个核心逻辑:特征的信息复杂度(或者说词表大小 $V$)决定了它需要多大的“信息容量”。

我们可以通过以下三个维度来彻底搞懂这个逻辑:

1. 核心原则:杀鸡不用牛刀(防止过拟合与浪费)

你可以把 Embedding 的维度想象成“用来描述这个特征所允许使用的词汇量”

  • 对于性别($V=3$:男、女、未知):
    性别的状态非常少,信息极其简单。如果你给性别分配一个 64 维的向量,这就好比你写了一篇 10000 字的论文,仅仅为了解释“什么是男人”。
    这会导致两个严重后果:

    1. 浪费内存和计算力
    2. 过拟合(Overfitting):模型可能会在 64 维的空间里去死记硬背训练集里的某些噪音(比如刚好训练集里某个买白酒的男性用户某天点错了,模型会把这个噪音记录在性别的 64 维向量里),导致泛化能力变差。
    • 结论:对性别来说,给 2 维、4 维就完全足够表达它所有的特征差异了。
  • 对于用户等级($V=10$:V1 到 V10):
    相比性别,用户等级的状态更多(10 种),且蕴含的信息更复杂(比如 V1 可能是纯新手,V5 是有一定经验的老股民,V10 是资金量巨大的 VIP,他们的交易频率、客单价、风险承受度都截然不同)。
    因此,我们需要一个稍微大一点的“空间”来刻画这 10 个等级之间的微妙差异。给 4 维可能有点挤(容易导致 V4 和 V5 的向量粘在一起区分不开),给 64 维又太浪费,所以 8 维或 16 维是一个比较合理的中间值。

2. 工业界的三大“经验法则”(Rule of Thumb)

既然是凭经验,那大厂的工程师们通常是怎么拍脑袋的呢?这里有几个常用的经验公式,你可以直接拿去用:

法则 A:Google 的四次方根法则
在很多 Google 的推荐系统论文中,提出过一个经验公式:
$$D \approx 6 \times \sqrt[4]{V}$$

  • 算一下性别 ($V=3$):$6 \times \sqrt[4]{3} \approx 6 \times 1.31 \approx 7.8$(这里算出来偏大一点,实际中极小词表通常直接给 2 或 4)。
  • 算一下投资偏好(假设 $V=500$):$6 \times \sqrt[4]{500} \approx 6 \times 4.7 \approx 28$(所以前面举例给 16 或 32 维是非常合理的)。

法则 B:对数法则
有的人喜欢用以 2 为底的对数来估算:
$$D \approx \log_2(V) \times C$$ ($C$ 是一个常数,比如 2 到 5)

法则 C:程序员的“2的整数次幂”强迫症(最实用)
在实际写代码(TensorFlow / PyTorch)时,为了底层硬件(GPU/CPU)的内存对齐和计算效率,工程师几乎总是把维度设定为 2 的整数次幂

  • 极小特征(性别):2 或 4
  • 中小特征(等级、风险偏好):8 或 16
  • 中大特征(投资偏好、城市):32 或 64
  • 超大特征(具体的股票 ID、用户 ID):64、128 或 256

3. 在你的股票系统里,到底该怎么定?

回到你的股票交易推荐系统,如果你在搭建粗排 DNN:

  1. 第一版(Baseline):你可以像我举例的那样,按 $2^n$ 去拍脑袋。性别 4,等级 8,风险偏好 8,投资偏好 32。这绝对能跑通,而且效果不会差。
  2. 第二版(调优):如果模型上线后,你发现用户等级对预测买不买股票非常关键(比如 V10 用户买某只股的概率极高),你可以尝试把用户等级的维度从 8 提升到 16,看看 Loss 能不能降得更低。
  3. 资源限制:如果你的服务器内存报警了,或者粗排要求 10 毫秒内必须返回结果,你可能需要强行把所有特征的维度都砍掉一半(比如降到 2, 4, 4, 16),牺牲一点点精度来换取速度。

总结一下:
4 和 8 只是为了匹配特征“词汇量”大小而合理猜测的容量值。维度太小,模型学不到东西(欠拟合);维度太大,模型死记硬背且浪费资源(过拟合)。在实际业务中,先用“常识+经验公式”定一个初始值,然后通过做实验(A/B Test 或看离线 Loss)来微调,这是最标准的做法。

posted @ 2026-03-17 22:35  向着朝阳  阅读(5)  评论(0)    收藏  举报