想象你走进一家自助餐厅,厨师端上来的每一道菜都是红烧肉——哪怕红烧肉再好吃,连续吃十盘也会腻得想逃。推荐系统就像这位厨师,如果它只盯着你曾经点赞的“红烧肉”疯狂投喂,你的信息餐桌上就会堆满同一种味道。这就是推荐多样性要解决的问题:在保证你爱吃的同时,让你每次“用餐”都有新鲜感。这套本领靠的是几件“厨具”:MMR算法像一位懂得配菜的品鉴师、DPP算法像一位统筹全局的营养师、类目打散像按冷菜热菜分区的餐台摆放,而探索与利用则像厨师偶尔试做新菜——冒险可能失败,但能让菜单永远充满惊喜。

岗位关联

多样性是推荐系统从“能推荐”走向“会推荐”的分水岭。掌握MMR、DPP、打散与探索利用策略,你将具备解决信息茧房问题的核心技术能力,这也是信息流、短视频、电商场景中资深算法工程师的核心竞争力之一。

目录
  1. 可视化演示
  2. 多样性的内涵
  3. 多样性的价值
  4. MMR算法的核心思想
  5. MMR算法的数学推导
  6. DPP算法与矩阵建模
  7. 类目打散与工程实现
  8. 探索与利用的权衡
  9. 多样性的评估与实战
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示推荐系统中MMR最大边际相关性算法的逐步选择过程,以及多样性优化前后推荐列表的类目分布对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

