Playbook

投机采样

- [Overview](#overview)

统一模板Markdown 驱动/speculative-decoding

投机采样

了解如何设置投机采样以在 Spark 上进行快速推理

目录


概述

基本思路

推测性解码通过使用 小而快速的模型 提前起草多个标记,然后让 更大的模型 快速验证或调整它们,从而加速文本生成。
这样,大模型不需要逐步预测每个令牌,从而在保持输出质量的同时减少延迟。

你将完成什么

您将使用两种方法在 NVIDIA Spark 上使用 TensorRT-LLM 探索投机采样:EAGLE-3 和 Draft-Target。
这些示例演示了如何在保持输出质量的同时加速大型语言模型推理。

为什么是两个火花?

单个 DGX Spark 具有在 CPU 和 GPU 之间共享的 128 GB 统一内存。这足以运行带有 EAGLE-3 的 GPT-OSS-120B 或带有 Draft-Target 的 Llama-3.3-70B 等模型,如 Instructions 选项卡中所示。

Qwen3-235B-A22B 等较大的模型超出了单个 Spark 在内存中的容量 - 即使使用 FP4 量化,模型权重、KV 缓存和 Eagle3 草稿头总共也需要超过 128 GB。通过连接两个 Spark,您可以将可用内存增加一倍,达到 256 GB,从而可以为这些更大的模型提供服务。

在两个 Sparks 上运行选项卡将逐步完成此设置。两个 Spark 通过 QSFP 电缆连接,并使用 张量并行性 (TP=2) 来分割模型 - 每个 Spark 保存每层权重矩阵的一半,并计算每个前向传递的其部分。节点使用 NCCL 和 OpenMPI 通过高带宽链路传送中间结果,因此该模型作为跨两个设备的单个逻辑实例运行。

简而言之:两个 Spark 可以让您运行对于一个来说太大的模型,而顶部的投机采样 (Eagle3) 通过并行起草和验证多个标记进一步加速推理。

开始之前需要了解什么

  • Docker 和容器化应用程序的经验
  • 了解推测性解码概念
  • 熟悉 TensorRT-LLM 服务和 API 端点
  • 了解大型语言模型的 GPU 内存管理

先决条件

  • 具有足够可用 GPU 内存的 NVIDIA Spark 设备

  • 启用 GPU 支持的 Docker

    docker run --gpus all nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc12 nvidia-smi
    
  • 用于模型访问的主动 HuggingFace 令牌

  • 用于模型下载的网络连接

时间与风险

  • 持续时间: 10-20 分钟用于设置,额外时间用于模型下载(因网络速度而异)
  • 风险: 大型模型的 GPU 内存耗尽、容器注册表访问问题、下载期间网络超时
  • 回滚: 停止 Docker 容器并可选择清理下载的模型缓存。
  • 最后更新: 2026 年 4 月 20 日
    • 升级到最新容器1.3.0rc12
    • 添加在两个 Spark 上使用 Qwen3-235B-A22B 进行投机采样的示例

指示

步骤1.配置Docker权限

要在不使用 sudo 的情况下轻松管理容器,您必须位于 docker 组中。如果您选择跳过此步骤,则需要使用 sudo 运行 Docker 命令。

打开新终端并测试 Docker 访问。在终端中,运行:

docker ps

如果您看到权限被拒绝错误(例如尝试连接到 Docker 守护进程套接字时权限被拒绝),请将您的用户添加到 docker 组,这样您就不需要使用 sudo 运行命令。

sudo usermod -aG docker $USER
newgrp docker

步骤2.设置环境变量

设置下游服务的环境变量:

export HF_TOKEN=<your_huggingface_token>

步骤 3. 运行投机采样方法

选项 1:EAGLE-3

通过执行以下命令来运行 EAGLE-3 投机采样:

docker run \
  -e HF_TOKEN=$HF_TOKEN \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  --rm -it --ulimit memlock=-1 --ulimit stack=67108864 \
  --gpus=all --ipc=host --network host \
  nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc12 \
  bash -c '
    hf download openai/gpt-oss-120b && \
    hf download nvidia/gpt-oss-120b-Eagle3-long-context \
        --local-dir /opt/gpt-oss-120b-Eagle3/ && \
    cat > /tmp/extra-llm-api-config.yml <<EOF
enable_attention_dp: false
disable_overlap_scheduler: false
enable_autotuner: false
cuda_graph_config:
    max_batch_size: 1
speculative_config:
    decoding_type: Eagle
    max_draft_len: 5
    speculative_model_dir: /opt/gpt-oss-120b-Eagle3/

kv_cache_config:
    free_gpu_memory_fraction: 0.9
    enable_block_reuse: false
EOF
    export TIKTOKEN_ENCODINGS_BASE="/tmp/harmony-reqs" && \
    mkdir -p $TIKTOKEN_ENCODINGS_BASE && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken
    trtllm-serve openai/gpt-oss-120b \
      --backend pytorch --tp_size 1 \
      --max_batch_size 1 \
      --extra_llm_api_options /tmp/extra-llm-api-config.yml'

服务器运行后,通过从另一个终端进行 API 调用来测试它:

## Test completion endpoint
curl -X POST http://localhost:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-oss-120b",
    "prompt": "Solve the following problem step by step. If a train travels 180 km in 3 hours, and then slows down by 20% for the next 2 hours, what is the total distance traveled? Show all intermediate calculations and provide a final numeric answer.",
    "max_tokens": 300,
    "temperature": 0.7
  }'

