LLM推理基础:采样(Sampling)常见知识概览 - 指南

本文作者:kaiyuan
LLM推理基础:采样(Sampling)常见知识概览

本文主要介绍LLM推理采样(Sampling)相关的基本概念,包括随机采样、确定性采样,涉及概念包括:Temperature/Topk/TopP/MinP/Penalty和Beam Search/Greedy Search等。结合原理、公式和示例,用通俗的语言帮助读者快速了解相关内容。

1.推理采样的概念

什么是采样?目前大语言模型(LLM)的文字生成,主要采用的机制是词语接龙,比如输入“我爱”,后面可以接“你”、“她”、“他”等词。这个“我爱”的输入称之为提示词(prompt),后续的输出文字是依次迭代产生的,迭代过程称之为自回归。

推理运算的基本过程:输入的prompt先经过模型的主体(Backbone),再经过解码层(LM Head)和Softmax层后能够得到序列(seq)中每个字符的输出概率数组(logits),这个数组的大小与字典大小(vocab_size)相等,字典中的每个词都有可能是输出结果。采样(Sampling)则是根据概率分布从字典中选取一个最合适的词。

在这里插入图片描述

‘n’的概率最高

为什么需要调整采样过程?对于LLM采样最直接的方式就是选取概率最大值的字符作为输出,但这会让模型的输出没有多样性,比如上例中输入“我爱”后面只会接“你”,不会是其它词。所以,得让模型能够“创造”出更多的结果。

常见的采样方式有哪些?

  • 随机:根据一定条件筛选符合条件的高概率值,然后从中随机挑选一个作为输出结果;
  • 贪心:每轮都挑选概率最大值作为本次的输出结果;
  • 累积概率最大:每一个步输出多个结果,进行多步后挑选概率乘积最大的一组,如BeamSearch。
  • 结构化:按照一定格式限定输出形式。比如提示词为:“你最喜欢一周中的哪天?”。一种结构化输形式为“周__”最后一个词的选项只有7个。

2 .随机采样

随机采样并非完全随机,而是有条件的随机。因为随机可能会降低输出质量,过量的随机性会导致输出混乱,一个极端的例子是对字典范围内进行简单随机抽样,这显然不对。比较常见随机采样是权重采样,把LLM的输出的logits分数作为采样的权重(参考torch.multinomial)。为了获得合理且丰富的输出结果,在随机采样中还引入了一些调整logits值的操作。

2.1 温度(Temperature)

温度(Temperature)操作用于调整logits的概率分布整体情况,能让概率分布变得尖锐或者平坦。利用了吉布斯分布的特点。标准的softmax计算: P ( i ) = exp ⁡ ( z i ) ∑ j = 1 V exp ⁡ ( z j ) (1) P(i) = \frac{\exp(z_i)}{\sum_{j=1}^{V} \exp(z_j)} \tag {1} P(i)=∑j=1V​exp(zj​)exp(zi​)​(1)增加了温度T的softmax计算:
P ( i ) = exp ⁡ ( z i T ) ∑ j = 1 V exp ⁡ ( z j T ) (2) P(i) = \frac{\exp(\frac{z_i}{T})}{\sum_{j=1}^{V} \exp(\frac{z_j}{T})} \tag {2} P(i)=∑j=1V​exp(Tzj​​)exp(Tzi​​)​(2)
改变T值的大小,能够影响输出的概率:

  • 0<T< 1,称之为低温。logits概率数组将会变得锐化,值与值之间的差距拉大。
  • T>1,称之为高温。logits概率数组变得更加平缓,值与值之间的差距缩小。

举个例子,看一下经过不同T后,概率的变化。

  • 字典的词组为:[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’]
  • 输出logits结果(LM head之后,softmax计算之前):[2.0, 1.0, 0.5, 0.1, -1.0, -2.0]

在这里插入图片描述

输入&低温处理

在这里插入图片描述

常温(T=1)和高温处理

特点:在LLM中,低温处理能够让概率大值更加突出,输出变得可预测、连贯,但也可能变得重复和缺乏创造力;高温处理输出会更加多样、有创意,但会变得不连贯或出现事实性错误。

2.2 Topk/TopP/MinP

目前一个LLM字典大小能到151K,随机采样时范围并不需要这么大,为了降低随机的范围,可去掉一些低概率值。常见的方式是用过滤来缩小范围。

  • TopK:概率排序后,保留概率最大的K个值。取值范围是1~vocab_size(-1表示禁用)。
  • TopP:概率排序后,取累积概率到P的值,先排序 p ( 1 ) ≥ p ( 2 ) ≥ ⋯ ≥ p ( V ) p_{(1)} \geq p_{(2)} \geq \cdots \geq p_{(V)} p(1)​≥p(2)​≥⋯≥p(V)​,然后算累加值: C k = ∑ i = 1 k p ( i ) C_k = \sum_{i=1}^k p_{(i)} Ck​=∑i=1k​p(i)​, 保留 C k ≥ P C_k \geq P Ck​≥P的前K个值。topP取值范围0~1。
  • MinP:保留所有概率至少为最高概率的P倍的候选词,保留所有满足 p i ≥ min-p × p max p_i\geq \text{min-p} \times p{\text{max}} pi​≥min-p×pmax的候选词,min_p的取值范围>0。

对于没保留的值,一般设置概率为0,举个例子:

若字典是[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’],logits为[0.6, 0.3, 0.05, 0.03, 0.015, 0.005]

  • 用TopK=4,则logits变为:[0.6, 0.3, 0.05, 0.03, 0, 0];
  • 用TopP=0.95,则logits变为:[0.6, 0.3, 0.05, 0, 0, 0];
  • 用MinP=0.5,则logits变为:[0.6, 0.3, 0, 0, 0, 0];

特点:TopK保留前K个最大值;TopP能够根据累积概率筛选掉低概率值。MinP能够保证每个值达到一定要求。

通过示例,了解这几个处理方式的问题:

  • TopK会选取到一些概率低的样本。例:logtis为[0.6, 0.3, 0.05, 0.03, 0.015,0.005],TopK=3,0.05概率被采样进去了。
  • TopP有采集的数量过多的问题。例:logtis为[0.2, 0.2, 0.2, 0.2, 0.1,0.1],TopP=0.95,全部值都有效。
  • MinP有采样过少的问题。例:logtis为[0.8, 0.1, 0.05, 0.03, 0.015,0.005],MinP=0.5时,仅保留了一个值。

所以通常的LLM推理过程中,会混合使用这些策略。

2.3 惩罚(Penalty)

惩罚(Penalty)是根据历史的输出字符情况增加一些超参来调整局部的logits值,是一个局部缩放操作。

为什么需要惩罚这种机制?一个理想LLM像人类一样输出合适的结果,但目前的LLM还不能完全达到人类的水平。在输出采样实践中,如果纯靠概率模型可能会犯一些错误,比如:循环、重复、冗余。

在这里插入图片描述

LLM输出的问题

惩罚作为对logits的一种处理手段,抑制一些基本的错误。常见的惩罚手段有:

  1. 频率惩罚(frequency penalty):对出现过的词,根据其出现频率降低logits值,频率越高衰减越严重。
  2. 存在惩罚(presence penalty):对出现过的词,在logits中减去一个相应惩罚值,每个词至多惩罚一次。
  3. 重复惩罚(repetition penalty):对重复出现的词进行衰减,类似频率处理。

一般计算方法:

  • logits[i] -= frequency_penalty × count
  • logits[i] -= presence_penalty
  • logits[i] /= (repetition _penalty ** (count - 1)) ,当logits[i] >0;
  • logits[i] *= (repetition _penalty ** (count - 1)) ,当logits[i] < 0;

其中frequency_penalty 和 presence_penalty 默认值0,取值范围[-2, 2];repetition _penalty 默认值1,取值范围[0, 2]。

2.4 操作顺序

除了温度、惩罚外,还有其它能修改logits数值的方式,比如:

  • 偏置(bias):给指定的词添加一个偏置项,计算方式:logits[i] += bias[i]);
  • 掩膜(mask):将掩膜外的值置为0;

在对每一轮的logits的调节上,操作的顺序会影响最后的结果。有些操作要需要固定,但有些操作之间的顺序又可以调节,比如温度操作在softmax之前,而top操作之间可以调整。对于采样顺序调整的问题参考:TopK/MinP/P/T/Softmax等的顺序对采样有何影响?

在这里插入图片描述
用pytorch实现的采样处理的示意代码:

# 伪代码,供参考:
# 惩罚处理
penalizer(logits)
# 添加bias值
logits.add_(self.logit_bias)
# 温度处理
logits.div_(temperatures)
# softmax 计算
logits[:] = torch.softmax(logits, dim=-1)
probs = logits
probs_sort, probs_idx = probs.sort(dim=-1, descending=True)
probs_sum = torch.cumsum(probs_sort, dim=-1)
# top-k 采样
probs_sort[
    torch.arange(0, probs.shape[-1]).view(1, -1)
    >= top_ks.view(-1, 1)
    ] = 0.0
# top-p 采样
probs_sort[(probs_sum - probs_sort) > top_ps.view(-1, 1)] = 0.0
# min-p 采样
min_p_thresholds = probs_sort[:, 0] * min_ps
probs_sort[probs_sort < min_p_thresholds.view(-1, 1)] = 0.0
# 权重采样
sampled_index = torch.multinomial_with_seed(probs_sort, sampling_seed, positions)

3 确定性采样

与随机采样对应的还有一类是确定性采样,主要是解决比如机器翻译、概要生成这类场景,其不需要多样性,而是期望结果更准确。确定性采样的目标就是找概率最大的输出结果。

3.1 贪婪采样

常见的确定性采样:贪婪搜索(greedy search),其思路是每一步取概率最大的logits作为输出,但贪婪搜索保证的是单步最优,没有考虑多步之间的联系,不能保证累积概率最大。

举个例子,如下图所示,采用贪婪采样,在step1选择“吃”(概率0.6),step2选择“你”。两步的累积概率为0.6*0.3=0.18;与之对比,若step1选“爱”、step2选“吃”,最后能够得到累积概率0.3x0.7=0.21,数值比贪婪算法的结果更大。

在这里插入图片描述
在LLM中前缀的变化会影响下一步的输出值,单个输出的累积概率是否最大,要穷举所有的可能进行对比。

3.2 问题的模型

计算累积概率最大是一个典型的多叉树找最优解的问题,可以用广度/深度搜索(BFS/DFS)来解问题。假设推导k步,字典大小(vocab_size)为n,不考虑推理提前结束(遇到stop、eos等字符),可能的计算路径数量: n k n^k nk。假设单步推理的计算量为 f ( L ) f(L) f(L),L表示序列长度,总的计算量为:
F ( k ) = Σ i = 0 k − 1 n i ∗ f ( L 0 + i ) (3) F(k)=\Sigma_{i=0}^{k-1} n^{i}*f(L_{0}+i)\tag{3} F(k)=Σi=0k−1​ni∗f(L0​+i)(3)但单步推理的计算成本是非常高的,且常见的字典大小会超过100K,所以这种暴力搜索的方式很显然不可取。要对公式3中的 n i n^i ni项进行压缩,常见处理方式:

  • n i n^i ni替换为1:搜索路径只有一条,即是之前提到的的贪婪搜索;
  • n i n^i ni替换为定值k:搜索路径有k条,得到一种叫束搜索(beam searh)的方式。

3.3 Beam Search

beam search是一种结合topK和剪枝的搜索算法,每次保留束宽(beam width)k个结果。beam search的基本步骤:

  1. 对首次输出进行topK排序,选取前k个值;
  2. 根据上一轮的k个值,分别获得k个输出,每个分支进行topK排序,计算累积概率;
  3. 对全局k * k个累积概率排序,选取前k个值;
  4. 重复步骤2、3,直到k个分支遇到结束符,最后以累积概率最大的分支作为结果。

在这里插入图片描述

k=2的beam search示意图

beam search是一种介于贪婪搜索和暴力搜索之间的方式,通过控制beam width在计算总量与结果质量中找到一个平衡位置。

beam search有什么不足?

  • 显存开销大。所以实现中一般会结合共享前缀的方式来降低存储、访存开销,参看paged attention[1]中相关介绍。
    在这里插入图片描述
vLLM中beam sreach的block使用示例
  • 输出“短视”。在计算累积概率时可知,因为概率范围是(0~1),对于同一个分支计算次数越少结果值越高。这样使得beam search趋向于选择短输出。

为了解决这一问题,引入了一个长度惩罚(length penalty),对于beam search的每个计算结束的分支累积概率乘以一个惩罚值。常用的计算方式: P c u m = P c u m L p (4) P_{cum} = \frac{P_{cum}}{L^{p} } \tag {4} Pcum​=LpPcum​​(4)

logprob = cumulative_logprob / (seq_len**length_penalty)

参数length_penalty< 1 时鼓励长输出,即长度越短分数衰减越大。

还可以通过min_new_tokens或者max_new_tokens这种参数,控制长度的范围。实现的方式比较简单:当长度小于min_new_tokens让LLM不能输出终止符号。

总结

本文介绍了常见的采样方式的概念,涉及到的关键问题有:

  • 什么是采样?
  • 为什么需要调整采样过程?
  • 常见的采样方式有哪些?
  • Topk/TopP/MinP各自有什么缺点?
  • 为什么需要惩罚这种机制?
  • beam search有什么不足?如何解决?

通过对采样有了基本认识后,接下来还需要了解如何设置采样参数,本文中未对调参内容做介绍,后续再补充。


参考
[1]Efficient Memory Management for Large Language Model Serving with PagedAttention [https://arxiv.org/pdf/2309.06180]

InfraTech申明:未经允许不得转载!

---

技能提升

以下资源可以帮助你进一步提升:

  • AI大模型之美 — 快速上手新一代AI应用开发,掌握大模型核心能力

开发资源

posted @ 2026-02-17 13:24  yjbjingcha  阅读(232)  评论(0)    收藏  举报