KTransformer:把 DeepSeek R1 推理机器成本打下来

大模型向量数据库机器学习

点击下方

卡片

,关注“

慢慢学AIGC

前言

前面几篇 DeepSeek R1 本地部署和测评文章:

DeepSeek 本地部署最佳实践(一):Ollama+Open WebUI + 蒸馏版

DeepSeek 本地部署最佳实践(二):llama.cpp + Open WebUI 部署 unsloth 量化版模型

DeepSeek 本地部署最佳实践(三):SGLang + 分布式推理部署满血版模型

DeepSeek 本地部署——蒸馏版、量化版和满血版实测效果对比

对比了不同版本 DeepSeek R1 的特点和部署需求,最后形成的基本结论是满血版效果最好,部署成本最高,而量化版居中,蒸馏版部署成本最低但效果也最差。

DeepSeek 云端部署成本估算

DeepSeek R1 满血版部署推荐配置需要 2 机 16 卡 H100 或 H800(显存容量 > 700 GB),但这个价格嘛,我们拿国内某云的价格为例:

picture.image

单机 8 卡 H100/H800 每个月租金 7.5 万元,2 机 16 卡就是 15 万元,平均每天 5000 元,每小时 208 元,对于普通人来说太奢侈了。

有朋友在单机 8 卡 H20(显存 96*8 GB) 上跑通了 671B 模型 AWQ 量化版:

picture.image

月租金降低至 5 万元上下。如果进一步采用 unsloth 1.58 bit 量化版模型,由于显存开销只要 131 GB 左右,因此可以在 8 卡或 16 卡 3090 上部署,成本降低到万元左右。

这价格已经降到最低了吗?

不,还能降!

KTransformers 简介

清华大学 KVCache.AI 团队(同时也是 Mooncake PD 分离推理框架的开发团队)与趋境科技于 2 月 10 日推出了 KTransformers 开源项目,通过一系列创新技术,使得只需一块 24G 显存的 4090D 显卡便能在本地运行全参数的 DeepSeek-R1,并实现了 286 tokens/s 的 Prefill 速度和 14tokens/s 的生成速度。我们在某云查看 4090 的价格:

picture.image

根据上面价格,单卡 4090 月租金算下来不到 1400 元,将 DeepSeek R1 满血版推理成本降低到千元量级!

不过,别高兴太早,理想丰满,现实还有很多坑要填。

官方推荐的机器配置如下:

picture.image

从上表可见 Q4 量化版 R1 和 V3 虽然显存(VRAM)需求较低,只有 14 GB,但对于内存(DRAM)需求高达 382 GB,推荐 512 GB。某云提供的 4090 实例只有 100 GB 内存,显然无法满足。笔者搜遍了全网也没找到单卡搭配如此高的内存配置,除非购买多卡实例(4 卡或 8 卡),那成本又要翻几倍,千元级变万元级,不划算。后面性能实测你还会看到,GPU 利用率也不高。

我们先简单了解下 KTransformers 这个项目。

Github 代码仓库:

https://github.com/kvcache-ai/ktransformers

picture.image

KTransformers(发音为 Quick Transformers)旨在通过先进的内核优化、设备放置及并行策略,为 🤗 Transformers 体验提供增强支持。

KTransformers 是一个以 Python 为中心的灵活框架,其核心设计强调可扩展性。用户仅需一行代码即可实现并注入优化模块,便可获得与 Transformers 兼容的接口。

KTransformers 的核心之一在于应用了异构计算策略,利用 DeepSeek 混合专家(MoE)架构的稀疏性和计算复杂度低的特点,将 MoE 部分放在内存并由 CPU 完成计算,而计算复杂度较高但相对更省显存的 MLA 部分继续留在 GPU 显存并由 GPU 完成计算,大幅降低了显存需求。

此外,采用的 4bit 量化技术和 MarlinGPU 算子配合,也使得推理效率得到了显著提升,推理速度提高了近四倍。CUDAGraph 加速技术通过优化 CPU/GPU 的通信效率,显著减少了延迟和资源消耗。

接下来我们对这个项目进行实测。

KTransformers 性能实测

笔者在一台线上退役的机器(原本 8 卡 H100,其中一卡出现故障导致无法用于训练,只能用剩余 7 卡做实验用)。机器配置如下:

CPU
Intel(R) Xeon(R) Platinum 8468,双路 96 物理核心 192 线程
内存
海力士 2TB DDR5 4800MT/s
GPU
7 卡 NVIDIA H100 80GB HBM3
磁盘
4 TB NVMe

检查 CPU 支持 AMX 指令:


        
            

          
 # lscpu | grep amx
 
        
      

笔者测试机输出如下:

picture.image

如果你的 CPU 不支持 AMX 也没关系,运行速度会慢一些。

获取 KTransformers 代码并进行编译:


          
git clone https://github.com/kvcache-ai/ktransformers --recursive
          
cd ktransformers/
          
export PATH=/usr/local/cuda/bin:$PATH
          
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
          