EAGLE-3 投机采样的主要特点

  • 更简单的部署 - EAGLE-3 没有管理单独的草稿模型,而是使用内置的草稿头在内部生成推测令牌。

  • 更高的准确性 - 通过融合模型多层的特征,草稿令牌更有可能被接受,从而减少浪费的计算。

  • 更快的生成 - 每个前向传递并行验证多个令牌,从而减少自回归推理的延迟。

选项 2:草案目标

执行以下命令来设置并运行草稿目标投机采样:

docker run \
  -e HF_TOKEN=$HF_TOKEN \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  --rm -it --ulimit memlock=-1 --ulimit stack=67108864 \
  --gpus=all --ipc=host --network host nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc12 \
  bash -c "
#    # Download models
    hf download nvidia/Llama-3.3-70B-Instruct-FP4 && \
    hf download nvidia/Llama-3.1-8B-Instruct-FP4 \
    --local-dir /opt/Llama-3.1-8B-Instruct-FP4/ && \

#    # Create configuration file
    cat <<EOF > extra-llm-api-config.yml
print_iter_log: false
disable_overlap_scheduler: true
speculative_config:
  decoding_type: DraftTarget
  max_draft_len: 4
  speculative_model_dir: /opt/Llama-3.1-8B-Instruct-FP4/
kv_cache_config:
  enable_block_reuse: false
EOF

#    # Start TensorRT-LLM server
    trtllm-serve nvidia/Llama-3.3-70B-Instruct-FP4 \
      --backend pytorch --tp_size 1 \
      --max_batch_size 1 \
      --kv_cache_free_gpu_memory_fraction 0.9 \
      --extra_llm_api_options ./extra-llm-api-config.yml
  "

服务器运行后,通过从另一个终端进行 API 调用来测试它:

## Test completion endpoint
curl -X POST http://localhost:8000/v1/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.3-70B-Instruct-FP4",
    "prompt": "Explain the benefits of speculative decoding:",
    "max_tokens": 150,
    "temperature": 0.7
  }'

草稿目标的主要特点:

  • 高效的资源利用:8B草稿模型加速70B目标模型
  • 灵活配置:可调整草案令牌长度以进行优化
  • 内存效率:使用 FP4 量化模型来减少内存占用
  • 兼容模型:使用具有一致标记化的 Llama 系列模型

步骤 4. 清理

完成后停止 Docker 容器:

## Find and stop the container
docker ps
docker stop <container_id>

## Optional: Clean up downloaded models from cache
## rm -rf $HOME/.cache/huggingface/hub/models--*gpt-oss*