MMR最大边际相关性选择 每步选择既相关又与已选内容差异最大的物品 调节λ参数平衡相关性与多样性的权重 MMR = λ·Sim(q,d) - (1-λ)·max Sim(d,d\') MMR是推荐多样性最经典的算法。

多样性的内涵

自助餐厅的经历我们都有过:第一盘红烧肉是美味,第二盘是满足,第三盘开始皱眉,到第四盘已经想逃。推荐系统面对的问题完全一样——哪怕模型精准地捕捉到你爱看“美食测评”,如果连续推十条同类的美食测评,你的耐心也会被迅速消耗。这个现象在业界有一个形象的名字:信息茧房,用户被困在自己兴趣的“蚕茧”里,越推越窄,最终失去新鲜感而离开。

这里的核心矛盾是:相关性和多样性是一对天生的对手。相关性说“推用户最喜欢的”,多样性说“推用户可能也会喜欢的其他东西”。一个好的推荐系统,需要像一位聪明的厨师:主菜做用户最爱的红烧肉,但也搭配两道清爽的素菜,让整顿饭有层次感。在技术上,多样性通常从三个维度来衡量:类目维度(美食、汽车、美妆)、主题维度(美食测评、美食教程、美食探店)、风格维度(幽默风、专业风、温馨风)。

回到自助餐厅的比喻:相似度公式就像厨师判断两道菜“有多像”的标尺——食材一样、做法一样、调味一样,那它们就是“撞味”了。理解了这个标尺,我们才能明白后续所有多样性算法都在忙活什么:拉开相似物品的距离,让用户的餐桌上永远有惊喜。

Similarity(i,j)=∑k=1dwk⋅1[ck(i)=ck(j)]∑k=1dwk\displaystyle \text{Similarity}(i, j) = \frac{\sum_{k=1}^{d} w_k \cdot \mathbb{1}[c_k(i) = c_k(j)]}{\sum_{k=1}^{d} w_k}
这条公式是多样性的“体温计”:两个物品在多少个维度上属于同类,加权求和后得到它们的相似度。相似度越高,说明二者越“撞味”,需要被分开。
  1. 维度加权用不同权重衡量类目、主题、风格三个维度对相似度的贡献,因为在实际业务中“是否同属一类”比“风格是否相似”对用户体验的影响更大。
    ∑k=1dwk⋅1[ck(i)=ck(j)]\displaystyle \sum_{k=1}^{d} w_k \cdot \mathbb{1}[c_k(i) = c_k(j)]
  2. 归一化处理将加权结果除以权重总和,得到 0 到 1 之间的相似度分数,方便在不同维度组合之间进行公平比较。
    加权相同维度数权重总和\displaystyle \frac{\text{加权相同维度数}}{\text{权重总和}}

多样性的价值

既然多样性会在某种程度上牺牲“最相关”的推荐,为什么还要做?因为在真实产品中,用户体验是长期博弈。你可以把用户想象成一位老食客:如果他每次来都只有红烧肉,哪怕这道菜是他亲口说最爱的,三个月后他也会厌倦。反过来,如果他每次来都能尝到一道新菜,偶尔踩雷也没关系,但他会愿意持续光顾。这就是多样性的价值——它不追求单次点击率的极致,而追求用户长期留存和总时长的最大化。

从数据上看,多样性带来的收益非常实际:推荐列表中不同类目的覆盖率提升,能让用户每次刷新都有新的探索空间,从而延长单次使用时长;长尾内容(那些小众但优质的内容)也能被更多用户看到,内容生态会更加健康;更重要的是,多样性缓解了“越推越窄”的恶性循环,让推荐系统在用户兴趣迁移时依然能跟上节奏。短视频平台特别看重这一点,因为用户刷视频的速度极快,如果连续几条都是同一主题,手指划过屏幕的速度会快到惊人。

在实际业务中有两个经典的矛盾:一是相关性即时反馈的诱惑,点击率指标最直观,追求它最简单;二是多样性长期价值的隐性,它带来的留存提升要几周甚至几个月才能显现。面试官最喜欢问的一个问题就是:如果上线多样性策略后点击率下降了 5%,你怎么办?正确的思路不是急着回滚,而是先看次留是否提升、人均时长是否增加、用户负反馈是否减少——这些指标往往比点击率更能说明问题。

多样性是递给用户的“搭配菜单”。它牺牲的那一点即时点击,换来的是用户“这个 App 总能给我惊喜”的长期认知。对于 AI 从业者来说,理解这种短期指标与长期价值的权衡,正是从“调参工程师”走向“策略设计师”的关键一步。

MMR算法的核心思想

自助餐厅的厨师如何配菜?他会一盘一盘地往托盘上放菜:先放用户最爱的红烧肉,然后下一道菜,他不仅考虑“用户是否爱吃”,还考虑“这道菜和已经选中的菜是否太像”。如果和红烧肉一样都是浓油赤酱的肉菜,哪怕用户也可能爱吃,厨师也会优先选择一道清爽的拍黄瓜来平衡味蕾。这就是 MMR(Maximal Marginal Relevance,最大边际相关性)算法的本质——它像一个精明的配菜师,每一轮都选择“和用户最相关,但和已选菜最不相似”的那道菜。

MMR 的“边际”二字,指的是每新增一个物品时带来的增量价值。这个增量价值由两部分决定:一是它本身与用户的相关性(正贡献),二是它与已选集合中任意物品的最大相似度(负贡献)。算法像做减法一样,用相关度减去冗余度,得到每个候选物品的“边际得分”,每一轮贪婪地选取得分最高的那个。

回到厨师配菜的比喻:MMR 的每一轮选择都像在问自己——“这道菜用户爱吃吗?”(相关项)“和已选的菜撞不撞味?”(惩罚项)。如果一道菜虽然很相关但和已选菜太像,它的边际得分就会被打折;一道略相关但完全不同的菜反而能胜出。这就是 MMR 强大的地方:不需要复杂的数学,一个简单的贪心策略就同时照顾了相关与多样。

MMR=arg⁡max⁡Di∈R∖S[λ⋅Rel(Di,Q)−(1−λ)⋅max⁡Dj∈SSim(Di,Dj)]\displaystyle \text{MMR} = \arg\max_{D_i \in R \setminus S} \left[ \lambda \cdot \text{Rel}(D_i, Q) - (1 - \lambda) \cdot \max_{D_j \in S} \text{Sim}(D_i, D_j) \right]
这条公式是 MMR 算法的“配菜决策规则”:λ 是配菜师手里的调味勺——调大 λ,他更偏向用户爱吃什么就端什么;调小 λ,他更注重每道菜之间不重样。
  1. 候选范围\( R \setminus S \) 表示从全部召回候选集 \( R \) 中,排除已经选入推荐列表 \( S \) 的物品,即“还没端上桌的菜”。
    R∖S\displaystyle R \setminus S
  2. 相关性加分项\( \lambda \cdot \text{Rel}(D_i, Q) \) 是第 \( i \) 个候选物品与用户查询(或用户兴趣) \( Q \) 的相关度打分,权重 \( \lambda \) 控制这一项的重要性。
    λ⋅Rel(Di,Q)\displaystyle \lambda \cdot \text{Rel}(D_i, Q)
  3. 相似度惩罚项\( (1 - \lambda) \cdot \max_{D_j \in S} \text{Sim}(D_i, D_j) \) 是候选物品与已选集合中最相似物品的相似度,作为一个“撞味惩罚”被减去。
    (1−λ)⋅max⁡Dj∈SSim(Di,Dj)\displaystyle (1 - \lambda) \cdot \max_{D_j \in S} \text{Sim}(D_i, D_j)
  4. 贪心选择每一步从所有候选中选出边际相关性得分最高的那个,加入推荐列表,然后重复这个过程。
    arg⁡max⁡Di∈R∖S[λ⋅Rel(Di,Q)−(1−λ)⋅max⁡Dj∈SSim(Di,Dj)]\displaystyle \arg\max_{D_i \in R \setminus S} \left[ \lambda \cdot \text{Rel}(D_i, Q) - (1 - \lambda) \cdot \max_{D_j \in S} \text{Sim}(D_i, D_j) \right]

MMR算法的数学推导

让我们把 MMR 的运作过程拆得更细一些。假设用户浏览一个视频 App,召回的候选集里有 6 条视频:3 条美食测评、2 条旅行 Vlog、1 条科技评测。第一条推荐自然是相关性得分最高的那条美食测评。关键在第二轮——如果第二相关的又是美食测评,MMR 会计算它与已选那条的相似度,乘以惩罚权重后扣分;而一条相关性稍低的旅行 Vlog,因为没有同类竞争者,反而可能胜出。

这个过程的数学核心在于相似度矩阵的构建。每个物品可以被表示成一个特征向量(类目、关键词、标签等),然后用余弦相似度来计算两个物品的相似度。实践中更常用的是:先算候选集两两之间的相似度矩阵,之后每一轮选择时,只需查表找到候选与已选集合中的最大相似度,计算复杂度可控。随着推荐列表逐渐丰满(已选集合变大),后续物品的惩罚项会越来越强,多样性自然会越来越好——这就是“边际”递减的效果。

理解 MMR 的工程实现还有一个关键细节:λ 参数怎么设。λ 越大,推荐越激进地追求相关性,多样性越弱;λ 越小,列表越“花心”,但可能牺牲用户的基本兴趣满足。实践中通常的做法是在离线数据集上搜索 λ 的空间,或者接 AB 实验根据线上指标动态调整。这就像配菜师根据食客反馈调整“辣度旋钮”——不同用户群体可能需要不同的 λ,有些用户喜欢保守的“全荤宴”,有些用户偏爱均衡的搭配。

Sim(Di,Dj)=v⃗i⋅v⃗j∥v⃗i∥⋅∥v⃗j∥\displaystyle \text{Sim}(D_i, D_j) = \frac{\vec{v}_i \cdot \vec{v}_j}{\|\vec{v}_i\| \cdot \|\vec{v}_j\|}
余弦相似度把每个物品表示成一个向量,计算两个向量夹角的余弦值:夹角越小越相似,取值范围在 -1 到 1 之间。它是 MMR 中“判断两道菜是否撞味”的标准尺子。
  1. 向量化表示每个物品(视频、商品、文章)用一个多维向量 \( \vec{v}_i \) 表示,每一维对应一个特征(如“是否美食类”“是否含'测评'关键词”)。
    v⃗i=(xi1,xi2,…,xid)\displaystyle \vec{v}_i = (x_{i1}, x_{i2}, \ldots, x_{id})
  2. 向量点积两向量对应维度相乘后求和,点积越大说明两个物品在特征空间中的“方向”越一致。
    v⃗i⋅v⃗j\displaystyle \vec{v}_i \cdot \vec{v}_j
  3. 模长归一各自向量长度的乘积把点积压缩到 [-1, 1],消除物品本身“特征数量”带来的偏差——一个特征多的物品不会天然和所有物品都更相似。
    ∥v⃗i∥⋅∥v⃗j∥\displaystyle \|\vec{v}_i\| \cdot \|\vec{v}_j\|

DPP算法与矩阵建模

MMR 是“每轮选最优”的贪心策略,它有个天然的短板:只看得到眼前的步,看不到全局。想象一位聪明的营养师配一周食谱:他不会一道菜一道菜地決定,而是把整周的菜单当做一个整体来规划,确保碳水化合物、蛋白质、维生素的比例全局最优。这就是 DPP(Determinantal Point Process,行列式点过程)算法的视角——它从全局概率的角度衡量一个物品集合的多样性,而不仅仅是两两之间的相似度。

DPP 的数学结构非常优雅:它将候选集合中每个物品映射为内核矩阵 \( L \) 中的一个维度,矩阵的每个元素 \( L_{ij} \) 同时编码了物品 \( i \) 自身的质量(与用户的相关性)和物品 \( i \) 与物品 \( j \) 之间的相似度。对于任意一个子集 \( S \),它被选中的概率正比于矩阵 \( L_S \)(由 \( S \) 中物品对应的行列构成的子矩阵)的行列式。

DPP 听起来比 MMR 复杂不少,但在工程上已经有高效的近似求解方法(如贪心最大后验推断),业界在大规模召回重排场景中已有成熟应用。它比 MMR 更强的点在于:MMR 每次只考虑“和已选集合最大相似度”这一个最坏情况,而 DPP 通过行列式考虑了集合整体的几何体积,能够捕捉到更细腻的多样性信号。回到营养师比喻:MMR 像在配菜时只盯着“别和上一道菜重复”,DPP 则是把整桌菜的营养结构放进眼里——后者在理论上更接近“全局最优”。

P(S)∝det⁡(LS)\displaystyle P(S) \propto \det(L_S)
行列式有一个奇妙的几何意义:它等于以 \( S \) 中物品的向量为边张成的平行多面体的体积。两个向量越相似,体积越小,行列式越接近 0——多样性越差的集合,“被选中”的概率就越低。这让 DPP 天然偏爱“体积大”(即多样性好)的集合。
  1. 核矩阵构建\( L = L_{\text{sim}} \odot L_{\text{rel}} \),其中 \( L_{\text{sim}} \) 是相似度矩阵(两两物品的余弦相似度),\( L_{\text{rel}} \) 是对角矩阵,对角线元素是每个物品的相关性得分。两个矩阵逐元素相乘得到最终的核矩阵。
    L=diag(r1,r2,…,rn)⋅S⋅diag(r1,r2,…,rn)\displaystyle L = \text{diag}(r_1, r_2, \ldots, r_n) \cdot S \cdot \text{diag}(r_1, r_2, \ldots, r_n)
  2. 行列式计算对于物品子集 \( S \),构造子矩阵 \( L_S \),计算其行列式。行列式的值越大,表示集合中物品的多样性越好。
    det⁡(LS)=∏kλk(S)\displaystyle \det(L_S) = \prod_{k} \lambda_k(S)
  3. 概率归一化整个候选集合的所有子集按行列式值归一化,构成一个概率分布。采样时,高行列式的子集(多样且相关)更容易被选中。
    P(S)=det⁡(LS)∑S′⊆Rdet⁡(LS′)\displaystyle P(S) = \frac{\det(L_S)}{\sum_{S' \subseteq R} \det(L_{S'})}

