# 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。

<img src="http://developer.hpccube.com/codes/modelzoo/GPT/-/raw/main/gpt2%E6%A8%A1%E5%9E%8B%E7%BB%93%E6%9E%84.png" alt="gpt2模型结构.png" style="zoom: 67%;" />

我们为了用户可以使用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），一般的自注意力模块允许某位置右侧的词计算时处于最大值。而掩模自注意力会阻止这种情况发生。

<img src="http://developer.hpccube.com/codes/modelzoo/GPT/-/raw/main/gpt2%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86.png" alt="gpt2算法原理.png" style="zoom:50%;" />

## 数据集

我们在libai目录下集成了部分小数据集供用户快速验证：

    ./nlp_data
## 环境配置

### 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
    # <Your Image ID>用上面拉取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 <Your Image ID> 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

## GPT2预训练

该预训练脚本运行环境为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

### 精度

训练数据：[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，尽量挤满显存，最终带来可观的性能提升。

当然，在不同参数量的网络下，以上配置需要进行调整，但是思路类似。

## 应用场景

### 算法类别

`自然语言处理`

### 热点应用行业

`nlp,智能聊天助手,科研`

## 源码仓库及问题反馈

- https://developer.hpccube.com/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