步骤 5. 后续步骤

  • 尝试不同的 max_draft_len 值(1、2、3、4、8)
  • 监控令牌接受率和吞吐量改进
  • 使用不同的提示长度和生成参数进行测试
  • 阅读有关投机采样 here 的更多信息。

靠两个火花奔跑

步骤1.配置Docker权限

在 Spark A 和 Spark B 上运行:

sudo usermod -aG docker $USER
newgrp docker

步骤 2. 网络设置

按照 Connect Two Sparks 手册中的网络设置说明进行操作。

[!NOTE]
在继续之前,请先完成《连接两个 Spark》手册中的步骤 1-3:

  • 第 1 步:确保两个系统上的用户名相同
  • 步骤 2:物理硬件连接(QSFP 电缆)
  • 步骤 3:网络接口配置
    • 使用 选项 2:通过 netplan 配置文件手动分配 IP
    • 每个 Spark 有两对网络端口。当您在两个 Spark 之间物理连接电缆时,连接的端口将显示为 Up。您可以使用任一对 - enp1s0f0np0enP2p1s0f0np0,或 enp1s0f1np1enP2p1s0f1np1
    • 本剧本假设您使用 enp1s0f1np1enP2p1s0f1np1。如果您的 Up 接口不同,请在以下命令中替换您的接口名称

对于本剧本,我们将使用以下 IP 地址:

Spark A(节点 1):

  • enp1s0f1np1:192.168.200.12/24
  • enP2p1s0f1np1:192.168.200.14/24

Spark B(节点 2):

  • enp1s0f1np1:192.168.200.13/24
  • enP2p1s0f1np1:192.168.200.15/24

完成连接两个 Spark 设置后,返回此处继续 TRT-LLM 容器设置。

步骤 3. 设置容器名称变量

在 Spark A 和 Spark B 上运行:

export TRT LLM_MN_CONTAINER=trtllm-multinode

步骤4.启动TRT-LLM多节点容器

在 Spark A 和 Spark B 上运行:

docker run -d --rm \
  --name $TRT LLM_MN_CONTAINER \
  --gpus '"device=all"' \
  --network host \
  --ulimit memlock=-1 \
  --ulimit stack=67108864 \
  --device /dev/infiniband:/dev/infiniband \
  -e UCX_NET_DEVICES="enp1s0f1np1,enP2p1s0f1np1" \
  -e NCCL_SOCKET_IFNAME="enp1s0f1np1,enP2p1s0f1np1" \
  -e OMPI_MCA_btl_tcp_if_include="enp1s0f1np1,enP2p1s0f1np1" \
  -e OMPI_MCA_orte_default_hostfile="/etc/openmpi-hostfile" \
  -e OMPI_MCA_rmaps_ppr_n_pernode="1" \
  -e OMPI_ALLOW_RUN_AS_ROOT="1" \
  -e OMPI_ALLOW_RUN_AS_ROOT_CONFIRM="1" \
  -e CPATH="/usr/local/cuda/include" \
  -e TRITON_PTXAS_PATH="/usr/local/cuda/bin/ptxas" \
  -v ~/.cache/huggingface/:/root/.cache/huggingface/ \
  -v ~/.ssh:/tmp/.ssh:ro \
  nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc12 \
  bash -c "curl https://raw.githubusercontent.com/NVIDIA/dgx-spark-playbooks/refs/heads/main/nvidia/trt-llm/assets/trtllm-mn-entrypoint.sh | bash"

核实:

docker logs -f $TRT LLM_MN_CONTAINER

最后的预期输出:

