用户塔embeding矩阵组成

特征向量类型

在机器学习和推荐系统中,离散特征连续特征是两种最基本的数据类型。它们的本质区别在于数值本身是否具有数学上的大小和连续意义

以下是通俗易懂的解释:

1. 离散特征(Discrete / Categorical Features)

也叫类别特征。它的值代表的是不同的“类别”或“状态”,数值之间没有大小、倍数或顺序关系

  • 通俗理解:它回答的是“是什么类别?”的问题。
  • 代码中的例子
    • 性别:0代表女,1代表男。这里的 1 并不比 0 大,也不能说“男是女的1倍”。
    • 投资偏好:0=保守型,1=稳健型,2=平衡型。这里的 0、1、2 只是个代号(标签)。
    • 商品 ID:比如产品编号 1008610087,它们只是不同的产品,数字大小毫无意义。
  • 模型如何处理它?
    神经网络不能直接拿这些代号去做乘法(否则模型会误以为“平衡型(2)”的权重是“稳健型(1)”的两倍)。因此,模型通常会通过 Embedding(嵌入层),把每一个类别“翻译”成一个固定长度的向量。比如在你的代码里,把“性别”翻译成了一个 8 维的向量。

2. 连续特征(Continuous / Dense Features)

也叫数值特征。它的值是实数,数值的大小、加减乘除是有实际物理意义的

  • 通俗理解:它回答的是“有多少?”或“有多大?”的问题。
  • 代码中的例子
    • 年龄:20岁、35岁、50岁。50岁就是比25岁大一倍,数字本身代表了真实的年龄大小。
    • 资产水位:账户里有 10,000 元还是 1,000,000 元。
    • 月交易频次:每个月交易 5 次还是 20 次。
  • 模型如何处理它?
    因为数字本身就有意义,所以它们可以直接作为数值输入给模型。但是,为了防止某些特征数值太大(比如资产 1000000)把数值小的特征(比如年龄 25)的影响力掩盖掉,通常会在输入模型前做归一化(Normalization),把它们缩放到 0 到 1 之间,或者变成均值为 0 的标准正态分布。

总结对比:

特征类型 含义 你的代码示例 模型处理方式
离散特征 表示“类别/标签”,数字只是代号,无大小之分 性别、用户等级、投资偏好、产品ID Embedding(查表变成向量,如 8维)
连续特征 表示“数量/程度”,数字大小有真实的数学意义 年龄、资产水位、月交易频次 归一化后直接输入(作为 1维 浮点数)

特征向量如何拼接成 DDN塔向量

在双塔模型中,用户的各个特征是通过 Embedding(嵌入)拼接(Concatenation) 的方式塞到用户塔(User Tower)的输入向量中的。

根据你提供的 config.py 代码,用户塔的输入向量维度实际上是 43维8*3 + 3 + 16 = 43),它的具体构成方式如下:

1. 离散特征(Sparse Features) -> 转化为 Embedding 向量

像性别、用户等级、投资偏好这类离散的类别特征,模型无法直接理解,所以会为它们各自初始化一个 Embedding 查找表(Lookup Table)。

  • 性别 (Gender):维度是 SPARSE_EMBEDDING_DIM = 8
  • 用户等级 (User Level):维度是 SPARSE_EMBEDDING_DIM = 8
  • 投资偏好 (Invest Pref):维度是 SPARSE_EMBEDDING_DIM = 8
    合计:8 + 8 + 8 = 24 维

2. 连续特征(Dense Features) -> 直接作为数值输入

像年龄、资产水位、月交易频次这类连续的数值特征,本身就带有大小意义,通常经过归一化(Normalization)后,直接作为一个浮点数值(1维)拼接到向量中。

  • 年龄、资产、交易频次:共 NUM_USER_DENSE_FEATURES = 3 个特征,每个特征 1 维。
    合计:3 维

3. 行为序列特征(Sequence Features) -> Pooling 后转为向量

用户过去交互过的产品历史(比如点击过的理财产品 ID)。每个产品 ID 会被映射为 ITEM_EMBEDDING_DIM = 16 维的向量。因为历史记录是一个序列(包含多个产品),通常会通过 Average Pooling(求平均)或 Sum Pooling(求和)操作,把多个 16 维的向量压缩成一个 16 维的综合向量,代表用户的历史兴趣。

  • 历史行为序列池化 (seq_pooled):维度是 ITEM_EMBEDDING_DIM = 16
    合计:16 维

