菜单

Token 采样

相关源文件

本文档介绍了 llama.cpp 中的 token 采样系统,该系统负责根据模型的输出 logits(对数概率)在文本生成过程中选择下一个 token。Token 采样是直接影响生成文本质量、多样性和特性的关键组成部分。

有关模型加载和推理执行的信息,请参阅 上下文管理和推理。有关语法约束生成的详细信息,请参阅 语法约束生成

架构概述

llama.cpp 中的 token 采样系统遵循分层架构,具有多种采样算法,可以组合成采样链。该系统既提供了低级别的 llama_sampler 接口,也提供了一个更高级别的 common_sampler 包装器。

核心采样架构

来源:src/llama-sampling.cpp1-2500 common/sampling.cpp103-131 common/sampling.h35-50

Token 数据流

来源:src/llama-sampling.cpp379-409 common/sampling.cpp338-382 src/llama-sampling.cpp430-438

核心采样接口

llama_sampler 接口提供了一个基于插件的架构,其中单个采样算法实现了通用接口。每个采样器都可以修改 token 概率分布并选择 token。

采样器接口结构

核心接口由 llama_sampler_i 结构体定义

来源:src/llama-sampling.cpp320-377 src/llama-sampling.cpp547-554 src/llama-sampling.cpp679-686

Token 数据数组

llama_token_data_array 结构体是在采样器之间传递的核心数据类型

字段类型目的
datallama_token_data*token 候选列表
sizesize_t候选数量
selectedint32_t选定 token 的索引(无则为 -1)
sortedbool列表是否按 logit 排序

每个 llama_token_data 包含:

  • id: Token 标识符
  • logit: 来自模型的原始 logit 值
  • p: 概率(通过 softmax 计算)

来源:src/llama-sampling.cpp379-409 tests/test-sampling.cpp15-19

单个采样算法

贪心采样

贪心采样总是选择概率最高的 token。它是确定性的,提供最有可能的续写。

来源:src/llama-sampling.cpp534-545 src/llama-sampling.cpp556-561

Top-K 采样

Top-K 采样将候选 token 限制为概率最高的 K 个,然后从这个缩减的集合中进行采样。

k > 128 时,该实现对大型词汇表使用了优化的桶排序。

来源:src/llama-sampling.cpp228-303 src/llama-sampling.cpp665-668 src/llama-sampling.cpp688-695

Top-P (Nucleus) 采样

Top-P 采样从累积概率超过阈值 P 的最小 token 集合中进行选择。

来源:src/llama-sampling.cpp708-734 src/llama-sampling.cpp754-762

Min-P 采样

Min-P 采样通过将概率低于 p * p_max 的 token 移除,其中 p_max 是最高的 token 概率。

来源:src/llama-sampling.cpp775-830 src/llama-sampling.cpp850-858

温度采样

温度缩放通过将 logit 除以温度来调整概率分布的“锐度”。

  • temp = 0.0: 贪心(确定性)
  • temp = 1.0: 分布不变
  • temp > 1.0: 更随机(更平坦的分布)
  • temp < 1.0: 随机性更小(更尖锐的分布)

来源:src/llama-sampling.cpp179-201 src/llama-sampling.cpp969-972

DRY (Don't Repeat Yourself) 采样

DRY 采样通过降低导致重复序列出现的 token 的概率来惩罚重复序列。

来源: common/sampling.cpp234-244 tests/test-sampling.cpp161-182

采样器链

采样器链允许按顺序应用多个采样算法。链中的每个采样器都会修改 token 概率分布,而最后一个采样器通常执行实际的 token 选择。

链构建示例

常见的采样链模式

Mirostat标准链
tempmirostat_v2penaltiesdrytop_ktop_pmin_ptempdist

来源: common/sampling.cpp225-287 src/llama-sampling.cpp482-527

链执行

当对链调用 llama_sampler_apply() 时,它会遍历所有采样器

来源: src/llama-sampling.cpp430-438 src/llama-sampling.cpp494-497

通用采样器包装器

common_sampler 提供了一个更高级的接口,增加了语法支持、性能跟踪和 token 历史管理。

通用采样器结构

来源: common/sampling.cpp103-131 common/sampling.h35-50

带语法的采样过程

common_sampler_sample() 函数实现了用于语法集成的两阶段采样方法

来源: common/sampling.cpp338-382 common/sampling.h58-61

语法集成

采样系统支持两种类型的语法约束

传统语法

使用 GBNF(Backus-Naur 形式的语法)进行结构化输出生成。

来源: common/sampling.cpp206-214

LLGuidance 集成

当使用 LLAMA_USE_LLGUIDANCE 编译时,支持更高级的语法系统。

来源: common/sampling.cpp157-164 common/sampling.h106-107

性能考量

优化

  1. 桶排序 Top-K:当 k > 128 时,使用优化的桶排序代替部分排序
  2. 懒惰语法:语法约束仅在需要时应用
  3. 循环缓冲区:具有固定容量的高效 token 历史存储
  4. 排序状态跟踪:避免不必要的排序操作

来源: src/llama-sampling.cpp248-298 common/sampling.cpp10-101

性能测量

采样系统包含内置的性能跟踪功能

来源: src/llama-sampling.cpp421-428 common/sampling.cpp327-336

使用模式

基本采样

投机采样

用于投机解码以进行草稿 token 验证

来源: examples/speculative/speculative.cpp240-416

并行处理

多个独立的采样上下文,用于并发生成

来源: examples/parallel/parallel.cpp222-227 examples/parallel/parallel.cpp423-425