total 56K
drwx------ 2 root root 4.0K Jan 13 05:13 .
drwx------ 1 root root 4.0K Jan 13 05:12 ..
-rw------- 1 root root  100 Jan 13 05:13 authorized_keys
-rw------- 1 root root   45 Jan 13 05:13 config
-rw------- 1 root root  411 Jan 13 05:13 id_ed25519
-rw-r--r-- 1 root root  102 Jan 13 05:13 id_ed25519.pub
-rw------- 1 root root  411 Jan 13 05:13 id_ed25519_shared
-rw-r--r-- 1 root root  100 Jan 13 05:13 id_ed25519_shared.pub
-rw------- 1 root root 3.4K Jan 13 05:13 id_rsa
-rw-r--r-- 1 root root  743 Jan 13 05:13 id_rsa.pub
-rw------- 1 root root 5.0K Jan 13 05:13 known_hosts
-rw------- 1 root root 3.2K Jan 13 05:13 known_hosts.old
Starting SSH

步骤 5. 配置 OpenMPI 主机文件

主机文件告诉 MPI 哪些节点参与分布式执行。使用步骤 2 中配置的 enp1s0f1np1 接口的 IP。

在 Spark A 和 Spark B 上,创建主机文件:

cat > ~/openmpi-hostfile <<EOF
192.168.200.12
192.168.200.13
EOF

在 Spark A 和 Spark B 上运行以将主机文件复制到每个容器中:

docker cp ~/openmpi-hostfile $TRT LLM_MN_CONTAINER:/etc/openmpi-hostfile

验证连接:

docker exec -it $TRT LLM_MN_CONTAINER bash -c "mpirun -np 2 hostname"

预期输出:

nvidia@spark-afe0:~$ docker exec -it $TRT LLM_MN_CONTAINER bash -c "mpirun -np 2 hostname"
Warning: Permanently added '[192.168.200.13]:2233' (ED25519) to the list of known hosts.
spark-afe0
spark-ae11
nvidia@spark-afe0:~$

步骤 6. 启动 Eagle3 投机采样

Eagle3 推测性解码通过提前预测多个标记,然后并行验证它们来加速推理。与标准自回归生成相比,这可以提供显着的加速。

设置您的拥抱脸标记

export HF_TOKEN=your_huggingface_token_here

在两个节点上下载 Eagle3 推测模型

docker exec \
  -e HF_TOKEN=$HF_TOKEN \
  -it $TRT LLM_MN_CONTAINER bash -c "
    mpirun -x HF_TOKEN -np 2 bash -c 'hf download nvidia/Qwen3-235B-A22B-Eagle3 --local-dir /opt/Qwen3-235B-A22B-Eagle3/'
"

创建 Eagle3 投机采样配置

此配置支持使用 3 个草案令牌和保守的内存设置进行 Eagle 投机采样。

docker exec -it $TRT LLM_MN_CONTAINER bash -c "cat > /tmp/extra-llm-api-config.yml <<EOF
enable_attention_dp: false
disable_overlap_scheduler: false
enable_autotuner: false
enable_chunked_prefill: false
cuda_graph_config:
    max_batch_size: 1
speculative_config:
    decoding_type: Eagle
    max_draft_len: 3
    speculative_model_dir: /opt/Qwen3-235B-A22B-Eagle3/
kv_cache_config:
    free_gpu_memory_fraction: 0.9
    enable_block_reuse: false
EOF
"

使用 Eagle3 投机采样启动服务器

仅在 Spark A 上运行。 这将使用启用了 Eagle3 投机采样的 FP4 基本模型启动 TensorRT-LLM API 服务器。 mpirun 命令协调两个节点之间的执行,因此只需要从 Spark A 启动。最大令牌长度设置为 1024(根据需要调整)。

docker exec \
  -e MODEL="nvidia/Qwen3-235B-A22B-FP4" \
  -e HF_TOKEN=$HF_TOKEN \
  -it $TRT LLM_MN_CONTAINER bash -c '
    mpirun -x CPATH=/usr/local/cuda/include \
           -x TRITON_PTXAS_PATH=/usr/local/cuda/bin/ptxas \
           -x HF_TOKEN \
           trtllm-llmapi-launch \
           trtllm-serve \
           $MODEL \
           --backend pytorch \
           --tp_size 2 \
           --max_num_tokens 1024 \
           --extra_llm_api_options /tmp/extra-llm-api-config.yml \
           --port 8355 --host 0.0.0.0