export CUDA_PATH=/usr/local/cuda
          
sudo apt-get update
          
sudo apt-get install gcc g++ cmake ninja-build
          
conda create -n ktransformers python=3.11
          
conda activate ktransformers
          
pip install torch packaging ninja cpufeature numpy
          
export USE_NUMA=1
          
bash install.sh
      

下载权重:


          
# 运行前先安装依赖:
          
# pip install huggingface_hub hf_transfer
          
from huggingface_hub import snapshot_download
          
snapshot_download(
          
    repo_id = "unsloth/DeepSeek-R1-GGUF",  # 指定Hugging Face仓库
          
    local_dir = "DeepSeek-R1-GGUF",         # 指定本地存储目录
          
    allow_patterns = ["*Q4_K_M*"],        # 仅下载 Q4 量化版本
          
)
      

权重文件大小和内容:


          
# du -hs ./*
          
377G	./DeepSeek-R1-Q4_K_M
          

          
# tree ./DeepSeek-R1-Q4_K_M
          
./DeepSeek-R1-Q4_K_M
          
├── DeepSeek-R1-Q4_K_M-00001-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00002-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00003-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00004-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00005-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00006-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00007-of-00009.gguf
          
├── DeepSeek-R1-Q4_K_M-00008-of-00009.gguf
          
└── DeepSeek-R1-Q4_K_M-00009-of-00009.gguf
          

          
0 directories, 9 files
      

看到这里,你应该发现 KTransformer 所谓的“满血版”,需要加引号,因为是做了 4 bit 量化,和纯 FP8 满血版是有明显区别的,权重大小从 642 GB 降到 377 GB。

启动 ktransformers api 服务:


          
ktransformers \
          
  --model_path /data/DeepSeek-R1 \
          
  --gguf_path DeepSeek-R1-Q4_K_M \
          
  --host 0.0.0.0 \
          
  --port 10002 \
          
  --cpu_infer 65 \
          
  --max_new_tokens 8192
      

其中 --model_path 需要指向原版权重(671B, FP8)所在目录,主要用到其中的 config 和 tokenizer,--gguf_path 指向 Q4 量化模型所在目录。--cpu_infer 65 表示使用 65 个 CPU 核心做 MoE 部分计算,--max_new_tokens 8192 表示最大生成 8192 个 token,如果显存和内存充足可以继续增加。

看到日志输出:

picture.image

说明服务已经启动。

我们通过 Open WebUI 连接该服务。配置过程和《DeepSeek 本地部署最佳实践(二):llama.cpp + Open WebUI 部署 unsloth 量化版模型》类似,不再赘述。先打个招呼:

picture.image

接着问一个经典的水杯倒水问题:

问:有 3 L 和 5L 的两个杯子和无穷的水,请问如何得到 4L 水?

<开始思考>

嗯,我现在有一个问题,需要用3升和5升的杯子量出4升水。我需要仔细想一下该怎么操作。首先,我应该回忆一下类似的问题,可能需要用倒水和装满的方法来得到目标容量。

让我先理清楚这两个杯子的容量:一个是3升,另一个是5升,目标是通过这两个容器装水或者倒水,最终得到刚好4升的水。可能需要重复几次装满、倒出或转移水的步骤。

首先,我应该考虑如何通过装满和倒水来获得不同的量。比如,如果把5升的杯子装满,然后倒入3升的杯子里,那剩下的就是5-3=2升。这时候5升杯子里面会有2升水,3升杯子满了。接着可以把3升的杯子倒空,然后把5升杯子里的2升倒到3升杯子里。此时,3升杯子里有2升,5升杯子是空的。

接下来,再把5升的杯子装满,然后继续向已经有2升的3升杯子里加水,此次只能再加1升(因为3升杯子的容量是3升,已经有2升,所以剩余空间是1升)。这样,5升杯子倒出1升后剩下的是5-1=4升,此时5升杯子里就正好有4升了。这就是目标量!

那么这个过程的步骤大概是这样的:

  1. 把5升的杯子装满。
  2. 将5升杯子的水倒入3升杯子,直到3升杯子满。此时,5升杯子里剩下2升,3升杯子满了。
  3. 将3升杯子倒空。
  4. 把5升杯子里剩下的2升水倒入3升杯子。这时,3升杯子里有2升,5升杯子是空的。
  5. 再次将5升杯子装满。
  6. 将5升杯子的水倒入3升杯子,直到3升杯子装满(因为已有2升,只需要再倒入1升即可装满)。此时,5升杯子倒出1升,剩下的水量就是5-1=4升。

这样就得到了4升的水在5升的杯子里。好像可行!

不过有没有其他方法?可能还有一种步骤更少的方式?比如先用3升杯子装满,然后倒入5升的杯子,反复多次?

