同一Qwen3.6-27B checkpoint在本地与官方demo表现不同,根因是推理栈差异;实验使用RTX PRO 6000 Blackwell,vLLM nightly容器涉及734个包。
在同一台机器上跑同一个 Qwen3.6-27B checkpoint,和实验室官方 Demo 跑出来的答案完全不同。这不是你的幻觉,也不是模型坏了:这是你拼装的那套推理栈的数字签名,有一个专业名称——KL divergence。
8 月 16 日,Level1Techs 论坛发布了一份分析报告,一层一层拆解了为什么同一个模型(相同架构、相同权重)在不同的注意力后端、不同的量化方式、不同的采样器配置下会产生不同的 token。
Level1Techs 论坛于 2026 年 8 月 16 日发布了这份分析,解释了为什么本地 LLM 与原版表现不同。KL divergence 衡量的是你的推理过程中 token 概率分布与基准线之间的偏离程度。实验在 BF16 精度下使用 Qwen3.6-27B,在 RTX PRO 6000 Blackwell 上运行,张量并行设为 1,且未对 KV cache 进行量化。测试的 vLLM nightly 容器包含 734 个包,其中 252 个通过 uv/pip 安装在 Python 中。每个注意力后端根据 GPU 的计算能力使用不同的 CUDA kernel,这会改变 prefill 阶段的精度。采样器温度过低可能使 Qwen 等模型陷入 think 标签内的循环。使用三个零温度 prompt 进行对比不能代表代理任务:需要长上下文评估和工具调用测试。
KL divergence(Kullback-Leibler 散度)是这个现象背后的技术指标:它衡量你的本地推理的 token 概率分布与参考基准线之间偏离多远。它不直接衡量智能,衡量的是 token 分布之间的距离。
分析的起点很简单:今天跑 LLM 的每一种硬件和软件组合都是不同的,这种差异在你看到第一个 token 之前很久就已经开始了。混用不同代际的 GPU、不同的指令集、为特定计算能力编译的 CUDA kernel——所有这些都会改变下一个 token 的数学计算方式,即使运行的是完全相同的权重。
用户 thr3e 在 Level1Techs 论坛的 Machine Learning, LLMs, & AI 分类下发布了一系列技术实验,旨在逐一隔离本地实现与训练模型的实验室(作者称之为 reference implementation)在原始基准测试中报告的结果之间产生分歧的环节。
基础实验在 RTX PRO 6000 Blackwell GPU 上以 BF16 精度运行 Qwen3.6-27B 官方 checkpoint,张量并行设为 1,未对权重、激活值或 KV cache 进行任何量化。为消除变量,作者禁用了 CUDA graphs、prefix caching 和 MTP(multi-token prediction),并强制以 eager 模式执行。
使用的软件栈是 vLLM 的 nightly build,锁定到特定版本以确保结果可复现。这个细节很重要:作者测量发现,他下载的 vLLM 容器 nightly 镜像包含 734 个包,其中 252 个是通过 uv/pip 安装的 Python 包。这 734 个不同的代码库,每一个都有自己未记录的 bug 和行为表现,站在你的 prompt 和屏幕上看到的文本之间的每一步。测试的 vLLM nightly 容器包含 734 个不同的包。
要理解 KL divergence 测量的是什么,首先需要理解什么是 logit。Logit 是模型给每个可能的 token 打出的原始分数,作为下一个 token 的候选。这些分数通过 softmax 函数归一化为概率,经过配置好的采样器(温度、top-p、top-k),然后 detokenizer 将它们逐 token 转换回可读文本。
Kullback-Leibler 散度是 1951 年信息论中的一个概念,用于量化一个概率分布偏离另一个参考概率分布的程度。应用到 LLM 上:将输出的 logit 转换为概率分布,然后测量该分布与参考 checkpoint 在文本同一位置产生的分布之间的距离。
分析强调了一个细节:KL divergence 是有方向的。你比较的两个分布的顺序很重要,调换顺序不会得到相同的数字。KLD 越低意味着离所选基准线越近,而不是越智能:这是两个容易混淆的不同说法。
⚠️ 注意:不要把量化模型卡上发布的低 KLD 等同于与原版质量相同。如果不知道确切的参考 checkpoint、完整的执行环境、评估文本、校准数据、上下文长度、采样位置、计算方向以及测量聚合方式,这个数字是无法解读的。
在 prefill 阶段(完整 prompt 的初始处理),推理引擎在多个可用的注意力后端之间进行选择。这个选择不是装饰性的:每个后端使用不同的 CUDA kernel,为特定架构和计算能力编译,这既影响速度,也影响结果的数值精度。
| 后端 | 何时使用 | 优势 | 限制 |
|---|---|---|---|
| FlashAttention-2 | 有原生支持的 Ampere、Hopper 或 Blackwell GPU | Prefill 速度快,数值精度良好 | 按 GPU 系列定制的 kernel,不覆盖所有硬件 |
| FlashInfer | 使用分页 KV cache 的 Decode 阶段 | 生成时速度和精度的良好平衡 | 需要针对你的确切 CUDA 版本编译 |
| xFormers | 没有 FlashAttention 支持的旧款 GPU | 兼容性广泛 | Prefill 较慢,内存占用更高 |
| Eager(PyTorch 原生) | 调试和精度验证 | 作为参考具有最高数值保真度 | 对生产来说太慢 |
Level1Techs 的分析在未量化的 BF16 基础上运行了第一个实验(注意力后端精度对比),以将后端的影响与量化的影响隔离开来。这是诚实了解两个变量中哪个在你的设置中引入更多噪声的唯一方法。
flowchart TD
A["Prompt de entrada"] --> B["Tokenizer"]
B --> C["Prefill: backend de atencion"]
C --> D["Logits crudos"]
D --> E["Sampler: temperatura y top-p"]
E --> F["Detokenizer"]
F --> G["Texto de salida"]
要测量你自己与基准线的差异,需要在文本同一位置获取两个模型的原始 logit。用 transformers 库实现的一个最小脚本如下:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
modelo_id = "Qwen/Qwen3.6-27B"
tokenizer = AutoTokenizer.from_pretrained(modelo_id)
modelo = AutoModelForCausalLM.from_pretrained(
modelo_id, torch_dtype=torch.bfloat16, device_map="cuda"
)
entrada = tokenizer("Que es un índice B+ en una base de datos?", return_tensors="pt").to("cuda")
with torch.no_grad():
salida = modelo(**entrada)
logits = salida.logits[:, -1, :]
print(logits.shape)
这段代码加载参考 checkpoint 并提取最后一个 token 的 logit。预期结果是一个形状为 [1, vocab_size] 的 tensor:词汇表中每个可能的 token 对应一个分数。用你的本地模型(量化后、使用不同后端)重复相同的过程,然后用 KL divergence 比较两个分布:
import torch.nn.functional as F
log_p_referencia = F.log_softmax(logits_referencia, dim=-1)
p_local = F.softmax(logits_local, dim=-1)
kld = F.kl_div(log_p_referencia, p_local, reduction="batchmean")
print(f"KL divergence en esta posicion: {kld.item():.6f}")
数值越接近零,你的本地分布在那个特定位置就越接近参考分布。要让这个数字有意义,必须在许多位置和许多代表你实际使用场景的 prompt 上取平均值,而不是用三个随机 prompt 在零温度下测试。每个注意力后端根据 GPU 使用不同的 CUDA kernel。
在运行任何东西之前,检查你要使用的 checkpoint 在 Hugging Face 上的 model card:那里通常会指定推荐的采样器(温度、top-p)和确切的聊天模板。使用另一个模型的参数是导致奇怪行为的常见原因。
💡 提示:如果你的 Qwen 困在 think 标签内不断循环而无法退出,先检查温度:温度过低可能使模型陷入重复相同推理而不收敛到答案的循环。
Linux(推荐,原生 CUDA 支持)
python3 -m venv .venv
source .venv/bin/activate
pip install vllm
VLLM_ATTENTION_BACKEND=FLASHINFER vllm serve Qwen/Qwen3.6-27B --tensor-parallel-size 1 --dtype bfloat16
macOS(Apple Silicon,无 CUDA 加速)
python3 -m venv .venv
source .venv/bin/activate
pip install vllm --extra-index-url https://download.pytorch.org/whl/cpu
Windows(通过 WSL)
wsl --install -d Ubuntu
wsl
python3 -m venv .venv
source .venv/bin/activate
pip install vllm
要确认你的进程中最终激活了哪个注意力后端,不需要猜测:vLLM 会在服务器启动日志中明确打印它,格式类似于 Using FlashInfer backend 或 Using FlashAttention backend。如果这行与你设置的 VLLM_ATTENTION_BACKEND 不一致,说明你的 GPU 不支持该后端,引擎静默切换到了另一个。
分析的实用结论有些令人不适,但很有用:你的本地实现永远不会在位级别与实验室发布的基准测试完全一致,但这对于任何不使用完全相同的硬件、完全相同的软件 build 和完全相同配置的人来说也是如此。真正的问题不是是否与原版相同,而是偏离有多远、朝哪个方向偏离。
这对你如何阅读 Hugging Face 上量化模型的卡有直接影响。当某个量化声称实现了极低的 KLD 但没有公布参考 checkpoint、执行环境、校准文本和计算方向时,这个数字是无法审计的。原始分析直白地说:方法论与数字同样重要,而相当多的人忽视了这一点。
这也质疑了一个普遍的习惯:用三个零温度 prompt 评估模型然后得出它是否可用的结论。这种零样本测试不能代表真实的代理任务,真实代理任务需要长上下文、工具调用和领域特定知识,才能暴露你的设置与运行相同权重的另一个设置之间实际存在的差异。
分析本身为后续研究留下了空间:使用相同的逐位置 KL divergence 方法,将不同位数的 GGUF 量化与已表征的 BF16 基准线进行对比。这将能够用你自己的、可复现的数据,分离出注意力后端引入的噪声与量化本身引入的噪声分别有多少。
在社区层面,预期是 Hugging Face 上更多模型的卡开始公布其 KLD 测量的完整方法论(参考 checkpoint、环境、校准数据),而不是一个孤立的数字。没有这些信息,不同作者之间的量化对比在实际上仍然是无法审计的。
📖 Telegram 摘要:在 Telegram 查看摘要
亲自试试:用 pip install vllm 安装 vLLM,启动你最喜欢的 checkpoint并将 VLLM_ATTENTION_BACKEND 设置为明确值,然后将 logit 与同一模型的官方 Demo 进行对比,得出你自己的 KL divergence。
什么是应用于 LLM 的 KL divergence?
它是一种指标,用于比较你的本地推理在文本给定位置生成的 token 概率分布,与参考 checkpoint 产生的分布。数值越接近零,两个分布在该点的相似度越高。
为什么我在 Ollama 中量化后的模型感觉比实验室官方 Demo 差?
因为改变的不仅仅是量化:注意力后端、KV cache 精度、默认采样器和聊天模板都变了。这条栈中的每一层都会与实验室发布原始基准测试时所使用的环境产生一点分歧,这些小差异会累积。
KLD 低能保证量化模型同样智能吗?
不能。KLD 低只说明 token 分布接近所选基准线。不知道完整的方法论(参考 checkpoint、环境、校准数据、计算方向),这个数字就无法解读,也无法与其他作者的同类数字进行比较。
我应该在 GPU 上使用哪个注意力后端?
取决于你的架构和用例:近期有良好支持的 GPU 用 FlashAttention-2 或 FlashInfer,旧硬件用 xFormers 作为替代,调试用 eager(因为太慢)。始终在启动日志中确认最终激活的是哪一个。
为什么我的模型困在 think 标签内无法完成?
几乎总是因为采样器温度对该特定模型配置过低。检查 Hugging Face 上的 model card,使用模型作者本人推荐的温度和 top-p 值。
三个零温度 prompt 的基准测试能用来评估本地 LLM 吗?
不能作为代表性评估。少量 prompt 的零样本基准测试无法捕捉带有长上下文和工具调用的真实代理任务。建议使用针对你具体用例的 terminal-bench、HLE 或 SWE-bench 等测试套件。
📱 喜欢这个内容吗?加入我们的 Telegram 频道 @programacion,我们每天发布技术、AI 和开发领域最相关的内容。每日快速摘要,新鲜内容天天有。