2、大模型文本生成基础:自回归生成原理、与tokenization、温度与top-p采样、beam search与贪心解码

大模型(如、LLaMA、Qwen等)的文本生成能力,本质上是一个逐字逐词“接龙”的过程。理解这一过程的核心机制,是掌握推理优化、参数调优和工程部署的前提。本章将围绕四个关键主题展开:自回归生成原理、与tokenization、温度与top-p采样、beam search与贪心解码。我们将从数学直觉、工程实现到实战调参,系统梳理大模型文本生成的底层逻辑。

大模型文本生成核心链路 输入文本 "今天天气" Tokenization BPE / SentencePiece → [今天, 天气] 自回归 Transformer P(x_t | x_<t) 逐预测概率分布 KV Cache 加速 解码策略 贪心 / Beam Top-p / 温度 生成文本 "很好,适合出行" 新拼接到输入,继续预测 关键参数与影响 温度 (Temperature) T→0: 确定性增强 T→高: 随机性增强 Top-p (核采样) p=0.9: 保留累计概率90% 动态截断低概率 Beam Search beam_size=4: 保留4条路径 全局最优近似,计算量大 实际部署中需根据任务类型(对话/翻译/代码生成)动态调整解码参数

2.1 自回归生成原理:逐的条件概率分解

核心原理

自回归(Autoregressive)生成是大模型文本生成的基础范式。其数学本质是将一个长度为 \(N\) 的文本序列 \(X = (x_1, x_2, ..., x_N)\) 的联合概率 \(P(X)\) 分解为条件概率的连乘形式:

P(X) = P(x_1) × P(x_2|x_1) × P(x_3|x_1,x_2) × ... × P(x_N|x_1,...,x_{N-1})

在实际推理时,模型每一步只预测下一个的概率分布,然后从分布中采样或选择最优,将其拼接到已有序列末尾,再作为下一步的输入。这种“吃一个、吐一个”的循环过程,就是自回归生成。

工程实现上,Transformer解码器利用因果注意力掩码(Causal Mask)确保每个位置只能看到它之前的。同时,为了加速推理,业界普遍使用KV Cache技术:将已计算过的Key和Value向量缓存起来,避免每一步都重新计算整个序列的注意力,从而将时间复杂度从 \(O(n^2)\) 降为 \(O(n)\)(仅计算新的注意力)。

判断方法与操作流程

  • 判断是否自回归:若模型生成下一个时依赖所有已生成的,且生成过程是串行的,则为自回归模型。BERT等双向编码器模型不属于自回归生成模型。
  • 推理流程:初始化prompt → 模型前向计算得到logits → 对logits应用解码策略(如贪心/采样) → 得到新 → 更新KV Cache → 重复直到遇到EOS(结束符)或达到max_new_tokens。

案例拆解:KV Cache的显存优化

假设输入序列长度为1024,模型隐藏层维度为4096,层数为32,batch size为1。若不使用KV Cache,每一步生成都需要重新计算所有的Key/Value矩阵,计算量约为 \(1024^2 \times 4096 \times 32\) 次乘加运算。使用KV Cache后,仅需计算新的Key/Value,计算量降为 \(1024 \times 4096 \times 32\),推理速度提升约1000倍。但代价是显存占用增加:KV Cache需要存储 \(2 \times 32 \times 4096 \times 1024 \times 2\) 字节(FP16),约512MB。因此,长序列生成时需关注显存上限,必要时使用KV Cache量化或分块KV Cache。

经验提醒:在部署推理服务时,建议将KV Cache的显存占用纳入容量规划。例如,使用vLLM或TensorRT-LLM框架时,可通过--max-model-len参数控制最大序列长度,从而预估KV Cache峰值显存。

常见误区

  • 误区1:认为自回归生成可以并行输出多个。实际上,自回归本质是串行的,但可以通过投机采样(Speculative Decoding)用一个小模型先草拟多个,再由大模型验证,实现“伪并行”加速。
  • 误区2:忽略EOS(End of Sequence)的处理。若未正确设置停止条件,模型可能无限生成或提前截断。生产环境应同时设置max_new_tokens和EOS判断。

2.2 与Tokenization:文本的原子单位

核心原理

是模型处理文本的最小单位。Tokenization(分词)是将原始字符串转换为 ID序列的过程。主流方法包括BPE(Byte Pair Encoding)、WordPiece和SentencePiece。BPE的核心思想是:从字符级开始,逐步合并出现频率最高的相邻字符对,直到达到预设的词表大小(如32K、64K或100K)。

例如,使用的BPE分词器:

输入: "I love NLP"
输出: ["I", " love", " NLP"]  →   IDs: [40, 3682, 12102]

