春节前夜,李家大院的厨房里,总指挥老李盯着菜单发愁。家人七嘴八舌:“红烧肉!”“糖醋鱼!”“……还有红烧肉!”如果照单全收,年夜饭就成了八道红烧肉的饕餮盛宴——第一道真香,第二道还行,第三道就腻了。老李明白,点菜不能只问“哪道菜最受欢迎”,还要看整桌菜搭配起来有没有荤素冷热、酸甜咸辣的均衡。这跟 AI 模型生成答案时遇到的问题是同一个:模型很会挑“最相关”的候选,但如果只按相关度排序,选出来的答案往往全是同一句话的八种说法,毫无新意。于是,工程师们发明了一系列“多样性探索”技术:MMR 像老李一样一道一道地斟酌,既看菜合不合口味,又看它跟已点的菜重不重复;DPP 则直接把整桌菜的“丰富程度”量化成一个数字,一锤定音;再往深走,还要在“好吃”和“丰富”之间做权衡,甚至把它变成多目标优化问题,让几桌客人同时满意。这篇文章,就从老李的年夜饭开始,把这一整套思路讲给你听。

岗位关联

掌握多样性探索方法,是 AI 从业者从“会调模型”走向“会设计决策机制”的关键一步——无论是 RAG 重排、推理链采样还是推荐系统,它都是直接提升结果质量上限的硬技能。

目录
  1. 可视化演示
  2. 多样性问题背景:为什么“全点爱吃的”会翻车
  3. MMR 原理精讲:主厨的“看菜下单”策略
  4. MMR 参数调优:给主厨的旋钮定刻度
  5. 行列式点过程:整体评估一桌菜
  6. DPP 算法推导:菜单是怎么一步步搭出来的
  7. 多样性准确性权衡:好吃与丰富不可兼得?
  8. 多目标优化:让几桌客人都满意
  9. 工程实践应用:年夜饭总指挥的落地手册
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示MMR最大边缘相关性贪心重排、DPP行列式点过程核矩阵选择、多样性-准确性帕累托权衡曲线及多目标加权求和优化策略,含粒子场背景动画。

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

多样性问题背景:为什么“全点爱吃的”会翻车

年夜饭点菜,主厨问家里人想吃什么,大家齐声说“红烧肉”。于是主厨一口气点了八道红烧肉。结果呢?第一道真香,第二道还行,第三道就腻了——再爱吃的东西,一桌全是它也会让人崩溃。点菜不是“每道都最好吃”就完事,还得让一桌菜荤素冷热甜咸搭配开。这个朴素的道理,恰恰是 AI 系统里“多样性探索”的起点:好答案不能只是“各自优秀”,还得彼此不同、互相补充。

推理模型也有同样的毛病。当我们让大模型生成多个候选答案时,如果只按“模型认为最靠谱”去挑,选出来的往往是一个意思换了几种说法——就像八道菜其实都是红烧肉。这种“候选趋同”现象在 beam search、自回归解码里非常普遍。比如让模型回答“如何缓解城市拥堵”,十个候选可能都在说“修地铁”三个字的变体。多样性(diversity)的价值在于:候选之间彼此差异足够大,才能覆盖不同的解题思路,供后续的投票、排序或人工挑选使用。

要量化“多样性”,最直接的做法是计算候选集合两两之间的平均距离。距离可以用嵌入向量的余弦距离、文本编辑距离等来定义:

一桌好菜靠荤素搭配,一组好候选也靠彼此拉开距离。有了“多样性”这把尺子,我们才能接着讲主厨是怎么一道道点菜的——这就是下一节的 MMR。

D(S)=2k(k−1)∑1≤i<j≤kd(si,sj)D(S) = \displaystyle\frac{2}{k(k-1)}\sum_{1\le i<j\le k} d(s_i, s_j)
这个公式把“这一桌菜有多丰富”浓缩成一个数字:把所有菜品两两之间的差异取平均,平均值越大,说明候选之间越“不重样”。
  1. 两两配对遍历枚举候选集合 $S$ 中所有不同的组合 $(i,j)$,一一计算它们之间的距离 $d(s_i, s_j)$。
    {(si,sj)∣1≤i<j≤k}\{(s_i, s_j) \mid 1 \le i < j \le k\}
  2. 距离函数选择$d(\cdot,\cdot)$ 是相似度度量,可以是余弦距离、编辑距离或语义嵌入距离,衡量两个候选的差异程度。
    d(si,sj)=1−cos⁡(si,sj)d(s_i, s_j) = 1 - \cos(s_i, s_j)
  3. 平均化处理组合总数是 $C_k^2 = \frac{k(k-1)}{2}$,乘以其倒数即可得到平均距离。
    2k(k−1)⋅∑i<jdij\frac{2}{k(k-1)} \cdot \sum_{i<j} d_{ij}