最终拼接(Concat)

模型在准备用户塔的输入时,会把上面三部分像拼积木一样横向拼接在一起:
[性别8维] + [等级8维] + [偏好8维] + [连续特征3维] + [行为序列16维] = 43 维

这个 43 维的向量就是 User Tower 的最底层输入。随后,这个 43 维的向量会经过几层全连接层(MLP,比如 128 -> 64),最终输出一个固定维度(如 64 维)的用户表征向量,去和商品塔输出的 64 维向量做点积计算相似度。

(注:你提到的 68 维可能是口误或者是之前版本的配置,根据当前代码第 56 行的推导,输入维度是 43 维。)

TIPS

为什么性别是8维呢

这是一个非常敏锐且专业的问题!

直觉上,性别只有“男”和“女”两种状态(或者加上“未知”算三种),用 1 维(比如 0 代表女,1 代表男)或者 2 维(One-Hot 编码:[1, 0][0, 1])来表示似乎就足够了。

但在深度学习推荐系统中,我们依然会把它映射成一个更高维(比如 8 维)的浮点数向量,主要有以下几个核心原因:

1. 赋予特征足够的“话语权”(防止被淹没)

在你的 config.py 中,用户塔的输入是把所有特征拼接在一起的。
假设商品 ID 是 16 维,投资偏好是 8 维。如果性别只给 1 维(比如 [1.0]),那么在拼接后的 43 维长向量中,性别的信息占比太小了。
当这个长向量进入全连接层(MLP)进行矩阵乘法时,只有 1 维的性别特征很容易在成千上万次的反向传播中被其他高维特征(如 16 维的商品特征)“淹没”或忽略。给它 8 维,相当于放大了它在神经网络第一层的“存在感”和权重容量,让模型更容易捕捉到性别与最终点击率之间的关系。

2. 学习丰富的“潜在语义”(Latent Semantics)

深度学习中的 Embedding 不仅仅是“身份标识”,它还能学习到业务属性
虽然性别只有两种,但这 8 个维度可以被模型训练成代表不同的“潜在偏好”。例如,模型在训练后可能会自动形成这样的理解:

  • 第 1 维代表:对美妆产品的偏好程度
  • 第 2 维代表:对高风险理财的接受度
  • 第 3 维代表:对电子产品的点击倾向
  • ...
    虽然我们输入的是“男/女”,但输出的 8 维向量实际上是“典型的男性/女性在我们的业务场景下,在 8 个不同维度的行为画像”。这比单纯的一个 0[1, 0] 包含的信息量大得多,也更容易和其他特征(如商品向量)做交叉计算。

3. 工程实现的统一与便利

在编写代码时,为了方便构建网络结构,我们通常会为所有的离散特征统一设置一个默认的 Embedding 维度(在你的代码里就是 SPARSE_EMBEDDING_DIM = 8)。
无论是性别(2种)、用户等级(5种)还是投资偏好(5种),统一用 8 维可以:

  • 简化代码逻辑(不需要为每个特征单独写维度配置)。
  • 在底层计算时,维度对齐的矩阵运算效率更高。

💡 工业界真实情况补充:

你觉得“8维有点浪费”的直觉是完全正确的!

在真实的工业级推荐系统(如阿里、字节跳动)中,为了极致的性能和节省内存,通常不会让所有离散特征共享同一个维度大小。
业界常用的做法是:特征的 Embedding 维度大小,应该与其类别数量(基数,Cardinality)成正比。

一个著名的经验公式(FastAI 提出)是:
$$ Dimension = \min(600, \text{round}(1.6 \times \text{类别数}^{0.56})) $$
或者更简单的经验法则:维度 = 类别数的 4 次方根

按照工业界极致优化的标准:

  • 性别(2 个类别):可能只给 2 维4 维
  • 商品 ID(100万 个类别):可能会给 64 维128 维

所以,在你的代码里,性别用 8 维更多是为了代码演示的简洁性保证模型能充分学习。如果在真实的大型项目中,确实可以把它降到 2 维或 4 维来减少参数量。

posted @ 2026-03-19 08:10  向着朝阳  阅读(1)  评论(0)    收藏  举报