推荐系统模型演进:从协同过滤到深度学习的技术突破与应用实践
1. 推荐系统的“石器时代”协同过滤与矩阵分解回想我刚入行那会儿推荐系统听起来特别“玄学”。很多公司的做法就是“猜你喜欢”靠一些简单的规则比如“买过这个的人也买了那个”。这其实就是协同过滤最朴素的思想。我最早接触的实战项目就是在一个社区论坛里用基于物品的协同过滤ItemCF给用户推荐他们可能感兴趣的热帖。那时候觉得这模型真简单计算也快但上线后效果总是不太理想用户反馈说推荐的东西要么太热门要么就是完全没兴趣。后来我仔细琢磨发现问题出在数据上。那个论坛的用户行为数据太稀疏了一个新用户可能只点过一两个帖子一个冷门帖子可能只有一两个人看过。用协同过滤算相似度就像在一片茫茫沙漠里找几粒特定的沙子太难了。模型严重欠拟合对新用户和新物品几乎无能为力。这就是早期协同过滤最大的痛点极度依赖密集的用户行为数据对数据稀疏性Sparsity和冷启动问题束手无策。直到2006年左右矩阵分解Matrix Factorization, MF技术被引入推荐领域情况才有所改观。我第一次用MF的时候感觉像是打开了一扇新世界的大门。它的核心思想是把用户和物品都映射到一个隐式的低维向量空间里。比如我们把用户和物品都表示成一个长度为10的向量这个长度就是隐语义维度。通过分解用户-物品评分矩阵我们就能得到每个用户的“兴趣向量”和每个物品的“特质向量”。我举个例子假设我们有一个电影评分数据集。经过矩阵分解我们可能发现某个维度代表了“电影是否包含科幻元素”另一个维度代表了“电影是否偏文艺”。那么一个用户的向量在“科幻”维度上值很高在“文艺”维度上值很低就说明他喜欢科幻片不喜欢文艺片。同样一部《星际穿越》的向量在“科幻”维度上值也会很高。这样即使用户没给《星际穿越》打过分我们也能通过计算两个向量的内积相似度来预测他可能喜欢这部电影。# 一个非常简化的矩阵分解思想示例非实际训练代码 # 假设我们有用户数m物品数n隐语义维度k import numpy as np # 随机初始化用户矩阵P (m x k) 和物品矩阵Q (n x k) m, n, k 1000, 2000, 10 P np.random.randn(m, k) # 用户隐向量矩阵 Q np.random.randn(n, k) # 物品隐向量矩阵 # 预测用户i对物品j的评分近似 def predict_rating(user_idx, item_idx): return np.dot(P[user_idx], Q[item_idx]) # 这样即使评分矩阵R中R[i,j]是缺失的为0我们也能预测出一个分数MF的优点是显而易见的它在一定程度上缓解了数据稀疏性问题能为没有直接交互过的用户和物品进行推荐而且得到的隐向量可以用于后续的相似度计算等任务。我当时用MF给那个BBS论坛做推荐效果比纯协同过滤好了不少。但它的缺点也很突出模型只能利用用户-物品交互这一种行为数据像用户的年龄、性别、物品的类别、标签这些丰富的特征信息完全用不上。这就像你判断一个人喜欢什么只看他买了什么却不去了解他的年龄、职业和过往经历判断难免片面。2. 特征工程的黄金年代从LR、FM到特征组合自动化因为MF的局限性大家很自然地把目光投向了当时正如日中天的传统机器学习模型比如逻辑回归LR。LR模型结构简单训练速度快最关键的是它能轻松地融入各种各样的特征。我记得当时做特征工程可是个体力活我们把用户画像年龄、性别、城市、物品属性类别、价格、品牌、上下文信息时间、地点、设备以及各种统计特征历史点击率、购买率全都做成0/1的二值特征一股脑儿喂给LR模型。LR模型确实强大它让我们从“协同”的单一维度迈入了“特征”的广阔天地。线上效果也有提升直到今天在一些对实时性要求极高的召回层LR依然有它的用武之地。但用久了问题又来了。LR是一个广义线性模型它默认特征之间是相互独立的。但现实世界中特征之间存在复杂的交叉效应。举个例子“女性用户”和“护肤品”单独看可能都有正向影响但“女性用户”与“高端护肤品”这个组合特征的影响力绝不是简单相加而是会产生“112”的效应。LR模型无法自动学习到这种特征间的交互关系全靠算法工程师手动去设计组合特征比如“性别_品类”交叉。这不仅工作量巨大而且很难穷尽所有有价值的组合。2010年FMFactorization Machines模型的提出可以说是特征处理上的一次重要突破。我第一次读FM论文时被它的巧妙设计吸引了。FM在LR线性部分的基础上增加了特征交叉项。但它没有像多项式模型那样直接引入巨量的交叉参数那会导致参数爆炸和过拟合而是借鉴了矩阵分解的思想为每个特征学习一个隐向量用两个特征隐向量的内积来表示它们交叉的权重。# FM模型二阶交叉部分的核心思想示意 # 假设我们有特征向量x每个特征i有一个隐向量v_i # 二阶交叉的贡献 sum_{i1}^{n} sum_{ji1}^{n} v_i, v_j x_i x_j # 通过数学变换其计算复杂度可以从O(kn^2)降到O(kn)k是隐向量维度n是特征数 # 这带来的好处是 # 1. 即使两个特征在训练数据中从未同时出现稀疏也能通过各自的隐向量估计其交叉权重。 # 2. 参数数量是O(kn)远低于多项式模型的O(n^2)。FM让模型具备了自动学习二阶特征交叉的能力效果通常比LR更好。后来出现的FFMField-aware FM更进一步它引入了“域”Field的概念。比如“性别”是一个域“品类”是另一个域。FFM认为同一个特征当它与不同域的特征进行交叉时应该使用不同的隐向量。这更符合直觉比如“女性”这个特征在与“护肤品”交叉和与“显卡”交叉时其重要性显然是不同的。FFM通过增加参数量换来了更精细的建模能力在不少CTR预估比赛中大放异彩。但无论是LR还是FM/FFM都还停留在“浅层”模型的范畴。特征交叉的阶数被限制在二阶FM/FFM或者需要手动指定LR。我们人类工程师的智慧和精力开始成为瓶颈。有没有办法让模型自己去挖掘更深层次、更复杂的特征组合模式呢这个问题的答案引向了深度学习的时代。3. 深度学习入场记忆与泛化的统一时间来到2016年谷歌发表了Wide Deep Learning for Recommender Systems这篇经典论文正式将深度学习大规模引入推荐系统。我当时在团队里复现这个模型感觉它的设计思想特别有启发性直击推荐系统的核心矛盾。Wide部分就是一个广义线性模型比如LR它的作用是“记忆”Memorization。什么是记忆就是学习历史数据中频繁出现的、强相关的特征组合。比如“用户安装了某款音乐App”与“给该用户推荐该App的会员服务”这个模式在历史数据中可能非常强。Wide部分通过大量的交叉特征能精准地记住并利用这些模式。它就像是一个经验丰富的老销售对哪些商品搭配着卖得好了如指掌。Deep部分则是一个前馈神经网络MLP它的作用是“泛化”Generalization。泛化是指模型能够发现一些潜在的、未见过的特征组合模式。它通过多层非线性变换将稀疏的类别型特征如用户ID、物品ID转换成稠密的嵌入向量Embedding然后在这些稠密向量上进行深层次的交互。这就像是一个善于分析用户潜在需求的顾问能从用户的基本属性中推断出他可能喜欢的新奇商品。Wide Deep的创新在于它不再纠结于“用线性模型还是非线性模型”而是通过联合训练让两者优势互补。Wide部分确保不错过那些简单明确的规则Deep部分则负责探索更复杂的模式。这个框架也成为了后来很多模型的Base Model。但Wide Deep的Wide部分依然需要人工设计交叉特征这还是个负担。2017年微软的DCNDeep Cross Network模型尝试解决这个问题。它用了一个叫做Cross Network的结构来替代Wide部分。这个Cross Network的设计非常巧妙每一层的输出都是前一层的输出与原始输入特征进行交叉外积再加上前一层的输出本身。通过堆叠多层它可以自动地、显式地构造出任意高阶的特征交叉。# DCN中Cross Network的核心操作简化示意 # x0: 原始输入特征向量 # x_l: 第l层的输出 # w_l, b_l: 第l层的权重和偏置 # 核心公式x_{l1} x0 * (x_l^T * w_l) b_l x_l # 这个操作意味着每一层都在进行特征交叉且交叉的阶数随着层数增加而增加。 # 例如x1包含了二阶交叉x2包含了三阶交叉以此类推。我试过DCN它在一些数据集上效果确实比Wide Deep有提升。但后来也发现它的一点不足Cross Network的特征交叉是在“位”bit-wise级别进行的也就是说它把整个特征嵌入向量拆成一个一个的元素进行交互。而有些研究认为在“向量”vector-wise级别进行交叉即把整个特征向量当做一个整体来交互可能更合理更能保留特征的整体语义信息。几乎在同一时期另一种思路也在蓬勃发展那就是用深度学习来改造最经典的协同过滤。新加坡国立大学提出的NeuralCF模型就是代表。它把矩阵分解中点积计算相似度的方式替换成了一个多层神经网络MLP。点积是一种固定的、相对简单的相似度计算函数而MLP可以拟合任意复杂的函数理论上能更精准地刻画用户和物品隐向量之间的复杂关系。更进一步NeuralCF发展成了著名的“双塔”结构用户侧和物品侧各用一个神经网络塔来分别处理用户特征和物品特征生成用户嵌入向量和物品嵌入向量最后再计算这两个向量的相似度比如余弦相似度。这种结构清晰线上服务时可以预先计算好所有物品的嵌入向量存入向量数据库线上只需要实时计算用户向量然后进行近邻搜索即可工程上非常友好。4. 序列与注意力捕捉用户兴趣的动态变化之前的模型大多把用户表示成一个固定的向量。但仔细想想这合理吗一个用户可能既喜欢篮球也喜欢古典音乐但在浏览体育网站时他的“篮球兴趣”应该被放大而在音乐APP里他的“音乐兴趣”应该占主导。他的兴趣应该是随着当前场景、待推荐物品而动态变化的。2017年阿里巴巴提出的DINDeep Interest Network模型就抓住了这个关键点。DIN的核心创新是引入了注意力机制。它的做法不是简单地把用户历史行为比如点击过的商品的嵌入向量平均池化而是让这些历史行为向量与当前待推荐的目标商品向量进行“对话”。通过一个小的注意力网络计算出每个历史行为与当前目标商品的相关性权重。相关度高的历史行为比如用户过去看过的其他篮球鞋在生成用户当前兴趣向量时占据更大比重相关度低的比如他买过的牛奶权重就小。注意这里的注意力机制不是NLP中标准的Transformer注意力而是一个更简单的、由MLP实现的激活单元Activation Unit。它的输入是历史行为向量和目标商品向量输出是一个标量权重。这样一来用户向量就不再是静态的而是“千物千面”的。给用户推荐篮球鞋时模型生成的用户向量富含他的运动商品兴趣推荐交响乐唱片时生成的又是另一个富含音乐兴趣的向量。这个设计非常符合直觉在实际业务中特别是电商场景对提升点击率和转化率效果显著。我参与过一个电商推荐项目上线DIN后核心的CTR指标有了肉眼可见的提升。但DIN也有其局限。它把用户的历史行为当成一个无序的集合Bag来处理忽略了行为之间的先后顺序。而用户兴趣是随时间演化的。今天你可能在关注手机下单后明天你的兴趣可能就转向了手机壳和贴膜。为了捕捉这种序列信息阿里在2019年又提出了DIENDeep Interest Evolution Network。DIEN的架构很有意思它用GRU门控循环单元来对用户历史行为序列进行建模模拟兴趣的演化过程。但光有GRU还不够因为并非所有历史行为都对预测下一个行为有同等贡献。所以DIEN在GRU的基础上又加入了注意力机制设计了一个“兴趣进化层”。简单说就是在GRU每一步更新隐藏状态时不仅考虑当前输入和上一个状态还会参考当前待推荐的目标商品信息以此来调整兴趣演化的方向让它更聚焦于与目标相关的兴趣演化路径。这相当于给用户的兴趣演化过程装上了“导航”让它能更精准地走向目标商品。5. 从图嵌入到Transformer技术融合与前沿探索推荐系统的演进从来不是孤立的它不断从其他AI领域汲取养分。NLP领域的Word2vec开启了Embedding的时代而Graph Embedding技术则让我们能更好地处理“关系”数据。早期的Item2Vec借鉴Word2vec把用户的行为序列看成句子物品看成词来学习物品嵌入。但它只适用于严格的序列数据。对于更复杂的社交网络、知识图谱等图结构数据就需要DeepWalk、Node2vec这类图嵌入算法。它们通过随机游走从图中生成序列再用Word2vec的方法学习节点嵌入。阿里的EGESEnhanced Graph Embedding with Side Information在此基础上做了关键改进它融入了物品的边信息Side Information比如品类、品牌等。在计算物品嵌入时不仅考虑物品ID本身的嵌入还加权融合了其各个属性的嵌入。这个改进对解决冷启动问题意义重大。一个新商品上线即使没有用户行为数据我们也可以通过它的品类、品牌等属性信息利用EGES模型得到一个相对合理的初始嵌入向量从而被推荐出去。这个工程实践性极强的思路对我后来处理新物料冷启动问题启发很大。而近年来NLP领域的霸主——Transformer模型也开始深刻影响推荐系统。Transformer的核心是自注意力Self-Attention机制它能让序列中的每个元素都直接与所有其他元素交互从而捕获长距离的、全局的依赖关系并且天生适合并行计算。一些前沿的推荐模型开始尝试用Transformer来替代RNN/GRU处理用户行为序列。比如BERT4Rec它借鉴了BERT的掩码语言模型训练方式通过随机掩码用户历史行为序列中的部分物品让模型去预测这些被掩码的物品以此来进行预训练。这种方式能更充分地利用双向上下文信息来学习用户兴趣表示。在我最近接触的一些工作中我们也开始实验将Transformer结构引入序列推荐。一个直观的感受是对于超长的用户行为序列比如上百甚至上千个历史交互Transformer比RNN系列模型更能有效地捕捉长期兴趣。因为RNN存在梯度消失/爆炸问题信息在长序列中传递会衰减或畸变而自注意力机制没有这个问题。我们可以把用户过去一个月甚至一年的行为序列都输入模型让模型自行判断哪些遥远的过去行为与当前预测是相关的。当然直接把NLP的Transformer搬过来也有挑战。比如推荐场景下的序列其“顺序”的重要性可能不如语言中那么严格和绝对如何设计更好的位置编码是个问题。另外用户行为序列的长度和噪声水平也远高于经过清洗的文本对模型的计算效率和抗噪能力提出了更高要求。目前业界更常见的做法是进行“微创新”比如在DIEN的框架下用Transformer层来增强序列特征的提取能力或者设计轻量级的Transformer变体来平衡效果和性能。从协同过滤的简单相似到深度学习对复杂模式和动态兴趣的捕捉推荐系统模型的演进本质上是我们对“用户-物品”连接关系理解不断深化的过程。这个领域没有银弹最好的模型永远是那个最贴合你具体业务场景和数据特性的模型。在我十年的项目经历里见过太多“纸上效果惊艳上线后拉胯”的案例。模型结构固然重要但特征的质量、数据的 pipeline、线上服务的延迟、AB测试的严谨性这些工程实践中的细节往往才是决定一个推荐系统成败的关键。下次我们可以再聊聊这些炫酷的模型到底是怎么从论文里走出来真正服务亿万用户的那里面踩过的坑和总结的经验又是另一番风景了。