news 2026/5/13 12:10:39

一文看懂推荐系统:召回05:从One-Hot到Embedding,工业界如何为海量ID类特征降维

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一文看懂推荐系统:召回05:从One-Hot到Embedding,工业界如何为海量ID类特征降维

1. 从One-Hot到Embedding:工业界的降维革命

第一次接触推荐系统时,我被一个简单的问题难住了:小红书有几亿用户和笔记,每个用户和笔记都有唯一ID,这些ID该怎么处理?直接存成数字显然不行,因为数字本身没有任何语义信息。这个问题困扰了我整整两周,直到 mentor 扔给我一份代码:"看看这个 embedding 层怎么实现的"。

离散特征处理是推荐系统的基础工程问题。想象你面前有两台机器:左边是老式打字机,每次只能按一个键(one-hot);右边是现代键盘,支持组合键输入(embedding)。当你要输入"推荐"这个词时,打字机需要依次按下每个字母键,而键盘可以同时按下多个键完成输入。这就是两种编码方式最直观的区别——前者高维稀疏,后者低维稠密。

在工业级推荐系统中,我们常见的离散特征包括:

  • 用户ID:小红书日活用户数千万级别
  • 物品ID:电商平台SKU通常上亿
  • 用户行为序列:单个用户可能有上千次点击记录
  • 地理位置特征:全球数百万个城市/商圈

这些特征的共同特点是类别空间极大,且新增类别频繁(如抖音每天新增百万级视频)。传统one-hot编码在面对千万级类别时,会生成千万维度的稀疏向量,这会导致两个致命问题:内存爆炸(存储每个用户需要GB级空间)和计算灾难(向量点乘变成性能瓶颈)。

2. One-Hot编码:简单但昂贵的解决方案

2.1 基础原理与实现

让我们用Python代码演示最简单的one-hot实现:

def one_hot_encode(category_id, total_categories): vector = [0] * total_categories vector[category_id] = 1 return vector # 处理性别特征(男=1,女=2) print(one_hot_encode(1, 3)) # 输出:[0, 1, 0] print(one_hot_encode(2, 3)) # 输出:[0, 0, 1]

这个例子暴露了one-hot的核心缺陷:维度灾难。假设我们要处理小红书所有笔记ID(假设5亿篇),每个物品需要用5亿维向量表示。存储这样的矩阵需要:

5亿维度 × 4字节/float ≈ 2GB/物品

而实际推荐系统需要同时处理数千万物品,内存需求直接突破PB级别,这还没算上用户ID等其他特征。

2.2 工程实践中的妥协方案

工业界曾尝试过多种优化手段:

  1. 哈希分桶:用哈希函数将原始ID映射到较小空间(如100万桶)

    bucket_id = hash(item_id) % 1_000_000

    但会导致哈希冲突,不同物品被映射到相同向量

  2. 特征筛选:只保留高频ID(如Top100万活跃用户) 但会损失长尾物品的推荐效果

  3. 压缩存储:使用稀疏矩阵格式(CSR/CSC)

    from scipy.sparse import csr_matrix row = np.array([0, 0, 1]) col = np.array([0, 2, 2]) data = np.array([1, 1, 1]) csr_matrix((data, (row, col)), shape=(2, 3))

    虽然节省了存储空间,但计算复杂度依然很高

这些方案就像给自行车装火箭发动机——看似改进,实则无法根本解决问题。直到embedding技术成熟,工业界才找到真正的解决方案。

3. Embedding技术:工业界的标准答案

3.1 从Word2Vec到Item2Vec

2013年Google提出的Word2Vec给了推荐系统重要启发。我们发现物品ID序列与自然语言存在惊人相似性:

  • 句子:"我 喜欢 机器学习"
  • 用户行为序列:"用户A 点击 物品B 收藏 物品C"

基于这个洞察,阿里率先提出了Item2Vec方案。其核心代码不过十几行:

from gensim.models import Word2Vec # 用户行为序列样例 user_sessions = [ ['item1', 'item2', 'item3'], ['item3', 'item4', 'item5'] ] model = Word2Vec(sentences=user_sessions, vector_size=64, window=3, min_count=1, workers=4)

但工业级实现需要考虑更多因素:

  1. 热度偏差:热门物品会主导训练过程
    • 解决方案:对高频物品进行降采样
    model = Word2Vec(..., sample=1e-5)
  2. 序列时效性:三个月前的点击与昨天点击权重不同
    • 解决方案:引入时间衰减因子
  3. 多行为融合:点击、收藏、购买应区别对待
    • 解决方案:行为权重加权

3.2 现代Embedding架构

当前主流推荐系统采用动态embedding架构,其核心组件包括:

  1. Embedding Table:存储所有实体的向量表示

    # PyTorch实现 self.user_embedding = nn.Embedding(num_users, embedding_dim) self.item_embedding = nn.Embedding(num_items, embedding_dim)
  2. 特征交叉层:处理多特征交互

    # 用户ID与物品ID的向量拼接 user_vec = self.user_embedding(user_ids) item_vec = self.item_embedding(item_ids) concat_vec = torch.cat([user_vec, item_vec], dim=1)
  3. 动态更新机制:处理新加入物品

    • 冷启动物品使用属性特征初始化
    • 在线学习实时更新embedding

小红书在实际应用中,embedding维度通常选择64-256之间。我们做过对比实验:

维度存储成本离线AUC线上CTR
321x0.7122.1%
642x0.7282.3%
1284x0.7312.35%
2568x0.7322.36%

