# GPT2
## 论文
`Language Models are Unsupervised Multitask Learners`
- [https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf](https://d4mucfpksywv.cloudfront.net/better-language-models/language-models.pdf)
## 模型结构
第二代生成式预训练模型(Generative Pre-Training2),GPT2使用 Transformer 的 Decoder 结构,并对 Transformer Decoder 进行了一些改动,原本的 Decoder 包含了两个 Multi-Head Attention 结构,GPT2 只保留了 Mask Multi-Head Attention。
我们为了用户可以使用OneFlow-Libai快速验证GPT2模型预训练,统计性能或验证精度,提供了一个GPT2网络示例,主要网络参数:
```
model.cfg.num_attention_heads = 16
model.cfg.hidden_size = 384
model.cfg.ffn_hidden_size = 1536
model.cfg.hidden_layers = 6
model.cfg.max_seq_length = 1024
```
完整的GPT2网络配置在configs/common/model/gpt.py中
同时,我们提供了一个更大的GPT2-13B网络为了用户可以快速在DCU集群上使用OneFlow-Libai进行较大规模的混合并行分布式预训练验证(该网络可能并不具有实际训练价值),该网络结构在GPT2基础上进行扩充,主要网络参数如下,参数量共有13.1B:
```
model.cfg.num_attention_heads = 32
model.cfg.hidden_size = 4096
model.cfg.ffn_hidden_size = 4096*4
model.cfg.hidden_layers = 64
model.cfg.max_seq_length = 1024
```
## 算法原理
GPT-2中使用掩模自注意力(masked self-attention),一般的自注意力模块允许某位置右侧的词计算时处于最大值。而掩模自注意力会阻止这种情况发生。
## 数据集
我们在libai目录下集成了部分小数据集供用户快速验证:
$ tree nlp_data
├── data
├── bert-base-chinese-vocab
├── gpt2-merges
└── gpt2-vocab
## 环境配置
### Docker
提供[光源](https://www.sourcefind.cn/#/service-details)拉取的训练以及推理的docker镜像:image.sourcefind.cn:5000/dcu/admin/base/oneflow:0.9.1-centos7.6-dtk-22.10.1-py39-latest
docker pull image.sourcefind.cn:5000/dcu/admin/base/oneflow:0.9.1-centos7.6-dtk-22.10.1-py39-latest
# 用上面拉取docker镜像的ID替换
docker run --shm-size 16g --network=host --name=gpt_oneflow --privileged --device=/dev/kfd --device=/dev/dri --group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -v $PWD/GPT:/home/GPT -it bash
cd /home/GPT
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
pip3 install pybind11 -i https://mirrors.aliyun.com/pypi/simple
pip3 install -e . -i https://mirrors.aliyun.com/pypi/simple
## 训练
该预训练脚本运行环境为1节点,4张DCU-Z100-16G。
并行配置策略在configs/gpt2_pretrain.py中,使用自动混合精度:
```
train.amp.enabled = True
train.train_micro_batch_size = 4
train.dist.data_parallel_size = 4
train.dist.tensor_parallel_size = 1
train.dist.pipeline_parallel_size = 1
```
预训练命令:
bash tools/train.sh tools/train_net.py configs/gpt2_pretrain.py 4
## result
### 精度
训练数据:[https://oneflow-static.oss-cn-beijing.aliyuncs.com/ci-files/dataset/libai/gpt_dataset](链接)
使用的GPGPU:4张DCU-Z100-16G。
模型精度:
| 卡数 | 分布式工具 | 收敛性 |
| :--: | :--------: | :---------------------------: |
| 4 | Libai-main | total_loss: 4.336/10000 iters |
## 混合并行配置指南
首先,可以在一个节点内的多卡上做模型并行切分。因为模型并行通信开销大(前后向可能都需要all-reduce通信),而节点内设备间带宽高;另外模型并行组大小越大,流水线Stage可以减少,继而可以减少流水线中的气泡;所以一般可以节点内所有设备作为一个模型并行组。
然后,在模型并行组大小确定后,单节点的可以容纳的模型大小基本确定,就可以据此再依次把多层 Layer 的模型分布到多个节点上,形成流水线并行。在实际中,先固定数据并行是1,参考上面总结固定模型并行大小,再加流水线并行的stage,直到模型可以放的下,不出现oom。
最后,情况就变得简单了,继续加节点,使用更高的数据并行规模,把一个模型并行组的模型复制出多个数据并行组的模型,对数据切分,形成更多的数据并行组,如此就可以形成一个3D并行的切分结果。
值得注意的是,在采用以上策略时,核心要素有几点。首先保证流水并行stage数量小,气泡尽可能少,所以有时可能会再扩大模型并行至2节点。其次,可以同时采用zero策略来不增加通信量的前提下减少显存占用,一般zero 1就可以最多减少75%左右的模型状态(下放的是优化器状态),当然使用zero 2也可以,但是需要注意是否会在真实训练场景中造成性能下降。然后,配合设置Gradient Accumulation Step以及Activation Checkpointing技术来进一步减少模型中间状态对显存的占用,一般Gradient Accumulation Step设置为流水并行度的1-2倍。最后,当显存占用优化明显后,就可以在相同规模的节点上放下更大的macro bs,尽量挤满显存,最终带来可观的性能提升。
当然,在不同参数量的网络下,以上配置需要进行调整,但是思路类似。
## 应用场景
### 算法类别
`自然语言处理`
### 热点应用行业
`医疗,教育,科研,金融`
## 源码仓库及问题反馈
- https://developer.sourcefind.cn/codes/modelzoo/GPT
## 参考
* https://libai.readthedocs.io/en/latest/tutorials/get_started/quick_run.html
* https://github.com/Oneflow-Inc/oneflow
* https://github.com/Oneflow-Inc/libai/blob/main/docs/source/notes/FAQ.md