MMR 原理精讲:主厨的“看菜下单”策略

老李学乖了。这回想吃红烧肉,但他不会连点八份。新策略是:先点一道招牌红烧肉解馋,之后每点一道新菜,都要看一眼它跟已经点好的菜像不像——如果跟桌上某道菜口味太接近,这道菜就要扣分;如果它既是大家想吃的、又跟桌上已有的菜都不太一样,那就果断加进菜单。

这个“既看相关性、又看重复度”的策略,就是 MMR(Maximum Marginal Relevance,最大边缘相关性)的精髓。MMR 不是一次性评估整桌菜,而是一道一道地挑(贪心策略),每挑一道都做一次权衡。在 AI 里,它常被用来重排(rerank)检索结果或生成候选:输入一堆初始结果,输出一个“彼此相关又互相拉开距离”的有序列表,因此它也被称为“多样性重排”。

数学上,MMR 每次从未选集合里挑得分最高的候选。得分由两部分构成:跟查询的相关度,减去跟已选集最大相似度的惩罚:

λ 就是主厨的性格旋钮:偏大,主厨在意“大家想吃”;偏小,主厨在意“别重样”。到了下一节,我们聊聊这个旋钮到底怎么调。

MMR=arg⁡max⁡Di∈R∖S[λ⋅sim1(Di,Q)−(1−λ)⋅max⁡Dj∈Ssim2(Di,Dj)]\text{MMR} = \displaystyle\arg\max_{D_i \in R\setminus S}\left[\lambda\cdot\text{sim}_1(D_i,Q) - (1-\lambda)\cdot\max_{D_j\in S}\text{sim}_2(D_i,D_j)\right]
这个公式就是老李的决策心理:候选菜 $D_i$ 的吸引力 = λ × “这道菜多合家人口味” − (1−λ) × “它跟桌上最像的那道菜有多像”。λ 越接近 1,老李越任性;λ 越接近 0,他越怕重样。
  1. 候选遍历域$R\setminus S$ 表示“还没被选中”的候选集合,也就是还没进菜单的候选菜,它们每一个都有机会被选。
    Di∈R∖SD_i \in R\setminus S
  2. 相关性得分$\lambda\cdot\text{sim}_1(D_i,Q)$ 衡量 $D_i$ 与查询需求 $Q$ 的匹配程度,越相关得分越高。
    λ⋅sim1(Di,Q)\lambda\cdot\text{sim}_1(D_i,Q)
  3. 最大相似惩罚$\max_{D_j\in S}\text{sim}_2(D_i,D_j)$ 在已选集 $S$ 中找与 $D_i$ 最相似的一个,越相似惩罚越大。
    (1−λ)⋅max⁡Dj∈Ssim2(Di,Dj)(1-\lambda)\cdot\max_{D_j\in S}\text{sim}_2(D_i,D_j)
  4. 权重系数$\lambda \in [0,1]$ 是平衡旋钮,$\lambda$ 大则更重视相关性,$\lambda$ 小则更重视多样性。
    λ+(1−λ)=1\lambda + (1-\lambda) = 1

MMR 参数调优:给主厨的旋钮定刻度

每家的口味不一样:有的家庭就爱“硬菜多来几道”,有的家庭就爱“琳琅满目不重样”。老李手里的 λ 旋钮,也要根据这桌客人来调。调 λ 没有放之四海而皆准的值——新闻推荐里恨不得每篇都不重样,代码生成里却要确保每条候选都正确,λ 的取值可能天差地别。

当 λ=1 时,MMR 只看相关性,退化成普通排序——菜全是“最爱吃的”,但一桌子重样;当 λ=0 时,完全不看相关,专门挑跟已选差别大的——结果可能上一堆没人爱吃的猎奇菜。实际中 λ 落在中间,具体值靠实验确定。工程上最常用的调参方法是网格搜索(grid search):在 $[0,1]$ 上按 0.1 步长枚举 λ,用验证集评估每个 λ 下的结果质量。评估时至少要盯两个维度:相关性指标(如 NDCG)和多样性指标。

评估多样性的常用指标是 Distinct-n,它统计生成结果里去重后的 n-gram 占全部 n-gram 的比例:

就像年夜饭总指挥拿一桌菜的照片让家人试尝打分,AI 工程师也在用“相关性+多样性”双重标准给不同 λ 打分。下一节,我们把思路从“一道一道点菜”升级成“整体评估一桌菜”——这就是行列式点过程。

Distinct-n=∣Gn∣∑g∈Gncount(g)\text{Distinct-}n = \displaystyle\frac{|\mathcal{G}_n|}{\sum_{g \in \mathcal{G}_n} \text{count}(g)}
把生成的所有句子切成一个个长度为 n 的“词语片段”(n-gram),数一数一共有多少种片段,除以片段出现的总次数。比例越高,说明句子之间的说法越不重复。
  1. 定义 n-gram 集合$\mathcal{G}_n$ 是所有结果中长度为 $n$ 的连续片段去重后的集合,例如 “我爱吃” 切出 “我爱”、“爱吃” 两个 2-gram。
    Gn={g∣g 是长度为 n 的连续片段}\mathcal{G}_n = \{g \mid g \text{ 是长度为 } n \text{ 的连续片段}\}
  2. 分子为种类数$|\mathcal{G}_n|$ 表示去重后 n-gram 的种类数量,种类越多说明表达越丰富。
    ∣Gn∣|\mathcal{G}_n|
  3. 分母为出现总数$\sum_{g \in \mathcal{G}_n}\text{count}(g)$ 把所有片段出现次数加总,得到片段总个数。
    ∑g∈Gncount(g)\sum_{g \in \mathcal{G}_n}\text{count}(g)
  4. 取比值两者相除得到“不重复比例”,值越高多样性越好,常用于对话与生成任务的效果评估。
    ∣Gn∣总 n-gram 个数\frac{|\mathcal{G}_n|}{\text{总 n-gram 个数}}

行列式点过程:整体评估一桌菜

MMR 像主厨一道菜一道菜地斟酌,效率不错但目光略短——只看新菜与“已点菜”的关系,没回头看整桌搭配是否全局最优。有没有一种方法,直接给“整桌菜的配置”打个分,一锤定音?

行列式点过程(Determinantal Point Process,DPP)就是这么一种“全局评估”的概率模型。它用一个方阵——核矩阵 $L$——把所有候选之间的关系都编码进去,然后对任何一个候选子集 $Y$,用行列式(determinant)给它打分。名字里的“点过程”来自统计学,你可以把它理解成“给每个可能出现的子集分配一个概率”的机制。

核矩阵 $L$ 的对角线 $L_{ii}$ 是候选 $i$ 的单点质量(相当于这道菜本身有多受欢迎);非对角线 $L_{ij}$ 是候选 $i$ 和 $j$ 的相似度(相当于这两道菜口味有多接近)。子集 $Y$ 的概率正比于 $L_Y$ 的行列式:

行列式的几何意义是“体积”:候选越相似,体积越小,行列式越小,被选中的概率越低——这就像一桌全是红烧肉,“搭配体积”几乎为零。DPP 天生只爱“体积大”的菜单。

P(Y)=det⁡(LY)det⁡(L+I)P(Y) = \displaystyle\frac{\det(L_Y)}{\det(L + I)}
其中 $L_Y$ 是核矩阵 $L$ 只保留 $Y$ 中对应行与列后的小矩阵。行列式越大,这个子集被选中的概率越高。相似度太高的候选会让行列式骤减,所以 DPP 天生偏爱“搭配丰富”的集合。
  1. 核矩阵构造$L$ 是对称正定矩阵,对角线 $L_{ii}$ 衡量候选 $i$ 的单独质量,非对角线 $L_{ij}$ 衡量 $i,j$ 之间的相似度。
    L=(L11L12L21L22)L = \begin{pmatrix} L_{11} & L_{12} \\ L_{21} & L_{22} \end{pmatrix}
  2. 子矩阵提取$L_Y$ 是 $L$ 只保留 $Y$ 中对应行与列后的小矩阵,例如 $Y=\{1,3\}$ 就取第 1、3 行与第 1、3 列。
    LY=L[{i}i∈Y,{j}j∈Y]L_Y = L[\{i\}_{i \in Y}, \{j\}_{j \in Y}]
  3. 行列式核心$\det(L_Y)$ 蕴含“体积”含义,等价于候选特征向量张成的平行多面体体积的平方,候选越相似体积越小。
    det⁡(LY)=Vol2({Bi}i∈Y)\det(L_Y) = \text{Vol}^2(\{B_i\}_{i\in Y})
  4. 归一化分母$\det(L+I)$ 对所有可能子集做归一化,使所有子集概率总和为 1,$I$ 是单位矩阵。
    ∑Y⊆Vdet⁡(LY)=det⁡(L+I)\sum_{Y \subseteq V} \det(L_Y) = \det(L + I)

