在NPU平台上,如何尝试跑通Ktransformers + DeepSeek R1?
1、Ktransformers介绍
KTransformers是一个灵活的、以Python为中心的框架,其核心是可扩展性。通过用一行代码实现和注入一个优化的模块,用户可以访问与Transformers兼容的接口、兼容OpenAI和Ollama的RESTful API,甚至是一个简化的类似ChatGPT的Web UI。
仓库地址:https://github.com/kvcache-ai/ktransformers
考虑到vLLM已经是大规模部署优化的优秀框架,KTransformers特别关注受有限资源限制的本地部署。我们特别关注异构计算机会,例如量化模型的GPU/CPU卸载。例如,我们分别为CPU和GPU支持高效的Llamafile和Marlin内核.
2、环境准备
硬件环境:
Kunpeng-920 + 800IA2
软件版本:
torch-2.4.0
torch_npu-2.4.0.post2-cp311
CANN:version=24.1.0
Python版本:python=3.11
DeepSeek-R1-GGUF:
Int8版本:https://modelscope.cn/models/unsloth/DeepSeek-R1-GGUF/files/ DeepSeek-R1-Q8_0
BF16版本:https://modelscope.cn/models/unsloth/DeepSeek-R1-GGUF/files/ 8bit版本:https://modelscope.cn/models/unsloth/DeepSeek-R1-GGUF/files/ DeepSeek-R1-Q8_0
3、编译 & 运行
根据官网提供的Installation Guide(https://kvcache-ai.github.io/ktransformers/en/install.html)文件,按照步骤进行编译安装。
需要注意点如下:
- 建议创建一个独立的conda环境,因为GLIBC的问题,可能需要升级安装libstdcxx-ng库文件
- torch,torchvision,torchaudio,这几个软件的版本与torch_npu的版本保持一致,否则会报错不兼容的问题。
3.1 编译
Step 1、下载源码:
git clone https://github.com/kvcache-ai/ktransformers.git
cd ktransformers
git submodule init
git submodule update
Step 2、执行编译安装脚本
bash install.sh
直接执行脚本,会到很多的问题。这里做一个需要修改内容的大概介绍,全量修改详见附件中提供ktransformers.patch文件。
1、 KTransformersOps
由于KTransformersOps提供的是cuda的相关的反量化算子,npu上肯定用不到,而且当前不会用AscendC进行源码编写替代,因此用到KTransformersOps的地方,需要屏蔽掉其调用。
2、cpufeature问题
cpufeature是x86上的一个wheel包,用来获取cpu相关的信息,arm上不存在这个包,需要通过其它的接口进行规避使用。
3、 cuda相关
KTransformers整个框架基于cuda来构建,与cuda的耦合度很高。Cuda相关的组件需要跳过或者规避,如setup.py中:
内容解释:
CMakeExtension部分的内容:通过CUDAExtension来编译安装ktransformers/ktransformers_ext下的一些手写算子(反量化算子),还有一些pybind11的编译(python提供反量化接口,最终执行是.cu中的手写算子)。
还有一些marlin gpu相关的代码需要修改,如:
/ktransformers/ktransformers/operators/linear.py
from torch import Tensor, nn
# import KTransformersOps
from ktransformers.util.custom_gguf import GGUFLoader
from ktransformers.util.utils import InferenceState
# from ktransformers.ktransformers_ext.operators.custom_marlin.quantize.utils.marlin_utils import (
# MarlinWorkspace,
# marlin_quantize,
# GPTQ_MARLIN_MIN_THREAD_N,
# GPTQ_MARLIN_MAX_PARALLEL,
# )
from ktransformers.operators.base_operator import BaseInjectedModule
还有,在KTransformersLinear类中,用到一些在cuda文件中定义的变量,如GPTQ_MARLIN_MIN_THREAD_N,这些只需要在用到的地方重新定义即可,如
/ktransformers/ktransformers/operators/linear.py
LINEAR_MAP = {"KLinearMarlin": KLinearMarlin,"KLinearTorch": KLinearTorch,"KLinearCPUInfer": KLinearCPUInfer
}GPTQ_MARLIN_MIN_THREAD_N = 64class KTransformersLinear(BaseInjectedModule, KLinearBase):
4、 量化相关
在ktransformers/ktransformers/util/custom_gguf.py文件中,定义了大量反量化相关的操作接口,这些接口调用的就是KTransformersOps中定义的手写反量化算子。因此,如果需要完成同样的接口功能,一方面可以通过AscendC进行适配;另一个方面,可以先不用反量化的功能(也就是不开始量化的功能)。接口修改,可以先规避掉:
5、 get_package_version
这个是获取cuda版本的接口,在npu上需要魔改。对外发布的时候,需要改成与npu相关的版本信息。
3.2 运行
由于DeepSeek R1的gguf权重文件大小,当前以DeepSeek-V2模型作为基础,先打通npu侧的流程。当前基于torch_npu的单算子的模式进行打通。
1、添加torch_npu
在ktransformers/ktransformers/models/modeling_deepseek.py中添加如下修改:
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELossimport torch_npu
from torch_npu.contrib import transfer_to_npufrom transformers.activations import ACT2FN
from transformers.cache_utils import Cache, DynamicCache, StaticCache
2、关闭use_cuda_graph
修改local_chat.py中的local_chat接口,关闭use_cuda_graph
def local_chat(model_path: str | None = None,optimize_rule_path: str = None,gguf_path: str | None = None,max_new_tokens: int = 300,cpu_infer: int = Config().cpu_infer,use_cuda_graph: bool = False,prompt_file : str | None = None,mode: str = "normal",force_think: bool = False,
):
2、 执行local_chat命令:
python -m ktransformers.local_chat --model_path ../DeepSeek-V2-Lite-Chat --gguf_path ../DeepSeek-V2-Lite-Chat-GGUF
model_path:在Hugging Face上在deepseek-ai/DeepSeek-V2-Lite-Chat的模型文件夹,因为使用gguf量化后的权重文件,这里的model-00001-of-000004.safetensors ~ model-00003-of-000004.safetensors可以不用下载,其它的tokenizer.json、config.json等文件均需要;
gguf_path:这个下载的gguf权重文件夹,就算只有一个gguf文件,也需要用文件夹的形式。
当前算子执行到如下位置会报错:
由于接下来调试调优的工作与其它同学相重合,就暂且不继续攻关,以配合的方式继续关注相关工作。
4 重要问题FAQ
问题1:GLIBCXX报错
参见:
libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 解决方案_libstdc++6 3.4.30-CSDN博客
解决办法:
conda install -c conda-forge libstdcxx-ng
问题2:flash_attn
flash_attn就是实现 FlashAttention and FlashAttention-2 的仓库。
这个仓库只实现了CUDA toolkit or ROCm toolkit的版本,针对npu并没有实现。因此需要替换为npu或者pytorch实现的相关的flash_attention相关计算的接口。
如果是cuda环境,直接:pip install flash_attn
问题3:block_q8_0_x4未定义
这个问题本质上是arm-neo版本指令实现问题,不同的arm机器上,对该指令的支持情况并
不一致。本次通过修改llama.cpp/ggml-common.h中的内容即可解决:
解决办法:
/home/disk_sdd/zjun/ktransformers/third_party/llama.cpp/ggml-common.h文件中,添加如下定义:
//xlong
typedef struct {
ggml_half d[8];
int8_t qs[4*QK8_1];
} block_q8_1_x4;//static_assert(sizeof(block_q8_1_x4) == 4*sizeof(block_q8_1),"Size of block_q8_1_x4 is not equal to 4 times the size of block_q8_1");
// printf("sizeof(block_q8_1) = %zu\n", sizeof(block_q8_1));
// printf("sizeof(block_q8_1_x4) = %zu\n", sizeof(block_q8_1_x4));
typedef struct {
ggml_half d[4];
int8_t qs[4*QK8_0];
} block_q8_0_x4;
修改的截图如下:
问题4:getauxval未声明
AT_HWCAP、getauxval、HWCAP_FPHP中是在auxv.h中定义的,因此需要在编译llamafile时包含此文件。
解决办法:
打开third_party/llamafile/sgemm.cpp中的#include <sys/auxv.h>,
原本是注释的:// #include <sys/auxv.h>
问题5:vmul_16
Vmul_16是在glibc中定义的一个函数,这里报错,说明调用函数的时候,给的函数入参的类型不匹配。但是,通过详细的入参比较,并没有发现参数类型不一致的情况。由于该函数主要也就是做2个数的乘法,如下:
33057 __extension__ extern __inline float16x4_t
33058 __attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
33059 vmul_f16 (float16x4_t __a, float16x4_t __b)
33060 {
33061 return __a * __b;
33062 }
33063
因此,在使用到的地方,直接通过乘法解决。
解决办法: