IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    加速大模型应用落地:DeepSeek-R1满血版分布式自建实战

    admin发表于 2025-03-17 06:29:50
    love 0

    作者:suliuer

    在开源大模型领域,DeepSeek-R1作为拥有超过600亿参数的混合专家(MoE)模型,凭借其强大的语言理解和卓越的性能表现成为当前最受关注的开源大模型之一。本文将基于官方推荐的 SGLang 推理框架,详细介绍如何使用单节点和多节点自建满血版 DeepSeek-R1 大模型。

    SGlang框架概述

    SGlang是一个高性能推理引擎,支持混合专家(MoE)类型的大语言模型,如DeepSeek-V3/R1。SGlang 支持多节点张量并行计算,能够在多服务器上协同工作,从而满足大规模模型的部署需求。

    选择 SGLang 推理引擎理由

    从官方说明文档可以看出,SGLang 是 DeepSeek 官方推荐的推理框架。SGLang 和 DeepSeek 团队合作,全面支持 DeepSeek-V3 模型的 BF16 和 FP8 推理模式,同时 SGLang 还支持多节点张量并行、MLA 优化、DP Attention 等,在开源框架中提供顶级的延迟和吞吐量性能,这使得 SGLang 成为运行 DeepSeek 模型的最佳开源大模型引擎之一。

    图:来自https://github.com/deepseek-ai/DeepSeek-V3。

    图:来自https://github.com/sgl-project/sglang/releases。

    GPU通信

    在部署 DeepSeek-R1之前,我们先了解一下 GPU 的相关通信技术。GPU 之间的高效通信能够显著提升大模型的推理速度和资源利用率,无论是单机内部的 NVLink 和 NVSwitch,还是多机之间的 RDMA 网络技术,它们都为 GPU 的高效协同工作提供了坚实的基础。

    单机 GPU 通信

    在高性能计算和人工智能应用中,NVIDIA 开发的NVLink 和 NVSwitch 技术,用于提高服务器内部 GPU 之间的通信效率。

    NVLink 是同主机内不同 GPU 之间的一种高速互联方式,用于增加 GPU 与 GPU 之间或者 GPU 与 CPU 之间的数据传输速率。它提供了比传统 PCIe 接口更高的带宽和更低的延迟,使得多个 GPU 可以高效地协同工作。

    NVSwitch 则是一个扩展了 NVLink 功能的专用交换芯片,它允许在一个系统内部署更多的 GPU,并让这些 GPU 之间实现全互联 (full mesh),从而进一步提升多 GPU 系统的可扩展性和性能。NVSwitch 通过提供大量的 NVLink 端口来支持这种高度互联的架构。

    图:八卡服务器 NVIDIA GPU 之间通过 NVLink互连的物理拓扑信息。

    多机 GPU 通信

    当分布式计算任务(如训练或推理大模型)跨越多个物理服务器时,RDMA(Remote Direct Memory Access)网络技术成为了提升 GPU 间通信性能的关键。

    什么是 RDMA

    RDMA(Remote Direct Memory Access)即远程直接内存访问,是一种使数据能直接从一台计算机的内存传输到另一台计算机内存的网络传输技术,无需双方操作系统介入,可实现高吞吐、低延迟的网络通信,特别适用于大规模并行计算机集群,能有效提升应用系统性能。

    图:TCP/IP与RDMA通信方式对比。

    RDMA 的必要性

    在多节点分布式推理场景中,GPU 之间需要频繁地进行数据通信以同步计算结果和传递梯度信息。传统的网络通信方式(如 TCP/IP)在这种高频率、大数据量的通信场景下存在明显的性能瓶颈,例如高延迟、低带宽和高 CPU 占用率等问题。RDMA 技术通过允许网络中的不同节点直接访问对方内存,无需 CPU 的深度参与,从而极大地降低了通信延迟和 CPU 负载,提高了数据传输的效率和带宽利用率。这对于需要在多个 GPU 之间快速传递大量数据的大模型推理任务来说是至关重要的。

    RDMA 对推理大模型的重要性

    对于像 DeepSeek-R1 参数量巨大的大规模语言模型,推理过程中需要处理大量的数据并进行复杂的计算。在多节点环境下,模型的不同部分分布在不同的 GPU 上,这些 GPU 之间需要高效地通信以完成整个模型的推理过程。RDMA 的低延迟和高带宽特性使得 GPU 之间的通信更加迅速和高效,从而减少了因通信延迟导致的计算资源浪费,提高了整个系统的推理速度和吞吐量。此外,RDMA 还可以减少因通信引起的 CPU 干扰,使得 CPU 能够更专注于其他计算任务,进一步提升了系统的整体性能。

    基础环境

    DeepSeek-R1 模型下载(两种方式选择)。

    # HuggingFace
    git clone https://huggingface.co/deepseek-ai/DeepSeek-R1

    # ModelScope
    git clone https://www.modelscope.cn/deepseek-ai/DeepSeek-R1.git

    SGLang 环境部署。

    # conda 环境
    conda create --name sglang python=3.10
    conda activate sglang

    # sgalng 部署
    pip install "sglang[all]>=0.4.3.post2" --find-links https://flashinfer.ai/whl/cu124/torch2.5/flashinfer-python

    # 一个官方 bug,目前兼容的 transformers 版本是 4.48.3
    # 安装 sglang 之后 transformers 版本是 4.49,将其重装为 4.48.3
    pip uninstall transformers -y
    pip install transformers==4.48.3

    大模型部署

    SGLang 原生支持跨机多节点部署,下面我们分别在单节点和多节点服务器部署线上环境满血版 DeepSeep-R1大模型。

    单节点八卡部署

    服务器环境:NVIDIA H20(96G)*8*1。

    # DeepSeek-R1-671B 单节点部署

    python3 -m sglang.launch_server \
    --model-path /data/llm/DeepSeek-R1 \
    --served-model-name DeepSeek-R1 \
    --tp 8 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --mem-fraction-static 0.95 \
    --max-running-requests 32 \
    --context-length 65536 \
    --quantization fp8 \
    --chunked-prefill-size 4096

    参数说明:

    --model-path /data/llm/DeepSeek-R1
    指定模型权重文件的本地路径,SGLang 从该目录加载模型结构与权重。

    --served-model-name DeepSeek-R1
    ​自定义 API 中显示的模型名称,与客户端请求的 model 字段匹配。

    --trust-remote-code
    信任远程代码,允许加载模型目录中的自定义代码(如 modeling_deepseek.py)。

    --tp 8
    启用 8 路张量并行(Tensor Parallelism)实现并行处理,提高推理速度。

    --host 0.0.0.0 --port 30000
    ​服务监听所有 IP 的 30000 端口,支持跨机器访问。

    --mem-fraction-static 0.95
    ​预留 95% 显存用于模型计算,5% 动态分配防止碎片,并发场景可降至 0.8-0.9,若出现 OOM 需降低该值。

    --context-length 65536
    设置模型支持的最大上下文长度(64K)

    --chunked-prefill-size 1024
    将长输入分块处理(每块 1024 Token),避免 OOM 并提升计算效率。64K 上下文推荐 1024-4096,过小会增加调度开销。

    --quantization fp8
    指定模型的量化精度为 FP8,DeepSeek-R1 原生版本的模型权重为 FP8 数据格式。

    --max-running-requests 32
    ​限制并发处理请求数,防止显存过载。

    两节点十六卡部署

    多节点部署的前置条件与单机部署一致,每个节点均需提前下载好模型文件,然后在每个节点执行启动命令。启动命令时,先启动主节点,再启动剩余节点。在服务启动后,发起请求时需指定为主节点 IP。

    服务器环境:NVIDIA H20(96G)*8*2,RDMA 通信。

    # DeepSeek-R1-671B 分布式多节点部署

    # 第一个节点
    python3 -m sglang.launch_server \
    --model-path /data/llm/DeepSeek-R1 \
    --served-model-name DeepSeek-R1 \
    --tp 16 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --mem-fraction-static 0.95 \
    --max-running-requests 64 \
    --context-length 131072 \
    --quantization fp8 \
    --chunked-prefill-size 2048 \
    --dist-init-addr 10.10.10.10:40000 \
    --nnodes 2 \
    --node-rank 0

    # 第二个节点
    python3 -m sglang.launch_server \
    --model-path /data/llm/DeepSeek-R1 \
    --served-model-name DeepSeek-R1 \
    --tp 16 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --mem-fraction-static 0.95 \
    --max-running-requests 64 \
    --context-length 131072 \
    --quantization fp8 \
    --chunked-prefill-size 2048 \
    --dist-init-addr 10.10.10.10:40000 \
    --nnodes 2 \
    --node-rank 1

    参数说明:

    --dist-init-addr 10.x.x.x:40000:
    用于指定分布式推理的初始化地址。在分布式部署中,所有节点需要通过这个地址进行通信和同步。主节点通过这个地址初始化分布式环境,其他节点通过这个地址连接到主节点,从而实现分布式推理。可以设置为第一台启动节点的 IP。

    --nnodes 2
    指定整个分布式推理集群中的总节点数。例如,如果使用两台机器进行分布式推理,则该的值应为 2。

    --node-rank
    指定当前节点在分布式训练中的排序(ID)。这个排名是从 0 开始的整数,例如在两节点的集群中,第一个节点的 --node-rank 为 0,第二个节点的 --node-rank 为 1。

    四节点八卡部署(非RDMA)

    作为对照,我们使用四节点服务器(每台服务器 H20*2,共 8 卡),基于普通网络分布式部署满血版 DeepSeep-R1。

    服务器环境:H20*2*4

    显卡通信方式:普通以太网通信,非RDMA,需要添加 GLOO_SOCKET_IFNAME=eth0 ,该参数是分布式通信的环境变量配置,用于显式指定分布式推理中节点间通信使用的网络接口。
    🚫注意:该部署只用于测试验证对比,不用于实际生产环境。
    # DeepSeek-R1-671B

    # 第一个节点
    GLOO_SOCKET_IFNAME=eth0 python3 -m sglang.launch_server \
    --model-path /disk2/llm/DeepSeek-R1/ \
    --served-model-name DeepSeek-R1 \
    --tp 8 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --dist-init-addr 10.10.10.10:40000 \
    --mem-fraction-static 0.95 \
    --disable-cuda-graph \
    --nnodes 4 \
    --node-rank 0

    # 第二个节点
    GLOO_SOCKET_IFNAME=eth0 python3 -m sglang.launch_server \
    --model-path /disk3/llm/DeepSeek-R1/ \
    --served-model-name DeepSeek-R1 \
    --tp 8 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --dist-init-addr 10.10.10.10:40000 \
    --mem-fraction-static 0.95 \
    --disable-cuda-graph \
    --nnodes 4 \
    --node-rank 1

    # 第三个节点
    GLOO_SOCKET_IFNAME=eth0 python3 -m sglang.launch_server \
    --model-path /disk2/llm/DeepSeek-R1/ \
    --served-model-name DeepSeek-R1 \
    --tp 8 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --dist-init-addr 10.10.10.10:40000 \
    --mem-fraction-static 0.95 \
    --disable-cuda-graph \
    --nnodes 4 \
    --node-rank 2

    # 第四个节点
    GLOO_SOCKET_IFNAME=eth0 python3 -m sglang.launch_server \
    --model-path /disk2/llm/DeepSeek-R1/ \
    --served-model-name DeepSeek-R1 \
    --tp 8 \
    --host 0.0.0.0 \
    --port 30000 \
    --trust-remote-code \
    --dist-init-addr 10.10.10.10:40000 \
    --mem-fraction-static 0.95 \
    --disable-cuda-graph \
    --nnodes 4 \
    --node-rank 3

    运行状况

    启动大模型时长:大约 40-60 分钟;

    集群显卡通信依赖普通网通信,在推理过程中网络流量高,在 30 个并发进程下服务器网络带宽达 4Gbps。

    大模型调用

    curl 代码:

    # curl 代码
    curl -X POST http://localhost:30000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
    "model": "DeepSeek-R1",
    "messages": [
    {
    "role": "user",
    "content": "你好,你是谁?"
    }
    ],
    "stream": true,
    "temperature": 0.6
    }'

    python 代码:

    # OpenAI 标准接口

    from openai import OpenAI

    client = OpenAI(api_key="<DeepSeek API Key>", base_url="http://localhost:30000/v1/")

    response = client.chat.completions.create(
    model="DeepSeek-R1",
    messages=[
    {"role": "system", "content": "You are a helpful assistant"},
    {"role": "user", "content": "写一首散文诗。"},
    ],
    stream=True
    )

    for chunk in response:
    print(chunk.choices[0].delta.content, end="", flush=True)

    性能评估

    性能指标

    • 首个词元生成时间(Time To First Token,简称TTFT):在用户输入查询的内容后,模型生成第一个输出token所需要的时间。这个指标直观反映了模型的初始响应速度,是衡量模型在实际应用中能否快速给出反馈的关键因素。例如在实时对话系统中,较短的 TTFT 能让用户更快地感受到模型的回应,增强交互的流畅性。
    • 单个输出词元的生成时间(Time Per Output Token,简称TPOT):表示推理系统根据用户请求生成后续词元的平均时间。TPOT 与用户对模型反应速度的感知紧密相连。在人机实时交互场景下,快速响应至关重要。过高的延时会使用户等待时间过长,严重影响交互体验,但只要生成速度超过人类阅读速度,就能获得良好的用户体验。
    • 吞吐量:指推理服务器在处理所有用户和请求时,每秒能够生成的输出词元数量。吞吐量是评估服务器整体性能和效率的关键指标,反映了服务器在高负载情况下的处理能力。较高的吞吐量意味着服务器能够在相同时间内处理更多的请求,适用于大规模用户同时使用模型的场景,如在线客服、内容推荐等。

    性能对比

    下载基准测试参考的数据集 ShareGPT_V3_unfiltered_cleaned_split.json (ShareGPT V3清洗版),SGLang 的基准测试通常基于此数据集评估模型在多样化对话场景下的性能。

    # 下载评估数据集
    git lfs clone https://www.modelscope.cn/datasets/gliang1001/ShareGPT_V3_unfiltered_cleaned_split.git

    使用 sglang.bench_serving 进行性能测试。

    # 性能测试命令

    python3 -m sglang.bench_serving --backend sglang --dataset-name random --random-range-ratio 1 --tokenizer /data/llm/DeepSeek-R1 --host 127.0.0.1 --port 30000 --output-file "ds-r1-bench.json" --dataset-path ./ShareGPT_V3_unfiltered_cleaned_split.json --random-input 1000 --random-output 3000 --max-concurrency 1 --num-prompts 32

    # 输出
    ============ Serving Benchmark Result ============
    Backend: sglang
    Traffic request rate: inf
    Max reqeuest concurrency: 1
    Successful requests: 32
    Benchmark duration (s): 3513.98
    Total input tokens: 32000
    Total generated tokens: 96000
    Total generated tokens (retokenized): 95703
    Request throughput (req/s): 0.01
    Input token throughput (tok/s): 9.11
    Output token throughput (tok/s): 27.32
    Total token throughput (tok/s): 36.43
    Concurrency: 1.00
    ----------------End-to-End Latency----------------
    Mean E2E Latency (ms): 109811.32
    Median E2E Latency (ms): 107513.41
    ---------------Time to First Token----------------
    Mean TTFT (ms): 348.89
    Median TTFT (ms): 346.58
    P99 TTFT (ms): 408.93
    -----Time per Output Token (excl. 1st token)------
    Mean TPOT (ms): 36.50
    Median TPOT (ms): 35.74
    P99 TPOT (ms): 44.16
    ---------------Inter-token Latency----------------
    Mean ITL (ms): 36.50
    Median ITL (ms): 35.95
    P99 ITL (ms): 46.25
    ==================================================

    下面我们选取输出 token 速率、平均首 token 时延(TTFT)和QPM三个指标综合对比:
    SGLang 测试对比 输入:1K 输出:3K
    并发数 输出 token 速率(tokens/s) 平均首 token 时延 TTFT(s) 每分钟查询次数 QPM
    单机八卡 四机八卡 单机八卡 四机八卡 单机八卡 四机八卡
    1 27.32 5.30 0.349 5.570 0.55 0.106
    20 234.86 83.26 2.718 76.095 4.697 1.67
    25 245.25 90.29 3.276 89.578 4.904 1.806
    32 382.01 107.47 4.917 103.616 7.64 2.149

    使用程序实际并发请求(并发数30),四节点八卡成功处理请求数少 40%,且平均耗时多一倍。

    图:四节点八卡和单节点八卡实际请求对比。

    从基准测试结果来看,单机八卡在各个并发数下的性能均显著优于四机八卡 H20(非 RDMA 通信)。具体表现在:

    • 输出 token 速率:单机八卡在所有并发数下的输出速率均远高于四机八卡(相差 3.5 倍),最高达到 382.01 tokens/s,而四机八卡最高仅为 107.47 tokens/s。
    • 平均首 token 时延:单机八卡的时延明显低于四机八卡,即使在高并发数下,也能保持在较低水平,如 32 并发时为 4.917 秒,而四机八卡则高达 103.616 秒(相差 21 倍)。
    • 每分钟查询次数 QPM:单机八卡的 QPM 值也远高于四机八卡,这意味着其在单位时间内能处理更多的查询。

    结论:单机八卡凭借其内部高效的通信机制(如 NVLink),在推理性能上具有显著优势。四机八卡由于仅使用普通以太网通信,跨节点通信开销吞噬了多机扩展的理论收益,通信效率低下,导致整体性能较差。在实际应用中,若对推理性能有较高要求,建议优先选择单机多卡部署方式;若需多机部署,应考虑配置 RDMA 网络以提升通信效率。

    问题记录

    思考过程丢失问题

    DeepSeek-R1 官方模型部署之后会有一定机率丢失思考过程。前段时间 DeepSeek-R1 官方修改了配置文件的聊天模板,在 tokenizer_config.json 文件的 chat_template 字段加了 <think>\\n ,该字段可以强制使回答有思考过程:

    # 原来
    {{'<|Assistant|>'}}

    # 修改之后
    {{'<|Assistant|><think>\\n'}}

    过实测对比:

    • 强制加了 <think>\\n 开头的,所有正常请求的 100% 都会输出思考过程,就是会丢失 <think> 左标签(可以通过在调用之后补全);
    • 聊天模板不加 <think>\\n 的,则有可能会丢失思考过程(特别是简单问题),丢失率 大约45%。

    图:添加模板是否添加<think>\\n 对比。

    参考:https://huggingface.co/deepseek-ai/DeepSeek-R1/commit/8a58a132790c9935686eb97f042afa8013451c9f

    RDMA 通信缺失问题

    RuntimeError: Gloo connectFullMesh failed with

    [rank0]:[E304 22:28:11.132616482 ProcessGroupGloo.cpp:143] Gloo connectFullMesh failed with [../third_party/gloo/gloo/transport/tcp/pair.cc:144] no error
    [2025-03-04 22:28:11 TP0] Scheduler hit an exception: Traceback (most recent call last):
    File "/data/miniconda3/root/envs/sglang/lib/python3.10/site-packages/sglang/srt/managers/scheduler.py", line 1816, in run_scheduler_process
    scheduler = Scheduler(server_args, port_args, gpu_id, tp_rank, dp_rank)
    ...
    backend_class = ProcessGroupGloo(
    RuntimeError: Gloo connectFullMesh failed with [../third_party/gloo/gloo/transport/tcp/pair.cc:144] no error
    ​
    [2025-03-04 22:28:11] Received sigquit from a child proces. It usually means the child failed.
    Killed

    解决:添加参数 GLOO_SOCKET_IFNAME=eth0,基于以太网 socket 通信。

    GLOO_SOCKET_IFNAME=eth0 python3 -m sglang.launch_server --model-path /disk1/llm/DeepSeek-R1-Distill-Qwen-1.5B --served-model-name DeepSeek-R1-1.5B --tp2--host0.0.0.0 --port30001--trust-remote-code--dist-init-addr10.91.4.9:40000 --nnodes2--node-rank0

    transformers 版本兼容问题

    cannot import name ‘is_valid_list_of_images’

    解决:参考:https://github.com/sgl-project/sglang/issues/3878

    pip uninstall transformers
    pip install transformers==4.48.3

    张量并行数需被注意力头数整除

    assert self.total_num_heads % tp_size == 0
    AssertionError​

    该错误是因为张量并行数(tp_size)与模型注意力头数不匹配导致的。查看大模型目录中config.json 文件的 num_attention_heads 字段,调整张量并行的数量能被该字段整除即可。

    多机部署扩容问题

    在基于 SGLang 分布式多机部署满血版 DeepSeek-R1 时,需要提前规划好服务器总节点数量和 GPU 数量,然后根据规划好的信息配置启动参数,比如 GPU 张量并行数量(–tp)和总节点数量(–nnodes)等。如果需要对集群进行扩容,通常的做法是增加新的 GPU 集群,然后通过负载均衡机制,将请求合理地分配到多个 GPU 集群上,以充分利用新增的计算资源,提高整个系统的处理能力。

    总结

    本文详细介绍了基于 SGLang 框架自建 DeepSeek-R1 满血版的全过程,从环境搭建到模型部署,再到性能评估与问题解决。通过 SGLang 强大的多节点张量并行计算能力,我们能够高效地部署和运行 DeepSeek-R1 这样参数量巨大的模型,并在实际应用中展现出卓越的推理性能。下来我们会持续跟进最新的开源大模型前沿技术发展,为应用的快速落地和性能提升提供更有力的支持。



沪ICP备19023445号-2号
友情链接