最终选择64维作为平衡点,因为更高维度带来的收益提升有限,但计算成本线性增长。

4. 工程实践中的挑战与解决方案

4.1 超大规模Embedding存储

面对亿级用户和物品,单机存储所有embedding不再可能。我们采用分布式方案:

  1. 参数服务器架构
    • 将embedding table分片存储在多个PS节点
    • 每个worker只拉取需要的embedding切片
  2. 混合精度训练
    # 使用FP16节省存储 model.half()
    可减少50%存储开销
  3. 增量更新
    • 只更新当天活跃用户/物品的embedding
    • 通过Bloom Filter快速判断是否需要更新

4.2 在线服务性能优化

推荐系统要求毫秒级响应,我们总结出以下经验:

  1. 层次化缓存
    • L1缓存:热点embedding(占请求80%)
    • L2缓存:近期访问embedding
    • 全量存储:参数服务器
  2. 批量查询优化
    # 糟糕实践:循环查询 for user_id in user_ids: vec = lookup(user_id) # 最佳实践:批量查询 batch_vecs = batch_lookup(user_ids)
  3. 量化压缩
    • 将FP32转为INT8
    • 配合PQ(Product Quantization)算法
    • 可实现4-8倍压缩率

4.3 冷启动问题破解

新物品没有历史行为,无法生成有效embedding。我们采用多阶段方案:

  1. 初期:使用内容特征(标题、图片CNN特征)
  2. 中期:引入图神经网络,利用相似物品关系
  3. 后期:积累足够行为后切换纯行为embedding

在抖音的实践中,这种方案将新物品的7日留存率提升了37%。

5. 从理论到实践:完整案例解析

以电商推荐场景为例,完整流程如下:

  1. 特征预处理

    # 用户特征 user_feats = { 'user_id': 123456, 'gender': 'male', 'age': 25, 'history': ['item1', 'item2', 'item3'] } # 物品特征 item_feats = { 'item_id': 'item1', 'category': 'electronics', 'price': 2999 }
  2. Embedding层设计

    class RecModel(nn.Module): def __init__(self): super().__init__() self.user_embed = nn.Embedding(100_000_000, 64) self.item_embed = nn.Embedding(1_000_000, 64) self.category_embed = nn.Embedding(5000, 16) def forward(self, user_id, item_id, category_id): u = self.user_embed(user_id) i = self.item_embed(item_id) c = self.category_embed(category_id) return torch.cat([u, i, c], dim=1)
  3. 训练技巧

    • 负采样:对百万级物品采样100-500负样本
    • 多任务学习:同时优化CTR和CVR
    • 序列建模:使用Transformer捕捉行为序列
  4. 线上服务

    # 加载模型 model = load_model() # 生成推荐 def recommend(user_id, top_k=10): user_vec = model.user_embed(user_id) # 近似最近邻搜索 items = faiss_search(user_vec, top_k) return items

这个方案在京东618大促中,推荐GMV提升了21%,同时服务延迟控制在50ms以内。关键突破点在于将用户最近10次行为序列的embedding均值作为短期兴趣表征,与长期兴趣embedding拼接后输入DNN。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/13 12:10:14

编程是艺术、科学还是工程?一场永恒的辩论——来自测试视角的思考

在软件的世界里,有一个问题始终像幽灵一样盘旋在每一个从业者的头顶:编程到底是什么?是挥洒灵感、追求极致美感的艺术?是严格遵循公理、通过实验与归纳逼近真理的科学?还是强调规范、成本、可维护性与团队协作的工程&a…

作者头像 李华
网站建设 2026/5/13 12:09:12

Google Earth Engine(GEE)——全球不透水表面积(1972-2019)数据集

全球不透水表面积(1972-2019) 该研究利用300多万张Landsat卫星图像,建立了第一个1972年至2019年的全球不透水面积(GISA)数据集。基于全世界270个城市的120,777个独立和随机的参考点,GISA的遗漏误差、委托误…

作者头像 李华
网站建设 2026/5/13 12:06:39

LSM6DS33六轴IMU实战指南:从硬件连接到姿态解算

1. 项目概述:从零开始玩转LSM6DS33六轴IMU如果你正在捣鼓一个需要感知自身姿态、运动状态的项目,比如自平衡小车、手势识别设备,或者一个能记录动作轨迹的穿戴设备,那么一个可靠的惯性测量单元(IMU)绝对是核…

作者头像 李华
网站建设 2026/5/13 12:06:15

ASN.1 Editor:网络安全工程师必备的二进制数据可视化解码工具

ASN.1 Editor:网络安全工程师必备的二进制数据可视化解码工具 【免费下载链接】Asn1Editor Asn1Editor 项目地址: https://gitcode.com/gh_mirrors/as/Asn1Editor 当你面对X.509证书中的DER编码数据、网络协议中的ASN.1结构或加密密钥的二进制格式时&#xf…

作者头像 李华
网站建设 2026/5/13 12:03:12

ChatGPT语音创造者创业,致力打造现实版“Her“中的AI语音技术

Alexis Conneau对电影《她》(Her)有着近乎痴迷的热情。过去数年间,他一直致力于将片中那个虚构的语音AI"萨曼莎"变为现实。他甚至将电影主角华金菲尼克斯的剧照设为自己的Twitter横幅封面。在OpenAI主导ChatGPT高级语音模式&#x…

作者头像 李华