'

端点就绪时的预期输出:

[01/13/2026-06:16:56] [TRT-LLM] [I] get signal from executor worker
INFO:     Started server process [2011]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

步骤 7. 验证 API

仅在 Spark A 上运行。 服务器正在侦听 Spark A,因此从那里测试端点:

curl -s http://localhost:8355/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Qwen3-235B-A22B-FP4",
    "messages": [{"role": "user", "content": "Paris is great because"}],
    "max_tokens": 64
  }'

预期:带有生成文本的 JSON 响应。这证实了具有 Eagle3 投机采样功能的多节点 TensorRT-LLM 服务器正常工作。

步骤 8. 清理

停止容器

在 Spark A 和 B 上运行:

docker stop $TRT LLM_MN_CONTAINER

由于 --rm 标志,容器将被自动删除。

(可选)删除下载的模型

如果您需要释放磁盘空间:

在 Spark A 和 B 上运行:

rm -rf $HOME/.cache/huggingface/hub/models--nvidia--Qwen3*

这将删除模型文件(约数百 GB)。如果您打算再次运行安装程序,请跳过此步骤。

步骤 9. 后续步骤

现在您已经运行了 Eagle3 投机采样,请考虑以下优化和实验:

  • 调整草稿长度: 修改配置中的 max_draft_len (尝试 2-5 之间的值)以平衡推测速度与准确性
  • 尝试不同的模型: 尝试支持 Eagle 投机采样的其他模型对
  • 优化批量大小: 调整 cuda_graph_config 中的 max_batch_size 以实现吞吐量-延迟权衡
  • 了解更多: 查看 TensorRT-LLM Speculative Decoding documentation 以获取高级调整选项
  • 基准性能: 比较有和没有投机采样的推理速度,以测量加速增益

故障排除

症状原因使固定
“CUDA 内存不足”错误GPU显存不足kv_cache_free_gpu_memory_fraction 减少到 0.9 或使用具有更多 VRAM 的设备
容器无法启动Docker GPU 支持问题验证 nvidia-docker 已安装并且支持 --gpus=all 标志
模型下载失败网络或身份验证问题检查 HuggingFace 身份验证和网络连接
无法访问 URL 的门禁存储库某些 HuggingFace 模型的访问受到限制重新生成你的 HuggingFace token;并请求在您的网络浏览器上访问 gated model
服务器没有响应端口冲突或防火墙检查8000端口是否可用且未被阻塞
mpirun 失败并拒绝 SSH 连接容器或节点之间未配置 SSH从 Connect Two Sparks playbook 完成 SSH 设置;验证 ssh <node_ip> 无需密码即可从两个节点正常工作
mpirun 与远程节点的连接挂起或超时主机文件 IP 与实际节点 IP 不匹配验证 /etc/openmpi-hostfile 中的 IP 与分配给具有 ip addr show 的网络接口的 IP 匹配
NCCL 错误:“非套接字上的套接字操作”指定的网络接口错误检查 ibdev2netdev 输出并确保 NCCL_SOCKET_IFNAMEUCX_NET_DEVICES 与活动接口 enp1s0f1np1,enP2p1s0f1np1 匹配
mpirun 期间的 Permission denied (publickey)容器之间不交换 SSH 密钥从 Connect Two Sparks playbook 重新运行 SSH 设置或手动验证 /root/.ssh/authorized_keys 包含来自两个节点的公钥
在多节点设置中模型下载失败且无提示HF_TOKEN 未传播到 mpirun-e HF_TOKEN=$HF_TOKEN 添加到 docker exec 命令,将 -x HF_TOKEN 添加到 mpirun 命令

[!NOTE]
DGX Spark 使用统一内存架构 (UMA),可实现 GPU 和 CPU 之间的动态内存共享。
由于许多应用程序仍在更新以利用 UMA,因此即使在
DGX Spark 的内存容量。如果发生这种情况,请使用以下命令手动刷新缓冲区缓存:

sudo sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches'