Beam Search是一种启发式的序列搜索算法,专门用于解决自回归模型的序列生成问题。它通过在每一步保留最有希望的”k”个候选路径,在生成质量和计算效率之间取得了极佳的平衡,是机器翻译、摘要生成和早期大模型中最常用的解码算法。

这个名字来源于一个非常形象的物理比喻:

  • Beam(光束/束):算法在搜索过程中,每一步都只保留固定数量(k个)的最优候选序列。这些候选序列就像一束束平行的”光束”一样,沿着各自的路径向前延伸。
  • Search(搜索):算法的本质是在所有可能的序列空间中,搜索概率最高的那个序列。

直观理解:想象你在黑暗的森林里找出口,手里拿着k个手电筒。每个手电筒照亮一条可能的路径。每走一步,你都会关掉那些看起来通向死胡同的手电筒,只保留最有希望的k个继续照亮前方。这就是Beam Search的工作方式。

自回归模型生成序列时,面临一个根本性的难题:

词汇表大小通常是几万甚至几十万,生成一个长度为n的序列,可能的组合数是词汇表大小的n次方。这是一个天文数字,不可能穷举所有可能的序列来找到全局最优解。

2.1 贪心搜索的缺陷

最简单的解码方法是贪心搜索(Greedy Search):每一步只选择概率最高的那个token。但它有一个致命的缺点:局部最优不等于全局最优

经典反例: 假设我们要生成句子”我正在吃饭”,模型的概率分布如下:

  • 第一步:“我”(0.6) > “正在”(0.3) > “吃饭”(0.1)
  • 如果选”我”,第二步:“正在”(0.4) > “吃饭”(0.3) > “是”(0.2)
  • 如果选”正在”,第二步:“吃饭”(0.9) > …

贪心搜索会选择”我正在…”,总概率0.6×0.4=0.24 但全局最优解是”正在吃饭”,总概率0.3×0.9=0.27

贪心搜索因为第一步选择了局部最优的”我”,错过了全局最优的序列。

2.2 Beam Search的解决方案

Beam Search的核心思想是:不把所有鸡蛋放在一个篮子里

  • 每一步,对当前所有的k个候选序列,分别生成所有可能的下一个token
  • 计算所有扩展后序列的总概率
  • 只保留总概率最高的k个序列,进入下一步
  • 重复这个过程,直到所有序列都生成结束符或达到最大长度

三、Beam Search完整工作流程(带实例)

我们用一个简单的机器翻译例子来演示Beam Search的完整过程,设置束宽k=2

任务:将英文”I love you”翻译成中文 词汇表:我、爱、你、他、喜欢、(结束符)

步骤1:初始化

  • 初始状态:只有一个空序列"",概率1.0
  • 候选列表:[("", 1.0)]

步骤2:第一步生成

对空序列生成第一个token的概率:

  • “我”: 0.7
  • “他”: 0.2
  • “爱”: 0.08
  • 其他: 0.02

扩展后得到所有可能的序列:

  • (“我”, 0.7), (“他”, 0.2), (“爱”, 0.08), …

保留前2个最优:

  • 候选列表:[(“我”, 0.7), (“他”, 0.2)]

步骤3:第二步生成

对每个候选序列分别生成下一个token:

  1. 对”我”生成:

    • “爱”: 0.8
    • “喜欢”: 0.15
    • 其他: 0.05
    • 扩展序列:(“我爱”, 0.7×0.8=0.56), (“我喜欢”, 0.7×0.15=0.105)
  2. 对”他”生成:

    • “爱”: 0.7
    • “喜欢”: 0.2
    • 其他: 0.1
    • 扩展序列:(“他爱”, 0.2×0.7=0.14), (“他喜欢”, 0.2×0.2=0.04)

现在有4个扩展序列,按总概率排序:

  1. “我爱”: 0.56
  2. “他爱”: 0.14
  3. “我喜欢”: 0.105
  4. “他喜欢”: 0.04

保留前2个最优:

  • 候选列表:[(“我爱”, 0.56), (“他爱”, 0.14)]

步骤4:第三步生成

对每个候选序列分别生成下一个token:

  1. 对”我爱”生成:

    • “你”: 0.9
    • “他”: 0.08
    • : 0.02
    • 扩展序列:(“我爱你”, 0.56×0.9=0.504), (“我爱他”, 0.56×0.08=0.0448), (“我爱”, 0.56×0.02=0.0112)
  2. 对”他爱”生成:

    • “你”: 0.8
    • “我”: 0.15
    • : 0.05
    • 扩展序列:(“他爱你”, 0.14×0.8=0.112), (“他爱我”, 0.14×0.15=0.021), (“他爱”, 0.14×0.05=0.007)

现在有6个扩展序列,按总概率排序:

  1. “我爱你”: 0.504
  2. “他爱你”: 0.112
  3. “我爱他”: 0.0448
  4. “他爱我”: 0.021
  5. “我爱”: 0.0112
  6. “他爱”: 0.007

保留前2个最优:

  • 候选列表:[(“我爱你”, 0.504), (“他爱你”, 0.112)]

步骤5:第四步生成

对每个候选序列生成下一个token,此时模型大概率会输出结束符:

  • “我爱你”: 0.504×0.95=0.4788
  • “他爱你”: 0.112×0.9=0.1008

步骤6:最终选择

所有候选序列都已生成结束符,选择概率最高的那个:

  • 最终输出:“我爱你”

四、关键参数:束宽(Beam Size)

束宽k是Beam Search最重要的超参数,它直接决定了算法的性能和质量:

束宽k优点缺点适用场景
1计算最快,内存最小就是贪心搜索,质量最差对速度要求极高的场景
4-10质量显著提升,计算量适中有少量重复和短句子倾向大多数生成任务的默认值
20-50质量接近最优,重复减少计算量和内存消耗大幅增加机器翻译、摘要生成等高质量需求
词汇表大小理论上能找到全局最优计算量爆炸,完全不可行仅存在于理论中

经验法则:在大模型中,束宽通常设置为4-8。超过10之后,质量提升会非常缓慢,但计算量会线性增加。

五、Beam Search的优缺点

5.1 优点

  1. 质量远高于贪心搜索:通过保留多个候选,有效避免了局部最优问题
  2. 效率远高于穷举搜索:将指数级复杂度降低到线性复杂度O(k×n)
  3. 生成结果确定性强:相同输入会得到相同输出,适合需要可复现性的场景
  4. 生成文本连贯性好:倾向于生成语法正确、逻辑通顺的句子

5.2 缺点

  1. 存在重复生成问题:容易出现”我我我”或”非常非常非常”这样的重复内容
  2. 倾向于生成短句子:长句子的概率会因为多次乘法而变得很小
  3. 缺乏多样性:总是生成最可能的结果,缺乏创造性和惊喜感
  4. 计算量仍然较大:比采样方法慢k倍,不适合实时交互场景

六、与其他解码方法的对比

解码方法核心思想生成质量多样性速度确定性代表应用
贪心搜索每步选概率最高的token最快早期简单模型
Beam Search每步保留k个最优候选机器翻译、T5、BART
温度采样按概率分布随机采样ChatGPT、GPT-4、LLaMA
Top-k采样只在概率最高的k个token中采样大多数现代大模型
Top-p(核)采样只在累积概率达到p的token中采样所有主流聊天机器人

七、常见误区澄清

  1. 误区:Beam Search是大模型的标准解码方法

    • 纠正:现在几乎所有的聊天机器人(ChatGPT、Claude、文心一言等)默认使用的都是Top-p采样,因为它生成的文本更自然、更有多样性。Beam Search主要用于需要高质量确定性输出的场景。
  2. 误区:束宽越大,生成质量一定越好

    • 纠正:当束宽超过一定阈值(通常是10)后,质量提升会非常有限,甚至可能因为引入过多低概率候选而导致质量下降。
  3. 误区:Beam Search能找到全局最优解

    • 纠正:Beam Search是一种启发式算法,它只能找到近似最优解。如果全局最优解在某一步没有进入前k个候选,就会被永远丢弃。

八、总结

Beam Search之所以被称为”束搜索”,是因为它像一束束平行的光束一样,在序列空间中探索最有希望的路径。它通过在每一步保留固定数量的候选,在生成质量和计算效率之间取得了完美的平衡,成为了自然语言处理领域最经典的解码算法之一。

虽然现在采样方法在大模型中占据了主导地位,但Beam Search在机器翻译、代码生成、摘要生成等需要高质量确定性输出的场景中,仍然发挥着不可替代的作用。

需要我为你生成一份纯Python实现的Beam Search代码,可以直接用于自回归文本生成吗?