类目打散与工程实现

算法论文再漂亮,落到工程上最常用的手段往往是最简单的:类目打散。它就像自助餐厅的菜品摆放规则——即使你想吃红烧肉,餐台上也不会连续挤满五盘红烧肉,而是每隔一两道菜就换一个类别。类目打散的直觉非常朴素:给推荐结果设定一个“窗口”,在窗口内,相同类目的物品最多只能出现一次。

类目打散有两种实现形态。硬打散:严格保证相邻 N 个位置内不出现重复类目,比如窗口为 3,则任意连续 3 个位置必须来自不同类目。硬打散的优点是约束明确、可解释性好;缺点是不够灵活,如果某个类目下的物品相关度都特别低,硬性规则可能牺牲太多相关性。软打散:不设绝对限制,而是给同类目重复出现的物品一个额外的分数惩罚,惩罚力度随重复次数递增。软打散更平滑,在相关性和多样性之间留出了弹性空间。

在工程实现上,类目打散通常在重排阶段完成:粗排选出的几百个候选,经过精排打分后,再由打散模块做“位置约束”。一个经典的实现是贪心插空法:先按相关性排序,然后从高分到低分逐个放入结果列表,如果放入后会违反复类目约束(比如窗口内已有同类别),则向后挪动到可放位置。这种方法的时间复杂度为 \( O(n \cdot k) \),其中 n 是候选数量、k 是窗口大小,在毫秒级延迟的实时推荐服务中完全可承受。

类目打散也是面试中最容易展开聊的工程题:候选人如果能说出“不仅要打散类目,还要在打散时保留每个类目内部的排序分数”“打散窗口的大小要和业务场景匹配——信息流可以窗口大些,电商搜索结果页窗口要小”这类细节,说明他真正做过线上系统。就像自助餐厅的菜品摆放不仅是为了美观,更是为了引导用户取餐动线——打散规则设计得当,能同时提升点击深度和浏览体验。

探索与利用的权衡

多样性策略做到极致后,推荐系统还会遇到更深一层的问题:用户自己都不知道自己喜欢什么。如果永远只推荐用户已经表现出偏好的内容(这叫作利用),用户永远没有机会发现自己新的兴趣点;但如果频繁推荐没人看过的内容(探索),又会牺牲当下的点击率。这个权衡在学术界叫 Exploration-Exploitation Dilemma(探索与利用困境),在工业界它直接决定了推荐系统的“冒险精神”。

最简单的探索策略叫 Epsilon-Greedy:每次推荐时,以 \( 1 - \epsilon \) 的概率推荐当前模型认为最相关的物品(利用),以 \( \epsilon \) 的概率随机从候选中抽取一个物品(探索)。\(\epsilon\) 通常设在 5%~15% 之间。这个策略简单到令人发指,但效果却出奇地好——因为哪怕完全随机的探索,也能积累到用户对新类目的真实反馈数据,从而让模型学到“用户其实喜欢旅行Vlog”。

