确定性推理#

为什么确定性推理至关重要#

确定性推理可确保 LLM 在多次运行中输出一致,这对于以下场景至关重要:

  • 强化学习:确保多次运行中的对数概率 (logprobs) 一致,减少随机噪声,使 RL 训练更稳定、可复现且易于调试。

  • 测试与调试:实现可复现的验证

  • 生产环境:提高可靠性和用户体验

即使设置 temperature=0,由于动态批处理和 GPU 算子中变化的归约 (reduction) 顺序,标准的 LLM 推理仍可能产生不同的输出。

非确定性的根本原因#

主要原因是 变化的批处理大小 (batch sizes)。不同的批处理大小会导致 GPU 算子以不同方式拆分归约操作,从而导致加法顺序不同。由于浮点数非结合律 ((a + b) + c a + (b + c)),即使输入相同,也会产生不同的结果。

SGLang 的解决方案#

基于 Thinking Machines Lab 的批次不变算子 (batch-invariant operators),SGLang 在保持与分块预填充 (chunked prefill)、CUDA 图 (CUDA graphs)、Radix 缓存和非贪婪采样兼容的同时,实现了完全确定的推理。确定性推理功能的发展路线图可以在此 issue 中找到。

受支持的后端#

确定性推理仅支持以下三种注意力后端:FlashInferFlashAttention 3 (FA3)Triton

下表显示了不同注意力后端在确定性推理方面的功能兼容性:

注意力机制后端

CUDA 图 (CUDA Graph)

分块预填充 (Chunked Prefill)

Radix 缓存 (Radix Cache)

非贪婪采样 (Temp > 0)

FlashInfer

✅ 是

✅ 是

❌ 否

✅ 是

FlashAttention 3 (FA3)

✅ 是

✅ 是

✅ 是

✅ 是

Triton

✅ 是

✅ 是

✅ 是

✅ 是

用法#

基本用法#

通过添加 --enable-deterministic-inference 标志来启用确定性推理

python3 -m sglang.launch_server \
    --model-path Qwen/Qwen3-8B \
    --attention-backend fa3 \
    --enable-deterministic-inference

服务器参数#

参数

类型/默认值

描述

--enable-deterministic-inference

标志;默认:禁用

使用批次不变操作启用确定性推理

--attention-backend

字符串;默认:fa3

选择注意力后端 (flashinfer, fa3, 或 triton)

示例配置#

Qwen3-8B#

python3 -m sglang.launch_server \
    --model-path Qwen/Qwen3-8B \
    --attention-backend flashinfer \
    --enable-deterministic-inference

Llama 模型#

python3 -m sglang.launch_server \
    --model-path meta-llama/Llama-3.1-8B-Instruct \
    --attention-backend fa3 \
    --enable-deterministic-inference

Qwen3-30B-A3B (MoE 模型)#

python3 -m sglang.launch_server \
    --model-path Qwen/Qwen3-30B-A3B \
    --attention-backend fa3 \
    --enable-deterministic-inference

非贪婪采样下的确定性推理 (Temperature > 0)#

SGLang 通过使用采样种子 (sampling seeds),即使在非贪婪采样下也支持确定性推理。这对于像 GRPO (Group Relative Policy Optimization) 这样的强化学习场景特别有用,因为这些场景需要多个多样化但可复现的响应。

默认行为#

默认情况下,SGLang 使用采样种子 42 以实现可复现的采样

import requests

response = requests.post(
    "https://:30000/generate",
    json={
        "text": "Tell me a joke",
        "sampling_params": {
            "temperature": 0.8,  # Non-greedy sampling
            "max_new_tokens": 128,
        },
    },
)
print(response.json())
# This will always produce the same response across runs

生成多个可复现的响应#

为了从同一提示词中采样出不同的响应,同时保持可复现性(例如用于 GRPO 训练),请在请求中提供不同的采样种子

import requests

# Prepare a list of sampling seeds for different responses
sampling_seeds = [42, 43, 44, 45, 46]

responses = []
for seed in sampling_seeds:
    response = requests.post(
        "https://:30000/generate",
        json={
            "text": "Tell me a joke",
            "sampling_params": {
                "temperature": 0.8,
                "max_new_tokens": 128,
                "sampling_seed": seed,  # Specify sampling seed
            },
        },
    )
    responses.append(response.json())

# Each seed will produce a different but reproducible response
# Using the same seed will always produce the same response

这种方法确保:

  • 不同的种子产生多样化的响应

  • 相同的种子在不同的运行中始终产生相同的响应

  • 结果对于调试和评估是可复现的

验证#

运行确定性测试以验证输出是否一致

# Single test: same prompt, varying batch sizes
python3 -m sglang.test.test_deterministic --test-mode single --n-trials 50

# Prefix test: prompts with different prefix lengths
python3 -m sglang.test.test_deterministic --test-mode prefix --n-trials 50

# Radix Cache Consistency mode: test radix cache determinism (cached vs uncached prefill)
python3 -m sglang.test.test_deterministic --test-mode radix_cache

预期结果:所有测试应显示 Unique samples: 1(完全确定的)。