本工程实现BM1684X部署语言大模型Qwen-7B-Chat。通过TPU-MLIR编译器将模型转换成bmodel,并采用c++代码将其部署到BM1684X的PCIE环境,或者SoC环境。
- 本工程也支持Qwen-14-Chat,操作方法与
Qwen-7B-Chat
一致。 - 本工程也支持Qwen-1_8-Chat,操作方法与
Qwen-7B-Chat
一致。
git lfs install
git clone [email protected]:Qwen/Qwen-7B-Chat
该工程比较大,会花较长时间。
并将本项目下的files/Qwen-7B-Chat
中的文件替换至Qwen-7B-Chat
下的对应文件。
下载本项目,并导出所有的ONNX(其中需要将本项目files
路径下的config.json
和modeling_qwen.py
文件替换到原模型的文件夹下,如下:
git clone [email protected]:sophgo/Qwen-TPU.git
cd Qwen-TPU
git submodule update --init
cp files/Qwen-7B-Chat/* ../Qwen-7B-Chat
export PYTHONPATH=$PWD/../Qwen-7B-Chat:$PYTHONPATH
cd compile
pip install transformers_stream_generator einops tiktoken
python3 export_onnx.py
因为我们采用BF16格式导出ONNX,需要您的环境上带有CUDA。默认x86不支持BF16。14B或1_8B模型需要将export
指定到对应路径,同时export_onnx.py中的模型路径也许做对应的修改
docker pull sophgo/tpuc_dev:latest
# myname1234 is just an example, you can set your own name
docker run --privileged --name myname1234 -v $PWD:/workspace -it sophgo/tpuc_dev:latest
后文假定环境都在docker的/workspace
目录。
(也可以直接下载编译好的release包解压)
git clone [email protected]:sophgo/tpu-mlir.git
cd tpu-mlir
source ./envsetup.sh
./build.sh
注意此时在Docker环境workspace目录。
目前TPU-MLIR支持对Qwen-7B
进行BF16、INT8和INT4量化,且支持多芯分布式推理,默认情况下会进行INT8量化和单芯推理,最终生成qwen-7b_int8.bmodel
文件。
./compile.sh
若要编译int4,或者bf16版本,则加入--model
参数。如下转int4,最终生成qwen-7b_int4.bmodel
:
./compile.sh --mode int4
若想进行2芯推理,则执行以下命令,最终生成qwen-7b_int8_2dev.bmodel
文件,4芯8芯同理:
./compile.sh --num_device 2
执行如下编译 (注意如果是SoC版本,需要把demo目录拷贝到SoC环境编译):
cd Qwen-TPU/demo
mkdir build
cd build
cmake ..
make
编译生成qwen可执行程序,将qwen
、qwen-7b_int8.bmodel
和qwen.tiktoken
拷贝到同一个目录下就可以执行了。
(qwen.tiktoken
来自Qwen-7B-Chat)。
运行qwen
:
./qwen --model qwen-7b_int8.bmodel
如果是2芯分布式推理,使用如下命令(比如指定在2号和3号芯片上运行, 用bm-smi
查询芯片id号):
./qwen --model qwen-7b_int8_2dev.bmodel --devid 2,3
以下为单芯片下INT8量化模式的运行效果:
如果demo程序拷贝到运行环境提示无法运行,比如接口找不到等等错误。
原因是运行环境的库有所不同,将demo中的lib_pcie
(PCIE)或者 lib_soc
(SoC)里面的so文件拷贝到运行环境,链接到里面的so即可。
tiktoken官方没有C++版本,只有python版本。 本工程使用QwenLM/qwen.cpp中的tiktoken处理代码。
将Qwen模型中的config.json中seq_length
改成对应想要的长度即可
只对config.json
和modeling_qwen.py
做了部分调整。
"bf16": true,
"max_position_embeddings": 512,
"seq_length": 512,
我们采用bf16导出ONNX模型,原因是该模型是通过bf16训练的。用F32也可以,但是这样ONNX体积太大。
-
第一点修改如下(这是因为TORCH2的算子转ONNX会失败):
# SUPPORT_TORCH2 = hasattr(torch, '__version__') and int(torch.__version__.split(".")[0]) >= 2 SUPPORT_TORCH2 = False
-
第二点修改如下(这是因为转ONNX,提示Shape推导失败):
# attn_weights = attn_weights / torch.full( # [], # size_temp ** 0.5, # dtype=attn_weights.dtype, # device=attn_weights.device, # ) attn_weights = attn_weights / (size_temp ** 0.5)
-
第三点修改如下(这段代码全部注释掉,是因为可以直接采用
attention_mask
,避免复杂逻辑,提升性能):# if self.use_cache_quantization: # query_length, key_length = query.size(-2), key[0].size(-2) # else: # query_length, key_length = query.size(-2), key.size(-2) # causal_mask = registered_causal_mask[ # :, :, key_length - query_length : key_length, :key_length # ] # mask_value = torch.finfo(attn_weights.dtype).min # mask_value = torch.full([], mask_value, dtype=attn_weights.dtype).to( # attn_weights.device # ) # attn_weights = torch.where( # causal_mask, attn_weights.to(attn_weights.dtype), mask_value # )
-
第四点修改如下(同上原因):
# query_length, key_length = query.size(-2), key.size(-2) # causal_mask = registered_causal_mask[ # :, :, key_length - query_length : key_length, :key_length # ] # mask_value = torch.finfo(attn_weights.dtype).min # mask_value = torch.tensor(mask_value, dtype=attn_weights.dtype).to( # attn_weights.device # ) # attn_weights = torch.where(causal_mask, attn_weights, mask_value)
-
第五点修改,将如下代码移至
if layer_past is not None:
之前:if use_cache: present = (key, value) else: present = None
这是因为kv cache只用输出1个单位就可以了,不用全部输出。提升效率。