更聪明的策略是 UCB(Upper Confidence Bound)算法。它给每个物品维护两个信号:它被推荐过多少次、它的平均点击率。UCB 的公式给每个物品加上一个“信心区间”——被探索得越少的物品,它的真实质量越不确定,信心区间就越宽。每次推荐时,选择“平均得分 + 信心宽度”最大的物品,这样既有希望选到高点击率的物品(利用),又不会放弃那些样本太少、潜力未被发现的物品(探索)。

探索与利用和多样性是什么关系?探索是为多样性提供“弹药库”的:如果没有探索机制,新类目的物品永远不会进入候选池,MMR 和 DPP 再厉害也巧妇难为无米之炊。回到自助餐厅比喻:利用是“只做食客点过的菜”,探索是“偶尔推出主厨创意新品”。新菜不被点就永远不会知道它是否受欢迎,但适度的试错能让整个菜单生态保持活力——这正是推荐系统持续进化的动力源泉。

UCBi=rˉi+c⋅ln⁡nni\displaystyle \text{UCB}_i = \bar{r}_i + c \cdot \sqrt{\frac{\ln n}{n_i}}
这条公式是“探索勇气”的量化:第一项 \( \bar{r}_i \) 是物品目前的平均表现(利用),第二项是“未知奖励”——物品被探索次数 \( n_i \) 越少,这个附加项就越大,帮它在推荐竞赛中获得更多机会。
  1. 平均收益项物品 \( i \) 的历史平均点击率,代表它已知的质量水平,这是“利用”的部分。
    rˉi\displaystyle \bar{r}_i
  2. 探索加成项\( c \) 是探索系数,\( n \) 是总推荐次数,\( n_i \) 是物品 \( i \) 被推荐的次数。物品被推荐得越少,这个值越大,相当于给“冷门物品”额外加分。
    c⋅ln⁡nni\displaystyle c \cdot \sqrt{\frac{\ln n}{n_i}}

多样性的评估与实战

说了这么多算法,最后一个关键问题是:多样性到底怎么度量?怎么知道我们做得好不好?这需要一套完整的评估体系。最常用的多样性指标包括:类目覆盖率(推荐结果中不同类目数占全部类目数的比例)、ILD(Internal List Diversity,列表内部多样性)(推荐结果中所有物品对之间相似度的平均值,越小说明列表越多样)、以及单会话曝光类目数(用户一次刷新看到的类目数量)。

在实战中,多样性的评估必须和业务指标联动。常见的坑是只盯着离线指标优化——离线 ILD 很好看,线上点击率却掉了,因为打分模型本身的相关性信息在打散时被破坏太多。正确的做法是建立多目标评估框架:短期看点击率、转化率,中期看人均浏览时长、次日留存,长期看类目渗透率和用户兴趣宽度。多样性策略的上线应该配合 A/B 实验,至少观察两周以上,因为多样性带来的用户习惯改变需要时间去积累。

最后分享一些落地经验:第一,多样性不是“越散越好”,要找到和业务场景匹配的度——电商购物车的多样性需求和短视频信息流的多样性需求完全不同;第二,多样性策略应该分用户层级——新用户需要更多探索,老用户可以在稳定偏好基础上适当探索;第三,多目标矛盾的解决之道是分层设计:召回层用 DPP 保证候选池本身多样,精排层用加权组合公式直接优化“相关+多样”的混合得分,重排层用类目打散做最后的约束。每一层各司其职,才能让“多样性”这盘大菜既有层次又有章法。

这就像一家成熟的餐厅的经营之道:菜单设计(打散)保证每餐荤素搭配,主厨推荐(MMR/DPP)既照顾食客口味又定期翻新,而“今日特价新菜”(探索)则给老食客持续的惊喜感。推荐系统的多样性优化,本质上就是在“照顾好用户的口味”和“不断拓展用户的味蕾边界”之间,找到那条最优雅的平衡线。

AI 应用场景

短视频信息流重排

阶段:重排阶段(Ranking & Re-ranking)

解决的问题:抖音、快手等短视频平台,精排模型按用户兴趣打分后,容易出现连续 3~5 条都是同一主题(如全是美食)的“撞车”现象,用户手指一划就走。传统做法是纯相关度排序,没有全局多样性约束,用户越刷越窄,最终厌烦流失。

具体案例:某短视频平台发现“喜欢看美食的用户”在刷到第 4 条美食视频后,划走概率是前 3 条的 2.3 倍。引入 MMR 重排后,系统在保持精排分数不变的前提下,对候选集中与已选集合高相似度的物品做惩罚,强制美食与其他类别(如旅行、萌宠)穿插出现。上线两周后,人均会话时长提升 9%,美食类目的长尾内容播放量提升 15%。

