# djl-serving deepspeed 模型部署流程 (python mode)

# 相关介绍

# DeepSpeed

deepspeed 是一款微软发布的深度学习优化软件套件,能提供一键深度学习加速推理和训练能力。

DeepSpeed 为使用 DeepSpeed、Megatron 和 HuggingFace 训练的基于兼容 Transformer 的模型提供无缝推理模式,无需修改模型即可实现高效推理。

# djl-serving

DJL Serving 是由 DJL 提供支持的高性能通用独立模型服务解决方案。它将深度学习模型或工作流程包装成服务,并通过 HTTP 提供它们。

image

djl-serving python mode

djl-serving 推理服务支持 python 模式,用户只需准备一个 Python 脚本文件即可执行,除此之外,djl-serving 还提供如下支持:

Java 模式:用户需要准备 Java 前 / 后处理脚本和模型文件。

二进制模式:用户只需要准备一个模型文件,我们就可以运行 tensor-in,tensor out 操作。

# 相应关系

image

对应的推理服务流程:

用户向 DJL-Serving 请求 -->DJL-serving 将请求定位到模型并将输入交给推理引擎(DeepSpeed)-->DeepSpeed 对 pytorch 模型进行自动优化,进行推理,推理结果返还给 DJL-Serving-->DJL-Serving 响应用户请求

# 测试模型

使用的测试模型为 Qwen-1.8B

通义千问 - 1.8B(Qwen-1.8B 是阿里云研发的通义千问大模型系列的 18 亿参数规模的模型。Qwen-1.8B 是基于 Transformer 的大语言模型,在超大规模的预训练数据上进行训练得到。预训练数据类型多样,覆盖广泛,包括大量网络文本、专业书籍、代码等。

# docker 部署

首先拉取 djl-serving:0.19.0-deepspeed 镜像

bash
1
docker pull deepjavalibrary/djl-serving:0.19.0-deepspeed

# 使用 docker compose

首先创建 model 目录

bash
1
mkdir model

建立使用 gpu 的容器

创建 docker-compose.yaml 文件并添加如下文本

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---
version: "2.1"
services:
djl_deepspeed:
image: deepjavalibrary/djl-serving:0.19.0-deepspeed
container_name: djl_deepspeed
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8080
- NVIDIA_DRIVER_CAPABILITIES=all
- NVIDIA_VISIBLE_DEVICES=0
deploy:
resources:
reservations:
devices:
- driver: "nvidia"
device_ids: ["0"]
capabilities: ["gpu"]
volumes:
- ./models:/opt/ml/model
ports:
- 8080:8080
restart: no

# 构建模型结构

bash
1
cd models & mkdir Qwen

创建 serving.properties 并写入如下配置项

bash
1
2
3
4
engine=DeepSpeed
option.model_id=Qwen/Qwen-1-8B
option.tensor_parallel_degree=1
option.enable_streaming=true

创建 model.py ,写入代码如下

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from modelscope import AutoModelForCausalLM, AutoTokenizer
from transformers import pipeline
import deepspeed
import os
import torch
from djl_python import Input, Output

def get_model():
model_name = 'Qwen-1_8B-Chat'
tokenizer = AutoTokenizer.from_pretrained(model_name, revision='master', trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True)
tensor_parallel = int(os.getenv('TENSOR_PARALLEL_DEGREE', '1'))
local_rank = int(os.getenv('LOCAL_RANK', '0'))

generator = pipeline('text-generation', model=model, tokenizer=tokenizer,
device=local_rank)

generator.model = deepspeed.init_inference(generator.model,
mp_size=tensor_parallel,
dtype=torch.float,
replace_method='auto',
replace_with_kernel_inject=True)

return generator

predictor = get_model()

def handle(inputs: Input) -> None:
global predictor
if predictor is None:
predictor = get_model()

if inputs.is_empty():
# Model server makes an empty call to warmup the model on startup
return None

data = inputs.get_as_string()
result = predictor(data, do_sample=True, min_length=50)
return Output().add(result)

# 下载模型

模型文件为使用 snapshot_download 从 modelscope/huggingface 中下载的快照

模型下载脚本如下

python
1
2
3
# from huggingface_hub import snapshot_download # 使用huggingface
from modelscope import snapshot_download # 使用modelscope
snapshot_download("Qwen/Qwen-1_8B-Chat",local_dir="./models/Qwen", revision='master')

# 安装依赖

进入容器中,进入 /opt/ml/model/Qwen 模型目录

# 自动化安装

正常情况下,容器在启动时会自动检测模型目录和存在的 requirements.txt 依赖文件,如果出现异常需要移除该文件,并手动进入容器内安装依赖。

# 手动安装

pip install -r requirements.txt

# 测试

容器内服务启动成功后, docker logs djl_deepspeed 应该会有如下输出

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[INFO ] PyProcess - [1,0]<stdout>:Python engine started.
[INFO ] PyProcess - Model [Qwen] initialized.
[INFO ] PyModel - Qwen model loaded in 24574 ms.
[INFO ] FolderScanPluginManager - scanning for plugins...
[INFO ] FolderScanPluginManager - scanning in plug-in folder :/opt/djl/plugins
[INFO ] PropertyFilePluginMetaDataReader - Plugin found: console/jar:file:/opt/djl/plugins/management-console-0.19.0.jar!/META-INF/plugin.definition
[INFO ] PropertyFilePluginMetaDataReader - Plugin found: static-file-plugin/jar:file:/opt/djl/plugins/static-file-plugin-0.19.0.jar!/META-INF/plugin.definition
[INFO ] FolderScanPluginManager - Loading plugin: {console/jar:file:/opt/djl/plugins/management-console-0.19.0.jar!/META-INF/plugin.definition}
[INFO ] PluginMetaData - plugin console changed state to INITIALIZED
[INFO ] FolderScanPluginManager - Loading plugin: {static-file-plugin/jar:file:/opt/djl/plugins/static-file-plugin-0.19.0.jar!/META-INF/plugin.definition}
[INFO ] PluginMetaData - plugin static-file-plugin changed state to INITIALIZED
[INFO ] PluginMetaData - plugin console changed state to ACTIVE reason: plugin ready
[INFO ] PluginMetaData - plugin static-file-plugin changed state to ACTIVE reason: plugin ready
[INFO ] FolderScanPluginManager - 2 plug-ins found and loaded.
[INFO ] ModelServer - Initialize BOTH server with: EpollServerSocketChannel.
[INFO ] ModelServer - BOTH API bind to: http://0.0.0.0:8080

此时从宿主机可以通过 curl 命令进行测试

bash
1
curl -X POST http://localhost:8080/predictions/Qwen --header 'content-type: text/string' --data-raw '通义千问是'

预期响应

bash
1
2
3
4
5
[
{
"generated_text":"通义千问是阿里云研发的超大规模语言模型,具有什么样的能力?\nA. A:理解自然语言、生成人类对话\nB. B:理解复杂文本、总结文本内容\nC. C:生成艺术作品、写故事\nD. D:写作诗歌、歌词\n\n答案:A\n"
}
]