Featured image of post llama.cpp DeepSeek V3 Deployment

llama.cpp DeepSeek V3 Deployment

使用 llama.cpp 部署 DeepSeek V3 模型

前言

本教程将介绍如何使用 llama.cpp 部署 DeepSeek V3 模型,首先是 32B 蒸馏版本,然后会尝试部署 671B 完整版本。以下是一些基础信息:

  • Ubuntu 20.04 amd64
  • Docker
  • Nvidia Container Toolkit
  • CUDA: 12.4
  • llama.cpp: b4743

llama.cpp 最大的优点就是兼容性好,不挑设备。vllm 虽然性能更强大,吞吐量高,但是必须 Ampere GPU 或者更高才能运行同样的模型。

需要管理员权限的环境配置

由于我使用的计算服务器没有外网,需要通过代理联网,所以 sudo 命令均带有-E 参数,以保留环境变量中的代理设置。

安装 Docker

建议 Ubuntu 20.04 或者更高,有必要的话先更新系统。目前 18.04 似乎也能运行,但是需要一点特殊处理。以下是官方文档

https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository

# Add Docker's official GPG key:
sudo -E apt-get update
sudo -E apt-get -y install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo -E curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo -E apt-get update
sudo -E apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

配置 Rootless Docker Daemon

以下是官方文档

https://docs.docker.com/engine/security/rootless/#prerequisites

sudo apt-get install -y dbus-user-session
sudo apt-get install -y uidmap
sudo apt-get install -y systemd-container
sudo apt-get install -y docker-ce-rootless-extras

这一步可选,关闭 Root Docker Daemon

sudo systemctl disable --now docker.service docker.socket
sudo rm /var/run/docker.sock

Ubuntu 18.04 的 slirp4netns 配置

Rootless Docker Daemon 需要一个用户态的网络支持,这需要用到slirp4netns。但是,该软件仅在 Ubuntu 19.10 后才能随 apt 安装,我们需要手动下载。

# 注意修改版本,推荐使用最新版
wget https://github.com/rootless-containers/slirp4netns/releases/download/v1.3.2/slirp4netns-x86_64
sudo mv slirp4netns-x86_64 /usr/local/bin/slirp4netns

安装或更新 Nvidia Driver 和 CUDA

根据 llama.cpp 的需求,推荐使用 12.4 的 CUDA,可以这样下载:

wget https://developer.download.nvidia.com/compute/cuda/12.4.1/local_installers/cuda_12.4.1_550.54.15_linux.run

接下来安装它,全部默认选项即可,选择Install

sudo sh cuda_12.4.1_550.54.15_linux.run

安装过程不会输出过程信息到终端,而是直接打印到文件,可以到以下文件看到安装过程的输出:

  • /var/log/nvidia-installer.log
  • /var/log/cuda-installer.log

安装 Nvidia Container Toolkit

以下是官方文档

https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo -E apt-get update
sudo -E apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place

一般用户的环境配置

配置和启动 Rootless Docker Daemon

首先,需要在配置后重新登录

接下来,使用 Docker 提供的脚本生成配置文件

dockerd-rootless-setuptool.sh install

修改.bashrc,添加环境变量

export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$UID/docker.sock

为 Docker Daemon 配置代理

这一步可选,因为前面提到的网络原因,docker pull等操作需要配置代理服务器。打开$HOME/.config/systemd/user/docker.service,添加代理服务器

[Service]
Environment=PATH=...
Environment="HTTP_PROXY=???"
Environment="HTTPS_PROXY=???"
Environment="NO_PROXY=10.10.10.10,*.my.registry.com"
...

接下来重新启动 Rootless Docker Daemon

systemctl --user daemon-reload
systemctl --user restart docker

配置和启动 Nvidia Container Toolkit

配置 Nvidia CTK 运行时

nvidia-ctk runtime configure --runtime=docker --config=$HOME/.config/docker/daemon.json
systemctl --user restart docker

测试配置是否成功