电商推荐“看了又看”场景

阶段:召回层 + 重排层(Recall & Re-ranking)

解决的问题:电商详情页的“看了又看”栏目,如果用户刚看完一台手机,系统连续推 5 台不同品牌的手机,用户只会觉得“广告轰炸”。而如果推出手机壳、蓝牙耳机、充电宝等互补类目商品,用户的点击意愿会明显提升。这需要从召回层的 DPP 做起,让候选池本身就横跨多个相关类目。

具体案例:某电商平台将 DPP 应用到“看了又看”召回中,核矩阵融合了“与当前商品的类目距离”特征——同品类距离为 1,跨品类距离按类目层级加权。模型选中的集合天然包含手机、配件、服务等多个维度。AB 实验显示,推荐位的整体点击率提升 6.8%,跨品类点击占比从 12% 提升到 23%,平台 GMV 增长 3.2%。

Python 演示

python79 行
import numpy as np  class MMRRecommender:    """MMR (Maximal Marginal Relevance) 推荐多样性打散"""     def __init__(self, lam=0.7, top_k=5):        self.lam = lam        self.top_k = top_k        self.sim_matrix = None        self.relevance = None     def fit(self, features, relevance):        """        features: (n_items, n_dims) 物品特征向量        relevance: (n_items,) 物品与用户的相关性得分        """        # 计算余弦相似度矩阵        norm = np.linalg.norm(features, axis=1, keepdims=True)        self.sim_matrix = (features @ features.T) / (norm @ norm.T + 1e-9)        self.relevance = relevance        return self     def predict(self):        """执行MMR贪心选择,返回推荐列表的物品索引"""        n = len(self.relevance)        selected = []        candidates = set(range(n))         while len(selected) < self.top_k and candidates:            best_score = -np.inf            best_item = None            for i in candidates:                # 与已选集合的最大相似度(冗余惩罚项)                if selected:                    max_sim = max(self.sim_matrix[i, j] for j in selected)                else:                    max_sim = 0.0                # MMR核心公式:相关度 - 冗余惩罚                score = self.lam * self.relevance[i] - (1 - self.lam) * max_sim                if score > best_score:                    best_score = score                    best_item = i            if best_item is not None:                selected.append(best_item)                candidates.remove(best_item)        return selected  if __name__ == "__main__":    # 模拟6个物品:4个美食类、1个旅行类、1个科技类    rng = np.random.RandomState(42)    n_items = 6    features = np.array([        [1, 1, 0, 0, 0],   # 美食1        [1, 1, 0, 0, 0],   # 美食2(与美食1极相似)        [1, 0, 1, 0, 0],   # 美食3(稍不同的美食)        [0, 1, 1, 0, 0],   # 美食4        [0, 0, 0, 1, 0],   # 旅行        [0, 0, 0, 0, 1],   # 科技    ], dtype=float)    # 相关性: 美食 >> 旅行 > 科技    relevance = np.array([0.95, 0.92, 0.90, 0.88, 0.70, 0.65])     model = MMRRecommender(lam=0.7, top_k=4)    model.fit(features, relevance)    result = model.predict()    print("MMR推荐顺序:", result)    print("对应物品: ", [        ["美食1", "美食2", "美食3", "美食4", "旅行", "科技"][i] for i in result    ])     # 对比:纯相关性排序    pure_rel = np.argsort(-relevance)[:4]    print("纯相关排序:", pure_rel)    print("对应物品: ", [        ["美食1", "美食2", "美食3", "美食4", "旅行", "科技"][i] for i in pure_rel    ])    print("\n观察:MMR把旅行和科技插入了连续美食之间")
目的:实现 MMR 算法的完整流程,用 6 个模拟物品演示“纯相关排序”和“MMR 重排”的区别——当高相关的连续美食被 MMR 打散后,推荐列表出现了类目穿插。
  1. features @ features.T向量点积矩阵,一次性算出所有物品两两之间的相似度分子
  2. norm @ norm.T + 1e-9将点积归一化为余弦相似度,加小常数防止除零
  3. max(self.sim_matrix[i, j] for j in selected)核心惩罚项:候选物品与已选集合中所有物品的最大相似度
  4. score = self.lam * self.relevance[i] - (1 - self.lam) * max_simMMR 核心公式,一行代码实现“相关减冗余”
  5. candidates.remove(best_item)每轮选中后从候选中移除,保证不重复推荐

常见误区

误区多样性不就是让推荐结果里每个类目出现的次数尽量平均吗?比如推荐 10 条,5 个类目,每类各 2 条就对了。

