在自然语言生成任务(如机器翻译、文本摘要、图像描述等)中,语言模型通常会输出一个词的概率分布 ,但模型本身并不会自动告诉你应该选哪些词作为最终的输出句子。因此,我们需要设计一个"解码策略"来从这些概率中生成可读文本。
一、什么是序列解码?
以语言模型为例,给定起始 token,比如 "<BOS>"
,模型会预测下一个词的概率分布:
P(w1 | <BOS>) = {"I": 0.5, "You": 0.3, "He": 0.2}
然后在预测了 "I" 之后,模型继续生成:
P(w2 | <BOS>, "I") = {"am": 0.6, "was": 0.4}
我们希望最终得到完整的句子如:
"I am happy"
二、贪婪解码(Greedy Decoding)
1. 原理
贪婪解码策略非常简单粗暴:每一步只选当前概率最大的词,不考虑未来潜在更优路径。
sequence = [<BOS>]
while not sequence ends:
next_token = argmax(P(token | sequence))
sequence.append(next_token)
3. 举例说明
假设某语言模型输出如下(简化为概率):
-
Step 1: {"I": 0.5, "You": 0.3, "He": 0.2} → 选 "I"
-
Step 2: {"am": 0.6, "was": 0.4} → 选 "am"
-
Step 3: {"happy": 0.4, "sad": 0.3, "angry": 0.3} → 选 "happy"
最终生成:"I am happy"
三、波束搜索(Beam Search)
1. 原理
波束搜索是一种启发式搜索策略 ,在每一步保留概率最高的前 k 个序列候选(称为 beam),然后依次扩展,直到生成 <EOS>
或达到最大长度。
相比贪婪解码,它可以更好地探索多条路径,从而获得更优结果。
beam = [("<BOS>", score=1.0)]
for step in range(max_len):
new_beam = []
for seq, score in beam:
next_tokens = top_k(P(token | seq), k)
for token, prob in next_tokens:
new_seq = seq + token
new_score = score * prob
new_beam.append((new_seq, new_score))
beam = top_k(new_beam, k)
3. 举例说明(beam size = 2)
假设第一步:
-
"I": 0.5
-
"You": 0.3
保留 "I" 和 "You"
第二步扩展:
-
"I" → {"am": 0.6, "was": 0.4}
-
"You" → {"are": 0.7, "were": 0.3}
组合成候选路径:
-
"I am": 0.5 × 0.6 = 0.30
-
"I was": 0.5 × 0.4 = 0.20
-
"You are": 0.3 × 0.7 = 0.21
-
"You were": 0.3 × 0.3 = 0.09
取 top 2:保留 "I am" 和 "You are" 继续扩展。