应该和 nvidia-smi 的效果一样,看到所有的 GPU,注意不需要 sudo 或者添加用户到 docker 用户组

docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

部署 DeepSeek-R1-Distill-Qwen-32B

初步测试

测试配置:4 x 2080Ti 11GB

下载模型

pip install huggingface_hub
mkdir -p models/models--unsloth--DeepSeek-R1-Distill-Qwen-32B-GGUF

HF_ENDPOINT=https://hf-mirror.com \
  huggingface-cli download \
  unsloth/DeepSeek-R1-Distill-Qwen-32B-GGUF \
  DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf \
  --local-dir models/models--unsloth--DeepSeek-R1-Distill-Qwen-32B-GGUF

拉取镜像,也可以在 run 的时候自动拉取。

docker pull ghcr.io/ggml-org/llama.cpp:server-cuda

启动服务器

docker run \
  --rm --gpus all -p 8080:8000 -v ./models:/models \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -m /models/models--unsloth--DeepSeek-R1-Distill-Qwen-32B-GGUF/DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf \
  --port 8000 --host 0.0.0.0 -t 8 -c 32768 -n 8192 --n-gpu-layers 512

资源占用:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:3B:00.0 Off |                  N/A |
| 31%   35C    P2             63W /  250W |   10092MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:5E:00.0 Off |                  N/A |
| 31%   37C    P2             60W /  250W |    9486MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   2  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:AF:00.0 Off |                  N/A |
| 31%   37C    P2             43W /  250W |    9486MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   3  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:D8:00.0 Off |                  N/A |
| 31%   36C    P2             62W /  250W |    9922MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

推理速度

prompt eval time =     544.32 ms /   233 tokens (    2.34 ms per token,   428.06 tokens per second)
       eval time =   16309.46 ms /   346 tokens (   47.14 ms per token,    21.21 tokens per second)
      total time =   16853.77 ms /   579 tokens

由于我使用的服务器是双路 CPU,没有 NVLink,存在 NUMA 问题,所以通讯速度很差。这个时候,如果使用-sm row参数,会导致速度降低到 15tokens/s。

进一步压榨性能

通过以下参数,可以进一步压榨 GPU 的能力:

  • -fa:使用 Flash Attention 可节约动态内存,提升推理速度
  • -ctk q8_0 -ctv q8_0:使用量化 KV Cache ,进一步节约内存
  • -c 128000:增加上下文窗口长度,允许模型进行更长的对话,处理更长的输入内容
  • --n-gpu-layers 512:尽可能多地将模型参数放入显存,可以提升推理速度
docker run \
  --rm --gpus all -p 8080:8000 -v ./models:/models \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -m /models/models--unsloth--DeepSeek-R1-Distill-Qwen-32B-GGUF/DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf \
  --port 8000 --host 0.0.0.0 -t 8 --n-gpu-layers 512 -c 128000 -n 8192 -fa -ctk q8_0 -ctv q8_0

资源占用:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:3B:00.0 Off |                  N/A |
| 31%   33C    P8             21W /  250W |   10847MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   1  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:5E:00.0 Off |                  N/A |
| 31%   36C    P8             18W /  250W |    9479MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   2  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:AF:00.0 Off |                  N/A |
| 31%   34C    P8              1W /  250W |    9479MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   3  NVIDIA GeForce RTX 2080 Ti     On  |   00000000:D8:00.0 Off |                  N/A |
| 31%   35C    P8             22W /  250W |    9947MiB /  11264MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

推理速度

prompt eval time =     490.96 ms /   232 tokens (    2.12 ms per token,   472.54 tokens per second)
       eval time =   11767.64 ms /   254 tokens (   46.33 ms per token,    21.58 tokens per second)
      total time =   12258.60 ms /   486 tokens

部署 DeepSeek-R1-UD-IQ1_M

测试配置:4 x V100 32GB

这个配置对于 671B 的模型来说非常丐,属于非常极限刚好能运行,推理速度很慢。

下载模型,模型是分成三个文件的,这是因为 git-lfs 的限制为 50GB

HF_ENDPOINT=https://hf-mirror.com \
  huggingface-cli download \
  unsloth/DeepSeek-R1-GGUF \
  --include "*UD-IQ1_S*" \
  --local-dir models/DeepSeek-R1-UD-IQ1_S

启动服务器,llama.cpp 会自动加载剩余的两个模型分片文件

docker run \
  --rm --gpus all -p 8080:8000 -v ./models:/models \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -m /models/DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
  --port 8000 --host 0.0.0.0 -t 8 -c 8192 -n 4096 \
  --n-gpu-layers 40 -ts 14,16,16,16 -ctk q8_0

这里的参数与先前有所不同:

  • -fa:DeepSeek-R1 模型使用 MLA,与 Flash Attention 不兼容
  • -ctk q8_0:对于 MLA 模型,只有 k 可以量化
  • --n-gpu-layers 40:只保留 40 层在显存中
  • -ts 14,16,16,16:DeepSeek-R1 默认状态下,GPU 0 的负载会稍高,通过调节切分参数,可以平衡显存占用

资源占用:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  Tesla V100-SXM2-32GB           On  |   00000000:00:05.0 Off |                    0 |
| N/A   41C    P0             55W /  300W |   30042MiB /  32768MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   1  Tesla V100-SXM2-32GB           On  |   00000000:00:06.0 Off |                    0 |
| N/A   39C    P0             57W /  300W |   29576MiB /  32768MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   2  Tesla V100-SXM2-32GB           On  |   00000000:00:07.0 Off |                    0 |
| N/A   41C    P0             54W /  300W |   29576MiB /  32768MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
|   3  Tesla V100-SXM2-32GB           On  |   00000000:00:08.0 Off |                    0 |
| N/A   40C    P0             55W /  300W |   29576MiB /  32768MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

推理速度

prompt eval time =   13482.15 ms /   226 tokens (   59.66 ms per token,    16.76 tokens per second)
       eval time =   87209.41 ms /   270 tokens (  323.00 ms per token,     3.10 tokens per second)
      total time =  100691.56 ms /   496 tokens

总结

llama.cpp 对于老旧硬件的支持非常好,像 V100,2080Ti 这样比较老旧的 GPU,也能运行最新的模型。
然而,671B 的模型还是太大了,为了能运行在 V100 上,只能将一部分参数放在 CPU 内存,这进一步降低了推理速度。这个部署尝试更像是一种极限运动,缺乏实用性。个人认为:

  • 解码速度需要达到 10token/s 才能算是比较流畅
  • 上下文窗口 8192,才能满足联网查询和深度思考的要求

如果需要自己部署,还是推荐使用 DeepSeek-R1-Distill-Qwen-32B-GGUF。在 Q4 量化下,使用一个 RTX A6000 就能进行推理,并享受到完整的 128k 长上下文,是比较实用的方案。

docker run \
  --rm --gpus all -p 8080:8000 -v ./models:/models \
  ghcr.io/ggml-org/llama.cpp:server-cuda \
  -m /models/models--unsloth--DeepSeek-R1-Distill-Qwen-32B-GGUF/DeepSeek-R1-Distill-Qwen-32B-Q4_K_M.gguf \
  --port 8000 --host 0.0.0.0 -t 8 --n-gpu-layers 512 -c 131072 -n 8192 -fa -ctk q8_0 -ctv q8_0

资源占用:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15              Driver Version: 550.54.15      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA RTX A6000               Off |   00000000:01:00.0 Off |                  Off |
| 30%   35C    P8             64W /  300W |   36649MiB /  49140MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

推理速度

prompt eval time =     271.75 ms /   233 tokens (    1.17 ms per token,   857.42 tokens per second)
       eval time =    7982.01 ms /   239 tokens (   33.40 ms per token,    29.94 tokens per second)
      total time =    8253.75 ms /   472 tokens