Skip to content

Commit 27727b1

Browse files
authored
[FasterTransformer] Transformer and GPT support dy2sta (PaddlePaddle#991)
1 parent 955c10b commit 27727b1

File tree

12 files changed

+636
-105
lines changed

12 files changed

+636
-105
lines changed

examples/language_model/gpt/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ GPT-[2](https://cdn.openai.com/better-language-models/language_models_are_unsupe
1414
├── decompress.sh # 数据集解压脚本
1515
├── deploy/ # 模型部署的inference脚本
1616
├── export_model.py # 导出预测部署的模型脚本
17+
├── faster_gpt/ # 使用 FasterGPT 高性能预测 sample
1718
├── lr.py # 学习率控制
1819
├── predict.py # 生成文本示例demo
1920
├── README.md # 文档
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Faster GPT 使用
2+
3+
在这里我们集成了 NVIDIA [Faster Transformer](https://github.com/NVIDIA/FasterTransformer/tree/v3.1) 用于预测加速。同时集成了 FasterGPT float32 以及 float16 预测。以下是使用 FasterGPT 的使用说明。
4+
5+
## 使用环境说明
6+
7+
* 本项目依赖于 PaddlePaddle 2.1.0 及以上版本或适当的 develop 版本
8+
* CMake >= 3.10
9+
* CUDA 10.1(需要 PaddlePaddle 框架一致)
10+
* gcc 版本需要与编译 PaddlePaddle 版本一致,比如使用 gcc8.2
11+
* 推荐使用 Python3
12+
* [Faster Transformer](https://github.com/NVIDIA/FasterTransformer/tree/v3.1#setup) 使用必要的环境
13+
14+
## 快速开始
15+
16+
我们实现了基于 GPU 的 FasterGPT 的自定义 op 的接入。接下来,我们将分别介绍基于 Python 动态图和预测库使用 FasterGPT 自定义 op 的方式,包括 op 的编译与使用。
17+
18+
## Python 动态图使用自定义 op
19+
20+
### 编译自定义OP
21+
22+
在 Python 动态图下使用自定义 OP 需要将实现的 C++、CUDA 代码编译成动态库,我们已经提供对应的 CMakeLists.txt ,可以参考使用如下的方式完成编译。同样的自定义 op 编译的说明也可以在自定义 op 对应的路径 `PaddleNLP/paddlenlp/ops/` 下面找到。
23+
24+
#### 克隆 PaddleNLP
25+
26+
首先,因为需要基于当前环境重新编译,当前的 paddlenlp 的 python 包里面并不包含 FasterGPT 相关 lib,需要从源码自行编译,可以直接使用 Python 的 package 下的 paddlenlp,或是可从 github 克隆一个 PaddleNLP,并重新编译。
27+
28+
``` sh
29+
git clone https://github.com/PaddlePaddle/PaddleNLP.git
30+
```
31+
32+
其次,配置环境变量,让我们可以使用当前 clone 的 paddlenlp,并进入到自定义 OP 的路径,准备后续的编译操作:
33+
34+
``` sh
35+
export PYTHONPATH=$PWD/PaddleNLP/:$PYTHONPATH
36+
cd PaddleNLP/paddlenlp/ops/
37+
```
38+
39+
#### 编译
40+
41+
编译之前,请确保安装的 PaddlePaddle 的版本是大于 2.1.0 或是最新的 develop 分支的代码编译,并且正常可用。
42+
43+
编译自定义 OP 可以参照一下步骤:
44+
45+
``` sh
46+
mkdir build
47+
cd build/
48+
cmake .. -DSM=xx -DCMAKE_BUILD_TYPE=Release -DPY_CMD=python3.x -DWITH_GPT=ON
49+
make -j
50+
cd ../
51+
```
52+
53+
其中,
54+
* `-DSM`: 是指的所用 GPU 的 compute capability。举例来说,可以将之指定为 70(V100) 或是 75(T4)。
55+
* `-DPY_CMD`: 是指编译所使用的 python,若未指定 `-DPY_CMD` 将会默认使用系统命令 `python` 对应的 Python 版本。
56+
* `-DWITH_GPT`: 是指是否编译带有 FasterGPT 自定义 op 的动态库。
57+
58+
59+
最终,编译会在 `./build/lib/` 路径下,产出 `libdecoding_op.so`,即需要的 FasterGPT decoding 执行的库。
60+
61+
### 使用 GPT-2 decoding 高性能推理
62+
63+
编写 python 脚本的时候,调用 `FasterGPT` API 并传入 `libdecoding_op.so` 的位置即可实现将 FasterGPT 用于当前的预测。
64+
65+
``` python
66+
from paddlenlp.ops import FasterGPT
67+
from paddlenlp.transformers import GPTModel, GPTForPretraining
68+
69+
MODEL_CLASSES = {
70+
"gpt2-medium-en": (GPTLMHeadModel, GPTTokenizer),
71+
}
72+
73+
model_class, tokenizer_class = MODEL_CLASSES[args.model_name]
74+
tokenizer = tokenizer_class.from_pretrained(args.model_name)
75+
model = model_class.from_pretrained(args.model_name)
76+
77+
# Define model
78+
gpt = FasterGPT(
79+
model=model,
80+
candidate_num=args.candidate_num,
81+
probability_threshold=args.probability_threshold,
82+
max_seq_len=args.max_seq_len,
83+
start_id=start_id,
84+
end_id=end_id,
85+
temperature=args.temperature,
86+
decoding_lib=args.decoding_lib,
87+
use_fp16_decoding=args.use_fp16_decoding)
88+
```
89+
90+
目前,GPT-2 的例子仅支持 `batch size``1` 或是 batch 内输入的样本的长度都是相同的情况。并且,仅支持 topk-sampling 和 topp-sampling,不支持 beam-search。
91+
92+
更详细的例子可以参考 `./infer.py`,我们提供了更详细用例。
93+
94+
#### 执行 GPT-2 decoding on PaddlePaddle
95+
96+
使用 PaddlePaddle 仅执行 decoding 测试(float32):
97+
98+
``` sh
99+
export CUDA_VISIBLE_DEVICES=0
100+
python infer.py --model_name_or_path gpt2-medium-en --decoding_lib ./build/lib/libdecoding_op.so --batch_size 1 --topk 4 --topp 0.0 --max_out_len 32 --start_token "<|endoftext|>" --end_token "<|endoftext|>" --temperature 1.0
101+
```
102+
103+
其中,各个选项的意义如下:
104+
* `--model_name_or_path`: 预训练模型的名称或是路径。
105+
* `--decoding_lib`: 指向 `libdecoding_op.so` 的路径。需要包含 `libdecoding_op.so`。若不存在则将自动进行 jit 编译产出该 lib。
106+
* `--batch_size`: 一个 batch 内,样本数目的大小。
107+
* `--candidate_num`: 执行 topk-sampling 的时候的 `k` 的大小,默认是 4。
108+
* `--probability_threshold`: 执行 topp-sampling 的时候的阈值的大小,默认是 0.0 表示不执行 topp-sampling。
109+
* `--max_seq_len`: 最长的生成长度。
110+
* `--start_token`: 字符串,表示任意生成的时候的开始 token。
111+
* `--end_token`: 字符串,生成的结束 token。
112+
* `--temperature`: temperature 的设定。
113+
* `--use_fp16_decoding`: 是否使用 fp16 进行推理。
114+
115+
116+
## C++ 预测库使用自定义 op
117+
118+
### 编译自定义OP
119+
120+
在 C++ 预测库使用自定义 OP 需要将实现的 C++、CUDA 代码**以及 C++ 预测的 demo**编译成一个可执行文件。因预测库支持方式与 Python 不同,这个过程将不会产生自定义 op 的动态库,将直接得到可执行文件。我们已经提供对应的 CMakeLists.txt ,可以参考使用如下的方式完成编译。并获取执行 demo。
121+
122+
#### 克隆 PaddleNLP
123+
124+
首先,仍然是需要克隆一个 PaddleNLP:
125+
126+
``` sh
127+
git clone https://github.com/PaddlePaddle/PaddleNLP.git
128+
```
129+
130+
其次,让我们可以使用当前 clone 的 paddlenlp,并进入到自定义 OP 的路径,准备后续的编译操作:
131+
132+
``` sh
133+
cd PaddleNLP/paddlenlp/ops/
134+
```
135+
136+
#### 编译
137+
138+
编译之前,请确保安装的 PaddlePaddle 预测库的版本是基于最新的 develop 分支的代码编译,并且正常可用。
139+
140+
编译自定义 OP 可以参照一下步骤:
141+
142+
``` sh
143+
mkdir build
144+
cd build/
145+
cmake .. -DSM=xx -DWITH_GPT=ON -DCMAKE_BUILD_TYPE=Release -DPADDLE_LIB=/path/to/paddle_inference_lib/ -DDEMO=./demo/gpt.cc -DWITH_STATIC_LIB=OFF -DON_INFER=ON -DWITH_MKL=ON
146+
make -j
147+
cd ../
148+
```
149+
150+
注意:
151+
* `xx` 是指的所用 GPU 的 compute capability。举例来说,可以将之指定为 70(V100) 或是 75(T4)。
152+
* `-DPADDLE_LIB` 需要指明使用的 PaddlePaddle 预测库的路径 `/path/to/paddle_inference_install_dir/`,并且在该路径下,预测库的组织结构满足:
153+
```text
154+
.
155+
├── CMakeCache.txt
156+
├── paddle/
157+
├── include/
158+
└── lib/
159+
├── third_party/
160+
├── cudaerror/
161+
├── install/
162+
└── threadpool/
163+
└── version.txt
164+
```
165+
* `-DDEMO` 说明预测库使用 demo 的位置。比如指定 -DDEMO=./demo/gpt.cc。最好使用绝对路径,若使用相对路径,需要是相对于 `PaddleNLP/paddlenlp/ops/faster_transformer/src/` 的相对路径。
166+
* `-DWITH_GPT`,如果是编译 GPT 的预测库可执行文件,需要加上 `-DWITH_GPT=ON`
167+
* **当使用预测库的自定义 op 的时候,请务必开启 `-DON_INFER=ON` 选项,否则,不会得到预测库的可执行文件。**
168+
169+
#### 执行 GPT decoding on PaddlePaddle
170+
171+
如果需要使用 Paddle Inference 预测库针对 GPT 进行预测,首先,需要导出预测模型,可以通过 `./export_model.py` 脚本获取预测库用模型,执行方式如下所示:
172+
173+
``` sh
174+
python ./export_model.py --model_name_or_path gpt2-medium-en --decoding_lib ./build/lib/libdecoding_op.so --topk 4 --topp 0.0 --max_out_len 32 --start_token "<|endoftext|>" --end_token "<|endoftext|>" --temperature 1.0 --inference_model_dir ./infer_model/
175+
```
176+
177+
各个选项的意义与上文的 `infer.py` 的选项相同。额外新增一个 `--inference_model_dir` 选项用于指定保存的模型文件、词表等文件。若是使用的模型是 gpt2-medium-en,保存之后,`./infer_model/` 目录下组织的结构如下:
178+
179+
``` text
180+
.
181+
├── gpt.pdiparams # 保存的参数文件
182+
├── gpt.pdiparams.info # 保存的一些变量描述信息,预测不会用到
183+
├── gpt.pdmodel # 保存的模型文件
184+
├── merges.txt # bpe
185+
└── vocab.txt # 词表
186+
```
187+
188+
同理,完成编译后,可以在 `PaddleNLP/paddlenlp/ops/build/bin/` 路径下将会看到 `gpt` 的一个可执行文件。通过设置对应的设置参数完成执行的过程。
189+
190+
``` sh
191+
cd bin/
192+
./gpt -batch_size 1 -gpu_id 0 -model_dir path/to/model -vocab_dir path/to/vocab -start_token "<|endoftext|>" -end_token "<|endoftext|>"
193+
```
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import sys
15+
import os
16+
import numpy as np
17+
from attrdict import AttrDict
18+
import argparse
19+
import time
20+
21+
import paddle
22+
23+
import yaml
24+
from pprint import pprint
25+
26+
from paddlenlp.ops import FasterGPT
27+
from paddlenlp.transformers import GPTModel, GPTLMHeadModel
28+
from paddlenlp.transformers import GPTChineseTokenizer, GPTTokenizer
29+
30+
from paddlenlp.utils.log import logger
31+
32+
MODEL_CLASSES = {
33+
"gpt-cpm-large-cn": (GPTLMHeadModel, GPTChineseTokenizer),
34+
"gpt2-medium-en": (GPTLMHeadModel, GPTTokenizer),
35+
}
36+
37+
38+
def parse_args():
39+
parser = argparse.ArgumentParser()
40+
parser.add_argument(
41+
"--model_name_or_path",
42+
default="gpt2-medium-en",
43+
type=str,
44+
help="The model name to specify the gpt to use. Can be one of ['gpt2-en', 'gpt2-medium-en', 'gpt-cpm-large-cn']. "
45+
)
46+
parser.add_argument(
47+
"--decoding_lib",
48+
default="../../build/lib/libdecoding_op.so",
49+
type=str,
50+
help="Path of libdecoding_op.so. ")
51+
parser.add_argument(
52+
"--inference_model_dir",
53+
default="./infer_model/",
54+
type=str,
55+
help="Path to save inference model of gpt. ")
56+
parser.add_argument(
57+
"--topk",
58+
default=4,
59+
type=int,
60+
help="The number of candidate to procedure beam search. ")
61+
parser.add_argument(
62+
"--topp",
63+
default=0.0,
64+
type=float,
65+
help="The probability threshold to procedure topp sampling. ")
66+
parser.add_argument(
67+
"--max_out_len", default=32, type=int, help="Maximum output length. ")
68+
parser.add_argument(
69+
"--start_token",
70+
default="<|endoftext|>",
71+
type=str,
72+
help="The start token. Defaults to <|endoftext|>. ")
73+
parser.add_argument(
74+
"--end_token",
75+
default="<|endoftext|>",
76+
type=str,
77+
help="The end token. Defaults to <|endoftext|>. ")
78+
parser.add_argument(
79+
"--temperature",
80+
default=1.0,
81+
type=float,
82+
help="The temperature to set. ")
83+
parser.add_argument(
84+
"--use_fp16_decoding",
85+
action="store_true",
86+
help="Whether to use fp16 decoding to predict. ")
87+
args = parser.parse_args()
88+
return args
89+
90+
91+
def do_predict(args):
92+
place = "gpu"
93+
place = paddle.set_device(place)
94+
95+
model_class, tokenizer_class = MODEL_CLASSES[args.model_name_or_path]
96+
tokenizer = tokenizer_class.from_pretrained(args.model_name_or_path)
97+
logger.info('Loading the model parameters, please wait...')
98+
model = model_class.from_pretrained(
99+
args.model_name_or_path, max_predict_len=args.max_out_len)
100+
101+
bos_id = tokenizer.convert_tokens_to_ids(args.start_token)
102+
eos_id = tokenizer.convert_tokens_to_ids(args.end_token)
103+
104+
gpt = FasterGPT(
105+
model=model,
106+
topk=args.topk,
107+
topp=args.topp,
108+
max_out_len=args.max_out_len,
109+
bos_id=bos_id,
110+
eos_id=eos_id,
111+
temperature=args.temperature,
112+
decoding_lib=args.decoding_lib,
113+
use_fp16_decoding=args.use_fp16_decoding)
114+
115+
# Set evaluate mode
116+
gpt.eval()
117+
118+
# Convert dygraph model to static graph model
119+
gpt = paddle.jit.to_static(
120+
gpt,
121+
input_spec=[
122+
# input_ids
123+
paddle.static.InputSpec(
124+
shape=[None, None], dtype="int32")
125+
])
126+
127+
# Save converted static graph model
128+
paddle.jit.save(gpt, os.path.join(args.inference_model_dir, "gpt"))
129+
logger.info("GPT has been saved to {}".format(args.inference_model_dir))
130+
131+
gpt.save_resources(tokenizer, args.inference_model_dir)
132+
133+
134+
if __name__ == "__main__":
135+
args = parse_args()
136+
pprint(args)
137+
do_predict(args)

0 commit comments

Comments
 (0)