正解多样性不是机械的“数量平均”,而是“在相关性约束下的最优分散”。如果用户明确只爱美食,硬把美食类目从 8 条砍到 2 条去凑旅行和科技,那是本末倒置。MMR 和 DPP 的核心精神是“根据相似度弹性调整”——同类物品相关性显著更高时可以多出现,但不要连续扎堆。就像配菜时,主菜可以分量大,但旁边必须点缀别的东西。

为何常见:因为“平均分配”是最直观的公平直觉,而且容易用 SQL 实现,所以很多初学者第一反应就是按类目做均匀配额。但这种静态规则忽略了相关性分数这个关键维度。

误区λ 控制相关性权重,那 λ 设成 0.9 甚至 1.0,推荐结果肯定最准,点击率最高。

正解λ=1.0 时惩罚项完全失效,MMR 退化为纯相关性排序,多样性为零。λ 的作用是“在相关性和多样性之间选边站”,而不是越大越好。实践经验是 λ 通常在 0.5~0.8 之间,具体要看业务场景:电商购物车场景 λ 可以高(用户目标明确),信息流场景 λ 应该低(用户边刷边逛,需要新鲜感)。

为何常见:人们天然认为“相关性”是推荐的第一目标,所以倾向于把它的权重调大。但推荐系统是长期交互,忽略了多样性的相关性优化会让用户很快感到疲劳。

误区DPP 要算行列式,复杂度太高,只能做离线实验,线上毫秒级延迟下根本跑不动。

正解虽然精确的 DPP 采样复杂度是指数级的,但工程上有成熟的高效近似:贪心 MLE 推断(每次选入使行列式增量最大的物品)和基于 Cholesky 分解的增量更新算法,都能把时间复杂度降到 \( O(k^2 n) \) 级别(k 是输出数量,n 是候选数),在几百个候选的重排场景下延迟完全可以控制在 10ms 以内。

为何常见:教材里只讲 DPP 的理论定义(带行列式的概率分布),不展开工程近似技巧。初学者看到行列式就望而生畏,误以为它只能活在论文里。

误区多样性是好事,那窗口设大一点,比如要求 10 个位置内类目不重复,多样性肯定更强。

正解窗口过大会导致两个问题:一是严重的相关性损失,因为有些用户就是“就想看美食”,强制 10 条不重复类目会让相关度排第 30 的科技视频挤进前三;二是推荐列表变得“为了多样而多样”,反而降低用户体验。实践中短视频信息流的窗口一般是 3~6,电商搜索页则更小而谨慎。窗口要配合 λ 一起调,找到平台收益曲线的拐点。

为何常见:多样性在感知上是“越多越好”,但忽略了推荐系统的第一性原则——满足用户当下的需求。一切多样性手段都是“戴着镣铐跳舞”,镣铐不能太松也不能太紧。

误区探索嘛,就是随机扔一些乱七八糟的内容给用户看,总有用户会点的。

正解纯随机探索是非常低效的——你在给用户推 100 条他可能完全无感的内容,换来 1 次点击。好的探索策略(UCB、Thompson Sampling)有明确的目标:以最小的试错成本学习用户兴趣。它们通过信心区间量化不确定性,优先探索“平均得分高但样本少”的物品,这比随机探索的收敛速度快一个量级。

为何常见:“探索”这个词的日常语义就是“随便试试”,但学术界对它的定义是“在不确定性中做信息增益最大化的选择”。半路出家的人容易把工程上的粗放实现等价成学术上的严谨定义。

面试题

Q1. MMR 算法中参数 \(\lambda\) 的作用是什么?

单选题 · mid

  • A.控制推荐列表的总长度
  • B.控制相关性与多样性的权衡权重← 正确答案
  • C.控制相似度计算的特征维度数
  • D.控制候选集合的召回数量

答案:B。MMR 公式中 \(\lambda\) 是相关性项的权重,\(1-\lambda\) 是冗余惩罚项的权重。\(\lambda\) 越大越注重相关性,越小越注重多样性。它不控制列表长度、特征维度或召回数量,所以 B 正确。

Q2. “信息茧房”现象的核心成因是什么?

单选题 · easy

  • A.用户只看不点,行为数据太少
  • B.推荐系统只优化短期相关性← 正确答案
  • C.服务器存储空间不足
  • D.模型训练数据标注错误

答案:B。信息茧房是推荐系统过度追求点击率,不断推荐与用户已知偏好相似的内容,用户接触的类目越来越少,最终困在单一兴趣圈层中。根本原因是只优化即时相关性的短视策略,不是数据量或存储问题。

Q3. DPP 算法通过什么数学工具来度量一个集合的多样性?

单选题 · mid

  • A.集合内元素相似度的平均值
  • B.集合内元素数量的平方
  • C.核矩阵子矩阵的行列式← 正确答案
  • D.集合内元素相关性的总和

