
1. 从“一个字一个字蹦”说起理解LLM的生成本质如果你用过任何一个大语言模型的聊天界面无论是网页版还是API你都会发现一个共同的现象模型的回复不是“唰”一下整段出现的而是一个词、一个字甚至一个标点符号地“蹦”出来。这个看似简单的交互细节背后隐藏着大语言模型最核心的生成机制——自回归生成。我们看到的每一个“蹦”出来的单位在技术术语里就叫作Token。理解Token是什么是理解后续所有解码策略的基石。Token并不是简单的“单词”。对于英文一个Token可能是一个完整的单词如“the”也可能是一个单词的一部分如“ing”、“ation”甚至是一个标点符号。对于中文一个Token通常对应一个汉字或一个常见的词语片段。模型内部有一个庞大的词汇表它将我们输入的文本和将要生成的文本都映射成一系列Token ID。当模型“思考”下一个该输出什么时它实际上是在计算在已经生成的所有Token的背景下词汇表中每一个可能的Token作为下一个出现的概率是多少。这个计算结果是整个词汇表上的一个概率分布。那么问题来了模型算出了下一个Token的概率分布我们该如何从这个分布中“选出”一个Token让它真正出现在屏幕上呢这个“选择”的过程以及为了优化这个选择而制定的一系列规则就是解码策略。它直接决定了模型输出的文本是严谨还是富有创意是稳定还是多变是流畅还是可能陷入循环。很多人调API时遇到的“模型开始胡言乱语”、“重复说车轱辘话”或者“毫无新意”根源往往就在于解码策略没有根据场景配置好。2. Token大语言模型的“原子”单元在深入解码策略之前我们必须先彻底搞懂Token。你可以把它想象成乐高积木的最小颗粒。我们人类阅读的句子在模型眼里就是一串特定顺序的Token ID序列。2.1 Tokenization从文本到数字的桥梁这个过程叫作分词。不同的模型使用不同的分词器。例如GPT系列通常使用基于字节对编码BPE的tiktoken而一些开源模型可能使用SentencePiece。分词器的核心作用有两个一是将文本切分成模型能理解的子词单元二是将这些单元映射成唯一的整数ID。举个例子句子“Happy-LLM is fun!” 经过分词后可能变成[“Happy”, “-”, “LL”, “M”, “ is”, “ fun”, “!”]然后每个Token被转换为对应的ID如[15496, 62, 22557, 65, 318, 11250, 0]。注意“LLM”被拆成了“LL”和“M”空格“ ”被合并到了下一个Token“ is”的开头前面有个空格。这种拆分方式使得模型能高效处理未见过的长单词通过组合已知的子词但也带来了一个关键影响模型生成是以Token为最小单位的它无法生成半个Token。这决定了解码动作的粒度。2.2 概率分布模型输出的“可能性地图”当模型接收了输入文本已转换为Token序列并经过内部复杂的神经网络计算后它会在输出层为词汇表中的每一个Token通常是数万个计算一个logit值可以理解为未归一化的得分。通过Softmax函数这些logit被转换成概率值所有Token的概率之和为1。假设当前上下文是“今天天气真”模型计算出的下一个Token概率分布可能类似于好: 0.65不错: 0.20糟糕: 0.10晴朗: 0.04...其他数万个Token分享剩余的0.01这个分布就是解码策略的“原料”。不同的策略就是从这张“可能性地图”上选取下一个目的地的不同导航规则。注意这个概率分布是条件概率严格依赖于之前生成的所有Token。改变前文分布就会彻底改变。这也是为什么解码策略需要每一步都动态决策。3. 核心解码策略详解从“最贪婪”到“最随机”解码策略的目标是在生成质量如流畅性、相关性和生成多样性如创造性、避免重复之间取得平衡。下面我们拆解最常见的几种策略。3.1 贪婪解码永远选择概率最高的那一个这是最简单、最直接的策略在每一步都毫不犹豫地选择概率分布中概率值最高的那个Token。沿用上面的例子概率最高的是“好”0.65那么模型就输出“好”。于是句子变成“今天天气真好”。然后模型基于新的上下文“今天天气真好”重新计算下一个Token的概率分布再选最高的如此循环。优点确定性高相同的输入永远产生相同的输出便于调试和复现。计算高效每一步只需做一次argmax操作开销极小。通常能生成语法正确、连贯的文本。缺点容易陷入重复或平庸由于每次都选最优模型可能会陷入局部最优的循环比如不断重复“真好真好真好”。它缺乏探索其他可能性的能力。可能错过全局更优序列就像下棋只考虑下一步最优而不是十步后的局面。局部最优的连续选择拼接起来的整个句子未必是全局最优、最通顺或最有创意的。适用场景适用于需要稳定、可预测输出的任务如代码补全、数据提取格式化输出等其中创造性和多样性不是首要目标。3.2 随机采样让不确定性主导这是另一个极端完全根据概率分布进行随机抽样。概率为0.65的“好”被抽中的机会是65%概率为0.04的“晴朗”也有4%的机会。这就像掷一个数万面的骰子每个面的权重不同。优点多样性极强每次生成都可能不同能产生非常新颖、出人意料的文本。能跳出常规思维。缺点不可控容易产生低质量或无意义输出低概率的Token可能是不合语法的或无关的被选中时会导致后续生成迅速跑偏产生胡言乱语。缺乏一致性无法用于需要稳定结果的场景。纯随机采样在实际中很少单独使用因为它太不稳定了。但它是一切“带约束的随机”策略的基础。3.3 核采样在“高概率区间”里随机为了解决纯随机采样的不稳定性核采样Top-p Sampling或称为Nucleus Sampling引入了一个聪明的约束只从累积概率达到阈值p的最高概率Token集合中随机采样。具体步骤将词汇表中的所有Token按概率从高到低排序。从概率最高的Token开始累加其概率直到累积概率刚好超过预设的阈值p例如p0.9。将这部分Token构成一个“候选集合”重新归一化它们的概率使它们的概率之和为1。从这个新的、缩小了的分布中进行随机采样。还是用“今天天气真”的例子假设p0.9排序后“好”(0.65), “不错”(0.20), “糟糕”(0.10), “晴朗”(0.04), ...累积概率0.65 - 0.85 - 0.95停止因为0.95 0.9。候选集合为 {“好”, “不错”, “糟糕”}它们的原始概率和为0.95。重新归一化P(好) 0.65/0.95 ≈ 0.684P(不错)0.21P(糟糕)0.105。在这个新的分布里随机选一个。优点动态候选集候选集的大小随当前概率分布的陡峭程度而变。分布集中时某个Token概率极高候选集小输出稳定分布平坦时多个Token概率相当候选集大输出多样。在创造性和连贯性之间取得了很好的平衡它排除了那些概率极低、几乎肯定是“噪音”的Token保证了生成的基本质量同时又保留了随机性带来的变化。缺点引入了超参数p需要根据任务调整。p值越大如0.95候选集越大越多样但也越不稳定p值越小如0.5输出越接近贪婪解码。适用场景这是目前用于创造性文本生成故事、对话、诗歌等最主流、效果最好的策略之一。大多数AI聊天应用的默认设置都采用了核采样。3.4 Top-k 采样固定数量的“精英”里随机Top-k 采样是核采样前更早流行的一种策略思路更简单只从概率最高的 k 个Token中随机采样。步骤按概率排序选出Top-k个Token。将这k个Token的概率重新归一化。从中随机采样。如果k3那么就从 {“好”, “不错”, “糟糕”} 里随机选。优点简单直观易于实现和理解。能有效避免选择极低概率的Token。缺点不够灵活k是固定值。当概率分布本身很尖锐时例如第一名概率0.99后面都很小固定选Top-k可能会包含进一些无关Token当分布很平坦时固定选Top-k又可能排除掉一些合理的选择。通常需要根据任务和模型大小仔细调整k值。在实际应用中核采样Top-p通常比Top-k表现更好因为它能自适应分布形状。因此很多现代框架如Hugging Face Transformers推荐甚至默认使用核采样或者将Top-p和Top-k结合使用先取Top-k再从中应用Top-p以获得更精细的控制。4. 高级控制参数微调解码的“手感”除了选择核心策略我们还可以通过一系列“旋钮”来精细调整解码过程解决一些常见问题。4.1 温度调节概率分布的“尖锐”程度温度参数是解码中最重要、最常用的调节器之一。它并不改变候选集而是在Softmax之前调整logits的尺度。公式本质是P(i) exp(logit_i / T) / sum(exp(logit_j / T))高温T 1例如1.2, 1.5放大低概率Token的可能性使分布更平坦。模型选择更随机输出更富有创造性、更多样但也更可能出错。低温T 1例如0.7, 0.2让高概率Token的概率更高分布更尖锐。模型选择更确定、更保守输出更连贯、更可预测但也更乏味。T 1标准Softmax不进行缩放。T - 0分布会变得极端尖锐无限接近贪婪解码。T - 无穷大分布趋于均匀分布接近完全随机。实操心得对于创意写作我通常从T0.8~1.2开始尝试对于需要事实准确、格式严格的问答或代码生成我会用更低的温度如T0.2~0.7。这是一个需要反复试验的参数。4.2 重复惩罚打破“车轱辘话”的魔咒模型有时会陷入重复生成相同词语或短语的循环比如“这个问题很好这个问题很好这个问题很好”。重复惩罚机制就是为了解决这个问题。最常见的实现是频率惩罚和存在惩罚在OpenAI API等接口中对应frequency_penalty和presence_penalty。频率惩罚降低那些在已生成文本中出现次数较多的Token的概率。出现越频繁惩罚越大。存在惩罚降低那些在已生成文本中出现过无论次数的Token的概率。只要出现过一次就给予惩罚。其原理是在计算下一个Token的概率时对候选Token的logits值进行扣减。扣减量正比于该Token在历史中的出现频率或是否出现。配置建议对于长文本生成如写文章设置较小的正惩罚如0.1~0.5可以有效避免段落内重复。对于对话需谨慎使用因为合理的词汇重复是对话的一部分。过高的惩罚可能导致模型回避使用必要的主语或关键词。我个人的经验是frequency_penalty比presence_penalty更常用也更容易控制效果。4.3 序列长度与停止符告诉模型“何时停下”模型本身不知道何时该结束生成。我们需要通过参数来控制。最大生成长度这是硬性限制防止生成无限长的文本。需要根据上下文长度和模型能力来设置。停止序列指定一个或多个字符串当模型生成的内容包含这些序列时立即停止生成。例如在对话中设置[\n\n, Human:]表示遇到两个换行可能表示话题结束或用户标识符时就停止。注意停止序列的判断是在Token被解码成文本之后进行的。如果停止序列的字符串被切分到不同的Token里可能会无法正确触发停止。这是一个需要留意的细节。5. 实战在代码中配置解码策略理论说得再多不如一行代码。我们以Hugging Face Transformers库为例看看如何在生成文本时应用这些策略。from transformers import AutoTokenizer, AutoModelForCausalLM import torch # 1. 加载模型和分词器 model_name gpt2 # 或任何你喜欢的模型 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) # 2. 准备输入 prompt 今天天气真 inputs tokenizer(prompt, return_tensorspt) # 3. 配置生成参数并生成 with torch.no_grad(): # 使用核采样 (Top-p) 温度调节 outputs model.generate( **inputs, max_new_tokens50, # 最大新生成长度 do_sampleTrue, # 开启采样贪婪解码时为False temperature0.8, # 温度 top_p0.92, # 核采样阈值 top_k50, # 可选与top_p结合先取top_k repetition_penalty1.1, # 重复惩罚 (1.0表示惩罚) no_repeat_ngram_size2, # 禁止重复的n-gram大小另一种防重复手段 pad_token_idtokenizer.eos_token_id, # 设置填充符 ) # 4. 解码输出 generated_text tokenizer.decode(outputs[0], skip_special_tokensTrue) print(generated_text)关键参数解析do_sampleFalse 启用贪婪解码。do_sampleTrue, temperature1.0, top_p1.0 启用纯随机采样实际上top_p1.0意味着从全部词汇采样。do_sampleTrue, temperature0.8, top_p0.9 这是经典的创造性文本生成配置。repetition_penalty1.2 对重复Token施加惩罚值1.01.2是一个中等强度的惩罚。no_repeat_ngram_size2 绝对禁止任何连续的2-gram两个Token的组合在文中重复出现这是一个非常强的约束适用于严格避免重复的场景但可能影响正常表达。6. 避坑指南解码策略配置的常见陷阱在实际应用中错误配置解码策略是输出质量不佳的主要原因之一。以下是我踩过的一些坑和对应的解决方案。6.1 陷阱一温度与采样策略的冲突配置问题现象设置了do_sampleTrue想随机采样但又设置了极低的temperature0.1。结果输出几乎完全确定失去了采样意义。根因分析低温使得概率分布极端尖锐即使进行采样高概率Token被选中的几率也无限接近100%采样失去了随机性。解决方案明确你的目标。若要稳定、确定的输出直接使用do_sampleFalse贪婪解码温度参数无效。若要多样、有创意的输出使用do_sampleTrue并搭配temperature通常0.7~1.2和top_p通常0.8~0.95。温度和Top-p共同作用低温高Top-p输出相对稳定但仍有一定变化高温低Top-p则变化更大。6.2 陷阱二重复惩罚过强导致语义断裂问题现象设置了很高的repetition_penalty如2.0或很小的no_repeat_ngram_size如2导致模型完全不敢使用常见的、必要的重复词汇。例如在写技术文章时无法连续使用“模型”、“用户”等关键词导致句子支离破碎。根因分析惩罚机制是无差别攻击它无法区分“有害的冗余重复”和“合理的词汇复现”。过强的惩罚会扭曲模型的原始概率分布使其偏离正常的语言模式。解决方案从低值开始repetition_penalty从1.05到1.2之间开始尝试观察效果。慎用no_repeat_ngram_size这个参数攻击性很强通常只在模型出现严重、明显的短词循环时才考虑使用且大小可以设为3或4避免影响正常的二元词组。结合生成长度在生成长文本时重复问题更容易出现。可以尝试分段生成或在长文本生成的后半段适当增加惩罚。6.3 陷阱三忽视停止序列的Token边界问题问题现象设置了停止序列[。]中文句号但模型生成了一堆文字后没有停止。排查过程检查生成文本确实没有出现“。”。将模型的原始Token ID输出解码发现末尾的Token是“。”全角句号的ID而我们设置的停止序列是半角句号。或者停止序列“用户”被分词器切成了[“用户”, “”]两个Token而模型生成时在“用户”后生成了其他Token如“您好”导致序列不匹配。解决方案统一字符格式确保提示词和停止序列的标点符号全半角一致。使用分词器验证将你设定的停止字符串用同样的分词器进行编码查看其Token序列。stop_string 。 stop_tokens tokenizer.encode(stop_string, add_special_tokensFalse) print(f停止字符串{stop_string}对应的Token IDs: {stop_tokens})考虑使用Token ID序列作为停止条件一些高级API允许直接传入Token ID列表作为停止条件这更精确。6.4 陷阱四最大长度设置不当导致截断或资源浪费问题模型输入上下文很长如2000个Tokenmax_new_tokens设置为1000但模型在生成300个Token后逻辑上已经结束例如已回答完问题。后续700个Token的生成纯属浪费算力甚至可能因为“无话可说”而开始生成乱码。解决方案合理预估根据任务类型预估回答长度。简短问答可能只需50-150个新Token文章生成可能需要500。使用停止序列设置有效的停止序列如[\n\n, ###, 问题]让模型在内容完成后自然停止比依赖最大长度更智能。动态监控如果API支持有些接口允许在流式输出时检测到停止条件后主动取消后续生成。解码策略的调优没有银弹它高度依赖于具体任务、模型特性甚至用户的个人偏好。最好的方法是建立一个评估流程针对你的典型输入用几组不同的参数例如(temp0.8, top_p0.9)(temp0.2, greedy)(temp1.0, top_p0.95, rep_penalty1.1)生成输出人工或通过简单规则如重复词比例、长度评估哪个结果最符合你的需求。把这些参数组合记录下来就形成了你自己应用场景下的“解码策略配置手册”。这个过程本身就是对模型生成行为最深入的学习。