DPP 算法推导:菜单是怎么一步步搭出来的

有了“整桌打分”的标准,老李怎么一步一步把菜单配出来?他可以在已有 $Y$ 道菜的基础上,挨个试没加过的菜,看哪道能让整桌“体积”增长最多。这就是 DPP 的贪心构建思路。

在概率上,“加一道菜能让整体搭配变好多少”恰好对应条件概率。DPP 的条件概率公式刻画了这个“增长比”:

主厨按“最大体积增量”逐道加入,这就是 DPP 的贪心近似算法(也称贪心 MAP 推断)。虽然不保证全局最优,但复杂度从指数级降到 $O(nk^2)$ 级别,是工程里最常用的 DPP 求解方式。

P(i∣Y)=det⁡(LY∪{i})det⁡(LY)P(i \mid Y) = \displaystyle\frac{\det(L_{Y\cup\{i\}})}{\det(L_Y)}
这个比值告诉你:在已有菜单 $Y$ 的基础上,再加菜 $i$,整桌菜的“搭配体积”被放大了多少倍。增长越猛,说明 $i$ 带来的新信息越多,越值得被加入。
  1. 条件概率含义$P(i|Y)$ 表示在已选集合 $Y$ 的基础上再加入候选 $i$ 的(未归一化)概率,刻画“边际贡献”。
    P(i∣Y)=det⁡(LY∪{i})det⁡(LY)P(i \mid Y) = \frac{\det(L_{Y\cup\{i\}})}{\det(L_Y)}
  2. 行列式增量比分子分母之比等于加入 $i$ 后体积的放大倍数,比值越大,说明新菜带来的新鲜感越强。
    det⁡(LY∪{i})det⁡(LY)\frac{\det(L_{Y\cup\{i\}})}{\det(L_Y)}
  3. 特征映射直觉若 $L = B B^T$(特征映射分解),则 $\det(L_Y)$ 是特征向量 $B_i$ 张成体积的平方,新增向量与已有向量越正交,体积增长越大。
    Lij=Bi⋅Bj,det⁡(LY)=Vol2({Bi}i∈Y)L_{ij} = B_i \cdot B_j,\qquad \det(L_Y) = \text{Vol}^2(\{B_i\}_{i \in Y})

多样性准确性权衡:好吃与丰富不可兼得?

老李发现自己面对一个哲学难题。为了让人人满意,他多点了红烧肉——桌上重复了;为了不重复,他点了许多猎奇菜——大家又下不去筷子。要了多样性,合口味(准确性)就少了;要了准确性,多样性又崩了。这个“多样性-准确性权衡”是排序与生成任务绕不过去的坎。

在数学上,这个矛盾可以建模成一个带预算的约束优化问题:在“候选两两相似度总和不超过 $\tau$”的约束下,最大化总相关度:

参数 $\tau$ 就像年夜饭的预算线:预算紧,主厨只能少点重复的硬菜,多上花样;预算松,主厨就能任性点大家最爱的。权衡不是谁吞掉谁,而是找一个家庭的最优解——这个最优解,在多目标优化的语言里有一个专门的名字:Pareto 前沿。

max⁡S⊆R ∑i∈Swiris.t.∑i<j∈Ssij≤τ\max_{S\subseteq R}\ \displaystyle\sum_{i\in S} w_i r_i \quad \text{s.t.}\quad \sum_{i<j\in S} s_{ij} \le \tau
这里 $S$ 是最终选出的候选集,$r_i$ 是候选 $i$ 的相关度,$s_{ij}$ 是 $i$、$j$ 之间的相似度。$\tau$ 像一道“多样性预算”:预算越小,越强制不重样,总相关度就可能被牺牲。
  1. 目标函数最大化选中集合 $S$ 的总相关度加权和,$w_i$ 是候选 $i$ 的权重,$r_i$ 是其相关度分数。
    ∑i∈Swiri\sum_{i\in S} w_i r_i
  2. 多样性预算约束所有选中项两两相似度之和不能超过阈值 $\tau$,$\tau$ 越小对多样性要求越苛刻。
    ∑i<j∈Ssij≤τ\sum_{i<j\in S} s_{ij} \le \tau
  3. 权衡本质$\tau$ 收紧时可行域变小,目标函数的上限随之下降,形成“此消彼长”的权衡关系。
    OPT(τ)≤OPT(τ′)(τ<τ′)\text{OPT}(\tau) \le \text{OPT}(\tau') \quad (\tau < \tau')

多目标优化:让几桌客人都满意