答案:C。DPP 将物品集合映射为核矩阵,集合被选中的概率正比于对应子矩阵的行列式。行列式的几何意义是向量张成的体积,体积越大说明向量越不相似、集合越多样。MMR 才用平均相似度,DPP 用的是行列式。

Q4. 类目打散中“硬打散”的含义是?

单选题 · easy

  • A.推荐列表必须全部来自同一类目
  • B.同一类目在窗口内最多出现一次← 正确答案
  • C.不同类目的物品必须等量出现
  • D.高分物品优先,不限制类目

答案:B。硬打散是硬性约束:在设定的窗口大小内(如相邻 3 个位置),不允许出现重复类目。它和“等量出现”不同,窗口之外的类目分布不受限制,所以 B 正确。

Q5. 评估推荐系统多样性时,哪些指标属于常用多样性度量?

多选题 · mid

  • A.类目覆盖率← 正确答案
  • B.列表内部多样性(ILD)← 正确答案
  • C.单一物品点击概率
  • D.单会话曝光类目数← 正确答案

答案:ABD。类目覆盖率(A)衡量推荐结果覆盖类目的广度;ILD(B)衡量列表内部物品对的平均相似度;单会话曝光类目数(D)直接反映用户感知的丰富度。单一物品点击概率(C)是相关性指标,不是多样性指标。

Q6. UCB 算法中“探索加成项” \(c \cdot \sqrt{\frac{\ln n}{n_i}}\) 的作用是什么?

单选题 · mid

  • A.让高点击率的物品获得更多曝光
  • B.给被探索次数少的冷门物品加额外分数← 正确答案
  • C.消除推荐系统的计算延迟误差
  • D.降低服务器在高并发下的压力

答案:B。\(n_i\) 是物品被探索的次数,它越小则探索加成项越大,冷门物品因此获得更多被推荐的机会。这体现了“不确定性越高越值得探索”的核心思想,防止系统永远被少数热门物品垄断。

Q7. 关于软打散与硬打散,下列说法正确的有?

多选题 · hard

  • A.硬打散严格限制窗口内类目不重复← 正确答案
  • B.软打散对重复类目做分数惩罚而非禁止← 正确答案
  • C.软打散会完全忽略相关性分数
  • D.硬打散比软打散更灵活

答案:AB。硬打散是绝对约束,A 正确。软打散通过给同类别重复项加分惩罚来弹性控制,B 正确。软打散并没有忽略相关性,而是在相关性基础上计算惩罚后的分数,C 错误。硬打散因为规则绝对,灵活性其实低于软打散,D 错误。

Q8. 某视频 App 上线 MMR 重排后,点击率下降了 4%,但次留提升了 2%,你该如何判断?

单选题 · hard

  • A.点击率下降,说明策略失败,立即回滚
  • B.次留提升说明用户长期体验变好,建议继续观察← 正确答案
  • C.两个指标矛盾,说明实验数据有误
  • D.点击率下降不影响营收,无需关注

答案:B。多样性的代价是即时点击率下降,但换来的是用户更愿意下次再来(次留提升)。两者并不矛盾,而是短期收益和长期价值的换位。正确做法是继续观察 A/B 实验,同时监控人均时长、负反馈率等指标,综合判断策略是否值得保留。

Q9. 推荐系统部署“探索与利用”策略的主要目的是?

单选题 · easy

  • A.降低推荐服务的整体延迟
  • B.在利用已知偏好的同时发现新兴趣← 正确答案
  • C.减少推荐结果的存储空间
  • D.提高推荐模型训练速度

答案:B。利用(Exploitation)是推荐用户已表现出的兴趣,保证当下体验;探索(Exploration)是推荐未被验证的物品,为未来收集反馈。两者平衡的目的是既稳住当下的相关性,又为长期发现用户新偏好留出空间。

Q10. 关于 MMR 与 DPP 的对比,下列说法正确的是?

多选题 · hard

  • A.MMR 是贪心逐步选优,DPP 是全局概率建模← 正确答案
  • B.DPP 的行列式天然惩罚相似物品的集合← 正确答案
  • C.MMR 比 DPP 在任何场景下效果都更好
  • D.两者的最终目标都是平衡相关性与多样性← 正确答案

答案:ABD。MMR 每次贪心选择当前边际得分最大的物品,是局部最优策略;DPP 通过行列式的概率分布对集合整体建模,能捕捉更全局的多样性信号,A、B 正确。两者各有适用场景,MMR 简单高效、DPP 效果更优但计算更复杂,不能一概而论说谁更好,C 错误。它们的目标都是让推荐结果既相关又多样,D 正确。