Skip to content
Self-Knowing

SamplingParams

约 614 个字 11 行代码 预计阅读时间 2 分钟

sampling_params.py 定义了采样参数 dataclass,绑定到每个 Sequence 上控制生成行为。三个字段:temperature 控制随机性,max_tokens 限制生成长度,ignore_eos 决定是否忽略结束符。

为什么叫 sampling?

模型前向传播后输出的不是「下一个 token 是什么」,而是一个长度为 vocab_size 的 logits 向量,经过 softmax 后变成词表上的概率分布。从概率分布中随机抽取一个具体 token 的动作,在概率论里就叫 sampling(采样)。

LLM:从已知分布中直接抽取

模型输出 logits → softmax 得到具体长度(vocab_size)的离散概率分布。这个分布是显式的、确定的,你能看到每个 token 的确切概率。Sampler 做的就是从这个分布里按概率随机抽一个结果,一步完成,拿到的是一个具体 token。

Diffusion:通过迭代去噪逼近分布

模型不输出概率分布,它输出的是噪声预测(score function,即 \(\nabla_x \log p(x)\))。你看不到「这张图的概率是多少」,你只知道「往哪个方向走能让图更真实」。Sampling 从纯高斯噪声出发,每一步都用这个 score 去一点噪声,走几十到上千步,最终收敛到数据分布中的一张干净图片。整个过程是在连续空间 \(\mathbb{R}^d\) 中沿一条轨迹移动,不是从分布里直接抽。

Temperature

Temperature 只做一件事:控制 softmax 前 logits 的缩放比例,从而改变离散概率分布的熵。

  • Temperature 在 softmax 之前把 logits 除以自己:\(logits / T\)
  • T 小 → logits 被放大 → softmax 后高分的 token 概率更高、低分的更低 → 分布尖锐 → 模型「更自信」,几乎总选那几个头部 token
  • T 大 → logits 被压缩 → 高分和低分的差距缩小 → 分布平坦 → 模型「更犹豫」,尾部 token 也有机会被抽到

nanovllm/layers/sampler.py 中:

logits = logits.float().div_(temperatures.unsqueeze(dim=1))

假设模型输出的 logits 是:

["猫", "狗", "鱼", "鸟", "马", "蛇", ...]
 [2.0,  1.0,  0.5,  0.3,  0.1, -0.5, ...]

Temperature = 1(正常)

logits 不变,softmax 后:

猫: 42%  狗: 15%  鱼: 9%  鸟: 7%  马: 6%  蛇: 2%

Top-3 就能拿走 66% 的概率。

Temperature = 2(高温 → 更平坦)

logits 除以 2:[1.0, 0.5, 0.25, 0.15, 0.05, -0.25],softmax 后:

猫: 29%  狗: 17%  鱼: 13%  鸟: 12%  马: 10%  蛇: 7%

Top-3 只能拿走 59%,你需要 Top-5 才能拿到 81%。

Temperature = 0.5(低温 → 更尖锐)

logits 除以 0.5:[4.0, 2.0, 1.0, 0.6, 0.2, -1.0],softmax 后:

猫: 64%  狗: 8%  鱼: 3%  鸟: 2%  马: 1%  蛇: 0.4%

Top-1 就拿走了 64%,Top-3 基本已是全部。

Source Code

from dataclasses import dataclass

@dataclass(slots=True)
class SamplingParams:
    temperature: float = 1.0
    max_tokens: int = 64
    ignore_eos: bool = False

    def __post_init__(self):
        assert self.temperature > 1e-10, "greedy sampling is not permitted"

Created: May 4, 2026
Last update: May 5, 2026

Discussion