有些家族年夜饭要开两桌:一桌长辈,要清淡软烂;一桌年轻人,要麻辣刺激。老李不能只优化一个目标,得让两桌都满意。AI 里的多目标优化也是这个道理:候选集的相关度、多样性、甚至生成成本,都是要同时优化的目标,不存在一个方案让所有目标同时达到最优。

于是有了 Pareto 最优(帕累托最优)的概念:一个方案如果在某个目标上变好的同时,必然会在另一个目标上变差,那它就是 Pareto 最优。所有 Pareto 最优方案的集合构成 Pareto 前沿(帕累托前沿),它像一条“不坏方案”的曲线,工程上我们只需在曲线上挑一个点。最常用的求解手段是加权组合法:给每个目标配上权重,压成单目标优化:

老李反复试不同的权重组合(8:2、6:4、5:5……),每一组权重都对应一桌不同的菜式,这些菜式都是 Pareto 点——不完美,但都不“差”。这就是多目标优化的日常。

min⁡S∈F ∑m=1Mwmfm(S),∑m=1Mwm=1, wm≥0\min_{S \in \mathcal{F}}\ \displaystyle\sum_{m=1}^{M} w_m f_m(S), \quad \sum_{m=1}^{M} w_m = 1,\ w_m \ge 0
权重 $w_m$ 代表对各目标的侧重:$w_1$ 大就更在意相关度,$w_2$ 大就更在意多样性。反复试不同的权重组合,就能扫出一条 Pareto 前沿,最后从中挑一个最符合业务口味的解。
  1. 多目标向量$f_1(S), \dots, f_M(S)$ 是多个目标函数,例如相关度、多样性、延迟或成本,它们往往互相冲突。
    f(S)=(f1(S),f2(S),…,fM(S))f(S) = (f_1(S), f_2(S), \ldots, f_M(S))
  2. 权重归一化每个目标配上权重 $w_m$,所有权重之和为 1,表示决策者对各目标的侧重程度。
    ∑m=1Mwm=1,wm≥0\sum_{m=1}^{M} w_m = 1,\quad w_m \ge 0
  3. 单目标化求解加权后变成普通单目标优化,可用现成优化器求解;不同权重组会得到不同 Pareto 点。
    S⋆(w)=arg⁡min⁡S∈F∑m=1Mwmfm(S)S^\star(w) = \arg\min_{S \in \mathcal{F}} \sum_{m=1}^M w_m f_m(S)

工程实践应用:年夜饭总指挥的落地手册

老李把以上策略整理成一套完整的流程:先按大家的喜好初步圈定一批硬菜,再用“看重复度”的办法压缩菜单,最后用“整桌体积”的标准敲定最终菜式。这套流程在真实系统里,就是“两阶段重排管线”。

以检索增强生成(RAG)为例:系统先召回大量相关文档(可能几十篇),直接全塞给大模型既费 token 又会把答案带偏。于是先用 MMR 做压缩重排,得到前 $k$ 个既相关又不重叠的文档;再用 DPP 在 $k$ 个里挑出 $m$ 个“整体搭配体积最大”的文档,送进大模型做最终回答:

在 LLM 解码里,同样可以用 DPP 对多个采样候选做子集选择,把“一组高多样性的回答”交给 rerank 或投票模块。o1/o3 这类推理模型在内部生成多条推理链时,也常借助相似的多样性机制,避免所有推理链困在同一条死路上。好菜单不是一步到位的,而是“初筛→重排→精选”的组合拳。

Sk=MMR(R,Q,λ,k),Y∗=arg⁡max⁡Y⊆Sk, ∣Y∣=mdet⁡(LY)S_k = \text{MMR}(R, Q, \lambda, k), \qquad Y^* = \displaystyle\arg\max_{Y \subseteq S_k,\ |Y|=m} \det(L_Y)
第一阶段 MMR 负责“保相关性 + 初步去重”,第二阶段 DPP 负责“全局多样性 + 上限定音”。两个模型各司其职,既控制了计算量,又拿到了接近全局最优的候选子集。
  1. 第一阶段粗选用 MMR 从全部候选 $R$ 中按“相关性−相似惩罚”筛选出 $k$ 个候选,记为 $S_k$,控制进入后续阶段的规模。
    Sk=MMR(R,Q,λ,k)S_k = \text{MMR}(R, Q, \lambda, k)
  2. 第二阶段精选在 $S_k$ 上构建核矩阵 $L$,用 DPP 选择使行列式最大的 $m$ 个子集 $Y^*$,保证子集整体的多样性和质量。
    Y∗=arg⁡max⁡Y⊆Sk, ∣Y∣=mdet⁡(LY)Y^* = \arg\max_{Y \subseteq S_k,\ |Y|=m} \det(L_Y)
  3. 规模控制先压缩候选规模再上 DPP,避免在大候选集上做行列式计算的高昂开销,兼顾效果与效率。
    ∣R∣≫k≥m|R| \gg k \ge m