比如说:

  1. 将3升杯子装满,倒入5升杯子中。此时,5升杯子中有3升,3升杯子空了。
  2. 再次装满3升杯子,继续倒入5升杯子,直到5升杯子满。由于5升杯子已经有3升,还能再装2升。这时3升杯子倒出了2升,剩下1升。
  3. 现在,5升杯子已经满了,里面有5升,3升杯子剩下1升。
  4. 把5升杯子的水全部倒掉,然后将3升杯子剩下的1升倒入5升杯子。此时,5升杯子里有1升,3升杯子空了。
  5. 再次装满3升杯子,倒入5升杯子中。现在,5升杯子里原本有1升,再加上3升,总共4升。这个时候,5升杯子里就有4升水了。

所以这种方法也能得到4升。两种不同的方法都可以达成目标。看来这个问题确实有两种可能的解决办法。第二种方法可能需要更多的步骤,但同样可行。

不过用户可能更容易理解哪种方法呢?或者说哪种方法需要更少的步骤?

让我再检查一遍步骤是否正确,尤其是第一种方法可能更直接:

原方法:

  • 装满5升 → 倒入3升杯子至满(剩2升)。
  • 倒空3升 → 将2升转移到3升杯子。
  • 再次装满5升 → 倒入3升杯子的2升至满,此时5升杯子中剩余4升。

正确。

另一个方法:

  • 用3升杯子装两次加到5升杯子里,直到第二次装满的时候余下1升,将满的5升杯子倒掉并保留1升,再次装满3升倒入,得到1+3=4。步骤有效。

这样,用户可以选择他们觉得更容易的方法。可能第二种方法需要倒的次数更多,或者是否存在错误?

比如第二种方法中的步骤:

步骤是否正确:

  1. 把3升杯子装满,倒入5升杯子。此时5升杯子有3升,3升杯子空。
  2. 再次装满3升杯子,倒入5升,直到5升满。这时,5升杯子需要再装2升,因此倒出3升的杯子里的2升,此时3升杯子剩下1升(3-2=1)。
  3. 倒掉5升杯子的5升水(现在它为空),将3升杯子剩下的1升倒入5升杯子。
  4. 再次装满3升杯子,倒入5升杯子,此时5升杯子已经有1升,加3升后变为4升。

是的,这个也是正确的。所以不管选择哪条路径,都可以得到结果。通常这类问题可能有多种解法,只要步骤正确,就可行。

不过用户可能需要一个明确的步骤说明,应该选其中一种方法详细列出步骤。可能第一种方法步骤更简洁一些。

综上,解决方案是正确的。

<思考结束>

方法一:

  1. 将5升的杯子装满。
  2. 将5升杯中的水倒入3升杯,直到3升杯满。此时5升杯中剩余

2升

,3升杯满。 3. 倒空3升杯。

  1. 将5升杯中剩余的2升水倒入3升杯。此时3升杯有2升,5升杯为空。
  2. 再次将5升杯装满。
  3. 将5升杯中的水倒入3升杯(直到3升杯满),此时只能倒出

1升

(3升杯已有2升,只需补1升)。

最终5升杯剩下的水为:5 - 1 =

4升


方法二:

  1. 将3升杯装满,倒入5升杯。此时5升杯中有3升,3升杯空。
  2. 再次装满3升杯,倒入5升杯直到其满。此时倒出

2升

,3升杯剩余

1升

。 3. 倒空5升杯,再将3升杯中剩余的1升倒入5升杯(此时5升杯有1升)。

  1. 最后一次装满3升杯,将其全倒入5升杯。此时5升杯中共有:1 + 3 =

4升


两种方法均能精准得到

4升

水。选择其中一种操作即可!

查看 CPU 和内存占用情况:

picture.image

GPU 占用情况:

picture.image

手机实拍,生成速度比较慢,不到 10 tokens/s。

笔者也尝试了一些优化规则(--optimized),例如增加显存占用,将部分 MoE layer 放到 GPU,结果和 cuda graph 优化冲突,不得不关掉 cuda graph 功能,导致生成速度更慢了ε=(´ο`*)))。

另外还尝试了增加 GPU 数量,用多张卡推理,速度依然没有提升。

picture.image

对 KTransformers 接下来的开发计划,项目负责人清华大学高性能计算研究所助理教授章明星博士在知乎上透露:

picture.image

期待后续通过软硬件优化,进一步将成本降低到真正“人均满血 DeepSeek R1”。比如利用本地 SSD 盘当内存,笔记本上也可以跑就更好了 (≧∇≦)ノ

对 DeepSeek 本地部署和推理最佳实践感兴趣的朋友可以扫码加入新建的微信群:

picture.image

参考链接

https://kvcache-ai.github.io/ktransformers/en/install.html

https://zhuanlan.zhihu.com/p/23899487659

https://github.com/kvcache-ai/ktransformers/blob/main/doc/en/DeepseekR1\_V3\_tutorial.md


扫描下方

二维码

,关注“

慢慢学AIGC

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
火山引擎大规模机器学习平台架构设计与应用实践
围绕数据加速、模型分布式训练框架建设、大规模异构集群调度、模型开发过程标准化等AI工程化实践,全面分享如何以开发者的极致体验为核心,进行机器学习平台的设计与实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论