注意,BPE通常会在英文单词前保留空格(如" love"),以便解码时恢复原始文本。中文分词则常以字或子词为单位,例如"今天天气很好"可能被切分为["今天", "天气", "很好"]。

判断方法与操作流程

分词方法 特点 适用场景
BPE 基于字节对合并,词表大小可控,能处理未登录词 系列、LLaMA、Qwen
WordPiece 基于概率似然增加合并,类似BPE但合并标准不同 BERT、DistilBERT
SentencePiece 将空格视为普通字符,支持直接处理原始文本 ALBERT、T5、LLaMA(部分)

操作流程:加载分词器 → 调用tokenizer.encode(text) → 得到 IDs → 模型前向计算 → 调用tokenizer.decode(ids)恢复文本。注意,decode时需设置skip_special_tokens=True以过滤[PAD]、[CLS]等特殊。

案例拆解:中文分词的特殊性

中文没有天然的空格分隔符,因此分词策略直接影响模型效果。以Qwen2.5为例,其分词器基于SentencePiece,词表大小为151K,其中包含大量中文字符和常用词。实测对比:

  • 输入"人工智能正在改变世界",Qwen分词结果:["人工智能", "正在", "改变", "世界"],共4个。
  • 若按单字切分,则为["人","工","智","能","正","在","改","变","世","界"],共10个。

显然,基于词的分词能减少数量,降低计算量,但可能遇到未登录词(如"")。此时BPE会将其拆分为["Chat", ""]或["Chat", "G", "PT"],确保任何词都能被表示。

风险提示:在微调或推理时,务必保持训练和推理阶段的分词器版本一致。若升级分词器导致 ID映射变化,模型输出将完全错乱。建议在模型仓库中固化分词器版本,并做回归测试。

常见误区

  • 误区1:认为数量等于字符数。实际上,英文中一个单词可能被拆成多个(如"unbelievable" → ["un", "belie", "vable"]),中文中一个词也可能被拆成多个字。
  • 误区2:忽略数量对成本的影响。API计费通常按数计算,优化prompt长度(如精简指令、去除冗余)能显著降低成本。

2.3 温度与Top-p采样:控制生成随机性

核心原理

模型输出的logits(未归一化的分数)通过Softmax转换为概率分布。温度(Temperature)和Top-p是两种常用的采样策略,用于调节生成文本的多样性与确定性。

  • 温度(T):对logits除以T后再做Softmax。当T→0时,概率分布趋于尖锐(近似one-hot),模型倾向于选择最高概率;当T→∞时,分布趋于均匀,生成更随机。典型取值范围:0.1~2.0。
  • Top-p(核采样):将按概率降序排列,累计概率达到p时截断,只从保留的集合中采样。例如p=0.9,表示只从累计概率90%的中采样,丢弃尾部低概率。这能动态过滤“长尾”噪声。

两者可组合使用:先应用温度缩放,再进行Top-p截断。实际中,的默认使用temperature=0.7,top_p=1.0(即不截断)。

判断方法与操作流程

# 伪代码示例
def sample_with_temperature_and_top_p(logits, temperature=0.7, top_p=0.9):
    # 1. 温度缩放
    scaled_logits = logits / temperature
    # 2. Softmax得到概率
    probs = softmax(scaled_logits)
    # 3. Top-p截断
    sorted_probs, sorted_indices = torch.sort(probs, descending=True)
    cumsum_probs = torch.cumsum(sorted_probs, dim=-1)
    # 找到累计概率超过p的位置
    mask = cumsum_probs - sorted_probs > top_p
    sorted_probs[mask] = 0.0
    # 4. 重新归一化并采样
    sorted_probs = sorted_probs / sorted_probs.sum()
    sampled_index = torch.multinomial(sorted_probs, 1)
    return sorted_indices[sampled_index]

案例拆解:对话生成中的参数调优

假设我们在构建一个客服机器人,需要生成准确、稳定的回答。初始配置temperature=1.0,top_p=0.95,发现回答经常出现“嗯…”“可能吧”等含糊词汇,且偶尔产生事实错误。分析后调整:

  • 将temperature降至0.3,回答变得确定,但略显机械。
  • 将temperature设为0.6,top_p设为0.85,回答既流畅又保持一定多样性。

进一步对比:

参数组合 生成效果 适用场景
T=0.1, p=1.0 几乎总是选择最高概率,回答固定 代码生成、数学推理
T=0.7, p=0.9 平衡多样性和准确性 通用对话、内容创作
T=1.2, p=0.95 高随机性,可能产生创意但易跑题 头脑风暴、故事生成
实战建议:在A/B测试中,不要只调单一参数。建议使用网格搜索(如T∈{0.3,0.6,0.9},p∈{0.8,0.9,1.0}),结合人工评估或BLEU/ROUGE指标选择最优组合。对于需要稳定输出的场景(如API接口),优先降低温度。

常见误区

  • 误区1:认为temperature=0就是贪心解码。实际上,T=0时Softmax退化为argmax,但数值计算中可能出现除零或NaN,建议使用do_sample=False直接走贪心路径。
  • 误区2:忽略top_p与temperature的相互作用。当T很高时,概率分布变平,top_p截断可能保留过多,导致生成质量下降。建议先固定一个参数,再调另一个。

2.4 Beam Search与贪心解码:搜索最优序列

核心原理

贪心解码(Greedy Decoding)每一步选择概率最高的,计算效率高,但容易陷入局部最优。Beam Search则维护一个大小为beam_size的候选序列集合,每一步从所有候选的扩展中保留概率乘积最高的beam_size个序列,最终选择整体概率最高的序列。

例如,beam_size=2时:

Step 1: 候选 ["我"(0.6), "你"(0.4)]
Step 2: 扩展 "我" → ["我爱"(0.7), "我恨"(0.3)]; "你" → ["你好"(0.8), "你坏"(0.2)]
保留概率乘积最高的2条: ["你好"(0.4×0.8=0.32), "我爱"(0.6×0.7=0.42)]
Step 3: 继续扩展,直到EOS或达到最大长度。

Beam Search的优点是能找到全局更优解,但计算量随beam_size线性增长,且容易产生重复文本(尤其是长文本生成时)。

判断方法与操作流程

  • 选择依据:若任务要求高准确性(如翻译、摘要),优先Beam Search;若任务要求多样性(如对话、故事),优先采样(temperature+top_p)。
  • 参数设置:num_beams(通常4~8)、no_repeat_ngram_size(防止重复,如设为3)、early_stopping(是否在遇到EOS时提前停止)。

案例拆解:机器翻译中的Beam Search调优

以英译中任务为例,使用HuggingFace Transformers库:

from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
model = AutoModelForSeq2SeqLM.from_pretrained("Helsinki-NLP/opus-mt-en-zh")
tokenizer = AutoTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-zh")
inputs = tokenizer("The quick brown fox jumps over the lazy dog.", return_tensors="pt")
# 贪心解码
greedy_output = model.generate(**inputs, max_new_tokens=50)
# Beam Search
beam_output = model.generate(**inputs, max_new_tokens=50, num_beams=5, early_stopping=True)
print(tokenizer.decode(greedy_output[0], skip_special_tokens=True))
print(tokenizer.decode(beam_output[0], skip_special_tokens=True))

实测结果:贪心输出“敏捷的棕色狐狸跳过懒狗。”,Beam Search输出“敏捷的棕色狐狸跃过懒狗。”后者用词更准确。但若beam_size过大(如10),生成速度下降40%,且可能出现重复“的的”等噪声。因此,生产环境建议beam_size=4~6,并配合no_repeat_ngram_size=3。

风险提示:Beam Search在开放式生成(如对话、故事)中效果不佳,容易产生“安全但无聊”的文本。的系列默认使用采样而非Beam Search,正是为了保持生成多样性。

常见误区

  • 误区1:认为beam_size越大越好。实际上,beam_size超过8后收益递减,且显存和延迟显著增加。
  • 误区2:忽略长度惩罚(length_penalty)。在长文本生成中,Beam Search倾向于短序列(因为概率乘积随长度递减),需设置length_penalty(如1.0~2.0)来平衡。

2.5 综合对比与工程选型建议

解码策略 确定性 多样性 计算开销 典型应用
贪心解码 最高 最低 最低 代码补全、关键词提取
Beam Search 高 低 中(beam_size倍) 机器翻译、文本摘要
温度采样 低 高 低 对话、创意写作
Top-p采样 中 中高 低 通用生成、API服务

工程选型时,还需考虑批处理(batch)的影响。Beam Search在batch推理时需维护多个候选序列,显存占用为batch_size × num_beams × max_len,容易OOM。建议在长序列生成时使用采样策略,或采用分组Beam Search(如每batch内分多组)降低显存峰值。

总结复盘:本章四个主题环环相扣——自回归是生成框架,tokenization是输入预处理,采样策略控制输出质量,Beam Search提供全局搜索路径。实际调优时,建议先固定tokenizer,再根据任务类型选择解码策略,最后用网格搜索微调温度与top-p。记住:没有“万能参数”,只有“最合适的组合”。