AI 应用场景

RAG 检索结果多样性重排

阶段:检索后处理 / 重排

解决的问题:传统向量检索按相似度取 top-k,返回的前几条文档往往高度雷同,覆盖不了问题的多个侧面。用 MMR 重排后,在保留相关度的同时拉开文档间距离,让大模型看到不同角度的证据。

具体案例:企业知识库问答中,用户问“如何降低服务器能耗”,向量检索返回的十条文档可能都指向同一种节能策略。用 $\lambda=0.7$ 的 MMR 重排后,结果同时覆盖散热优化、负载调度、硬件选型等不同方案,大模型综合回答后准确率明显提升。

LLM 推理候选多样性采样

阶段:解码采样 / 后处理

解决的问题:大模型在 temperature 采样下多次生成的结果仍可能趋同,浪费算力。用 DPP 从候选集合中选取“体积最大”的子集,保证推理链覆盖多种解法且质量不塌方。

具体案例:数学推理任务中,让模型生成 16 条解题路径,用 DPP 挑出 4 条代表性路径:有的是代数法、有的是几何法、有的是枚举验证法,再交给投票器做多数表决,最终准确率比直接取前 4 条相关度最高的路径提升约 12%。

推荐系统多目标排序

阶段:排序 / 重排

解决的问题:推荐系统要在“点击率”和“品类多样性”之间平衡。单目标 CTR 排序容易让用户陷入信息茧房,多目标加权与 Pareto 求解可以兼顾平台指标与用户体验。

具体案例:短视频推荐中,将相关度、多样性、停留时长作为三个目标,用加权组合法遍历多组权重,在线上 A/B 测试中找到 Pareto 前沿上“CTR 不掉、推荐流品类更丰富”的一组参数,最终人均浏览时长提升 8%,长尾内容曝光量翻倍。

Python 演示

python66 行
import numpy as np  def mmr_select(relevance, similarity, lam=0.7, k=4):    """MMR: 最大边缘相关性重排。    relevance[i] 是候选 i 的相关度,    similarity 是候选间相似度矩阵。    每次从未选集合中挑 相关 - 相似惩罚 最大的候选。    """    n = len(relevance)    picked = []    remain = set(range(n))    for _ in range(k):        best_i, best_score = -1, -1e9        for i in remain:            if picked:                max_sim = max(similarity[i][j] for j in picked)                score = lam * relevance[i] - (1 - lam) * max_sim            else:                score = relevance[i]            if score > best_score:                best_score, best_i = score, i        picked.append(best_i)        remain.remove(best_i)    return picked  def dpp_select(relevance, similarity, k, lam=0.7):    """DPP 贪心选择: 每次挑行列式增量比最大的候选。"""    n = len(relevance)    L = np.zeros((n, n))    for i in range(n):        for j in range(n):            L[i, j] = relevance[i] if i == j else lam * similarity[i][j]    picked = []    remain = set(range(n))    while len(picked) < k:        best_i, best_ratio = -1, -1e9        for i in remain:            sub = list(picked) + [i]            if not picked:                ratio = L[i, i]            else:                ratio = np.linalg.det(L[np.ix_(sub, sub)]) / np.linalg.det(                    L[np.ix_(picked, picked)])            if ratio > best_ratio:                best_ratio, best_i = ratio, i        picked.append(best_i)        remain.remove(best_i)    return picked  if __name__ == "__main__":    rel = [0.90, 0.82, 0.75, 0.88, 0.70, 0.65, 0.78, 0.60]    sim = np.array([        [1.0, 0.45, 0.10, 0.00, 0.00, 0.00, 0.00, 0.00],        [0.45, 1.0, 0.40, 0.00, 0.10, 0.10, 0.00, 0.00],        [0.10, 0.40, 1.0, 0.50, 0.00, 0.00, 0.20, 0.00],        [0.00, 0.00, 0.50, 1.0, 0.20, 0.10, 0.00, 0.10],        [0.00, 0.10, 0.00, 0.20, 1.0, 0.60, 0.10, 0.30],        [0.00, 0.10, 0.00, 0.10, 0.60, 1.0, 0.20, 0.40],        [0.00, 0.00, 0.20, 0.00, 0.10, 0.20, 1.0, 0.55],        [0.00, 0.00, 0.00, 0.10, 0.30, 0.40, 0.55, 1.0],    ])    print("MMR 选中的菜:", mmr_select(rel, sim, 0.7, 4))    print("DPP 选中的菜:", dpp_select(rel, sim, 4, 0.7))
目的:用 8 个候选(相关度 + 相似度矩阵)分别演示 MMR 逐项重排和 DPP 行列式贪心选择,两个算法的结果差异直观体现了“逐项贪心”与“整体评估”的不同。
  1. score = lam * relevance[i] - (1 - lam) * max_simMMR 核心决策:相关度减去与已选集合的最大相似度惩罚,这就是“最大边缘相关性”的数学落点。
  2. for i in remain:遍历还没有被选中的候选,保证每轮只从“未入菜单”的菜里挑。
  3. ratio = np.linalg.det(...) / np.linalg.det(...)DPP 的行列式增量比,刻画加一道菜对子集“体积”的放大倍数。
  4. L[i, j] = relevance[i] if i == j else lam * similarity[i][j]构造核矩阵:对角线上是单点质量,非对角线是相似度,整个 DPP 都建立在这个矩阵上。

常见误区

误区λ 是 MMR 里最重要的参数,那我把它调大一点,相关性更高,结果肯定更好,多样性也丢不了多少。

正解当 λ=1 时,MMR 完全退化为普通的相关性排序,多样性保护彻底失效。λ 调大的直接后果是候选之间越来越像,最终都在重复同一个意思。正确做法是用 Distinct-n 和自 BLEU 等指标监测多样性,在相关性不跌的前提下把 λ 压到尽量小,而不是一味调大。

为何常见:相关性可以直接用 NDCG 等指标感知到涨跌,而多样性的损失往往要到“结果无法区分”时才暴露,所以容易被忽略。

误区DPP 是一种概率采样方法,那不就是从候选里随机抽几个嘛,抽到啥算啥,跟抓阄差不多。

正解DPP 虽然用了“概率”,但它偏向采样“体积大”的子集——行列式大的集合概率高,相似重复的集合概率被压制到接近零。它不是均匀随机,而是“多样性加权”的随机。实际工程里更常取最大后验(MAP),即选行列式最大的子集,连随机性都不要。

为何常见:理解概率模型时,最容易把“随机”和不加区分的均匀采样划等号,而 DPP 的核心恰恰是质量与多样性的权衡。

误区既然多样性这么重要,那我让生成结果之间完全不重叠、一个比一个新奇,是不是最好?

正解把多样性无限拉高,会牺牲准确性。全选冷门怪答案,相关度崩盘,就像年夜饭全是猎奇菜没人下得去筷子。多样性-准确性本身就是此消彼长的权衡,目标不是无脑最大化多样性指标,而是在给定多样性预算下保相关性,或找 Pareto 前沿上的最优解。

为何常见:很多文章强调“重复率高是病”,让读者产生“多样性越高越健康”的错觉,忘了它只是目标之一。

误区MMR 和 DPP 都是做多样性的,随便用一个不就行了,两个效果差不多。

正解MMR 是顺序贪心重排,强调“排序视角”,每次只看候选与已选集合的最大相似度;DPP 是集合整体评估,强调“全局视角”,用行列式同时考虑集合内所有两两关系。排序任务适合 MMR(输出有先后),子集选择任务适合 DPP(输出无顺序、强调整体多样)。工程上常把两者串成两阶段管线,而不是替代关系。

为何常见:两者目标相似,被大量文章混着讲,初学者容易忽略“逐项贪心”和“整体最优”在建模视角上的本质差异。

面试题

Q1. MMR 重排中,λ=1 时算法退化为哪种行为?

单选题 · easy

  • A.只看相关性,退化为普通排序← 正确答案
  • B.只看多样性,忽略查询相关
  • C.相关与多样各占一半权重
  • D.完全不考虑评分随机挑选

答案:A。λ 是 MMR 里相关性与多样性的权重旋钮。λ=1 时惩罚项 (1−λ)·max sim 消失,MMR 只看相关性得分,退化为按相关度排序的普通排序器,故选 A。

Q2. MMR 中“最大边缘相关性”的核心计算环节是?

单选题 · easy

  • A.候选与已选集的最大相似度← 正确答案
  • B.已选集内部的平均距离
  • C.候选与查询的编辑距离
  • D.候选集合的整体方差大小

答案:A。MMR 得分的惩罚项是 max_{D_j∈S} sim(D_i, D_j),即在已选集合里找与当前候选最相似的一条,用这条“最大相似度”来扣分。“最大边缘”指的就是这个最大相似度的边。

Q3. DPP 核矩阵 L 的对角线元素 L_ii 衡量的是?

单选题 · mid

  • A.候选 i 与 j 的相似程度
  • B.候选 i 单点质量或相关度← 正确答案
  • C.整个候选集的整体规模
  • D.候选 i 在排序中的位置

答案:B。核矩阵的对角线 L_ii 是候选 i 自身的质量分数(相关度),非对角线 L_ij 才是两两相似度。这就像菜单上每道菜自己的受欢迎程度写在菜名旁边,两两之间的口味接近程度写在搭配表里。

Q4. 当核矩阵 L 的子矩阵行列式 det(L_Y) 变小时,说明?

单选题 · mid

  • A.集合 Y 内元素彼此越发相似← 正确答案
  • B.集合 Y 内元素变得更丰富
  • C.集合 Y 的元素数量在增加
  • D.核矩阵 L 的秩显著升高

答案:A。行列式对应特征向量张成的体积平方。集合内元素越相似,特征向量越“挤”在一起,体积越小,行列式越小。所以 det 变小说明集合内候选彼此越发相似,多样性在下降。

Q5. MMR 与 DPP 算法最本质的区别在于?

单选题 · mid

  • A.MMR 用于生成,DPP 用于检索
  • B.MMR 逐项挑选,DPP 评估整体子集← 正确答案
  • C.MMR 需要标注数据,DPP 不需要
  • D.MMR 是深度模型,DPP 是传统方法

答案:B。MMR 是贪心逐项重排,每一步只看当前候选与已选集合的关系;DPP 用行列式直接对整体子集打分,同时考虑集合内所有两两关系。一个是“一道一道点菜”,一个是“整桌拍照评分”,建模视角完全不同。

Q6. DPP 贪心选择中,det(L_Y∪{i})/det(L_Y) 的比值反映?

单选题 · hard

  • A.候选 i 与查询 Q 的相关度
  • B.加入 i 对子集体积的放大程度← 正确答案
  • C.已选集合 Y 的平均相似度
  • D.核矩阵 L 的逆矩阵大小

答案:B。这个比值是条件概率 P(i|Y) 的未归一化形式,刻画加入候选 i 后行列式(体积)的放大倍数。比值越大,说明 i 带来的新方向越多,越值得加入。这就像试菜时看新菜能不能给整桌带来“新花样”。

Q7. 下列哪些操作能有效提升生成结果的多样性?

多选题 · mid

  • A.调大 MMR 中的 λ 参数值
  • B.调小 MMR 中的 λ 参数值← 正确答案
  • C.使用 DPP 对候选集重采样← 正确答案
  • D.统一使用贪心束搜索解码

答案:BC。λ 调小会降低相关性权重、提高多样性权重,所以 B 对;DPP 通过行列式天然偏好“体积大”的多样子集,C 对。λ 调大和贪心束搜索都会让结果更趋同,排除 A、D。

Q8. Distinct-n 指标分子统计的是?

单选题 · easy

  • A.去重后 n-gram 的种类数量← 正确答案
  • B.所有 n-gram 的出现总次数
  • C.文本中句子数量的多少
  • D.n-gram 平均长度的值

答案:A。Distinct-n 的分子是 |G_n|,即去重后 n-gram 的种类数;分母是总出现次数。种类越多、占比越高,说明生成文本的说法越丰富,多样性越好。

Q9. 多样性-准确性权衡问题中,合理的建模或求解手段有?

多选题 · hard

  • A.设置相似度总和约束进行优化← 正确答案
  • B.多目标加权组合后求 Pareto 解← 正确答案
  • C.只保留相关性最高的少数结果
  • D.随机打乱候选顺序再取前 k 个

答案:AB。约束优化(A)把多样性写成相似度预算,在预算内最大化相关度;多目标加权(B)把相关度和多样性作为两个目标求 Pareto 解,都是标准做法。C 会完全丢掉多样性,D 是随机噪声,没有任何权衡保证。

Q10. 关于“MMR 粗选 + DPP 精选”两阶段重排管线,说法正确的有?

多选题 · hard

  • A.先用 MMR 粗选控制候选规模← 正确答案
  • B.再用 DPP 在子集上做整体精选← 正确答案
  • C.两阶段都直接用暴力穷举即可
  • D.管线可兼顾顺序相关性与整体多样← 正确答案

答案:ABD。两阶段管线先用 MMR 压缩规模以控制计算量(A 对),再用 DPP 挑行列式最大的子集(B 对),同时拿到顺序上的相关性(MMR 是排序的)和集合层面的整体多样性(DPP 是全局的)(D 对)。C 的暴力穷举在候选规模大时是指数级的,不现实。