!18374 CPM model added.

Merge pull request !18374 from casgj/master_0615
This commit is contained in:
i-robot 2021-06-17 20:38:52 +08:00 committed by Gitee
commit 1e0766ab0a
31 changed files with 4988 additions and 0 deletions

View File

@ -51,6 +51,7 @@ In order to facilitate developers to enjoy the benefits of MindSpore framework,
- [LSTM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/lstm/README.md)
- [MASS](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/mass/README.md)
- [Transformer](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/transformer/README.md)
- [CPM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm/README.md)
- [Recommender Systems](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend)
- [DeepFM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend/deepfm/README.md)
- [NAML](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend/naml/README.md)

View File

@ -51,6 +51,7 @@
- [LSTM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/lstm/README.md)
- [MASS](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/mass/README.md)
- [Transformer](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/transformer/README.md)
- [CPM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm/README.md)
- [推荐系统](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend)
- [DeepFM](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend/deepfm/README.md)
- [NAML](https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/recommend/naml/README.md)

View File

@ -0,0 +1,433 @@
# Contents
[查看中文](./README_CN.md)
<!-- TOC -->
- [CPM Description](#CPM-Description)
- [Model Architecture](#Model-Architecture)
- [Dataset](#Dataset)
- [Environment Requirements](#Environment-Requirements)
- [Quick Start](#Quick Start)
- [Script Description](#Script-Description)
- [Script and Sample Code](#Script and Sample Code)
- [Script Parameters](#Script Parameters)
- [Zero-shot Inference](#zero-shot inference)
- [Pre-training Model Download](#Pre-training Model Download)
- [Dataset Preparation](#Dataset Preparation)
- [Zero-shot Inference Process](#Zero-shot Inference Process)
- [Finetune](#Finetune)
- [Dataset Preparation](#Dataset Preparation)
- [Finetune Training Process](#Finetune Training Process)
- [Evaluation Process](#Evaluation Process)
- [Performance](#Performance)
- [Zero-shot Performance](#Zero-shot Performance)
- [Finetune Performance](#Finetune Performance)
- [Description of Random Situation](#Description of Random Situation)
- [Other](#Other)
- [ModelZoo Homepage](#ModelZoo Homepage)
<!-- /TOC -->
# CPM CPM-Description
This is the fine-tune code warehouse of CPM model, which can be used for multi-machine and multi-card training/testing of model finetune. CPM[Project Home Page](https://cpm.baai.ac.cn/) was proposed in 2020 and a large-scale model based on Chinese processing. CPM is mainly used in the field of Chinese natural language processing (NLP) and generating tasks, such as machine translation, word selection and text summarization.
[Paper](https://arxiv.org/abs/2012.00413): Zhang Z, Han X, Zhou H, et al. CPM: A Large-scale Generative Chinese Pre-trained Language Model[J]. arXiv preprint arXiv:2012.00413, 2020.
# Model Architecture
CPM is implemented by GPT, which includes multi-layer decoder module.
# Dataset
- Training dataset*ChID*
- Training dataset*ChID*
ChID is a large-scale dataset of Chinese idioms for cloze, and it comes from the paper [ChID: A Large-scale Chinese IDiom Dataset for Cloze Test](https://www.aclweb.org/anthology/P19-1075/). This warehouse uses [Json format](https://drive.google.com/file/d/1KkwLSLgrV9JknO8rxxfmU5Iql-D4O_-6/view).
# Environment Requirements
- HardwareAscend)
- Prepare hardware environment with Ascend processor.
- Framework
- [MindSpore](https://gitee.com/mindspore/mindspore)
- For more information, please check the resources below:
- [MindSpore Tutorials](https://www.mindspore.cn/tutorial/training/zh-CN/master/index.html)
- [MindSpore Python API](https://www.mindspore.cn/doc/api_python/zh-CN/master/index.html)
# Quick Start
After dataset preparation, you can start zero-shot inference and finetune, evaluation as follows:
```bash
# run zero-shot inference example
cd scripts
sh run_zero-shot_inference_distribute_ascend.sh /path/test.mindrecord /path/true_labels.txt /path/cpm_mindspore_1p_fp32.ckpt /path/rank_table_2p.json
# run distributed finetune example
cd scripts
sh run_distribute_train_ascend_single_machine.sh /path/train.mindrecord /path/cpm_mindspore_1p_fp32.ckpt /path/rank_table_8p.json
# run evaluation example
cd scripts
bash run_eval_distribute_ascend.sh /path/finetune_test.mindrecord /path/test.json /path/ckpt_dictionary/ 8 /path/rank_table_2p.json
# Selects the best model on the dev dataset, and then tests the example on the test dataset
cd scripts
bash run_test_distribute_ascend.sh /path/finetune_dev.mindrecord /path/dev.json /path/finetune_test.mindrecord /path/test.json /path/ckpt_dictionary/ 8 /path/rank_table_2p.json
```
# Script Description
## Script and Sample Code
```shell
.
└─CPM
├─README.md // Introduction of CPM model.
├─scripts
├─run_zero-shot_inference_standalone_ascend.sh // Shell script for standalone zero-shot on ascend.
├─run_zero-shot_inference_distribute_ascend.sh // Shell script for distributed zero-shot on ascend.
├─run_distribute_train_ascend_single_machine.sh // Shell script for distributed finetune on ascend with single machine.
├─run_distribute_train_ascend_multi_machine.sh // Shell script for distributed finetune on ascend with multi-machine.
├─run_test_distribute_ascend.sh // Shell script for distributed evaluation and test on ascend.
└─run_eval_distribute_ascend.sh // Shell script for distributed evaluation on ascend.
├─data_process
├─make_zero_shot_mindrecord.py // Make dataset for zero-shot.
├─make_finetune_mindrecord.py // Make dataset for finetune.
└─tokenizer_cpm.py // Tokenization.
├─src
├─attention.py // attention mechanism.
├─config.py // Configuration file for zero-shot or finetune.
├─cpm_loss.py // Loss function.
├─cpm.py // CPM model.
├─cpm_train.py // Use CPM to train.
├─embedding.py // Embedding component.
├─loss_monitor.py // Callback of monitering loss during training step.
├─lr_schedule.py // Learning rate scheduler.
├─model_cpm.py // Model use for gradient cumulative.
├─util.py // User interface.
└─weight_init.py // Weight init.
├─gpt_ckpt_2_mindspore.py // Transform the model that MindSpore can load.
├─requirements.txt // Requirements of third party package.
├─zero-shot.py // Zero-shot api entry.
├─export.py // Export model.
├─train.py // Train api entry.
├─test.py // Evaluation and test api entry.
└─eval.py // Infer api entry.
```
## Script Parameters
The CPM network configuration parameters are in `src/config.py`, and the main parameters are described as follows:
```text
Parameters for dataset and network (Training/Evaluation):
mp Number of Model parallel.
batch_size Global batch size of input dataset.
seq_length max length of input sequence.
vocab_size size of each embedding vector.
hidden_size size of Transformer encoder layers.
num_hidden_layers number of hidden layers.
num_attention_heads number of attention heads.
lr init learning rate.
end_learning_rate end of learning rate.
weight_decay weight decay.
warmup_steps_rate rate of warmup steps.
dropout dropout probability.
grad_accumulation_step gradient cumulative steps.
sink_size control the amount of data in each sink.
epoch total number of iterations on the data per epoch.
```
## Zero-shot Inference
### Pre-training Model Download
- The CPM network pre training model can be downloaded here: [Model Download](https://cpm.baai.ac.cn/download.html).
Suppose you have the following documents:
- CPM-large/latest_checkpointed_iteration.txt
- CPM-large/80000/mp_rank_00_model_states.pt
- CPM-large/80000/mp_rank_01_model_states.pt
Next, the model integration script[change_mp.py](https://github.com/TsinghuaAI/CPM-Generate/blob/main/change_mp.py) is used to synthesize the above two fragment models into a complete single model.
```[bash]
python change_mp.py /path/to/CPM 1
```
The complete single model is as follows:
- CPM-large_MP1/latest_checkpointed_iteration.txt
- CPM-large_MP1/iter_0080000/mp_rank_01_model_states.pt
Then run the file`gpt_ckpt_2_mindspore.py` in the warehouse to convert the model into the model that can be loaded directly by Mindstore in the warehouse. Pay attention to modify the input and output file address in the file.
We get the model that mindpool can load, such as:`cpm_mindspore_1p_fp32.ckpt`.
- Word segmentation Download: [Model Download](https://github.com/TsinghuaAI/CPM-Finetune/tree/main/bpe_3w_new).
Suppose you have the following documents:
- bpe_3w_new/chinese_vocab.model
- bpe_3w_new/chinese_vocab.vocab
- bpe_3w_new/vocab.json
### Dataset Preparation
- The original dataset download address is [ChiD-Dataset](https://drive.google.com/drive/folders/1gL01xbFBcrgP0TmgOhJ_uplkeG-BCwvM)and we can refer to [ChiD-Dataset](https://github.com/chujiezheng/ChID-Dataset).
Suppose you have the following documents:
- chid_json/train.json
- chid_json/train_answer.json
- chid_json/dev.json
- chid_json/dev_answer.json
- chid_json/test.json
- chid_json/test_answer.json
- Data preprocessing: you may use [preprocess_chid_zeroshot.py](https://github.com/TsinghuaAI/CPM-Finetune/blob/main/preprocess_chid_zeroshot.py)to process the original data into the corresponding JSON format.
```[bash]
python preprocess_chid_zeroshot.py --data_dir ${PATH_TO_DATA_DIR} --tokenizer_path ${PATH_TO_TOKENIZER VOCAB} --output_dir ${PATH_TO_OUTPUT_JSON}
```
Mainly, `data_dir` is the address of the json data, such as `/home/dataset/chid_json`.
`tokenizer_path` is the address folder for the dictionary, such as `/home/bpe_3w_new/`.
`output_dir` is the preprocessing output address, such as`/home/dataset/test_dataset`.
The file will fill each candidate idiom into the corresponding blank of the article, and each blank will generate 10 new candidate articles. Finally, the data format generated by the file is as follows:
```[python]
{
"contents": [
[8, 15, ....],
....
], # After BPE word segmentation, all samples have the ID corresponding to the token.
"sids": [
0,
0,
...
1,
1,
...
], # Each generated candidate article corresponds to the number of the original sample.
"cids": [
0,
1,
2,
...
9,
0,
1,
...
], # The number of idioms corresponding to each generated candidate article.
"labels": [
3,
2,
...
], # Correct answer number of each original sample (integer between 0 and 9).
}
```
After the data preprocessing,the `test.json` file will be generated in the `--output_dir` directory.
- Then the file `test.json` obtained by data preprocessing is converted to mindrecord format:
```[bash]
python make_zero_shot_mindrecord.py --data_file ${PATH_TO_DATA_FILE} --vocab_path ${PATH_TO_TOKENIZER VOCAB} --output_path ${PATH_TO_OUTPUT FILE}
```
Mainly, `data_file` is the data address, such as `/home/dataset/test_dataset/test.json`.
`vocab_path` is the address folder directory of the dictionary, its definition is the same as before.
`output_path` is the output result file of the generated mindrecord, such as `/home/dataset/test_dataset/test.mindrecord`.
After processing, the specified directory `--output_path` will generate the inference mindrecord file and the ground file `true_labels.txt` in the same directory.
### Zero-shot Inference Process
- Set parameters in file `src/config.py`.
- Run`run_zero-shot_inference_distribute_ascend.sh` to zero shot inference.
```bash
cd scripts
bash run_zero-shot_inference_distribute_ascend.sh Test_MindRecord_addr test_json_addr model_addr rank_table_addr
```
Mainly, `Test_MindRecord_addr` is the address of the dataset, such as `/home/dataset/test_dataset/test.mindrecord`.
`test_json_addr` is the groundtruth file obtained from the data preprocessing, such as `/home/dataset/test_dataset/true_labels.txt`.
`model_addr` is the pre training model address, such as `/home/cpm_ckpt_ms/cpm_mindspore_1p.ckpt`.
`rank_table_addr` is a rank address for distributed reasoning, such as `/home/rank_table_2p.json`.
After reasoning, the accuracy rate will be generated. Please refer to the`zero-shot.py` file of thie warehouse for details.
## Finetune
In addition to zero shot reasoning, the pre training model can also be trained by finetune.
### Dataset Preparation
- The original data set is downloaded as above.
Suppose you have the following documents:
- chid_json/train.json
- chid_json/train_answer.json
- chid_json/dev.json
- chid_json/dev_answer.json
- chid_json/test.json
- chid_json/test_answer.json
- Data preprocessing: you may use [preprocess_chid_finetune.py](https://github.com/TsinghuaAI/CPM-Finetune/blob/main/preprocess_chid_finetune.py) scripts process the original data into the corresponding JSON format.
```[bash]
python preprocess_chid_finetune.py --data_dir ${PATH_TO_DATA_DIR} --tokenizer_path ${PATH_TO_TOKENIZER VOCAB} --output_dir ${PATH_TO_OUTPUT_JSON}
```
Mainly, `data_dir` is the address of the json data, such as `/home/dataset/chid_json`.
`tokenizer_path` is the address folder for the dictionary, such as `/home/vocab/`.
`output_dir` is the preprocessing output address, such as `/home/dataset/finetune_dataset`.
The template is defined and implemented in `process_sample` function of the `preprocess_chid_finetune.py` file. Finally, the data format generated by the file is as follows:
```[python]
[
{
"sent": [8, 15, ....], # The ID corresponding to the token after BPE segmentation.
"truth": 3 # The number of the correct answer idiom (an integer between 0 and 9).
}
...
]
```
After processing, three files, namely `train.json`, `valid.json` and `test.json` will be generated in the output directory `--output_dir`.
- After data preprocessing, the JSON data is transformed into mindrecord data set.
```[bash]
cd ./data_process/
python3 make_finetune_mindrecord.py --data_file ${PATH_TO_OUTPUT_JSON} --vocab_path ${PATH_TO_TOKENIZER VOCAB} --output_path ${PATH_TO_OUTPUT FILE} --num_patitions ${NUMBER_OF_MINDRECORD_PARTITIONS}
```
Mainly, `data_file` is the JSON data address, such as`/home/dataset/finetune_dataset/train.json` and `/home/dataset/finetune_dataset/test.json`.
`vocab_path` is the address folder directory of the dictionary, its definition is the same as before.
`output_path` is the preprocessing output address, such as`/home/dataset/finetune_dataset/`.
After processing, the mindrecord file of training and reasoning is generated in the specified directory `--output_path`, such as`train.mindrecord` and`test.mindrecord`.
### Finetune Training Process
- Set options in `src/config.py`, including loss_scale, learning rate and network hyperparameters. Click [here](https://www.mindspore.cn/tutorial/training/zh-CN/master/use/data_preparation.html) for more information about dataset.
- Run `run_distribute_train_ascend_single_machine.sh` for distributed and single machine training of CPM model.
``` bash
cd scripts
bash run_distribute_train_ascend_single_machine.sh Dataset_addr PreTrain_ckpt_addr Rank_table_addr
```
- Run `run_distribute_train_ascend_multi_machine.sh`for distributed and multi-machines training of CPM model.
``` bash
cd scripts
bash run_distribute_train_ascend_multi_machine.sh Dataset_addr PreTrain_ckpt_addr Rank_table_addr SERVER_ID
```
Mainly, `Dataset_addr` is the address of the dataset, such as `/home/dataset/finetune_dataset/train.mindrecord`.
`PreTrain_ckpt_addr` is the address for the pre training model, such as `/home/cpm_mindspore_1p_fp32.ckpt`.
`Rank_table_addr` is a rank address for distributed training, such as `/home/rank_table_8p.json`.
`SERVER_ID` is the sequence of the machine numbers from 0 in the multi machine process, such as: 0.
**Attention**Because the CPM model is too large to train on one card, distributed training is needed, including model parallel and data parallel.
In distributed parallel training, the device of the machine is the device of the device_ ID is numbered from 1 and incremented by 1.
Run a stand-alone 8 card, the index of rank is numbered from 0,2,4,6,1,3,5,7. When running multiple machines, the rank of the first machine's ID is 0,2,4,6,1,3,5,7. Rank of the second machine_ ID is 8,10,12,14,9,11,13,15. The rank of other machines and so on.
### Evaluation Process
- Set options in `src/config.py`.
- After finetune training, place the partition model with a specified epoch number to the same directory, including `train_strategy.ckpt`, `cpm_rank_1-*.ckpt`, where `train_strategy.ckpt` is the distributed policy file.
- Run `run_eval_distribute_ascend.sh` to evaluate.
```bash
cd scripts
bash run_eval_distribute_ascend.sh Test_MindRecord_addr Test_json_addr Model_addr Model_patition_number Rank_table_addr
```
In general, we select the model with the highest accuracy on the dev dataset, then infer on the test dataset, and finally generate the accuracy on the test dataset. Model selection can refer to the `run_test_distribute_ascend.sh` and `test.py` files for details.
```bash
cd scripts
bash run_test_distribute_ascend.sh Dev_MindRecord_addr Dev_json_addr Test_MindRecord_addr Test_json_addr Model_addr Model_patition_number Rank_table_addr
```
Mainly, `Test_MindRecord_addr` is the address of the test dataset, such as `/home/dataset/finetune_dataset/test.mindrecord`.
`Test_json_addr` is the test JSON file after data preprocessing, such as `/home/dataset/finetune_dataset/test.json`.
`Dev_MindRecord_addr` is the address of the dev dataset, such as `/home/dataset/finetune_dataset/dev.mindrecord`.
`Dev_json_addr` is the dev JSON file after data preprocessing, such as `/home/dataset/finetune_dataset/dev.json`.
`Model_addr` is used to infer the partition model of the folder, such as `/home/finetune_model/`.
`Model_patition_number`is the number of fragmentation modelsexcluding policy file`train_strategy.ckpt`. For example, after 8-card training, the number of partitioned models is 8.
`Rank_table_addr` is a rank address for distributed evaluation, such as `/home/rank_table_2p.json`.
**Attention**: the dataset preprocessing methods of zero-shot and finetuene are different.
# Performance
## Zero-shot Performance
The inference performance and accuracy of zero-shot single machine and dual cards are as follows:
| Parameters | Ascend |
| -------------------------- | --------------------------- |
| Resource |Ascend 910CPU 2.60GHz192 coresMemory 755GBOS Euler2.8 |
| MindSpore Version | 1.3.0 |
| Dataset | ChID |
| Number of parallel models | 2 |
| Speed | 152ms/step (2pcs) |
| batch_size | 2 |
| Output | Accuracy |
| Accuracy | accuracy=67.94% |
## Finetune Performance
The finetune performance and accuracy of single machine and 8 cards are as follows:
| Parameters | Ascend |
| -------------------------- | -------------------------------------------------------------- |
| Resource |Ascend 910CPU 2.60GHz192 coresMemory 755GBOS Euler2.8 |
| uploaded Date | 2021-06-07 |
| MindSpore Version | 1.3.0 |
| Dataset | ChID |
| Training Parameters | epoch=10, global_batch_size=16 |
| Number of parallel models | 2 |
| Optimizer | Adam |
| Accuracy | 80.4% |
| Speed | 1683ms/step (8pcs) |
| Loss | 0.7 |
| Params (M) | 2597.1 |
| Checkpoint for inference | 76G .ckpt file |
| Scripts | <https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm> |
The finetune performance and accuracy of 4 machines and 32 cards are as follows:
| Parameters | Ascend |
| -------------------------- | -------------------------------------------------------------- |
| Resource |Ascend 910CPU 2.60GHz192 coresMemory 755GBOS Euler2.8 |
| uploaded Date | 2021-06-07 |
| MindSpore Version | 1.3.0 |
| Dataset | ChID |
| Training Parameters | epoch=10, global_batch_size=128 |
| Number of parallel models | 2 |
| Optimizer | Adam |
| Accuracy | 81.4% |
| Speed | 2740ms/step (32pcs) |
| Loss | 0.008 |
| Params (M) | 2597.1 |
| Checkpoint for inference | 57G .ckpt file |
| Scripts | <https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm> |
# Description of Random Situation
There are two random situations:
- Shuffle of the dataset.
- Dropout operations.
Some seeds have already been set in train.py to avoid the randomness of dataset shuffle and weight initialization. If you want to disable dropout, please set the corresponding dropout_prob parameter to 0 in src/config.py.
# Other
The accuracy and performance of this model have been verified in Ascend environment, but not in CPU and GPU.
# ModelZoo Homepage
Please check the official [homepage](https://gitee.com/mindspore/mindspore/tree/master/model_zoo).

View File

@ -0,0 +1,435 @@
# 目录
[view English](./README.md)
<!-- TOC -->
- [目录](#目录)
- [CPM 概述](#CPM-概述)
- [模型架构](#模型架构)
- [数据集](#数据集)
- [环境要求](#环境要求)
- [快速入门](#快速入门)
- [脚本说明](#脚本说明)
- [脚本和样例代码](#脚本和样例代码)
- [网络参数说明](#网络参数说明)
- [Zero-shot推理](#Zero-shot推理)
- [预训练模型下载](#预训练模型下载)
- [Zero-shot准备数据集](#Zero-shot准备数据集)
- [Zero-shot推理过程](#Zero-shot推理过程)
- [Finetune微调训练](#Finetune微调训练)
- [Finetune准备数据集](#准备数据集)
- [Finetune训练过程](#训练过程)
- [Finetune评估过程](#评估过程)
- [性能和精度](#性能和精度)
- [Zero-shot评估性能和精度](#Zero-shot评估性能)
- [Finetune训练性能和精度](#Finetune训练性能)
- [随机情况说明](#随机情况说明)
- [其他](#其他)
- [ModelZoo主页](#modelzoo主页)
<!-- /TOC -->
# CPM 概述
本仓库为CPM模型的fine-tune代码仓库可用于模型finetune的多机多卡训练/测试。CPM网络[项目首页](https://cpm.baai.ac.cn/)于2020年提出是一种以中文处理为核心的大规模模型。CPM主要应用于中文自然语言处理NLP领域、生成任务等,如机器翻译、选词填空或文本摘要等任务。
[论文](https://arxiv.org/abs/2012.00413): Zhang Z, Han X, Zhou H, et al. CPM: A Large-scale Generative Chinese Pre-trained Language Model[J]. arXiv preprint arXiv:2012.00413, 2020.
# 模型架构
CPM网络由GPT实现GPT包括多层解码器模块。
# 数据集
- 训练数据集*ChID*
- 评估数据集*ChID*
ChID 是一种面向完形填空的大规模汉语成语数据集,其来源于论文 [ChID: A Large-scale Chinese IDiom Dataset for Cloze Test](https://www.aclweb.org/anthology/P19-1075/). 本仓库中使用 [Json 格式](https://drive.google.com/file/d/1KkwLSLgrV9JknO8rxxfmU5Iql-D4O_-6/view).
# 环境要求
- 硬件Ascend处理器
- 使用Ascend处理器准备硬件环境。
- 框架
- [MindSpore](https://gitee.com/mindspore/mindspore)
- 如需查看详情,请参见如下资源:
- [MindSpore教程](https://www.mindspore.cn/tutorial/training/zh-CN/master/index.html)
- [MindSpore Python API](https://www.mindspore.cn/doc/api_python/zh-CN/master/index.html)
# 快速入门
数据集准备完成后请按照如下步骤开始Zero-shot推理、Finetune训练和评估
```bash
# zero-shot推理示例
cd scripts
sh run_zero-shot_inference_distribute_ascend.sh /path/test.mindrecord /path/true_labels.txt /path/cpm_mindspore_1p_fp32.ckpt /path/rank_table_2p.json
# 运行分布式训练Finetune示例
cd scripts
sh run_distribute_train_ascend_single_machine.sh /path/train.mindrecord /path/cpm_mindspore_1p_fp32.ckpt /path/rank_table_8p.json
# Finetune模型评估示例
cd scripts
bash run_eval_distribute_ascend.sh /path/finetune_test.mindrecord /path/test.json /path/ckpt_dictionary/ 8 /path/rank_table_2p.json
# Finetune模型在dev数据集上选最优再在test数据集上测试示例
cd scripts
bash run_test_distribute_ascend.sh /path/finetune_dev.mindrecord /path/dev.json /path/finetune_test.mindrecord /path/test.json /path/ckpt_dictionary/ 8 /path/rank_table_2p.json
```
# 脚本说明
## 脚本和样例代码
```shell
.
└─CPM
├─README.md // Introduction of CPM model.
├─scripts
├─run_zero-shot_inference_standalone_ascend.sh // Shell script for standalone zero-shot on ascend.
├─run_zero-shot_inference_distribute_ascend.sh // Shell script for distributed zero-shot on ascend.
├─run_distribute_train_ascend_single_machine.sh // Shell script for distributed finetune on ascend with single machine.
├─run_distribute_train_ascend_multi_machine.sh // Shell script for distributed finetune on ascend with multi-machine.
├─run_test_distribute_ascend.sh // Shell script for distributed evaluation and test on ascend.
└─run_eval_distribute_ascend.sh // Shell script for distributed evaluation on ascend.
├─data_process
├─make_zero_shot_mindrecord.py // Make dataset for zero-shot.
├─make_finetune_mindrecord.py // Make dataset for finetune.
└─tokenizer_cpm.py // Tokenization.
├─src
├─attention.py // attention mechanism.
├─config.py // Configuration file for zero-shot or finetune.
├─cpm_loss.py // Loss function.
├─cpm.py // CPM model.
├─cpm_train.py // Use CPM to train.
├─embedding.py // Embedding component.
├─loss_monitor.py // Callback of monitering loss during training step.
├─lr_schedule.py // Learning rate scheduler.
├─model_cpm.py // Model use for gradient cumulative.
├─util.py // User interface.
└─weight_init.py // Weight init.
├─gpt_ckpt_2_mindspore.py // Transform the model that MindSpore can load.
├─requirements.txt // Requirements of third party package.
├─zero-shot.py // Zero-shot api entry.
├─export.py // Export model.
├─train.py // Train api entry.
├─test.py // Evaluation and test api entry.
└─eval.py // Infer api entry.
```
## 网络参数说明
网络配置参数在src/config.py中将主要参数说明
```text
Parameters for dataset and network (Training/Evaluation):
mp Number of Model parallel.
batch_size Global batch size of input dataset.
seq_length max length of input sequence.
vocab_size size of each embedding vector.
hidden_size size of Transformer encoder layers.
num_hidden_layers number of hidden layers.
num_attention_heads number of attention heads.
lr init learning rate.
end_learning_rate end of learning rate.
weight_decay weight decay.
warmup_steps_rate rate of warmup steps.
dropout dropout probability.
grad_accumulation_step gradient cumulative steps.
sink_size control the amount of data in each sink.
epoch total number of iterations on the data per epoch.
```
## Zero-shot推理
### 预训练模型下载
- CPM网络预训练模型下载[模型下载](https://cpm.baai.ac.cn/download.html)。
假设您已获得下列文件:
- CPM-large/latest_checkpointed_iteration.txt
- CPM-large/80000/mp_rank_00_model_states.pt
- CPM-large/80000/mp_rank_01_model_states.pt
接下来,您可能会使用模型合并脚本[change_mp.py](https://github.com/TsinghuaAI/CPM-Generate/blob/main/change_mp.py)将上述两个分片模型合成完整的单个模型:
```[bash]
python change_mp.py /path/to/CPM 1
```
上述,得到完整的单个模型:
- CPM-large_MP1/latest_checkpointed_iteration.txt
- CPM-large_MP1/iter_0080000/mp_rank_01_model_states.pt
再运行本仓库中的`gpt_ckpt_2_mindspore.py`文件将模型转化为本仓库中mindspore能直接加载的模型注意修改该文件中的输入输出文件地址。
由此得到mindspore可加载的模型`cpm_mindspore_1p_fp32.ckpt`。
- 分词器下载:[模型下载](https://github.com/TsinghuaAI/CPM-Finetune/tree/main/bpe_3w_new)。
假设您已获得下列文件:
- bpe_3w_new/chinese_vocab.model
- bpe_3w_new/chinese_vocab.vocab
- bpe_3w_new/vocab.json
### Zero-shot准备数据集
- 原始数据集下载地址[ChiD-Dataset](https://drive.google.com/drive/folders/1gL01xbFBcrgP0TmgOhJ_uplkeG-BCwvM),可参考[ChiD-Dataset说明](https://github.com/chujiezheng/ChID-Dataset)。
假设您已获得下列文件:
- chid_json/train.json
- chid_json/train_answer.json
- chid_json/dev.json
- chid_json/dev_answer.json
- chid_json/test.json
- chid_json/test_answer.json
- 数据预处理:您可能会使用脚本[preprocess_chid_zeroshot.py](https://github.com/TsinghuaAI/CPM-Finetune/blob/main/preprocess_chid_zeroshot.py)将原始数据处理成相应的json格式。
```[bash]
python preprocess_chid_zeroshot.py --data_dir ${PATH_TO_DATA_DIR} --tokenizer_path ${PATH_TO_TOKENIZER VOCAB} --output_dir ${PATH_TO_OUTPUT_JSON}
```
主要地,`data_dir`是数据集的地址,如`/home/dataset/chid_json`
`tokenizer_path`为字典的地址文件夹,如`/home/bpe_3w_new/`
`output_dir`为预处理输出结果地址,如`/home/dataset/test_dataset`。
该文件会将每个候选的成语填入文章相应的空白中每个空白生成10个新的候选文章。最终该文件生成的数据格式为
```[python]
{
"contents": [
[8, 15, ....],
....
], # 所有样本经过 bpe 分词之后 token 对应的 id。
"sids": [
0,
0,
...
1,
1,
...
], # 每个生成出的候选文章对应原来样本的编号
"cids": [
0,
1,
2,
...
9,
0,
1,
...
], # 每个生成出的候选文章对应的成语的编号
"labels": [
3,
2,
...
], # 每个原样本的正确答案编号0~9之间的整数
}
```
预处理完成后,在上述指定的`--output_dir`输出目录下会生成`test.json`文件。
- 将上一步得到的`--output_dir`路径下产生的json数据转换为MindRecord数据格式
```[bash]
python make_zero_shot_mindrecord.py --data_file ${PATH_TO_DATA_FILE} --vocab_path ${PATH_TO_TOKENIZER VOCAB} --output_path ${PATH_TO_OUTPUT FILE}
```
主要地,`data_file`是数据地址,如`/home/dataset/test_dataset/test.json`
`vocab_path`为字典的地址文件夹目录,同上;
`output_path`为生成的mindrecord的输出结果文件,如`/home/dataset/test_dataset/test.mindrecord`。
处理完成后,指定的`--output_path`目录下生成推理的mindrecord文件以及同目录下生成ground_truth文件`true_labels.txt`。
### Zero-shot推理过程
- 在`src/config.py`中设置参数;
- 运行`run_zero-shot_inference_distribute_ascend.sh`进行zero-shot推理。
```bash
cd scripts
bash run_zero-shot_inference_distribute_ascend.sh Test_MindRecord_addr test_json_addr model_addr rank_table_addr
```
主要地, `Test_MindRecord_addr`为推理数据集mindrecord如`/home/dataset/test_dataset/test.mindrecord`
`test_json_addr`为预处理后的数据集的groundtruth文件如`/home/dataset/test_dataset/true_labels.txt`
`model_addr`为预训练模型地址,如`/home/cpm_ckpt_ms/cpm_mindspore_1p.ckpt`
`rank_table_addr`为进行推理的时候的分布式推理的rank_table地址如`/home/rank_table_2p.json`。
推理完后,会生成准确率,具体可参考本仓库`zero-shot.py`文件。
## Finetune微调训练
上述预训练模型除了可以进行zero-shot推理外还可以进行Finetune训练。
### Finetune准备数据集
- 原始数据集下载同上。
假设您已获得下列文件:
- chid_json/train.json
- chid_json/train_answer.json
- chid_json/dev.json
- chid_json/dev_answer.json
- chid_json/test.json
- chid_json/test_answer.json
- 数据预处理:您可能会使用脚本[preprocess_chid_finetune.py](https://github.com/TsinghuaAI/CPM-Finetune/blob/main/preprocess_chid_finetune.py)将原始数据处理成相应的json格式。
```[bash]
python preprocess_chid_finetune.py --data_dir ${PATH_TO_DATA_DIR} --tokenizer_path ${PATH_TO_TOKENIZER VOCAB} --output_dir ${PATH_TO_OUTPUT_JSON}
```
主要地,`--data_dir`是数据集的地址,如`/home/dataset/chid_json``--tokenizer_path`为字典的地址文件夹,如`/home/vocab/`
`--output_dir`为预处理输出结果地址,如`/home/dataset/finetune_dataset`。
其中,模板定义与实现在 `preprocess_chid_finetune.py` 文件 `process_sample` 函数中。最终,该文件生成的数据格式为:
```[python]
[
{
"sent": [8, 15, ....], # 经过 bpe 分词之后 token 对应的 id
"truth": 3 # 正确答案成语的编号0~9之间的整数
}
...
]
```
处理完成后,在上述指定的`--output_dir`输出目录下会生成 `train.json`, `valid.json`, `test.json` 三个文件。
- 将上一步得到的`--output_dir`路径下产生的json数据转换为MindRecord数据格式进行训练
```[bash]
cd ./data_process/
python3 make_finetune_mindrecord.py --data_file ${PATH_TO_OUTPUT_JSON} --vocab_path ${PATH_TO_TOKENIZER VOCAB} --output_path ${PATH_TO_OUTPUT FILE} --num_patitions ${NUMBER_OF_MINDRECORD_PARTITIONS}
```
主要地,`--data_file`是数据地址,如`/home/dataset/finetune_dataset/train.json``--vocab_path`为字典的地址文件夹目录,同上;
`--output_path`为生成的mindrecord的输出结果文件夹目录,如`/home/dataset/finetune_dataset/`
处理完成后,指定的`--output_path`目录下生成训练和推理的mindrecord文件如`train.mindrecord`和`test.mindrecord`。
### Finetune训练过程
- 在`src/config.py`中设置包括模型并行、batchsize、学习率和网络超参数。点击[这里](https://www.mindspore.cn/tutorial/training/zh-CN/master/use/data_preparation.html)查看更多数据集信息。
- 运行`run_distribute_train_ascend_single_machine.sh`进行CPM模型的单机8卡分布式训练。
``` bash
cd scripts
bash run_distribute_train_ascend_single_machine.sh Dataset_addr PreTrain_ckpt_addr Rank_table_addr
```
- 运行`run_distribute_train_ascend_multi_machine.sh`进行CPM模型的多机多卡分布式训练。
``` bash
cd scripts
bash run_distribute_train_ascend_multi_machine.sh Dataset_addr PreTrain_ckpt_addr Rank_table_addr SERVER_ID
```
主要地,`Dataset_addr` 是数据地址,如`/home/dataset/finetune_dataset/train.mindrecord`
`PreTrain_ckpt_addr` 为预训练模型的地址,如`/home/cpm_mindspore_1p_fp32.ckpt`
`Rank_table_addr` 为Rank_table的地址,如`/home/rank_table_8p.json`
`SERVER_ID` 为多机过程中机器从0开始编号的的依次顺序0。
**注意**由于本CPM模型较大无法在一张卡上训练需要进行分布式训练包括模型并行和数据并行。
分布式并行训练时机器的device的device_id从1开始编号依次递增1。
运行单机8卡rank_table里的rank_id从0,2,4,6,1,3,5,7编号
运行多机多卡时第一台机器的rank_id分别为0,2,4,6,1,3,5,7第2台机器的rank_id是8,10,12,14,9,11,13,15后面的机器的rank_id依次类推。
### Finetune评估过程
- 在`src/config.py`中设置参数;
- 上述Finetune训练结束将指定某个epoch数的分片模型放置到同指定目录下包括`train_strategy.ckpt`, `cpm_rank_1-*.ckpt`等,其中`train_strategy.ckpt`为分布式训练的策略文件。
- 运行`run_eval_distribute_ascend.sh`评估某个CPM模型。
```bash
cd scripts
bash run_eval_distribute_ascend.sh Test_MindRecord_addr Test_json_addr Model_addr Model_patition_number Rank_table_addr
```
通常我们会选择在dev数据集上精度最高的模型再在test数据集上进行推理最后会生成测试集上的准确率模型选择可参考`run_test_distribute_ascend.sh`或`test.py`文件。
```bash
cd scripts
bash run_test_distribute_ascend.sh Dev_MindRecord_addr Dev_json_addr Test_MindRecord_addr Test_json_addr Model_addr Model_patition_number Rank_table_addr
```
主要地, `Test_MindRecord_addr`为test数据集mindrecord如`/home/dataset/finetune_dataset/test.mindrecord`
`Test_json_addr`为预处理后的test数据集的json文件如`/home/dataset/finetune_dataset/test.json`
`Dev_MindRecord_addr`为dev数据集mindrecord如`/home/dataset/finetune_dataset/dev.mindrecord`
`Dev_json_addr`为预处理后的dev数据集的json文件如`/home/dataset/finetune_dataset/dev.json`
`Model_addr`为Finetune得到的模型文件夹如`/home/finetune_model/`
`Model_patition_number`为Finetune得到的模型的分片数量不包括策略文件`train_strategy.ckpt`, 如单机8卡得到的为8
`Rank_table_addr`为进行推理的时候的分布式推理的rank_table地址如`/home/rank_table_2p.json`。
注意Finetune的推理的数据集预处理和zero-shot的数据集预处理方式不一样。
# 性能和精度
## Zero-shot评估性能和精度
Zero-shot单机双卡推理性能和精度如下
| 参数 | Ascend |
| ------------------- | --------------------------- |
| 资源 |Ascend 910CPU 2.60GHz192核内存 755GB系统 Euler2.8 |
| MindSpore版本 | 1.3.0 |
| 数据集 | ChID数据集 |
| 模型并行数 | 2 |
| 速度 | 152毫秒/步 |
| Ascend芯片使用数量 | 2 |
| batch_size | 2 |
| 输出 | 准确率 |
| 准确率 | accuracy=67.94% |
## Finetune训练性能和精度
单机8卡Finetune性能和精度如下
| 参数 | Ascend |
| -------------------------- | -------------------------------------------------------------- |
| 资源 |Ascend 910CPU 2.60GHz192核内存 755GB系统 Euler2.8 |
| 上传日期 | 2021-06-07 |
| MindSpore版本 | 1.3.0 |
| 数据集 | ChID |
| 训练参数 | epoch=10, global_batch_size=16 |
| 模型并行数 | 2 |
| 优化器 | Adam |
| 准确率 | 80.4% |
| 速度 | 1683毫秒/步(8卡) |
| 损失 | 0.7 |
| 参数 (M) | 2597.1 |
| 推理检查点 | 76G .ckpt文件 |
| 脚本 | <https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm> |
四机32卡Finetune性能和精度如下
| 参数 | Ascend |
| -------------------------- | -------------------------------------------------------------- |
| 资源 |Ascend 910CPU 2.60GHz192核内存 755GB系统 Euler2.8 |
| 上传日期 | 2021-06-07 |
| MindSpore版本 | 1.3.0 |
| 数据集 | ChID |
| 训练参数 | epoch=10, global_batch_size=128 |
| 模型并行数 | 2 |
| 优化器 | Adam |
| 准确率 | 81.4% |
| 速度 | 2740毫秒/步(32卡) |
| 损失 | 0.08 |
| 参数 (M) | 2597.1 |
| 推理检查点 | 57G .ckpt文件 |
| 脚本 | <https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/nlp/cpm> |
# 随机情况说明
以下两种随机情况:
- 数据集Shuffle
- Dropout随机丢弃
train.py已经设置了一些种子避免数据集轮换和权重初始化的随机性。
# 其他
本模型已经在Ascend环境上验证了精度和性能还没有在CPU和GPU上验证.
# ModelZoo主页
请浏览官网[主页](https://gitee.com/mindspore/mindspore/tree/master/model_zoo)。

View File

@ -0,0 +1,125 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""make finetune-mindrecord dataset."""
import os
import json
import argparse
import numpy as np
from tqdm import trange
from mindspore.mindrecord import FileWriter
from tokenizer_cpm import CPMTokenizer
class CHIDDataset():
"""Dataset define for ChId."""
def __init__(self, data_path, tokenizer):
self.pad_id = tokenizer.pad_id
with open(data_path, "r") as f:
self.cand_ids, data_cpm = json.load(f)
self.samples, self.sizes = self.process(data_cpm)
self.max_size = max(self.sizes)
def process(self, process_data, num_samples=10):
"""Dataset process."""
sizes = []
samples = []
for d in process_data:
loss_mask = [0] * (len(d["sent"]) - 2) + [1]
samples.append((
d["sent"][:-1], # ids for the tokenized sentence
loss_mask, # mask of the loss
d["sent"][1:], # token labels of each sentence
d["truth"], # labels if each sentence, should be an integer in [0, 9]
))
sizes.append(len(d["sent"]) - 1)
return samples, sizes
def _pad_process(self, input_ids, loss_mask, labels):
"""Dataset padding process."""
pad_input_ids = np.ones(shape=(self.max_size), dtype=np.int64) * self.pad_id
pad_loss_mask = np.zeros(shape=(self.max_size)) * 1.0
pad_labels = np.ones(shape=(self.max_size), dtype=np.int64) * self.pad_id
pad_input_ids[:len(input_ids)] = input_ids
pad_loss_mask[:len(loss_mask)] = loss_mask
pad_labels[:len(labels)] = labels
return pad_input_ids, pad_loss_mask, pad_labels
def __len__(self):
return len(self.sizes)
def __getitem__(self, idx):
input_ids, loss_mask, labels, truth = self.samples[idx]
pad_input_ids, pad_loss_mask, pad_labels = self._pad_process(input_ids, loss_mask, labels)
sample = {
"truth": truth,
"input_ids": pad_input_ids,
"loss_mask": pad_loss_mask,
"labels": pad_labels,
"size": self.sizes[idx]
}
return sample
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CPM dataset.')
parser.add_argument("--vocab_path", type=str, required=False,
default="./vocab",
help="the tokenizer vocab path.")
parser.add_argument("--data_file", type=str, required=False,
default="./preprocessed/train.json",
help="finetune train dataset files.")
parser.add_argument("--output_path", type=str, required=False,
default="./output/train.mindrecord",
help="mindrecord dataset output path.")
parser.add_argument("--num_partitions", type=int, required=False,
default=1, help="the number of mindrecord partitions.")
args = parser.parse_args()
# get the tokenizer
tokenizer_cpm = CPMTokenizer(os.path.join(args.vocab_path, 'vocab.json'),
os.path.join(args.vocab_path, 'chinese_vocab.model'))
os.makedirs(os.path.dirname(args.output_path), exist_ok=True)
chidDataset = CHIDDataset(args.data_file, tokenizer_cpm)
chid_schema = {"truth": {"type": "int64"},
"input_ids": {"type": "int64", "shape": [-1]},
"loss_mask": {"type": "float64", "shape": [-1]},
"labels": {"type": "int64", "shape": [-1]},
"size": {"type": "int64"}}
writer = FileWriter(file_name=args.output_path, shard_num=args.num_partitions)
writer.add_schema(chid_schema, "preprocessed chid dataset")
data = []
for i in trange(len(chidDataset)):
data.append(chidDataset[i])
if i % 100 == 0:
writer.write_raw_data(data)
data = []
if data:
writer.write_raw_data(data)
writer.commit()
print("transform mindrecord successfully, refer: {}".format(args.output_path))

View File

@ -0,0 +1,140 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""make zero-shot mindrecord dataset."""
import os
import json
import argparse
import numpy as np
from tqdm import tqdm
from mindspore.mindrecord import FileWriter
from tokenizer_cpm import CPMTokenizer
class CHIDDataset():
"""Dataset define for ChId."""
def __init__(self, data_path, tokenizer):
self.pad_id = tokenizer.pad_id
self.eod_token = tokenizer.eod_id
with open(data_path, "r") as f:
data_cpm = json.load(f)
self.samples, self.sizes, self.truth_labels = self.process(data_cpm)
with open(os.path.join(os.path.dirname(args.output_path), "true_labels.txt"), "w") as f:
for it in range(0, len(self.truth_labels)):
f.write(str(self.truth_labels[it]) + "\n")
self.max_size = max(self.sizes)
def process(self, process_data):
"""Dataset process."""
contents = process_data["contents"]
sids = process_data["sids"]
truth_labels = process_data["labels"]
cids = process_data["cids"]
sizes = []
samples = []
for content, sid, cid in zip(contents, sids, cids):
input_ids = content
input_ids = input_ids + [self.eod_token]
length = len(input_ids) - 1
sizes.append(length)
samples.append((
sid,
cid,
input_ids[:-1],
[1.0] * length,
input_ids[1:]
))
return samples, sizes, truth_labels
def _pad_process(self, input_ids, loss_mask, labels):
"""Dataset padding process."""
pad_input_ids = np.ones(shape=(self.max_size), dtype=np.int64) * self.pad_id
pad_loss_mask = np.zeros(shape=(self.max_size)) * 1.0
pad_labels = np.ones(shape=(self.max_size), dtype=np.int64) * self.pad_id
pad_input_ids[:len(input_ids)] = input_ids
pad_loss_mask[:len(loss_mask)] = loss_mask
pad_labels[:len(labels)] = labels
return pad_input_ids, pad_loss_mask, pad_labels
def __len__(self):
return len(self.sizes)
def __getitem__(self, idx):
sid, cid, input_ids, loss_mask, labels = self.samples[idx]
pad_input_ids, pad_loss_mask, pad_labels = self._pad_process(input_ids, loss_mask, labels)
sample = {
"sid": sid,
"cid": cid,
"input_ids": pad_input_ids,
"loss_mask": pad_loss_mask,
"labels": pad_labels,
"size": self.sizes[idx]
}
return sample
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CPM dataset.')
parser.add_argument("--vocab_path", type=str, required=False,
default="./vocab",
help="the tokenizer vocab path.")
parser.add_argument("--data_file", type=str, required=False,
default="./preprocessed/zeroshot/test.json",
help="zero-shot test dataset files.")
parser.add_argument("--output_path", type=str, required=False,
default="./output/test.mindrecord",
help="mindrecord dataset output path.")
parser.add_argument("--num_partitions", type=int, required=False,
default=4, help="the number of mindrecord partitions.")
args = parser.parse_args()
# get the tokenizer
tokenizer_cpm = CPMTokenizer(os.path.join(args.vocab_path, 'vocab.json'),
os.path.join(args.vocab_path, 'chinese_vocab.model'))
os.makedirs(os.path.dirname(args.output_path), exist_ok=True)
chidDataset = CHIDDataset(args.data_file, tokenizer_cpm)
chid_schema = {"sid": {"type": "int64"},
"cid": {"type": "int64"},
"input_ids": {"type": "int64", "shape": [-1]},
"loss_mask": {"type": "float64", "shape": [-1]},
"labels": {"type": "int64", "shape": [-1]},
"size": {"type": "int64"}}
writer = FileWriter(file_name=args.output_path, shard_num=args.num_partitions)
writer.add_schema(chid_schema, "preprocessed chid dataset")
data = []
for i in tqdm(range(len(chidDataset))):
data.append(chidDataset[i])
if i % 100 == 0:
writer.write_raw_data(data)
data = []
if data:
writer.write_raw_data(data)
writer.commit()
print("transform mindrecord successfully, refer: {}".format(args.output_path))

View File

@ -0,0 +1,71 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Tokenization classes for CPM."""
from functools import lru_cache
from io import open
import logging
import json
import jieba
import sentencepiece as spm
jieba.setLogLevel(logging.INFO)
class CPMTokenizer():
"""Tokenizer for CPM network."""
def __init__(self, vocab_json, chinese_vocab_file):
self.tok2idx = json.load(open(vocab_json))
self.idx2tok = {token_idx: token_key for token_key, token_idx in self.tok2idx.items()}
self.spp = spm.SentencePieceProcessor(model_file=chinese_vocab_file)
self.translator = str.maketrans(" \n", "\u2582\u2583")
self.eod_id = self.tok2idx['<eod>']
self.unk_id = self.tok2idx['<unk>']
self.mask_id = self.tok2idx['<mask>']
self.pad_id = self.tok2idx['<pad>']
@property
def vocab_size(self):
return len(self.tok2idx)
def __len__(self):
return len(self.tok2idx)
@property
def unk(self):
return self.unk_id
@property
def mask(self):
return self.mask_id
@property
def eod(self):
return self.eod_id
@lru_cache()
def tokenize_op(self, input_text):
input_tokens = [x_item.translate(self.translator) for x_item in jieba.cut(input_text, cut_all=False)]
return self.spp.encode(" ".join(input_tokens))
def encode(self, input_text):
result_encode = self.tokenize_op(input_text)
return result_encode
def decode(self, input_tokens):
text_decoder = self.spp.decode(input_tokens)
text_decoder = text_decoder.replace(' ', '').replace('\u2582', ' ').replace('\u2583', '\n')
return text_decoder

View File

@ -0,0 +1,296 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Eval."""
import os
import ast
import argparse
import json
import numpy as np
from mindspore import context, load_distributed_checkpoint
import mindspore.nn as nn
from mindspore.common.tensor import Tensor
from mindspore.train.model import Model
import mindspore.common.dtype as mstype
from mindspore.train.serialization import load_checkpoint, load_param_into_net
from mindspore.communication import management as MultiAscend
from mindspore.context import ParallelMode
from mindspore.parallel import set_algo_parameters
from src.cpm import CPMModel
from src.cpm_train import VirtualDatasetOneInputCell
from src.cpm_loss import Cross_entropy_eval
from src.config import finetune_test_distrubute, finetune_test_standalone
from train import load_dataset
device_id = int(os.getenv("DEVICE_ID"))
rank_size = os.getenv('RANK_SIZE')
context.set_context(mode=context.GRAPH_MODE,
save_graphs=False,
device_target="Ascend",
device_id=device_id)
class CPMForInfer(nn.Cell):
"""
Encapsulation class of CPM network infer.
Args:
network (nn.Cell): CPM model.
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
config: The config of networks.
Returns:
Tensor, losses.
"""
def __init__(self, network, batch_size, seq_length, vocab_size, config):
super(CPMForInfer, self).__init__(auto_prefix=False)
self.network = network
self.batch_size = batch_size
self.seq_length = seq_length
self.vocab_size = vocab_size
self.loss_net = Cross_entropy_eval(batch_size=self.batch_size,
seq_length=self.seq_length,
vocab_size=self.vocab_size,
config=config)
def construct(self, input_ids, position_ids, attention_mask, loss_mask):
logits = self.network(input_ids, position_ids, attention_mask)
loss = self.loss_net(logits, loss_mask)
return loss
class CPM_LAYER(nn.Cell):
"""
CPM model training with loss function.
"""
def __init__(self, config_eval):
super(CPM_LAYER, self).__init__()
self.cpm_model = CPMModel(batch_size=config_eval.batch_size,
seq_length=config_eval.seq_length,
vocab_size=config_eval.vocab_size,
hidden_size=config_eval.hidden_size,
num_hidden_layers=config_eval.num_hidden_layers,
num_attention_heads=config_eval.num_attention_heads,
config=config_eval)
def construct(self, input_ids, position_ids=None, attention_mask=None):
output = self.cpm_model(input_ids, position_ids, attention_mask)
return output
def run_eval(args, config_eval, ckpt_file_list=None):
"""
Building infer pipeline
"""
with open(args.data_path, "r") as f:
# cand_ids, data
cand_ids, _ = json.load(f)
print("++++ cand_ids: ", cand_ids)
if args.distribute:
dataset = load_dataset(args.dataset, config_eval.batch_size,
rank_size=MultiAscend.get_group_size(),
rank_id=MultiAscend.get_rank(),
drop_remainder=False,
is_training=False,
shuffle=False)
else:
dataset = load_dataset(args.dataset,
config_eval.batch_size,
drop_remainder=False,
is_training=False,
shuffle=False)
cpm_model = CPM_LAYER(config_eval)
if args.distribute:
cpm_model = VirtualDatasetOneInputCell(cpm_model)
params = cpm_model.trainable_params()
print("+++++++current network parameter+++++")
for pas in params:
print(pas.name)
print("++++++++++++")
if not args.has_train_strategy:
# load the checkpoint without train strategy.
weights = load_checkpoint(args.ckpt_path_doc)
can_be_loaded = {}
print("+++++++loading weights+++++")
for name, _ in weights.items():
print('oldname: ' + name)
if 'cpm_model.' not in name:
can_be_loaded['cpm_model.' + name] = weights[name]
print('newname: cpm_model.' + name)
else:
can_be_loaded[name] = weights[name]
print("+++++++loaded weights+++++")
load_param_into_net(cpm_model, parameter_dict=can_be_loaded)
infer_net = CPMForInfer(network=cpm_model,
batch_size=config_eval.batch_size,
seq_length=config_eval.seq_length,
vocab_size=config_eval.vocab_size,
config=config_eval)
model = Model(infer_net)
if args.has_train_strategy and not args.distribute:
# load sliced checkpoint with train strategy, but will run standalone inference without model parallel.
load_distributed_checkpoint(infer_net, ckpt_file_list, None)
if args.has_train_strategy and args.distribute:
# load sliced checkpoint with train strategy, will run distribute inference with model parallel.
fake_input_ids = Tensor(np.ones((config_eval.batch_size, config_eval.seq_length)), mstype.int64)
fake_position_ids = Tensor(np.random.randint(0, 10, [config_eval.batch_size, config_eval.seq_length]),
mstype.int64)
fake_attention_mask = Tensor(
np.random.randn(config_eval.batch_size, config_eval.seq_length, config_eval.seq_length), mstype.float16)
fake_loss_mask = Tensor(np.random.randn(config_eval.batch_size, config_eval.seq_length), mstype.float16)
predict_layout = model.infer_predict_layout(fake_input_ids,
fake_position_ids,
fake_attention_mask,
fake_loss_mask)
print("Loaded sliced checkpoint, will run distribute inference with model parallel.", flush=True)
load_distributed_checkpoint(infer_net, ckpt_file_list, predict_layout)
all_losses = []
truth_labels = []
steps_per_epoch = dataset.get_dataset_size()
print("++++++Dataset size", steps_per_epoch, flush=True)
for batch in dataset.create_dict_iterator(output_numpy=True, num_epochs=1):
print("++++ start")
ms_truth = batch['truth']
input_ids = Tensor(batch['input_ids'], mstype.int64)
position_ids = Tensor(batch['position_ids'], mstype.int64)
attention_mask = Tensor(batch['attention_mask'], mstype.float16)
loss_mask = Tensor(batch['loss_mask'], mstype.float16)
pred_id_tensor = model.predict(input_ids, position_ids, attention_mask, loss_mask)
# numpy do it.
pred_id_np = pred_id_tensor.asnumpy()
pred_id_np = pred_id_np[:, cand_ids]
pred_id = pred_id_np.argmax(axis=-1)
print("++++ pred_id_np: ", pred_id_np)
print("++++ ms_truth: ", ms_truth)
all_losses.append(pred_id)
truth_labels.append(ms_truth)
all_losses = np.stack(all_losses).reshape(-1)
truth_labels = np.stack(truth_labels).reshape(-1)
print("++++ all_losses= \n", all_losses)
print("++++ truthlabel= \n", truth_labels)
result = sum([int(p == l) for p, l in zip(all_losses, truth_labels)]) / len(truth_labels)
print("RESULT: ", result)
return result
def set_parallel_env():
r"""
Parallel environment.
"""
context.reset_auto_parallel_context()
MultiAscend.init()
context.set_auto_parallel_context(parallel_mode=ParallelMode.SEMI_AUTO_PARALLEL,
device_num=MultiAscend.get_group_size(),
gradients_mean=True,
full_batch=True)
set_algo_parameters(elementwise_op_strategy_follow=True)
def find_file(path, qianzhui):
r'''
Find the file address according to the prefix.
'''
result_addr = None
for i, _, k in os.walk(path):
for file in k:
if file.startswith(qianzhui):
result_addr = os.path.join(i, file)
break
return result_addr
def create_ckpt_file_list(args, max_index=None, train_strategy=None, steps_per_epoch=4509):
"""user-defined ckpt file list"""
ckpt_file_list = []
# train_strategy
if train_strategy is not None:
true_path = find_file(args.ckpt_path_doc, train_strategy)
if true_path is not None:
ckpt_file_list.append(true_path)
else:
raise Exception("+++ ckpt not found!!! +++")
return ckpt_file_list
# order in rank_id
for i in range(0, args.ckpt_partition):
path_name = "cpm_rank_" + str(i) + "-"
if max_index is not None:
path_name = path_name + str(max_index) + "_"
true_path = find_file(args.ckpt_path_doc, path_name)
if true_path is not None:
ckpt_file_list.append(true_path)
else:
path_name = "cpm_rank_" + str(i) + "-" + str(max_index * steps_per_epoch)
true_path = find_file(args.ckpt_path_doc, path_name)
if true_path is not None:
ckpt_file_list.append(true_path)
else:
raise Exception("+++ ckpt not found!!! +++")
return ckpt_file_list
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="CPM inference")
parser.add_argument('--dataset', type=str, default="", help="dataset path.")
parser.add_argument("--data_path", type=str, default="/disk0/dataset/finetune_dataset/test.json",
help='test_json path.')
parser.add_argument('--ckpt_path_doc', type=str, default="", help="Checkpoint path document.")
parser.add_argument('--ckpt_partition', type=int, default=8, help="Number of checkpoint partition.")
parser.add_argument("--distribute", type=ast.literal_eval, default=False,
help='Distribute evaluating with model parallel.')
parser.add_argument("--has_train_strategy", type=ast.literal_eval, default=True,
help='Model has distributed training strategy.')
args_eval = parser.parse_args()
ckpt_file_list_test = None
if args_eval.has_train_strategy:
# Get the checkpoint with train strategy.
train_strategy_list = create_ckpt_file_list(args_eval, train_strategy="train_strategy.ckpt")
context.set_auto_parallel_context(
strategy_ckpt_load_file=train_strategy_list[0]
)
ckpt_file_list_test = create_ckpt_file_list(args_eval)
print("++++ Get sliced checkpoint file, lists: ", ckpt_file_list_test, flush=True)
result_accuracy = 0.0
if args_eval.distribute:
set_parallel_env()
print("Start validation on 2 devices with model parallel.")
result_accuracy = run_eval(args_eval, finetune_test_distrubute, ckpt_file_list_test)
else:
print("Start validation on 1 device without model parallel.")
result_accuracy = run_eval(args_eval, finetune_test_standalone, ckpt_file_list_test)
print("++++ Accuracy=", result_accuracy)

View File

@ -0,0 +1,87 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""export checkpoint file into air models"""
import ast
import argparse
from easydict import EasyDict as ed
import numpy as np
from mindspore import context, load_distributed_checkpoint
from mindspore.common.tensor import Tensor
import mindspore.common.dtype as mstype
from mindspore.train.serialization import load_checkpoint, load_param_into_net, export
from eval import CPM_LAYER, create_ckpt_file_list
parser = argparse.ArgumentParser(description="CPM export")
parser.add_argument('--ckpt_path_doc', type=str, default="", help="checkpoint path document.")
parser.add_argument('--ckpt_partition', type=int, default=8, help="Number of checkpoint partition.")
parser.add_argument("--has_train_strategy", type=ast.literal_eval, default=True,
help='has distributed training strategy')
parser.add_argument("--file_name", type=str, default="cpm", help="output file name.")
parser.add_argument('--file_format', type=str, choices=["AIR", "MINDIR"], default='AIR', help='file format')
args = parser.parse_args()
context.set_context(mode=context.GRAPH_MODE,
save_graphs=False,
device_target="Ascend")
finetune_eval_single = ed({
"dp": 1,
"mp": 1,
"batch_size": 1,
"rank_size": 1,
"vocab_size": 30000,
'seq_length': 666,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
if __name__ == '__main__':
config_eval = finetune_eval_single
cpm_model = CPM_LAYER(config_eval)
if not args.has_train_strategy:
weights = load_checkpoint(args.ckpt_path)
can_be_loaded = {}
print("+++++++loading weights+++++")
for name, _ in weights.items():
print('oldname: ' + name)
if 'cpm_model.' not in name:
can_be_loaded['cpm_model.' + name] = weights[name]
print('newname: cpm_model.' + name)
else:
can_be_loaded[name] = weights[name]
print("+++++++loaded weights+++++")
load_param_into_net(cpm_model, parameter_dict=can_be_loaded)
else:
context.set_auto_parallel_context(
strategy_ckpt_load_file=args.ckpt_path_doc + "/train_strategy.ckpt"
)
ckpt_file_list = create_ckpt_file_list(args)
print("Get checkpoint file lists++++", ckpt_file_list, flush=True)
load_distributed_checkpoint(cpm_model, ckpt_file_list, None)
input_ids = Tensor(np.ones((config_eval.batch_size, config_eval.seq_length)), mstype.int64)
position_ids = Tensor(np.random.randint(0, 10, [config_eval.batch_size, config_eval.seq_length]),
mstype.int64)
attention_mask = Tensor(np.random.randn(config_eval.batch_size, config_eval.seq_length, config_eval.seq_length),
mstype.float16)
export(cpm_model, input_ids, position_ids, attention_mask, file_name=args.file_name,
file_format=args.file_format)

View File

@ -0,0 +1,122 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Checkpoint."""
import numpy as np
import torch
from mindspore.train.serialization import save_checkpoint
from mindspore import Tensor
param_names = {
"word_embeddings.weight": "word_embedding.embedding_table",
"position_embeddings.weight": "position_embedding.position_embedding_table",
"transformer.final_layernorm.weight": "transformer.final_layernorm.gamma",
"transformer.final_layernorm.bias": "transformer.final_layernorm.beta",
}
for i in range(0, 32):
param_names["transformer.layers." + str(i) + ".attention.query_key_value.weight"] = ""
param_names["transformer.layers." + str(i) + ".attention.query_key_value.bias"] = ""
param_names["transformer.layers." + str(i) + ".attention.dense.weight"] = "transformer.layers." + str(
i) + ".masked_multi_head_attention.masked_self_attention.dense.weight"
param_names["transformer.layers." + str(i) + ".attention.dense.bias"] = "transformer.layers." + str(
i) + ".masked_multi_head_attention.masked_self_attention.dense.bias"
param_names["transformer.layers." + str(i) + ".input_layernorm.weight"] = "transformer.layers." + str(
i) + ".masked_multi_head_attention.layernorm.gamma"
param_names["transformer.layers." + str(i) + ".input_layernorm.bias"] = "transformer.layers." + str(
i) + ".masked_multi_head_attention.layernorm.beta"
param_names["transformer.layers." + str(i) + ".mlp.dense_h_to_4h.weight"] = "transformer.layers." + str(
i) + ".mlp.dense_fc.weight"
param_names["transformer.layers." + str(i) + ".mlp.dense_h_to_4h.bias"] = "transformer.layers." + str(
i) + ".mlp.dense_fc.bias"
param_names["transformer.layers." + str(i) + ".mlp.dense_4h_to_h.weight"] = "transformer.layers." + str(
i) + ".mlp.dense_proj.weight"
param_names["transformer.layers." + str(i) + ".mlp.dense_4h_to_h.bias"] = "transformer.layers." + str(
i) + ".mlp.dense_proj.bias"
param_names["transformer.layers." + str(i) + ".post_attention_layernorm.weight"] = "transformer.layers." + str(
i) + ".mlp.layernorm.gamma"
param_names["transformer.layers." + str(i) + ".post_attention_layernorm.bias"] = "transformer.layers." + str(
i) + ".mlp.layernorm.beta"
def torch2ms(torch_ckpt):
"""Translate the model to mindspore checkpoint."""
torch_param_dict = torch.load(torch_ckpt, map_location=torch.device('cpu'))['module']
with open("weight_torch.txt", "w") as f:
for key, value in torch_param_dict.items():
print(f'torch key = {key}')
f.write(key + ' ' + 'dtype=' + str(value.dtype) + "\n")
print(f'value = {value}')
print("-----------------------------------------")
new_params_list = []
for torch_name in torch_param_dict:
ms_param_dict = {}
torch_value = torch_param_dict[torch_name]
ms_name = param_names[torch_name]
if "word_embeddings.weight" in torch_name:
ms_param_dict['name'] = ms_name
ms_param_dict['data'] = Tensor(torch_value.numpy().astype(np.float32))
new_params_list.append(ms_param_dict)
print(f'torch_name = {torch_name}, ms_name = {ms_name}, fp32')
elif "layernorm" in torch_name:
ms_param_dict['name'] = ms_name
ms_param_dict['data'] = Tensor(torch_value.numpy().astype(np.float32))
new_params_list.append(ms_param_dict)
print(f'torch_name = {torch_name}, ms_name = {ms_name}, fp32')
elif "query_key_value" not in torch_name:
ms_param_dict['name'] = ms_name
ms_param_dict['data'] = Tensor(torch_value.numpy().astype(np.float32))
new_params_list.append(ms_param_dict)
print(f'torch_name = {torch_name}, ms_name = {ms_name}, fp16')
else:
_, _, index, _, _, end = torch_name.split(".")
prefix = "transformer.layers."
q_mid = ".masked_multi_head_attention.masked_self_attention.dense1."
k_mid = ".masked_multi_head_attention.masked_self_attention.dense2."
v_mid = ".masked_multi_head_attention.masked_self_attention.dense3."
q_name = prefix + str(index) + q_mid + end
k_name = prefix + str(index) + k_mid + end
v_name = prefix + str(index) + v_mid + end
print(f'q_name = {q_name}')
print(f'k_name = {k_name}')
print(f'v_name = {v_name}')
query, key, value = torch_param_dict[torch_name].chunk(3, dim=0)
print(f"query shape = {query.shape}, key shape = {key.shape}, value shape = {value.shape}")
q_param_dict = {}
k_param_dict = {}
v_param_dict = {}
q_param_dict['name'] = q_name
k_param_dict['name'] = k_name
v_param_dict['name'] = v_name
q_param_dict['data'] = Tensor(query.numpy().astype(np.float32))
k_param_dict['data'] = Tensor(key.numpy().astype(np.float32))
v_param_dict['data'] = Tensor(value.numpy().astype(np.float32))
new_params_list.append(q_param_dict)
new_params_list.append(k_param_dict)
new_params_list.append(v_param_dict)
print(f'torch_name = {torch_name}, ms_name = {ms_name}, fp16')
save_checkpoint(new_params_list, '/home/cpm_mindspore_1p_fp32.ckpt')
if __name__ == '__main__':
original_ckpt = "/home/CPM-large_MP1/iter_0080000/mp_rank_00_model_states.pt"
torch2ms(original_ckpt)

View File

@ -0,0 +1,2 @@
jieba
numpy

View File

@ -0,0 +1,89 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 4 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_distribute_train_ascend_multi_machine.sh DATASET_PATH CKPT_PATH RANK_TABLE_PATH SERVER_ID"
echo "for example:"
echo "sh run_distribute_train_ascend_multi_machine.sh /disk0/dataset/finetune_dataset/train.mindrecord /disk0/cpm_ckpt_ms/cpm_mindspore_1p_fp32.ckpt /disk0/rank_table_32p.json 0"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DATASET=$(get_real_path $1)
echo $DATASET
PRECKPT=$(get_real_path $2)
RANK_TABLE_PATH=$(get_real_path $3)
SERVER_ID=$4
echo $DATANAME
current_exec_path=$(pwd)
echo ${current_exec_path}
export RANK_TABLE_FILE=$RANK_TABLE_PATH
echo $RANK_TABLE_FILE
export RANK_SIZE=8
export DEVICE_NUM=8
RANK_START=$(($DEVICE_NUM * $SERVER_ID))
GROUP_RANK=2
GROUP_DIFF=7
for((i=0;i<=3;i++));
do
rm -rf ${current_exec_path}/device$i
mkdir ${current_exec_path}/device$i
cd ${current_exec_path}/device$i
cp ../../*.py ./
cp -r ../../src ./
cp -r ../*.sh ./
export RANK_ID=$(((i*GROUP_RANK)+RANK_START))
export DEVICE_ID=$i
echo "start training for device $DEVICE_ID, rank $RANK_ID"
python ../../train.py --dataset $DATASET --pretrain_ckpt_path $PRECKPT --multi_machine True > log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}
for((i=4;i<=7;i++));
do
rm -rf ${current_exec_path}/device$i
mkdir ${current_exec_path}/device$i
cd ${current_exec_path}/device$i
cp ../../*.py ./
cp -r ../../src ./
cp -r ../*.sh ./
export RANK_ID=$(((i*GROUP_RANK)-GROUP_DIFF+RANK_START))
export DEVICE_ID=$i
echo "start training for device $DEVICE_ID, rank $RANK_ID"
python ../../train.py --dataset $DATASET --pretrain_ckpt_path $PRECKPT --multi_machine True > log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}

View File

@ -0,0 +1,85 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 3 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_distribute_train_ascend_single_machine.sh DATASET_PATH CKPT_PATH RANK_TABLE_PATH"
echo "for example:"
echo "sh run_distribute_train_ascend_single_machine.sh /disk0/dataset/finetune_dataset/train.mindrecord /disk0/cpm_ckpt_ms/cpm_mindspore_1p_fp32.ckpt /disk0/rank_table_8p.json"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DATASET=$(get_real_path $1)
echo $DATASET
PRECKPT=$(get_real_path $2)
RANK_TABLE_PATH=$(get_real_path $3)
echo $DATANAME
current_exec_path=$(pwd)
echo ${current_exec_path}
export RANK_TABLE_FILE=$RANK_TABLE_PATH
echo $RANK_TABLE_FILE
export RANK_SIZE=8
export DEVICE_NUM=8
GROUP_RANK=2
GROUP_DIFF=7
for((i=0;i<=3;i++));
do
rm -rf ${current_exec_path}/device$i
mkdir ${current_exec_path}/device$i
cd ${current_exec_path}/device$i
cp ../../*.py ./
cp -r ../../src ./
cp -r ../*.sh ./
export RANK_ID=$((i*GROUP_RANK))
export DEVICE_ID=$i
echo "start training for device $DEVICE_ID, rank $RANK_ID"
python ../../train.py --dataset $DATASET --pretrain_ckpt_path $PRECKPT --multi_machine False > log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}
for((i=4;i<=7;i++));
do
rm -rf ${current_exec_path}/device$i
mkdir ${current_exec_path}/device$i
cd ${current_exec_path}/device$i
cp ../../*.py ./
cp -r ../../src ./
cp -r ../*.sh ./
export RANK_ID=$(((i*GROUP_RANK)-GROUP_DIFF))
export DEVICE_ID=$i
echo "start training for device $DEVICE_ID, rank $RANK_ID"
python ../../train.py --dataset $DATASET --pretrain_ckpt_path $PRECKPT --multi_machine False > log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}

View File

@ -0,0 +1,65 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 5 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_eval_distribute_ascend.sh DATASET_PATH LABEL_PATH MODEL_CKPT CKPT_NUMBER RANK_TABLE_PATH"
echo "for example:"
echo "sh run_eval_distribute_ascend.sh /disk0/dataset/finetune_dataset/finetune_test.mindrecord /disk0/dataset/finetune_dataset/test.json /disk2/ckpt_32p_0602 32 /disk0/rank_table_2p.json"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DATASET=$(get_real_path $1)
echo $DATASET
LABEL=$(get_real_path $2)
MODEL_CKPT=$(get_real_path $3)
CKPT_NUMBER=$4
RANK_TABLE_PATH=$(get_real_path $5)
current_exec_path=$(pwd)
echo ${current_exec_path}
export RANK_SIZE=2
export DEVICE_NUM=2
export RANK_TABLE_FILE=$RANK_TABLE_PATH
for((i=0;i<=1;i++));
do
rm -rf ${current_exec_path}/eval$i
mkdir ${current_exec_path}/eval$i
cd ${current_exec_path}/eval$i
cp -r ../../*.py ./
cp -r ../../src ./
cp -r ../../scripts/*.sh ./
export RANK_ID=$i
export DEVICE_ID=$i
echo "start eval for rank $RANK_ID, device $DEVICE_ID"
env > env.log
python ../../eval.py --dataset $DATASET --data_path $LABEL --ckpt_path $MODEL_CKPT --ckpt_partition $CKPT_NUMBER --distribute True --has_train_strategy True> log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}

View File

@ -0,0 +1,77 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 7 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_test_distribute_ascend.sh DEV_DATASET_PATH DEV_JSON_PATH TEST_DATASET_PATH TEST_JSON_PATH MODEL_CKPT CKPT_NUMBER RANK_TABLE_PATH"
echo "for example:"
echo "sh run_test_distribute_ascend.sh /disk0/dataset/finetune_dataset/finetune_dev.mindrecord /disk0/dataset/finetune_dataset/dev.json /disk0/dataset/finetune_dataset/finetune_test.mindrecord /disk0/dataset/finetune_dataset/test.json /disk2/ckpt_8p 8 /disk0/rank_table_2p.json"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DEV_DATASET=$(get_real_path $1)
echo $DEV_DATASET
DEV_LABEL=$(get_real_path $2)
echo $DEV_LABEL
TEST_DATASET=$(get_real_path $3)
echo $TEST_DATASET
TEST_LABEL=$(get_real_path $4)
echo $TEST_LABEL
MODEL_CKPT=$(get_real_path $5)
echo $MODEL_CKPT
CKPT_NUMBER=$6
echo $CKPT_NUMBER
RANK_TABLE_PATH=$(get_real_path $7)
echo $RANK_TABLE_PATH
current_exec_path=$(pwd)
echo ${current_exec_path}
export RANK_SIZE=2
export DEVICE_NUM=2
export RANK_TABLE_FILE=$RANK_TABLE_PATH
for((i=0;i<=1;i++));
do
rm -rf ${current_exec_path}/eval$i
mkdir ${current_exec_path}/eval$i
cd ${current_exec_path}/eval$i
cp -r ../../*.py ./
cp -r ../../src ./
cp -r ../../scripts/*.sh ./
export RANK_ID=$i
export DEVICE_ID=$i
echo "start eval for rank $RANK_ID, device $DEVICE_ID"
env > env.log
python ../../test.py --dev_dataset $DEV_DATASET --dev_data_path $DEV_LABEL \
--test_dataset $TEST_DATASET --test_data_path $TEST_LABEL \
--ckpt_path $MODEL_CKPT --ckpt_partition $CKPT_NUMBER \
--distribute True --has_train_strategy True> log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}

View File

@ -0,0 +1,64 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 4 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_zero-shot_inference_distribute_ascend.sh DATASET_PATH LABEL_PATH MODEL_CKPT RANK_TABLE_PATH"
echo "for example:"
echo "sh run_zero-shot_inference_distribute_ascend.sh /disk0/dataset/zero_shot_dataset_infer/test.mindrecord /disk0/dataset/zero_shot_dataset_infer/true_labels.txt /disk0/cpm_ckpt_ms/cpm_mindspore_1p_fp32.ckpt /disk0/rank_table_2p.json"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DATASET=$(get_real_path $1)
echo $DATASET
LABEL=$(get_real_path $2)
MODEL_CKPT=$(get_real_path $3)
RANK_TABLE_PATH=$(get_real_path $4)
current_exec_path=$(pwd)
echo ${current_exec_path}
export RANK_SIZE=2
export DEVICE_NUM=2
export RANK_TABLE_FILE=$RANK_TABLE_PATH
for((i=0;i<=1;i++));
do
rm -rf ${current_exec_path}/eval$i
mkdir ${current_exec_path}/eval$i
cd ${current_exec_path}/eval$i
cp -r ../../*.py ./
cp -r ../../src ./
cp -r ../../scripts/*.sh ./
export RANK_ID=$i
export DEVICE_ID=$i
echo "start eval for rank $RANK_ID, device $DEVICE_ID"
env > env.log
python ../../zero-shot.py --dataset $DATASET --truth_labels_path $LABEL --ckpt_path $MODEL_CKPT --distribute True --has_train_strategy False> log_cpm.log 2>&1 &
cd ${current_exec_path}
done
cd ${current_exec_path}

View File

@ -0,0 +1,59 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
if [ $# != 4 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
echo "sh run_zero-shot_inference_standalone_ascend.sh DATASET_PATH LABEL_PATH MODEL_CKPT DEVICE_ID"
echo "for example: "
echo "sh run_zero-shot_inference_standalone_ascend.sh /disk0/dataset/zero_shot_dataset_infer/test.mindrecord /disk0/dataset/zero_shot_dataset_infer/true_labels.txt /disk0/cpm_ckpt_ms/cpm_mindspore_1p_fp32.ckpt 5"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
fi
get_real_path(){
if [ "${1:0:1}" == "/" ]; then
echo "$1"
else
echo "$(realpath -m $PWD/$1)"
fi
}
DATASET=$(get_real_path $1)
echo $DATASET
LABEL=$(get_real_path $2)
MODEL_CKPT=$(get_real_path $3)
DEVICEID=$4
export DEVICE_NUM=1
export DEVICE_ID=$DEVICEID
export RANK_ID=0
export RANK_SIZE=1
if [ -d "eval" ];
then
rm -rf ./eval
fi
mkdir ./eval
cp ../*.py ./eval
cp -r ../src ./eval
cp -r ../scripts/*.sh ./eval
cd ./eval || exit
echo "start training for device $DEVICE_ID"
env > env.log
python ../../zero-shot.py --dataset $DATASET --truth_labels_path $LABEL --ckpt_path $MODEL_CKPT --has_train_strategy False > log_cpm.log 2>&1 &
cd ..

View File

@ -0,0 +1,283 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Attention module"""
import math
import numpy as np
import mindspore.common.dtype as mstype
import mindspore.nn as nn
import mindspore.ops.functional as F
from mindspore.ops import operations as P
from mindspore.common.tensor import Tensor
from src.util import LayerNorm
from src.util import LinearLayer, ResidualConnection, Dropout
class MaskedSelfAttention(nn.Cell):
"""
Self-Attention module for each layer.
Args:
batch_size (int): Batch size of input dataset.
hidden_size (int): Length of last dim of hidden layer.
seq_length (int): Length of input tensor sequence.
num_attention_heads (int): Number of attention heads.
dim_per_head (int): Size of each attention head.
config: The config of networks.
has_attention_mask (bool): Specifies whether to use attention mask.
do_return_2d_tensor (bool): Whether use 2-dimension.
attention_dropout (float): The dropout probability for attention.
is_training (bool): Whether is training.
compute_type (:class:`mindspore.dtype`): Compute type in attention.
Returns:
Tensor, with the shape [batch_size, hidden_size]
"""
def __init__(self,
batch_size,
hidden_size,
seq_length,
num_attention_heads,
dim_per_head,
config=None,
has_attention_mask=True,
do_return_2d_tensor=True,
attention_dropout=0.0,
is_training=False,
compute_type=mstype.float16):
super(MaskedSelfAttention, self).__init__()
self.hidden_size = hidden_size
self.batch_size = batch_size
self.seq_length = seq_length
self.num_heads = num_attention_heads
self.dim_per_head = dim_per_head
self.has_attention_mask = has_attention_mask
self.compute_type = compute_type
self.is_training = is_training
self.scale = Tensor(math.sqrt(float(self.dim_per_head)), dtype=compute_type)
self.mask_data = Tensor([-10000.0], dtype=mstype.float32)
self.split_head_shape = (self.batch_size, self.seq_length, self.num_heads, self.dim_per_head)
self.dense = LinearLayer(hidden_size, hidden_size)
self.dense.matmul.shard(((config.dp, config.mp), (1, config.mp)))
self.dense.bias_add.shard(((config.dp, 1), (1,)))
self.dense.bias.parallel_optimizer = False
self.reshape = P.Reshape()
self.transpose = P.Transpose().shard(((config.dp, 1, config.mp, 1),))
self.merge_transpose = P.Transpose().shard(((config.dp, config.mp, 1, 1),))
self.trans_shape = (0, 2, 1, 3)
self.trans_shape2 = (0, 2, 3, 1)
self.matmul_trans_b = P.BatchMatMul().shard(((config.dp, config.mp, 1, 1), (config.dp, config.mp, 1, 1)))
self.matmul = P.BatchMatMul().shard(((config.dp, config.mp, 1, 1), (config.dp, config.mp, 1, 1)))
self.multiply = P.Mul().shard(((config.dp, 1, 1, 1), (1,))).add_prim_attr("_side_effect", True)
self.realdiv = P.RealDiv().shard(((config.dp, config.mp, 1, 1), ()))
if self.has_attention_mask:
self.expand_dims = P.ExpandDims().shard(((config.dp, 1, 1),))
self.sub = P.Sub().shard(((1,), (config.dp, 1, 1, 1))).add_prim_attr("_side_effect", True)
self.add = P.TensorAdd().shard(((config.dp, 1, 1, 1), (config.dp, config.mp, 1, 1)))
self.cast = P.Cast()
self.get_dtype = P.DType()
if do_return_2d_tensor:
self.shape_return = (-1, hidden_size)
else:
self.shape_return = (-1, seq_length, hidden_size)
self.softmax = nn.Softmax()
self.softmax.softmax.shard(((config.dp, config.mp, 1, 1),))
self.softmax_cast = P.Cast()
self.shape = P.Shape()
self.dropout = Dropout(1 - attention_dropout)
self.dropout.dropout_gen_mask.shard(((config.dp, 1),))
self.dropout.dropout_do_mask.shard(((config.dp, 1),))
self.dropout_probs = Dropout(1 - attention_dropout)
self.dropout_probs.dropout_gen_mask.shard(((config.dp, config.mp, 1, 1),))
self.dropout_probs.dropout_do_mask.shard(((config.dp, config.mp, 1, 1),))
self.use_attention_dropout = is_training
self.dense1 = LinearLayer(self.hidden_size, self.hidden_size)
self.dense1.matmul.shard(((config.dp, 1), (config.mp, 1)))
self.dense1.bias_add.shard(((config.dp, config.mp), (config.mp,)))
self.dense2 = LinearLayer(self.hidden_size, self.hidden_size)
self.dense2.matmul.shard(((config.dp, 1), (config.mp, 1)))
self.dense2.bias_add.shard(((config.dp, config.mp), (config.mp,)))
self.dense3 = LinearLayer(self.hidden_size, self.hidden_size)
self.dense3.matmul.shard(((config.dp, 1), (config.mp, 1)))
self.dense3.bias_add.shard(((config.dp, config.mp), (config.mp,)))
attention_mask = np.tril(np.ones(shape=(config.seq_length, config.seq_length),))
attention_mask = np.expand_dims(attention_mask, 0)
attention_mask = np.tile(attention_mask, (config.batch_size, 1, 1))
attention_mask = np.expand_dims(attention_mask, 1)
self.attention_mask = Tensor(attention_mask, dtype=compute_type)
def construct(self, input_tensor, attention_mask=None):
"""do masked self-attention"""
# input_tensor [batch_size, seq_length, hidden_size], eg:[1,571,2560].
query = self.dense1(input_tensor)
key = self.dense2(input_tensor)
value = self.dense3(input_tensor)
# split head
query = self.reshape(query, self.split_head_shape)
# query shape [2, 571, 32, 80] -> [2, 32, 571, 80]
query = self.transpose(query, self.trans_shape)
key = self.reshape(key, self.split_head_shape)
# key shape [batch_size, num_heads, dim_per_head, seq_len]
key = self.transpose(key, self.trans_shape2)
value = self.reshape(value, self.split_head_shape)
# value shape [batch_size, num_heads, seq_len, dim_per_head]
value = self.transpose(value, self.trans_shape)
# precision transition fp32 -> fp16
query = self.cast(query, self.compute_type)
key = self.cast(key, self.compute_type)
# 8, 32, 725, 80|8, 32, 80, 725 -> 8, 32, 725, 725
attention_scores = self.matmul_trans_b(query, key)
attention_scores = self.cast(attention_scores, mstype.float32)
attention_scores = self.realdiv(attention_scores, self.cast(self.scale, self.get_dtype(attention_scores)))
attention_scores = P.Cast()(attention_scores, mstype.float32)
if self.has_attention_mask:
if attention_mask is None:
attention_mask = self.attention_mask
else:
attention_mask = self.expand_dims(attention_mask, 1)
multiply_out = self.sub(self.cast(F.tuple_to_array((1.0,)), self.get_dtype(attention_scores)),
self.cast(attention_mask,
self.get_dtype(attention_scores)))
# 1, 1, 725, 725
adder = self.multiply(multiply_out, self.mask_data)
adder = self.cast(adder, mstype.float32)
attention_scores = self.cast(attention_scores, mstype.float32)
# 1, 1, 725, 725|8, 32, 725, 725-》8, 32, 725, 725
attention_scores = self.add(adder, attention_scores)
attention_scores = self.softmax_cast(attention_scores, mstype.float32)
# [8, 32, 725, 725] ->8, 32, 725, 725
attention_probs = self.softmax(attention_scores)
attention_probs = self.softmax_cast(attention_probs, self.get_dtype(key))
if self.use_attention_dropout:
attention_probs = self.dropout_probs(attention_probs)
value = self.cast(value, self.compute_type)
attention_probs = self.cast(attention_probs, self.compute_type)
if self.is_training:
# 1, 8, 2, 725, 725 -> 8, 2, 725, 725
attention_probs = self.reshape(attention_probs, (
self.batch_size, self.num_heads, self.seq_length,
self.seq_length))
# 8, 32, 725, 725 | 8, 32, 725, 80 -> 8, 2, 725, 1280
outputs = self.matmul(attention_probs, value)
outputs = self.cast(outputs, mstype.float32)
# merge heads, [8, 2, 725, 1280]->8, 725, 2, 1280
outputs = self.merge_transpose(outputs, self.trans_shape)
# 8, 725, 2, 1280->5800, 2560
outputs = self.reshape(outputs,
self.shape_return)
# project
outputs = self.dense(outputs)
if self.is_training:
outputs = self.dropout(outputs)
return outputs
class MaskedMultiHeadAttention(nn.Cell):
"""
Constructor for the MaskedMultiHeadAttention.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
hidden_size (int): Length of last dim of hidden layer.
config: The config of networks.
num_attention_heads (int): Number of attention heads.
attention_dropout (float): The dropout probability for attention.
hidden_dropout (float): The dropout probability for hidden layer.
has_attention_mask (bool): Specifies whether to use attention mask.
is_training (bool): Whether to train.
compute_type (:class:`mindspore.dtype`): Compute type in attention.
Returns:
Tensor, shape (N, T).
"""
def __init__(self,
batch_size,
seq_length,
hidden_size,
config=None,
num_attention_heads=12,
attention_dropout=0.02,
hidden_dropout=0.1,
has_attention_mask=True,
is_training=False,
compute_type=mstype.float16
):
super(MaskedMultiHeadAttention, self).__init__()
if hidden_size % num_attention_heads != 0:
raise ValueError("The hidden size (%d) is not a multiple of the number "
"of attention heads (%d)" % (hidden_size, num_attention_heads))
self.dim_per_head = int(hidden_size / num_attention_heads)
self.masked_self_attention = MaskedSelfAttention(
batch_size=batch_size,
hidden_size=hidden_size,
seq_length=seq_length,
config=config,
num_attention_heads=num_attention_heads,
dim_per_head=self.dim_per_head,
has_attention_mask=has_attention_mask,
do_return_2d_tensor=True,
attention_dropout=attention_dropout,
is_training=is_training,
compute_type=compute_type
)
self.layernorm = LayerNorm((hidden_size,), config, epsilon=1e-5).to_float(mstype.float32)
self.layernorm.gamma.parallel_optimizer = False
self.layernorm.beta.parallel_optimizer = False
self.residual_connection = ResidualConnection(dropout_prob=0.1)
self.residual_connection.add.shard(((config.dp, 1), (config.dp, 1)))
self.reshape = P.Reshape()
self.shape = P.Shape()
self.new_shape = (-1, hidden_size)
def construct(self, input_tensor, attention_mask=None):
# input tensor shape[batch_size*sen_length, hidden_size]
output_tensor = self.layernorm(input_tensor)
# attention_output shape [batch_size * sen_length, hidden_size]
attention_output = self.masked_self_attention(output_tensor, attention_mask)
# residual connection, [5800, 2560] | [5800, 2560] -> [5800, 2560]
output = self.residual_connection(attention_output, input_tensor)
return output

View File

@ -0,0 +1,132 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Configure"""
from easydict import EasyDict as ed
config_zero_shot_standalone = ed({
"dp": 1,
"mp": 1,
"batch_size": 1,
"rank_size": 1,
"vocab_size": 30000,
'seq_length': 571,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
config_zero_shot_distrubute = ed({
"dp": 1,
"mp": 2,
"batch_size": 2,
"rank_size": 2,
"vocab_size": 30000,
'seq_length': 571,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
finetune_dev_standalone = ed({
"dp": 1,
"mp": 1,
"batch_size": 1,
"rank_size": 1,
"vocab_size": 30000,
'seq_length': 696,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
finetune_dev_distrubute = ed({
"dp": 1,
"mp": 2,
"batch_size": 1,
"rank_size": 2,
"vocab_size": 30000,
'seq_length': 696,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
finetune_test_standalone = ed({
"dp": 1,
"mp": 1,
"batch_size": 1,
"rank_size": 1,
"vocab_size": 30000,
'seq_length': 666,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
finetune_test_distrubute = ed({
"dp": 1,
"mp": 2,
"batch_size": 1,
"rank_size": 2,
"vocab_size": 30000,
'seq_length': 666,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32
})
config_train_8p = ed({
"dp": 4,
"mp": 2,
"epoch": 10,
"batch_size": 16,
"rank_size": 8,
"vocab_size": 30000,
'seq_length': 725,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32,
"lr": 1e-5,
"eps": 1e-8,
"dropout": 0.2,
"end_learning_rate": 1e-7,
"weight_decay": 1e-2,
"warmup_steps": 0.05,
"power": 1.0,
"grad_accumulation_step": 4,
"sink_size": 1
})
config_train_32p = ed({
"dp": 16,
"mp": 2,
"epoch": 10,
"batch_size": 128,
"rank_size": 32,
"vocab_size": 30000,
'seq_length': 725,
"hidden_size": 2560,
"num_hidden_layers": 32,
"num_attention_heads": 32,
"lr": 2e-5,
"eps": 1e-8,
"dropout": 0.1,
"end_learning_rate": 1e-7,
"weight_decay": 1e-2,
"warmup_steps": 0.1,
"power": 1.0,
"grad_accumulation_step": 1,
"sink_size": 1
})

View File

@ -0,0 +1,369 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""CPM model."""
import mindspore.common.dtype as mstype
import mindspore.nn as nn
from mindspore.ops import operations as P
from src.embedding import EmbeddingPostprocessor, EmbeddingLookup
from src.attention import MaskedMultiHeadAttention
from src.util import LinearLayer, ResidualConnection, LayerNorm, Dropout
class MLPLayer(nn.Cell):
"""
The output mapping module for each layer.
Args:
hidden_size (int): Length of last dim of hidden layer.
config: The config of networks.
dropout_prob (float): The dropout probability for network.
is_training (bool): Whether is training.
Inputs:
x: output of the self-attention module.
Returns:
output: Tensor, the output of this layer after mapping.
"""
def __init__(self, hidden_size, config=None, dropout_prob=0.1, is_training=False):
super(MLPLayer, self).__init__()
self.hidden_size = hidden_size
self.dense_fc = LinearLayer(hidden_size, 4 * hidden_size)
self.dense_fc.bias_add.shard(((config.dp, config.mp), (config.mp,)))
self.dense_fc.matmul.shard(((config.dp, 1), (config.mp, 1)))
self.dense_proj = LinearLayer(4 * hidden_size, hidden_size)
self.dense_proj.bias_add.shard(((config.dp, 1), (1,)))
self.dense_proj.matmul.shard(((config.dp, config.mp), (1, config.mp)))
self.dense_proj.matmul.add_prim_attr("recompute_comm_op", False)
self.layernorm = LayerNorm((hidden_size,), config, epsilon=1e-5).to_float(mstype.float32)
# parallel optimizer
self.dense_proj.bias.parallel_optimizer = False
self.layernorm.gamma.parallel_optimizer = False
self.layernorm.beta.parallel_optimizer = False
self.residual_connect = ResidualConnection()
self.residual_connect.add.shard(((config.dp, 1), (config.dp, 1))).add_prim_attr("recompute", False)
self.gelu = P.Gelu().shard(((config.dp, config.mp),))
self.dropout = Dropout(1 - dropout_prob)
self.dropout.dropout_gen_mask.shard(((config.dp, 1),))
self.dropout.dropout_do_mask.shard(((config.dp, 1),))
self.use_dropout = is_training
self.reshape = P.Reshape()
def construct(self, input_tensor):
"""FeedForward construct function."""
# LayerNorm, eg: [5800, 2560].
output = self.layernorm(input_tensor)
# Feed Forward
output = self.dense_fc(output)
# eg: 5800, 10240
output = self.gelu(output)
output = self.dense_proj(output)
if self.use_dropout:
output = self.dropout(output)
output = self.residual_connect(output, input_tensor)
return output
class CPMTransformerLayer(nn.Cell):
"""
The basic block of GPT network.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
hidden_size (int): Length of last dim of hidden layer.
config: The config of networks.
num_attention_heads (int): Number of attention heads.
attention_dropout (float): The dropout probability for attention.
hidden_dropout (float): The dropout probability for hidden layers.
has_attention_mask (bool): Specifies whether to use attention mask.
is_training (bool): Whether is training.
compute_type (:class:`mindspore.dtype`): Compute type in attention.
Inputs:
input_tensor: the output of previous layer(input_ids for the first layer).
attention_mask: the attention mask matrix with shape (batch_size, seq_length, seq_length).
Returns:
output: Tensor, the output logit of this layer.
"""
def __init__(self,
batch_size,
seq_length,
hidden_size,
config=None,
num_attention_heads=32,
attention_dropout=0.1,
hidden_dropout=0.1,
has_attention_mask=True,
is_training=False,
compute_type=mstype.float16
):
super(CPMTransformerLayer, self).__init__()
if hidden_size % num_attention_heads != 0:
raise ValueError("The hidden size (%d) is not a multiple of the number "
"of attention heads (%d)" % (hidden_size, num_attention_heads))
self.dim_per_head = int(hidden_size / num_attention_heads)
self.masked_multi_head_attention = MaskedMultiHeadAttention(
batch_size=batch_size,
seq_length=seq_length,
hidden_size=hidden_size,
config=config,
num_attention_heads=num_attention_heads,
attention_dropout=attention_dropout,
hidden_dropout=hidden_dropout,
has_attention_mask=has_attention_mask,
is_training=is_training,
compute_type=compute_type
)
self.mlp = MLPLayer(hidden_size=hidden_size,
config=config,
dropout_prob=hidden_dropout,
is_training=is_training)
self.reshape = P.Reshape()
self.new_shape = (-1, hidden_size)
def construct(self, input_tensor, attention_mask=None):
# input tensor shape[batch_size, seq_length, hidden_size]
input_tensor = self.reshape(input_tensor, self.new_shape)
# masked multi head attention with ln, res
attention_output = self.masked_multi_head_attention(input_tensor, attention_mask)
# feed forward, [batch_size * seq_length, hidden_size]
output = self.mlp(attention_output)
return output
class CPMTransformer(nn.Cell):
"""
Implements of gpt module.
Args:
batch_size (int): Batch size of input dataset.
hidden_size (int): Length of last dim of hidden layer.
seq_length (int): Length of input tensor sequence.
config: The config of networks.
num_hidden_layers (int): Numbers of hidden layers.
num_attention_heads (int): Number of attention heads.
has_attention_mask (bool): Specifies whether to use attention mask.
attention_dropout (float): The dropout probability for attention.
hidden_dropout (float): The dropout probability for hidden layers.
is_training (bool): Whether is training.
compute_type (:class:`mindspore.dtype`): Compute type in attention.
Returns:
Tensor, shape of (N, T').
"""
def __init__(self,
batch_size,
hidden_size,
seq_length,
config=None,
num_hidden_layers=12,
num_attention_heads=12,
has_attention_mask=True,
attention_dropout=0.1,
hidden_dropout=0.1,
is_training=False,
compute_type=mstype.float16):
super(CPMTransformer, self).__init__()
fusion_group_num = 4
fusion_group_size = num_hidden_layers // fusion_group_num
fusion_group_size = max(fusion_group_size, 1)
layers = []
for i in range(num_hidden_layers):
layer = CPMTransformerLayer(batch_size=batch_size,
seq_length=seq_length,
hidden_size=hidden_size,
config=config,
num_attention_heads=num_attention_heads,
attention_dropout=attention_dropout,
hidden_dropout=hidden_dropout,
has_attention_mask=has_attention_mask,
is_training=is_training,
compute_type=compute_type).set_comm_fusion(int(i / fusion_group_size) + 2)
layer.recompute()
layer.masked_multi_head_attention.masked_self_attention.dropout.dropout_gen_mask.recompute(False)
layer.masked_multi_head_attention.masked_self_attention.dropout_probs.dropout_gen_mask.recompute(False)
layer.mlp.dropout.dropout_gen_mask.recompute(False)
layer.masked_multi_head_attention.masked_self_attention.dropout.dropout_do_mask.recompute(False)
layer.masked_multi_head_attention.masked_self_attention.dropout_probs.dropout_do_mask.recompute(False)
layer.mlp.dropout.dropout_do_mask.recompute(False)
layer.masked_multi_head_attention.masked_self_attention.dropout.dropout_gen_mask.add_prim_attr(
"_side_effect", True)
layer.masked_multi_head_attention.masked_self_attention.dropout_probs.dropout_gen_mask.add_prim_attr(
"_side_effect", True)
layer.mlp.dropout.dropout_gen_mask.add_prim_attr("_side_effect", True)
layers.append(layer)
self.layers = nn.CellList(layers)
self.reshape = P.Reshape()
self.final_layernorm = LayerNorm((hidden_size,), config, epsilon=1e-5).to_float(mstype.float32).set_comm_fusion(
int((num_hidden_layers - 1) / fusion_group_size) + 2)
self.final_layernorm.gamma.parallel_optimizer = False
self.final_layernorm.beta.parallel_optimizer = False
self.new_shape = (-1, hidden_size)
def construct(self, input_tensor, attention_mask=None):
"""gpt module."""
prev_output = self.reshape(input_tensor, self.new_shape)
for layer_module in self.layers:
layer_output = layer_module(prev_output, attention_mask)
prev_output = layer_output
prev_output = self.final_layernorm(prev_output)
return prev_output
class CPMModel(nn.Cell):
"""
Implements of CPM model.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
hidden_size (int): Length of last dim of hidden layer.
num_hidden_layers (int): Numbers of hidden layers.
num_attention_heads (int): Number of attention heads.
config: The config of networks.
use_one_hot_embedding (bool): Whether use one-hot embedding. Default: False.
hidden_dropout (float): The dropout probability for hidden layers.
attention_dropout (float): The dropout probability for attention.
max_position_embeddings (int): The max length of position embedding.
initializer_range (int): The initialize range of parameters.
input_mask_from_dataset (bool): Specifies whether to use input mask.
is_training (bool): Whether is training.
compute_type (:class:`mindspore.dtype`): Compute type in attention.
Returns:
Tensor, shape of (N, T').
"""
def __init__(self,
batch_size,
seq_length,
vocab_size,
hidden_size,
num_hidden_layers,
num_attention_heads,
config=None,
use_one_hot_embedding=False,
hidden_dropout=0.1,
attention_dropout=0.1,
max_position_embeddings=1024,
initializer_range=0.02,
input_mask_from_dataset=True,
is_training=False,
compute_type=mstype.float16):
super(CPMModel, self).__init__()
self.is_training = is_training
self.batch_size = batch_size
self.seq_length = seq_length
self.vocab_size = vocab_size
self.hidden_size = hidden_size
self.embedding_dim = hidden_size
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.hidden_dropout = hidden_dropout
self.attention_dropout = attention_dropout
self.max_position_embeddings = max_position_embeddings
self.initializer_range = initializer_range
self.input_mask_from_dataset = input_mask_from_dataset
self.compute_type = compute_type
self.last_idx = self.num_hidden_layers - 1
self.word_embedding = EmbeddingLookup(
batch_size=self.batch_size,
seq_length=self.seq_length,
vocab_size=self.vocab_size,
embedding_dim=self.hidden_size,
config=config,
use_one_hot_embeddings=use_one_hot_embedding,
compute_type=self.compute_type
).set_comm_fusion(1)
self.position_embedding = EmbeddingPostprocessor(
max_seq_length=self.max_position_embeddings,
embedding_dim=self.embedding_dim,
config=config,
use_one_hot_embeddings=use_one_hot_embedding,
compute_type=self.compute_type
).set_comm_fusion(1)
self.transformer = CPMTransformer(batch_size=self.batch_size,
hidden_size=self.hidden_size,
seq_length=self.seq_length,
config=config,
num_hidden_layers=self.num_hidden_layers,
num_attention_heads=self.num_attention_heads,
has_attention_mask=True,
attention_dropout=self.attention_dropout,
hidden_dropout=self.hidden_dropout,
is_training=self.is_training,
compute_type=self.compute_type)
self.dropout = Dropout(1 - self.hidden_dropout)
self.dropout.dropout_gen_mask.shard(((config.dp, 1, 1),))
self.dropout.dropout_do_mask.shard(((config.dp, 1, 1),))
self.matmul = P.MatMul(transpose_b=True).shard(((config.dp, 1), (1, 1)))
self.reshape = P.Reshape()
self.add = P.TensorAdd().shard(((config.dp, 1, config.mp), (config.dp, 1, config.mp)))
self.cast = P.Cast()
self.out_shape = (-1, self.seq_length, self.vocab_size)
def construct(self, input_ids, position_ids=None, attention_mask=None):
"""
Construct network.
Args:
input_ids (Tensor): Input sentences with shape (N, T).
position_ids (Tensor): Target of input sentences with shape (N, T).
attention_mask (Tensor): Source sentences padding mask with shape (N, T, T).
Returns:
Tensor, network outputs.
"""
words_embeddings, embedding_tab = self.word_embedding(input_ids)
position_embedding = self.position_embedding(position_ids)
embedding = self.add(words_embeddings, position_embedding)
if self.is_training:
embedding = self.dropout(embedding)
transformer_output = self.transformer(embedding, attention_mask)
transformer_output = self.cast(transformer_output, self.compute_type)
logits_output = self.matmul(transformer_output,
self.cast(embedding_tab, self.compute_type))
logits_output = self.reshape(logits_output, self.out_shape)
logits_output = self.cast(logits_output, mstype.float32)
return logits_output

View File

@ -0,0 +1,168 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Loss."""
import numpy as np
import mindspore.common.dtype as mstype
import mindspore.nn as nn
from mindspore.ops import operations as P
from mindspore.common.tensor import Tensor
class Cross_entropy(nn.Cell):
"""
Calculate loss of Training mode and zero-shot mode.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
config: The config of networks.
is_training (bool): Whether is training.
Returns:
Tensor, shape of (batch_size,).
"""
def __init__(self, batch_size, seq_length, vocab_size, config, is_training=False):
super(Cross_entropy, self).__init__()
self.batch_size = batch_size
self.vocab_size = vocab_size
self.argmax = P.ArgMaxWithValue(axis=-1).shard(((config.dp, 1, 1),))
self.expanddim = P.ExpandDims().shard(((config.dp, 1),))
self.sub = P.Sub().shard(((config.dp, 1), (config.dp, 1)))
self.sub_logist = P.Sub().shard(((config.dp, 1, 1), (config.dp, 1, 1)))
self.exp = P.Exp().shard(((config.dp, 1, 1),))
self.till = P.Tile().shard(((1, 1),))
self.till_expand = P.Tile().shard(((config.dp, 1, 1),))
self.reduce_sum = P.ReduceSum().shard(((config.dp, 1, 1),))
self.reshape = P.Reshape()
self.is_training = is_training
if self.is_training:
self.seq_length = 1
else:
self.seq_length = seq_length
self.first_dim = self.batch_size * self.seq_length
self.start = Tensor(np.zeros((self.batch_size, self.seq_length), dtype=np.int32))
self.zero = Tensor(np.zeros((self.batch_size, self.seq_length), dtype=np.float32))
self.end = Tensor(np.array([[self.vocab_size]], dtype=np.int32))
self.arange = Tensor(np.expand_dims(np.arange(0, self.first_dim), -1), mstype.int32)
self.greater = P.GreaterEqual().shard(((config.dp, 1), (config.dp, 1)))
self.logicalor = P.LogicalOr().shard(((config.dp, 1), (config.dp, 1)))
self.less = P.Less().shard(((config.dp, 1), (config.dp, 1)))
self.squeeze = P.Squeeze(axis=0)
self.log = P.Log().shard(((config.dp, 1),))
self.cast = P.Cast()
self.select = P.Select().shard(((config.dp, 1), (config.dp, 1), (config.dp, 1)))
self.select_target = P.Select().shard(((config.dp, 1), (config.dp, 1), (config.dp, 1)))
self.concat = P.Concat(axis=-1).shard(((config.dp, 1), (config.dp, 1)))
self.gathernd = P.GatherNd().shard(((1, 1), (1, 1)))
self.sub_last = P.Sub().shard(((config.dp, 1), (config.dp, 1)))
self.realdiv = P.RealDiv().shard(((1,), (1,)))
self.mul = P.Mul().shard(((1, 1), (1, 1)))
self.reduce_sum2 = P.ReduceSum().shard(((1, 1),))
self.reduce_sum3 = P.ReduceSum().shard(((1, 1),))
def construct(self, logits, target, loss_mask=None):
r"""
Compute loss using logits, target and loss mask.
"""
# [8, 1, 30000]
_, logits_max = self.argmax(logits)
# [8 1]
logits_max_expand = self.expanddim(logits_max, -1)
logits_max_expand = self.till_expand(logits_max_expand, (1, 1, self.vocab_size))
# [8, 1, 30000] | [8, 1, 30000]
logits_sub = self.sub_logist(logits, logits_max_expand)
logits_exp = self.exp(logits_sub)
# [8, 1, 30000] ->[8,30000]
sum_exp_logits = self.reduce_sum(logits_exp, -1)
# create a mask of a valid vocab ids
ends = self.end
ends = self.till(ends, (self.batch_size, self.seq_length))
vocab_start_res = self.less(target, self.start)
vocab_end_res = self.greater(target, ends)
# training mode: [batch 1].
target_mask = self.logicalor(vocab_start_res, vocab_end_res)
masked_target = self.sub(target, self.start)
masked_target = self.select_target(target_mask, self.zero, self.cast(masked_target, mstype.float32))
# [batch, vocab]
logits_2d = self.reshape(logits_sub, (-1, self.vocab_size))
masked_target_1d = self.reshape(masked_target, (-1, 1))
masked_target_1d = self.cast(masked_target_1d, mstype.int32)
zeros = self.zero
# the next stack/concat means: predicted_logits_1d, logits_2d[self.arange, masked_target_1d]
stack_out = self.concat((self.arange, masked_target_1d))
predicted_logits_1d = self.gathernd(logits_2d, stack_out)
predicted_logits = self.reshape(predicted_logits_1d, (self.batch_size, -1))
predicted_logits_masked = self.select(target_mask, zeros, self.cast(predicted_logits, mstype.float32))
losses = self.sub_last(self.log(sum_exp_logits), predicted_logits_masked)
if (not self.is_training) and (loss_mask is not None):
# loss calculate.
loss_mask_sum = self.reduce_sum2(loss_mask, -1)
loss_with_mask = self.mul(losses, loss_mask)
loss_with_mask_sum = self.reduce_sum3(loss_with_mask, -1)
loss = self.realdiv(loss_with_mask_sum, loss_mask_sum)
return loss
return losses
class Cross_entropy_eval(nn.Cell):
"""
Calculate loss of validation mode.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
config: The config of networks.
Returns:
Tensor, shape of (batch_size,).
"""
def __init__(self, batch_size, seq_length, vocab_size, config):
super(Cross_entropy_eval, self).__init__()
self.batch_size = batch_size
self.vocab_size = vocab_size
self.argmax = P.ArgMaxWithValue(axis=-1).shard(((config.dp, 1),))
self.squeeze = P.Squeeze()
self.expanddims = P.ExpandDims().shard(((config.dp, 1),))
self.expanddims1 = P.ExpandDims().shard(((config.dp,),))
self.tile = P.Tile().shard(((config.dp, 1, 1),))
self.reducesum = P.ReduceSum().shard(((config.dp, 1, 1),))
self.reducesum2 = P.ReduceSum().shard(((config.dp, 1),))
self.readdiv = P.RealDiv().shard(((config.dp, 1), (config.dp, 1)))
self.mul = P.Mul().shard(((config.dp, 1, 1), (config.dp, 1, 1)))
self.reshape = P.Reshape()
def construct(self, logist, loss_mask):
r"""
Compute loss using logits and loss mask.
"""
loss_mask_expand = self.expanddims(loss_mask, -1)
loss_masks = self.tile(loss_mask_expand, (1, 1, self.vocab_size))
loss_mask_sum = self.expanddims1(self.reducesum2(loss_mask, -1), -1)
logist_mask_mul = self.mul(logist, loss_masks)
logist_mask_sum = self.reducesum(logist_mask_mul, 1)
output = self.readdiv(logist_mask_sum, loss_mask_sum)
return output

View File

@ -0,0 +1,386 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""CPM train module"""
import numpy as np
import mindspore.nn as nn
from mindspore.common.initializer import initializer
from mindspore.common.tensor import Tensor
from mindspore.ops import operations as P
from mindspore.ops import composite as C
from mindspore import context
import mindspore.common.dtype as mstype
from mindspore.common.parameter import Parameter
from mindspore.ops.operations.comm_ops import _VirtualDataset
from mindspore.ops import functional as F
from mindspore.nn.wrap.loss_scale import TrainOneStepWithLossScaleCell
from src.cpm_loss import Cross_entropy
from src.cpm import CPMModel
from src.util import ClipByGlobalNorm
class CPMWithLoss(nn.Cell):
"""
Provide CPM training loss through network.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the vocabulary list.
hidden_size (int): Internal feature dimension.
config: The config of CPM network.
num_hidden_layers (int): Number of hidden layers.
num_attention_heads (int): Number of attention heads.
Returns:
Tensor, the loss of the network.
"""
def __init__(self, batch_size, seq_length, vocab_size, hidden_size,
config, num_hidden_layers, num_attention_heads):
super(CPMWithLoss, self).__init__()
self.batch_size = batch_size
self.seq_length = seq_length
self.vocab_size = vocab_size
self.hidden_size = hidden_size
self.num_hidden_layers = num_hidden_layers
self.num_attention_heads = num_attention_heads
self.squeeze = P.Squeeze()
self.expanddims = P.ExpandDims().shard(((config.dp, 1),))
self.expanddims1 = P.ExpandDims().shard(((config.dp,),))
self.tile = P.Tile().shard(((config.dp, 1, 1),))
self.reducesum = P.ReduceSum().shard(((config.dp, 1, 1),))
self.reducesum2 = P.ReduceSum().shard(((config.dp, 1),))
self.reducemean = P.ReduceMean().shard(((1, 1),))
self.cast = P.Cast()
self.readdiv = P.RealDiv().shard(((config.dp, 1), (config.dp, 1)))
self.readdiv2 = P.RealDiv().shard(((1,), (1,)))
self.mul = P.Mul().shard(((config.dp, 1, 1), (config.dp, 1, 1)))
self.mul2 = P.Mul().shard(((config.dp, 1), (config.dp, 1)))
self.cpm_model = CPMModel(batch_size=self.batch_size,
seq_length=self.seq_length,
vocab_size=self.vocab_size,
hidden_size=self.hidden_size,
config=config,
hidden_dropout=config.dropout,
attention_dropout=config.dropout,
num_hidden_layers=self.num_hidden_layers,
num_attention_heads=self.num_attention_heads,
is_training=True)
self.loss_net = Cross_entropy(batch_size=self.batch_size,
seq_length=self.seq_length,
vocab_size=self.vocab_size,
config=config,
is_training=True)
self.slice = P.StridedSlice().shard(((config.dp, 1),))
self.slice_mask = P.StridedSlice().shard(((config.dp, 1, 1),))
def construct(self, input_ids, attention_mask=None, position_ids=None, loss_mask=None, labels=None):
r"""
CPM model with loss.
"""
input_ids = self.slice(input_ids, (0, 0),
(self.batch_size, self.seq_length),
(1, 1))
position_ids = self.slice(position_ids, (0, 0),
(self.batch_size, self.seq_length),
(1, 1))
attention_mask_1 = self.slice_mask(attention_mask, (0, 0, 0),
(self.batch_size, self.seq_length, self.seq_length),
(1, 1, 1))
logist = self.cpm_model(input_ids, position_ids, attention_mask_1)
loss_mask_expand = self.expanddims(loss_mask, -1)
# 8 725 -> 8, 725, 1
loss_masks = self.tile(loss_mask_expand, (1, 1, self.vocab_size))
# 8 725 30000
loss_mask_sum = self.expanddims1(self.reducesum2(loss_mask, -1), -1)
# [8, 725, 30000|8, 725, 30000
logist_mask_mul = self.mul(logist, loss_masks)
# 8, 725, 30000->8, 30000
logist_mask_sum = self.reducesum(logist_mask_mul, 1)
# 8, 30000| 8 1
output = self.readdiv(logist_mask_sum, loss_mask_sum)
# 8 725 | 8 725
label_mul_mask = self.mul2(labels, loss_mask)
# 8 725 -> 8
label_mask = self.reducesum2(label_mul_mask, 1)
# 8 725 -> 8
loss_mask_for_label = self.reducesum2(loss_mask, -1)
# 8 / 8
label_final = self.readdiv2(label_mask, loss_mask_for_label)
# batch 1 vocabe_size
output = self.expanddims(output, 1)
# batchsize 1
label_final = self.expanddims1(label_final, 1)
# batchsize 1
losses = self.loss_net(output, self.cast(label_final, mstype.float32))
loss = self.reducemean(losses, 0)
return loss
GRADIENT_CLIP_TYPE = 1
GRADIENT_CLIP_VALUE = 1.0
clip_grad = C.MultitypeFuncGraph("clip_grad")
@clip_grad.register("Number", "Number", "Tensor")
def _clip_grad(clip_type, clip_value, grad):
"""
Clip gradients.
Inputs:
clip_type (int): The way to clip, 0 for 'value', 1 for 'norm'.
clip_value (float): Specifies how much to clip.
grad (tuple[Tensor]): Gradients.
Outputs:
tuple[Tensor], clipped gradients.
"""
if clip_type not in [0, 1]:
return grad
dt = F.dtype(grad)
if clip_type == 0:
new_grad = C.clip_by_value(
grad, F.cast(F.tuple_to_array((-clip_value,)), dt),
F.cast(F.tuple_to_array((clip_value,)), dt))
else:
new_grad = nn.ClipByNorm()(grad,
F.cast(F.tuple_to_array((clip_value,)),
dt))
return new_grad
class VirtualDatasetOneInputCell(nn.Cell):
def __init__(self, backbone):
super(VirtualDatasetOneInputCell, self).__init__(auto_prefix=False)
self._backbone = backbone
self._virtual_dataset = _VirtualDataset()
def construct(self, *data):
data_ = self._virtual_dataset(*data)
return self._backbone(*data_)
grad_scale = C.MultitypeFuncGraph("grad_scale")
reciprocal = P.Reciprocal()
@grad_scale.register("Tensor", "Tensor")
def tensor_grad_scale(scale, grad):
return grad * reciprocal(scale)
class CPMTrainOneStepWithLossScaleCell(TrainOneStepWithLossScaleCell):
"""
Encapsulation class of CPM network training.
Append an optimizer to the training network after that the construct
function can be called to create the backward graph.
Args:
network (Cell): The training network. Note that loss function should have been added.
optimizer (Optimizer): Optimizer for updating the weights.
scale_update_cell (Cell): Cell to do the loss scale. Default: None.
enable_global_norm (Bool): Whether using global normalization.
"""
def __init__(self,
network,
optimizer,
scale_update_cell=None,
enable_global_norm=True):
super(CPMTrainOneStepWithLossScaleCell,
self).__init__(network, optimizer, scale_update_cell)
self.network = network
self.weights = optimizer.parameters
self.optimizer = optimizer
self.default_lr = Tensor([0.0], dtype=mstype.float32)
self.enable_global_norm = enable_global_norm
self.cast = P.Cast()
self.clip = ClipByGlobalNorm(self.weights)
def construct(self,
input_ids,
attention_mask,
position_ids,
loss_mask,
labels,
sens=None):
"""Defines the computation performed."""
weights = self.weights
loss = self.network(input_ids,
attention_mask,
position_ids,
loss_mask,
labels)
scaling_sens = self.scale_sense
# alloc status and clear should be right before grad operation.
status, scaling_sens = self.start_overflow_check(loss, scaling_sens)
scaling_sens_filled = C.ones_like(loss) * F.cast(scaling_sens, F.dtype(loss))
grads = self.grad(self.network,
weights)(input_ids,
attention_mask,
position_ids,
loss_mask,
labels,
scaling_sens_filled)
# apply grad reducer on grads.
grads = self.grad_reducer(grads)
grads = self.hyper_map(
F.partial(grad_scale, scaling_sens), grads)
if self.enable_global_norm:
grads, _ = self.clip(grads)
else:
grads = self.hyper_map(
F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE),
grads)
cond = self.get_overflow_status(status, grads)
overflow = self.process_loss_scale(cond)
if overflow:
succ = False
else:
succ = self.optimizer(grads)
return F.depend(loss, succ), cond, scaling_sens
cast = P.Cast()
update_accu_grads = C.MultitypeFuncGraph("update_accu_grads")
@update_accu_grads.register("Tensor", "Tensor")
def _update_accu_grads(accu_grad, grad):
succ = True
return F.depend(succ, F.assign_add(accu_grad, cast(grad, mstype.float32)))
zeroslike = P.ZerosLike()
reset_accu_grads = C.MultitypeFuncGraph("reset_accu_grads")
@reset_accu_grads.register("Tensor")
def _reset_accu_grads(accu_grad):
succ = True
return F.depend(succ, F.assign(accu_grad, zeroslike(accu_grad)))
class CPMTrainAccuStepsWithLossScaleCell(TrainOneStepWithLossScaleCell):
"""
Encapsulation class of CPM network training with loss scale.
Append an optimizer to the training network after that the construct
function can be called to create the backward graph.
Args:
network (Cell): The training network. Note that loss function should have been added.
optimizer (Optimizer): Optimizer for updating the weights.
scale_update_cell (Cell): Cell to do the loss scale. Default: None.
enable_global_norm (Bool): Whether using global normalization.
"""
def __init__(self,
network,
optimizer,
scale_update_cell=None,
enable_global_norm=True):
super(CPMTrainAccuStepsWithLossScaleCell, self).__init__(network, optimizer, scale_update_cell)
self.accumulation = False
self.accumulation_steps = context.get_auto_parallel_context("grad_accumulation_step")
self.one = Tensor(np.array([1]).astype(np.int32))
self.zero = Tensor(np.array([0]).astype(np.int32))
self.accu_grads = self.weights.clone(prefix="accu_grads", init='zeros')
self.accu_overflow = Parameter(initializer(0, [1], mstype.int32))
self.accu_loss = Parameter(initializer(0, [1], mstype.float32))
self.cast = P.Cast()
self.logical_or = P.LogicalOr()
self.not_equal = P.NotEqual()
self.select = P.Select()
self.reshape = P.Reshape()
self.enable_global_norm = enable_global_norm
self.clip = ClipByGlobalNorm(self.weights)
def construct(self,
input_ids,
attention_mask,
position_ids,
loss_mask,
labels,
sens=None):
"""Defines the computation performed."""
weights = self.weights
loss = self.network(input_ids,
attention_mask,
position_ids,
loss_mask,
labels)
scaling_sens = self.scale_sense
status, scaling_sens = self.start_overflow_check(loss, scaling_sens)
scaling_sens_filled = C.ones_like(loss) * F.cast(scaling_sens, F.dtype(loss))
grads = self.grad(self.network,
weights)(input_ids,
attention_mask,
position_ids,
loss_mask,
labels, scaling_sens_filled)
if self.accumulation and self.accumulation_steps > 1:
accu_succ = self.hyper_map(update_accu_grads, self.accu_grads, grads)
loss = F.depend(loss, accu_succ)
overflow = self.get_overflow_status(status, grads)
overflow = self.logical_or(self.not_equal(self.accu_overflow, self.zero), overflow)
accu_overflow = self.select(overflow, self.one, self.zero)
if self.accumulation:
succ = False
self.accu_overflow = accu_overflow
else:
my_zero = F.depend(self.zero, accu_overflow)
initialize = P.Assign()(self.accu_overflow, my_zero)
grads1 = F.depend(grads, initialize)
# apply grad reducer on grads
grads = self.grad_reducer(grads1)
scaling = scaling_sens * self.accumulation_steps
grads = self.hyper_map(F.partial(grad_scale, scaling), grads)
if self.enable_global_norm:
grads, _ = self.clip(grads)
else:
grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads)
accu_overflow = self.allreduce(accu_overflow)
overflow = self.less_equal(self.base, accu_overflow)
accu_grads = F.depend(self.accu_grads, grads)
accu_succ = self.hyper_map(reset_accu_grads, accu_grads)
overflow = F.depend(overflow, accu_succ)
overflow = self.reshape(overflow, (()))
overflow = self.process_loss_scale(overflow)
if overflow:
succ = False
else:
succ = self.optimizer(grads)
return F.depend(loss, succ), overflow, scaling_sens

View File

@ -0,0 +1,166 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Embedding."""
import numpy as np
import mindspore.common.dtype as mstype
import mindspore.nn as nn
from mindspore.ops import operations as P
from mindspore.common.tensor import Tensor
from mindspore.common.parameter import Parameter
from src.weight_init import normal_weight
class EmbeddingLookup(nn.Cell):
"""
A embeddings lookup table with a fixed dictionary and size.
Args:
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
embedding_dim (int): The size of each embedding vector.
use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form.
config: The config of networks.
compute_type (:class:`mindspore.dtype`): Compute type.
"""
def __init__(self,
batch_size,
seq_length,
vocab_size,
embedding_dim,
config=None,
use_one_hot_embeddings=True,
compute_type=mstype.float16):
super(EmbeddingLookup, self).__init__()
self.vocab_size = vocab_size
self.embedding_dim = embedding_dim
self.use_one_hot_embeddings = use_one_hot_embeddings
self.compute_type = compute_type
self.batch_size = batch_size
self.seq_length = seq_length
self.embedding_table = Parameter(normal_weight([vocab_size, embedding_dim], embedding_dim),
name='embedding_table')
self.embedding_table.parallel_optimizer = False
self.shape_flat = (-1,)
self.gather = P.GatherV2().shard(((1, 1), (config.dp,)))
self.reshape = P.Reshape()
self.shape = P.Shape()
self.cast = P.Cast()
self.less = P.Less().shard(((config.dp, 1), (config.dp, 1)))
self.greaterequal = P.GreaterEqual().shard(((config.dp, 1), (config.dp, 1)))
self.logicalor = P.LogicalOr().shard(((config.dp, 1), (config.dp, 1)))
self.zero = Tensor(np.zeros((self.batch_size, self.seq_length), dtype=np.int32))
self.zero_2 = Tensor(np.zeros((self.batch_size, self.seq_length, 1), dtype=np.int32))
self.start = Tensor(np.zeros((self.batch_size, self.seq_length), dtype=np.int32))
self.end = Tensor(np.array([[self.vocab_size]], dtype=np.int32))
self.expanddim_first = P.ExpandDims().shard(((config.dp, 1),))
self.expanddim = P.ExpandDims().shard(((config.dp, 1),))
self.tile_in_mask = P.Tile().shard(((config.dp, 1, 1),))
self.tile_2 = P.Tile().shard(((1, 1),))
self.tile = P.Tile().shard(((config.dp, 1, 1),))
self.select = P.Select().shard(
((config.dp, 1, config.mp), (config.dp, 1, config.mp), (config.dp, 1, config.mp)))
self.mask_select = P.Select().shard(((config.dp, 1), (config.dp, 1), (config.dp, 1)))
self.sub = P.Sub().shard(((config.dp, 1), (config.dp, 1)))
self.get_dtype = P.DType()
def construct(self, input_ids):
"""
get embedding according to input_ids.
"""
input_less = self.less(input_ids, self.start)
ends = self.end
ends = self.tile_2(ends, (self.batch_size, self.seq_length))
input_greater = self.greaterequal(input_ids, ends)
input_mask = self.logicalor(input_less, input_greater)
masked_input = self.sub(input_ids, self.start)
# [batchsize, seq_length]
masked_input = self.mask_select(input_mask, self.zero, self.cast(masked_input, mstype.int32))
input_shape = self.shape(masked_input)
flat_ids = self.reshape(masked_input, self.shape_flat)
flat_ides = self.cast(flat_ids, mstype.int32)
output_for_reshape = self.gather(self.embedding_table, flat_ides, 0)
out_shape = input_shape + (self.embedding_dim,)
output = self.reshape(output_for_reshape, out_shape)
# [batchsize, seq_length]
input_masks = self.expanddim(input_mask, -1)
input_mask_tile = self.tile_in_mask(self.cast(input_masks, mstype.int32), (1, 1, self.embedding_dim))
zero_expand = self.zero_2
zero_tiled = self.tile(zero_expand, (1, 1, self.embedding_dim))
zero_tiled = self.cast(zero_tiled, self.get_dtype(output))
output = self.select(self.cast(input_mask_tile, mstype.bool_), zero_tiled, output)
output = self.cast(output, mstype.float32)
return output, self.embedding_table
class EmbeddingPostprocessor(nn.Cell):
"""
Positional embeddings.
Args:
max_seq_length (int): the length of input sequence.
embedding_dim (int): The size of each embedding vector.
config: The config of networks.
use_one_hot_embeddings (bool): Whether use one-hot embedding.
compute_type (:class:`mindspore.dtype`): Compute type. Default: mstype.float16.
"""
def __init__(self,
max_seq_length,
embedding_dim,
config=None,
use_one_hot_embeddings=True,
compute_type=mstype.float16):
super(EmbeddingPostprocessor, self).__init__()
self.max_seq_length = max_seq_length
self.embedding_dim = embedding_dim
self.use_one_hot_embeddings = use_one_hot_embeddings
self.compute_type = compute_type
self.position_embedding_table = Parameter(normal_weight([max_seq_length, embedding_dim], embedding_dim),
name='position_embeddings')
self.position_embedding_table.parallel_optimizer = False
self.shape_flat = (-1,)
self.gather = P.GatherV2().shard(((1, 1), (config.dp,)))
self.reshape = P.Reshape()
self.shape = P.Shape()
self.cast = P.Cast()
position_ids = np.expand_dims(np.arange(config.seq_length * 1), 0)
self.position_ids = Tensor(np.tile(position_ids, (config.batch_size, 1)), dtype=mstype.int64)
def construct(self, input_ids=None):
r"""
get embedding according to input_ids.
"""
if input_ids is None:
input_ids = self.position_ids
input_shape = self.shape(input_ids)
flat_ids = self.reshape(input_ids, self.shape_flat)
output_for_reshape = self.gather(self.position_embedding_table, flat_ids, 0)
out_shape = input_shape + (self.embedding_dim,)
output = self.reshape(output_for_reshape, out_shape)
return output

View File

@ -0,0 +1,121 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Loss monitor."""
import time
import math
from mindspore.train.callback import Callback
class LossCallBack(Callback):
"""
Monitor the loss in training.
If the loss is NAN or INF terminating training.
"""
time_stamp_init = False
time_stamp_first = 0
def __init__(self, per_print_times=-1):
super(LossCallBack, self).__init__()
self._per_print_times = per_print_times
if not self.time_stamp_init:
self.time_stamp_first = self._get_ms_timestamp()
self.time_stamp_init = True
def step_end(self, run_context):
"""Print loss after each step."""
cb_params = run_context.original_args()
file_name = "./loss.log"
with open(file_name, "a+") as f:
time_stamp_current = self._get_ms_timestamp()
if self._per_print_times > 0:
_, epoch_num = math.modf(cb_params.cur_step_num / self._per_print_times)
f.write("time: {},epoch: {},step: {},outputs:[loss: {},overflow: {}, loss scale value: {} ].\n".format(
time_stamp_current - self.time_stamp_first,
int(epoch_num),
cb_params.cur_step_num,
str(cb_params.net_outputs[0].asnumpy()),
str(cb_params.net_outputs[1].asnumpy()),
str(cb_params.net_outputs[2].asnumpy())
))
else:
f.write("time: {},epoch: {},step: {},outputs: [loss: {},overflow: {},loss scale value: {} ].\n".format(
time_stamp_current - self.time_stamp_first,
cb_params.cur_epoch_num,
cb_params.cur_step_num,
str(cb_params.net_outputs[0].asnumpy()),
str(cb_params.net_outputs[1].asnumpy()),
str(cb_params.net_outputs[2].asnumpy())
))
@staticmethod
def _get_ms_timestamp():
"""Get timestamp."""
t = time.time()
return int(round(t * 1000))
class TimeCallBack(Callback):
"""
Monitor the time in training.
Args:
data_size (int): Dataset size. Default: None.
"""
def __init__(self, data_size=None):
super(TimeCallBack, self).__init__()
self.data_size = data_size
def step_begin(self, run_context):
"""Step begin."""
self.epoch_time = time.time()
def step_end(self, run_context):
"""Step end."""
epoch_seconds = (time.time() - self.epoch_time) * 1000
step_size = self.data_size
cb_params = run_context.original_args()
if hasattr(cb_params, "batch_num"):
batch_num = cb_params.batch_num
if isinstance(batch_num, int) and batch_num > 0:
step_size = cb_params.batch_num
if not isinstance(step_size, int) or step_size < 1:
logger.error("data_size must be positive int.")
return
print("epoch time: {:5.3f} ms".format(epoch_seconds), flush=True)
def epoch_begin(self, run_context):
"""Epoch begin."""
self.epoch_time = time.time()
def epoch_end(self, run_context):
"""Epoch end."""
epoch_seconds = (time.time() - self.epoch_time) * 1000
step_size = self.data_size
cb_params = run_context.original_args()
if hasattr(cb_params, "batch_num"):
batch_num = cb_params.batch_num
if isinstance(batch_num, int) and batch_num > 0:
step_size = cb_params.batch_num
if not isinstance(step_size, int) or step_size < 1:
logger.error("data_size must be positive int.")
return
print("epoch time: {:5.3f} ms".format(epoch_seconds), flush=True)

View File

@ -0,0 +1,73 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Learning rate schedule."""
import numpy as np
from mindspore.ops import operations as P
from mindspore.common.tensor import Tensor
from mindspore.common import dtype as mstype
from mindspore.nn.learning_rate_schedule import LearningRateSchedule, WarmUpLR
class DecayLR(LearningRateSchedule):
"""
Implements of decay learning rate scheduler.
Args:
learning_rate (float): Initial learning rate.
warmup_steps (int): Warmup steps.
end_steps (int): A value used to calculate decayed learning rate.
Returns:
np.ndarray, learning rate of each step.
"""
def __init__(self, learning_rate, warmup_steps, end_iter):
super(DecayLR, self).__init__()
self.learning_rate = learning_rate
self.warmup_steps = warmup_steps
self.end_iter = end_iter
self.cast = P.Cast()
def construct(self, global_step):
warmup_percent = self.cast((self.end_iter - (global_step - self.warmup_steps)), mstype.float32) / self.end_iter
return self.learning_rate * warmup_percent
class CPMLearningRate(LearningRateSchedule):
"""
Implements of warmup-polynomial decay learning rate scheduler.
Args:
learning_rate (float): The initial value of learning rate.
warmup_steps (int): The warm up steps of learning rate.
end_steps (int): A value used to calculate decayed learning rate.
Returns:
Tensor. The learning rate value for the current step.
"""
def __init__(self, learning_rate, warmup_steps, end_steps):
super(CPMLearningRate, self).__init__()
self.warmup_lr = WarmUpLR(learning_rate, warmup_steps)
self.decay_lr = DecayLR(learning_rate, warmup_steps, end_steps)
self.warmup_steps = Tensor(np.array([warmup_steps]).astype(np.float32))
def construct(self, global_step):
if global_step < self.warmup_steps:
lr = self.warmup_lr(global_step)
else:
lr = self.decay_lr(global_step)
return lr

View File

@ -0,0 +1,172 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Model CPM"""
import math
from mindspore.train.callback import RunContext
from mindspore import context
from mindspore.context import ParallelMode
from mindspore import Model, connect_network_with_dataset
from mindspore.common.dtype import pytype_to_dtype
from mindspore._c_expression import init_exec_dataset
from mindspore.train.train_thor.dataset_helper import DatasetHelper
def _convert_type(types):
"""
Convert from numpy type to tensor type.
Args:
types (list): Numpy type list of element in dataset.
Returns:
list, list of element in dataset.
"""
ms_types = []
for np_type in types:
ms_type = pytype_to_dtype(np_type)
ms_types.append(ms_type)
return ms_types
def _get_types_and_shapes(dataset):
"""Get dataset types and shapes."""
dataset_types = _convert_type(dataset.output_types())
dataset_shapes = dataset.output_shapes()
return dataset_types, dataset_shapes
def _exec_datagraph(exec_dataset, dataset_size, phase='dataset'):
"""Initialize and execute the dataset graph."""
batch_size = exec_dataset.get_batch_size()
input_indexs = exec_dataset.input_indexs
# transform data format
dataset_types, dataset_shapes = _get_types_and_shapes(exec_dataset)
init_exec_dataset(exec_dataset.__transfer_dataset__.queue_name,
dataset_size,
batch_size,
dataset_types,
dataset_shapes,
input_indexs,
phase=phase,
need_run=False)
class Model_ACCU(Model):
r"""
Overwrite Model class for gradient accumulation, docking dataset interface.
"""
def __init__(self, network, loss_fn=None, optimizer=None, metrics=None, eval_network=None,
eval_indexes=None, amp_level="O0", **kwargs):
super(Model_ACCU, self).__init__(network, loss_fn, optimizer, metrics, eval_network,
eval_indexes, amp_level, **kwargs)
self._frequency = context.get_auto_parallel_context("grad_accumulation_step")
self._train_network = self._build_train_network()
def _exec_preprocess(self, network, is_train, phase, dataset, dataset_sink_mode, sink_size=-1,
epoch_num=1, iter_first_order=1):
"""Initializes dataset."""
if dataset_sink_mode and not is_train:
dataset.__loop_size__ = 1
dataset_helper = DatasetHelper(dataset, dataset_sink_mode, sink_size, epoch_num, iter_first_order)
if dataset_sink_mode and context.get_context("device_target") != "GPU":
network = connect_network_with_dataset(network, dataset_helper)
network.set_train(is_train)
network.phase = phase
if self._parallel_mode in (ParallelMode.SEMI_AUTO_PARALLEL, ParallelMode.AUTO_PARALLEL):
network.set_auto_parallel()
return dataset_helper, network
def _train_dataset_sink_process(self, epoch, train_dataset, list_callback=None, cb_params=None, sink_size=-1):
"""
Training process. The data would be passed to network through dataset channel.
Args:
epoch (int): Total number of iterations on the data.
train_dataset (Dataset): A training dataset iterator. If there is no
loss_fn, a tuple with multiple data (data1, data2, data3, ...) should be
returned and passed to the network. Otherwise, a tuple (data, label) should
be returned. The data and label would be passed to the network and loss
function respectively.
list_callback (Callback): Executor of callback list. Default: None.
cb_params (_InternalCallbackParam): Callback parameters. Default: None.
sink_size (int): Control the amount of data in each sink. Default: -1.
"""
if sink_size == -1:
epoch_num = epoch
else:
epoch_num = math.ceil(epoch * sink_size / train_dataset.get_dataset_size())
iter_first_order = 1
iter_second_order = self._frequency - 1
train_dataset.__loop_size__ = iter_second_order
dataset_helper, train_network = self._exec_preprocess(self._train_network,
is_train=True,
phase='train',
dataset=train_dataset,
dataset_sink_mode=True,
sink_size=sink_size,
epoch_num=epoch_num,
iter_first_order=iter_first_order)
self._train_network = train_network
cb_params.train_network = self._train_network
cb_params.cur_step_num = 0
run_context = RunContext(cb_params)
list_callback.begin(run_context)
# used to stop training for early stop, such as stopAtTIme or stopATStep
should_stop = False
switch_branch_one = True
train_network_init_flag = True
has_do_dataset_init = False
for i in range(epoch):
cb_params.cur_epoch_num = i + 1
list_callback.epoch_begin(run_context)
# for data sink dataset_helper only iter once, other wise iter epoch_size times.
for inputs in dataset_helper:
list_callback.step_begin(run_context)
if switch_branch_one:
cb_params.cur_step_num += iter_second_order
if train_network_init_flag:
self._train_network.add_flags_recursive(accumulation=True)
self._train_network.phase = 'train0'
else:
cb_params.cur_step_num += iter_first_order
if train_network_init_flag:
self._train_network.add_flags_recursive(accumulation=False)
train_network_init_flag = False
self._train_network.phase = 'train1'
if not has_do_dataset_init:
_exec_datagraph(train_dataset, iter_first_order, phase='train1_dataset')
has_do_dataset_init = True
switch_branch_one = not switch_branch_one
outputs = self._train_network(*inputs)
cb_params.net_outputs = outputs
list_callback.step_end(run_context)
list_callback.epoch_end(run_context)
should_stop = should_stop or run_context.get_stop_requested()
if should_stop:
break
dataset_helper.stop_send()
list_callback.end(run_context)

View File

@ -0,0 +1,230 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Utils."""
import mindspore.common.dtype as mstype
import mindspore.nn as nn
import mindspore.ops.functional as F
from mindspore.ops import operations as P
from mindspore.ops import composite as C
from mindspore.common.tensor import Tensor
from mindspore.common.parameter import Parameter
from mindspore.communication.management import get_group_size
from mindspore.common.seed import _get_graph_seed
from mindspore._checkparam import Validator
from mindspore import context
from src.weight_init import normal_weight, zero_weight, one_weight
class ResidualConnection(nn.Cell):
"""
Add residual to output.
Args:
dropout_prob (float): Dropout rate.
Returns:
Tensor, with the same shape of hidden_tensor
"""
def __init__(self, dropout_prob=0.0):
super(ResidualConnection, self).__init__()
self.add = P.TensorAdd()
def construct(self, hidden_tensor, input_tensor):
# hidden_tensor is the output of sublayer
output = hidden_tensor
output = self.add(output, input_tensor)
return output
class LinearLayer(nn.Cell):
"""
Args:
input_size (int): The number of input features.
output_size (int): The number of output features.
"""
def __init__(self,
input_size,
output_size):
super(LinearLayer, self).__init__()
self.input_size = input_size
self.output_size = output_size
self.weight = Parameter(normal_weight([output_size, input_size], output_size), name='projection_weight')
self.bias = Parameter(zero_weight(output_size), name='projection_bias')
self.matmul = P.MatMul(transpose_b=True)
self.bias_add = P.BiasAdd()
self.cast = P.Cast()
def construct(self, input_tensor):
input_tensor = self.cast(input_tensor, mstype.float16)
fp16_weight = self.cast(self.weight, mstype.float16)
output_tensor = self.matmul(input_tensor, fp16_weight)
fp16_bias = self.cast(self.bias, mstype.float16)
output_tensor = self.bias_add(output_tensor, fp16_bias)
output_tensor = self.cast(output_tensor, mstype.float32)
return output_tensor
class LayerNorm(nn.Cell):
r"""
A self-defined layer norm operation using reduce sum and reduce mean
"""
def __init__(self, normalized_shape, config=None, epsilon=1e-5, scale=1e-3):
super(LayerNorm, self).__init__()
self.gamma = Parameter(one_weight(normalized_shape, mstype.float32), name="gamma")
self.beta = Parameter(zero_weight(normalized_shape, mstype.float32), name="beta")
self.mean = P.ReduceMean(keep_dims=True).shard(((config.dp, 1),))
self.square = P.Square().shard(((config.dp, 1),))
self.sqrt = P.Sqrt().shard(((config.dp, 1),))
self.sub1 = P.Sub().shard(((config.dp, 1), (config.dp, 1)))
self.add = P.TensorAdd().shard(((config.dp, 1), ()))
self.eps = epsilon
self.mul = P.Mul().shard(((config.dp, 1), (1,)))
self.add2 = P.TensorAdd().shard(((config.dp, 1), (1,)))
self.real_div = P.RealDiv().shard(((config.dp, 1), (config.dp, 1)))
def construct(self, x):
mean = self.mean(x, -1)
diff = self.sub1(x, mean)
variance = self.mean(self.square(diff), -1)
variance_eps = self.sqrt(self.add(variance, self.eps))
output = self.real_div(diff, variance_eps)
output = self.add2(self.mul(output, self.gamma), self.beta)
return output
get_square_sum = C.MultitypeFuncGraph("get_square_sum")
@get_square_sum.register("Tensor", "Tensor")
def _get_square_sum(grad, value):
norm = P.ReduceSum(False)(F.square(grad) / value, ())
norm = F.expand_dims(F.cast(norm, mstype.float32), 0)
return norm
@get_square_sum.register("Tensor", "Number")
def _get_square_sum_number(grad, value):
norm = P.ReduceSum(False)(F.square(grad), ()) / value
norm = F.expand_dims(F.cast(norm, mstype.float32), 0)
return norm
apply_global_norm = C.MultitypeFuncGraph("apply_global_norm")
@apply_global_norm.register("Tensor", "Tensor", "Tensor")
def _apply_global_norm(clip_norm, global_norm, grad):
grad = grad * clip_norm / global_norm
return grad
class GlobalNorm(nn.Cell):
r"""
Calculate the global norm value of given tensors
"""
def __init__(self, params):
super(GlobalNorm, self).__init__()
self.norm = nn.Norm()
self.hyper_map = C.HyperMap()
self.allreduce_filter = tuple(
"layernorm" not in x.name and
"dense_proj.bias" not in x.name and
"embedding_table" not in x.name for x in params)
self.values = []
self.group_size = get_group_size()
for item in self.allreduce_filter:
if item:
self.values.append(Tensor([1.0], mstype.float32))
else:
self.values.append(Tensor([self.group_size * 1.0], mstype.float32))
self.values = tuple(self.values)
def construct(self, grads):
square_sum_dp = self.hyper_map(get_square_sum, grads, self.values)
global_norms = F.sqrt(P.AllReduce()(F.addn(square_sum_dp)))
return global_norms
class ClipByGlobalNorm(nn.Cell):
r"""
Clip grads by global norm
"""
def __init__(self, params, clip_norm=1.0):
super(ClipByGlobalNorm, self).__init__()
self.global_norm = GlobalNorm(params)
self.clip_norm = Tensor([clip_norm], mstype.float32)
self.hyper_map = C.HyperMap()
def construct(self, grads):
global_norm_value = self.global_norm(grads)
cond = P.GreaterEqual()(global_norm_value, self.clip_norm)
# P.Print()("do clip ", cond, ", global norm is ", global_norm_value)
global_norm = F.select(cond, global_norm_value, self.clip_norm)
grads = self.hyper_map(F.partial(apply_global_norm, self.clip_norm, global_norm), grads)
return grads, global_norm_value
class Dropout(nn.Cell):
r"""
A Dropout Implements with P.DropoutGenMask and P.DropoutDoMask for parallel training.
"""
def __init__(self, keep_prob=0.5, dtype=mstype.float32):
super(Dropout, self).__init__()
if keep_prob <= 0 or keep_prob > 1:
raise ValueError("dropout probability should be a number in range (0, 1], but got {}".format(keep_prob))
Validator.check_subclass("dtype", dtype, mstype.number_type, self.cls_name)
Validator.check_value_type('keep_prob', keep_prob, [float], self.cls_name)
self.keep_prob = keep_prob
seed0, seed1 = _get_graph_seed(0, "dropout")
self.seed0 = seed0
self.seed1 = seed1
self.dtype = dtype
self.get_shape = P.Shape()
self.dropout_gen_mask = P.DropoutGenMask(Seed0=self.seed0, Seed1=self.seed1)
self.dropout_do_mask = P.DropoutDoMask()
self.cast = P.Cast()
self.is_ascend = context.get_context('device_target') in ["Ascend"]
self.dropout = P.Dropout(keep_prob)
def construct(self, x):
r"""
Input: a tensor
Returns: a tensor
"""
if not self.training:
return x
if not self.is_ascend:
out, _ = self.dropout(x)
return out
if self.keep_prob == 1:
return x
shape = self.get_shape(x)
dtype = P.DType()(x)
keep_prob = self.cast(self.keep_prob, dtype)
output = self.dropout_gen_mask(shape, keep_prob)
return self.dropout_do_mask(x, output, keep_prob)
def extend_repr(self):
return 'keep_prob={}, dtype={}'.format(self.keep_prob, self.dtype)

View File

@ -0,0 +1,63 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Initializer."""
import math
import numpy as np
from mindspore.common.tensor import Tensor
import mindspore.common.dtype as mstype
def _average_units(shape):
if not shape:
return 1
if len(shape) == 1:
return float(shape[0])
if len(shape) == 2:
return float(shape[0] + shape[1]) / 2.
raise RuntimeError("not support shape.")
def weight_variable(shape):
scale_shape = shape
avg_units = _average_units(scale_shape)
scale = 1.0 / max(1., avg_units)
limit = math.sqrt(3.0 * scale)
values = np.random.uniform(-limit, limit, shape).astype(np.float32)
return Tensor(values)
def one_weight(shape, dtype=mstype.float32):
ones = np.ones(shape).astype(np.float32)
return Tensor(ones, dtype=dtype)
def zero_weight(shape, dtype=mstype.float32):
zeros = np.zeros(shape).astype(np.float32)
return Tensor(zeros, dtype=dtype)
def zero_weight_fp32(shape, dtype=mstype.float32):
zeros = np.zeros(shape).astype(np.float32)
return Tensor(zeros, dtype=dtype)
def normal_weight(shape, num_units, dtype=mstype.float32):
norm = np.random.normal(0.0, num_units ** -0.5, shape).astype(np.float32)
return Tensor(norm, dtype=dtype)
def normal_weightfp32(shape, num_units, dtype=mstype.float32):
norm = np.random.normal(0.0, num_units ** -0.5, shape).astype(np.float32)
return Tensor(norm, dtype=dtype)

View File

@ -0,0 +1,115 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Test."""
import os
import ast
import argparse
from mindspore import context
from mindspore.communication import management as MultiAscend
from mindspore.context import ParallelMode
from mindspore.parallel import set_algo_parameters
from src.config import finetune_test_standalone, finetune_test_distrubute, \
finetune_dev_distrubute, finetune_dev_standalone
from eval import run_eval, create_ckpt_file_list
device_id = int(os.getenv("DEVICE_ID"))
rank_size = os.getenv('RANK_SIZE')
context.set_context(mode=context.GRAPH_MODE,
save_graphs=False,
device_target="Ascend",
device_id=device_id)
def set_parallel_env():
r"""
Parallel environment.
"""
context.reset_auto_parallel_context()
MultiAscend.init()
context.set_auto_parallel_context(parallel_mode=ParallelMode.SEMI_AUTO_PARALLEL,
device_num=MultiAscend.get_group_size(),
gradients_mean=True,
full_batch=True)
set_algo_parameters(elementwise_op_strategy_follow=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="CPM inference")
parser.add_argument('--dev_dataset', type=str, default="", help="dev_dataset path.")
parser.add_argument("--dev_data_path", type=str, default="/disk0/dataset/finetune_dataset/dev.json",
help='dev_json path.')
parser.add_argument('--test_dataset', type=str, default="", help="test_dataset path.")
parser.add_argument("--test_data_path", type=str, default="/disk0/dataset/finetune_dataset/test.json",
help='test_json path.')
parser.add_argument('--ckpt_path_doc', type=str, default="", help="checkpoint path document.")
parser.add_argument('--ckpt_partition', type=int, default=8, help="Number of checkpoint partition.")
parser.add_argument("--distribute", type=ast.literal_eval, default=False,
help='Whether distributed evaluation with model parallel.')
parser.add_argument("--has_train_strategy", type=ast.literal_eval, default=True,
help='Whether the loaded checkpoints have distributed training strategy.')
args_eval = parser.parse_args()
if args_eval.distribute:
set_parallel_env()
print("Start validation on 2 devices.")
else:
print("Start validation on 1 device.")
args_eval.dataset = args_eval.dev_dataset
args_eval.data_path = args_eval.dev_data_path
if args_eval.has_train_strategy:
# Get the checkpoint with train strategy.
train_strategy_list = create_ckpt_file_list(args_eval, train_strategy="train_strategy.ckpt")
context.set_auto_parallel_context(
strategy_ckpt_load_file=train_strategy_list[0]
)
# start run in dev dataset.
result_dev = []
for i in range(4, 11):
ckpt_file_list_dev = None
if args_eval.has_train_strategy:
# Get the checkpoint slice.
ckpt_file_list_dev = create_ckpt_file_list(args_eval, i)
print("++++ Get sliced checkpoint file, lists: ", ckpt_file_list_dev, flush=True)
result_i = 0.0
if args_eval.distribute:
result_i = run_eval(args_eval, finetune_dev_distrubute, ckpt_file_list_dev)
else:
result_i = run_eval(args_eval, finetune_dev_standalone, ckpt_file_list_dev)
print("+++++ i=", i, ", dev_dataset Accuracy: ", result_i)
result_dev.append(result_i)
print("++++ The accuracy of each checkpoint on the validation dataset is:", result_dev)
print("++++ Then we take the model with the highest accuracy ")
print(" on the validation dataset to predict on the test dataset.")
index_max_dev = result_dev.index(max(result_dev)) + 4
ckpt_file_list_test = None
if args_eval.has_train_strategy:
# Get the best precision checkpoint slice.
ckpt_file_list_test = create_ckpt_file_list(args_eval, index_max_dev)
args_eval.dataset = args_eval.test_dataset
args_eval.data_path = args_eval.test_data_path
# start run in test dataset.
result_last = 0.0
if args_eval.distribute:
result_last = run_eval(args_eval, finetune_test_distrubute, ckpt_file_list_test)
else:
result_last = run_eval(args_eval, finetune_test_standalone, ckpt_file_list_test)
print("++++ Accuracy on test dataset is: ", result_last)

View File

@ -0,0 +1,269 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Train."""
import os
import ast
import argparse
import math
import numpy as np
import mindspore.dataset as ds
from mindspore import context
from mindspore.train.model import Model
from mindspore.nn.optim import AdamWeightDecay
from mindspore.communication import management as MultiAscend
from mindspore.context import ParallelMode
from mindspore.common import set_seed
from mindspore.train.callback import TimeMonitor, ModelCheckpoint, CheckpointConfig
from mindspore.nn.wrap.loss_scale import DynamicLossScaleUpdateCell
from mindspore.train.serialization import load_checkpoint, load_param_into_net
import mindspore.common.dtype as mstype
import mindspore.dataset.transforms.c_transforms as C
from mindspore.parallel import set_algo_parameters
from src.config import config_train_8p, config_train_32p
from src.cpm_train import CPMWithLoss, CPMTrainOneStepWithLossScaleCell, VirtualDatasetOneInputCell, \
CPMTrainAccuStepsWithLossScaleCell
from src.lr_schedule import CPMLearningRate
from src.loss_monitor import LossCallBack, TimeCallBack
from src.model_cpm import Model_ACCU as Model_CPM
device_id = int(os.getenv("DEVICE_ID"))
set_seed(23333)
context.set_context(mode=context.GRAPH_MODE,
save_graphs=False,
device_target="Ascend",
device_id=device_id)
context.set_context(variable_memory_max_size="30GB")
def collate(truth, input_ids, BatchInfo):
"""Collate operation for dataset."""
bs = len(truth)
max_size = np.size(input_ids, 1)
attn_mask = np.tril(np.ones(shape=(max_size, max_size),))
attention_mask = np.expand_dims(attn_mask, 0)
attention_mask = np.tile(attention_mask, (bs, 1, 1)).astype(np.float32)
position_ids = np.expand_dims(np.arange(max_size * 1), 0)
position_ids = np.tile(position_ids, (bs, 1)).astype(np.int32)
truth_list = np.zeros(bs, dtype=np.int32)
for i in range(bs):
truth_list[i] = truth[i]
return input_ids, attention_mask, position_ids, truth_list
def _load_dataset(dataset_path, batch_size, rank_size=None, rank_id=None, shuffle=True, drop_remainder=True,
is_training=True):
"""Loader for data."""
ds.config.set_seed(1)
data = ds.MindDataset(dataset_file=dataset_path,
columns_list=["truth", "input_ids", "loss_mask", "labels", "size"],
shuffle=shuffle)
type_cast_op = C.TypeCast(mstype.float32)
type_cast_op_int = C.TypeCast(mstype.int32)
data = data.map(input_columns="input_ids", operations=type_cast_op_int)
data = data.map(input_columns="labels", operations=type_cast_op_int)
data = data.map(input_columns="loss_mask", operations=type_cast_op)
if is_training:
data = data.batch(batch_size,
per_batch_map=collate,
input_columns=["truth", "input_ids"],
output_columns=["input_ids", "attention_mask", "position_ids", "truth"],
column_order=["input_ids", "attention_mask", "position_ids", "loss_mask", "labels"],
num_parallel_workers=4,
drop_remainder=drop_remainder)
else:
data = data.batch(batch_size,
per_batch_map=collate,
input_columns=["truth", "input_ids"],
output_columns=["input_ids", "attention_mask", "position_ids", "truth"],
column_order=["input_ids", "attention_mask", "position_ids", "loss_mask", "labels", "truth"],
num_parallel_workers=4,
drop_remainder=drop_remainder)
return data
def load_dataset(dataset, batch_size,
rank_size=None, rank_id=None,
shuffle=True,
drop_remainder=True,
is_training=True):
"""
Load dataset.
Args:
dataset (class): Dataset.
batch_size (int): Batch size.
rank_size (int): Rank size.
rank_id (int): Rank id.
shuffle (bool): Whether shuffle dataset.
drop_remainder (bool): Determines whether or not to drop the last possibly incomplete batch.
is_training (bool): Whether training mode.
Returns:
Dataset, dataset instance.
"""
return _load_dataset(dataset,
batch_size, rank_size=rank_size,
rank_id=rank_id, shuffle=shuffle,
drop_remainder=drop_remainder,
is_training=is_training)
def _build_training_pipeline(datasets, pretrain_ckpt_path, config_train):
"""
Building training pipeline
"""
net_with_loss = CPMWithLoss(batch_size=config_train.batch_size,
seq_length=config_train.seq_length,
vocab_size=config_train.vocab_size,
hidden_size=config_train.hidden_size,
config=config_train,
num_hidden_layers=config_train.num_hidden_layers,
num_attention_heads=config_train.num_attention_heads)
net_with_loss = VirtualDatasetOneInputCell(net_with_loss)
param_dict = load_checkpoint(pretrain_ckpt_path)
can_be_loaded = {}
for name, _ in param_dict.items():
if 'cpm_model.' not in name:
can_be_loaded['cpm_model.' + name] = param_dict[name]
else:
can_be_loaded[name] = param_dict[name]
load_param_into_net(net_with_loss, parameter_dict=can_be_loaded)
print("------->Load pretrained parameter successfully<------------")
steps_per_epoch = datasets.get_dataset_size()
print("++++++Dataset size= ", steps_per_epoch, flush=True)
print("++++++MP= ", str(config_train.mp), flush=True)
print("++++++DP= ", str(config_train.dp), flush=True)
print("++++++Global_batch_size= ", str(config_train.batch_size), flush=True)
lr_schedule = CPMLearningRate(learning_rate=config_train.lr,
warmup_steps=int(steps_per_epoch * config_train.epoch * config_train.warmup_steps),
end_steps=steps_per_epoch * config_train.epoch)
params = net_with_loss.trainable_params()
decay_filter = lambda x: 'layernorm' not in x.name.lower() and "bias" not in x.name.lower()
decay_params = list(filter(decay_filter, params))
other_params = list(filter(lambda x: not decay_filter(x), params))
group_params = [{'params': decay_params, 'weight_decay': config_train.weight_decay},
{'params': other_params, 'weight_decay': 0.0},
{'order_params': params}]
optimizer = AdamWeightDecay(group_params, lr_schedule, eps=config_train.eps, beta1=0.9, beta2=0.95)
callback_size = config_train.grad_accumulation_step if config_train.grad_accumulation_step > 1 \
else config_train.sink_size
actual_epoch_num = int(config_train.epoch * steps_per_epoch // callback_size)
print("++++++actual_epoch_num= ", str(actual_epoch_num), flush=True)
if config_train.grad_accumulation_step > 1:
callback = [TimeCallBack(), LossCallBack(steps_per_epoch)]
else:
callback = [TimeMonitor(), LossCallBack(steps_per_epoch)]
ckpt_config = CheckpointConfig(save_checkpoint_steps=steps_per_epoch,
integrated_save=False,
keep_checkpoint_max=config_train.epoch)
ckpt_model = ModelCheckpoint(prefix='cpm_rank_{}'.format(os.getenv("RANK_ID")),
directory=os.path.join('./', 'ckpt_rank_{}'.format(os.getenv("RANK_ID"))),
config=ckpt_config)
callback.append(ckpt_model)
dynamic_loss_cale = DynamicLossScaleUpdateCell(loss_scale_value=math.pow(2, 32),
scale_factor=2,
scale_window=1000)
print(dynamic_loss_cale)
print(" | Start pre-training job.")
if config_train.grad_accumulation_step > 1:
cpm_with_grads = CPMTrainAccuStepsWithLossScaleCell(net_with_loss, optimizer=optimizer,
scale_update_cell=dynamic_loss_cale)
model = Model_CPM(cpm_with_grads)
model.train(config_train.epoch, datasets, callbacks=callback,
dataset_sink_mode=True)
else:
cpm_with_grads = CPMTrainOneStepWithLossScaleCell(net_with_loss, optimizer, dynamic_loss_cale)
model = Model(cpm_with_grads)
model.train(epoch=actual_epoch_num,
train_dataset=datasets,
callbacks=callback,
sink_size=callback_size,
dataset_sink_mode=True)
def set_parallel_env(config_train):
r"""
Parallel environment.
"""
context.reset_auto_parallel_context()
MultiAscend.init()
context.set_auto_parallel_context(parallel_mode=ParallelMode.SEMI_AUTO_PARALLEL,
device_num=MultiAscend.get_group_size(),
gradients_mean=True,
grad_accumulation_step=config_train.grad_accumulation_step,
full_batch=True)
context.set_auto_parallel_context(enable_parallel_optimizer=True)
context.set_auto_parallel_context(strategy_ckpt_save_file='./train_strategy.ckpt')
set_algo_parameters(elementwise_op_strategy_follow=True)
def train_single(input_file, pretrain_ckpt_path, config_train):
"""
Training on single device
"""
print("Staring training on single device")
preprocessed_data = load_dataset(dataset=input_file,
batch_size=config_train.batch_size)
_build_training_pipeline(preprocessed_data, pretrain_ckpt_path, config_train)
def train_paralle(input_file, pretrain_ckpt_path, config_train):
"""
Training on multi device
"""
set_parallel_env(config_train)
print("Staring training on multiple device")
processed_data = load_dataset(dataset=input_file,
batch_size=config_train.batch_size,
rank_size=MultiAscend.get_group_size(),
rank_id=MultiAscend.get_rank())
_build_training_pipeline(processed_data, pretrain_ckpt_path, config_train)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="CPM training.")
parser.add_argument("--dataset", type=str, default="", help="CPM dataset path")
parser.add_argument("--pretrain_ckpt_path", type=str, default="",
help="Load the checkpoint file path for train.")
parser.add_argument("--multi_machine", type=ast.literal_eval, default=False, help='distributed training')
args = parser.parse_args()
if args.multi_machine:
print("Training on multiple machines")
train_paralle(args.dataset, args.pretrain_ckpt_path, config_train_32p)
else:
print("Training on single machine and using 8 cards.")
train_paralle(args.dataset, args.pretrain_ckpt_path, config_train_8p)

View File

@ -0,0 +1,289 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Zero-shot."""
import os
import ast
import argparse
import time
import numpy as np
from mindspore import context, load_distributed_checkpoint
import mindspore.nn as nn
import mindspore.dataset as ds
from mindspore.common.tensor import Tensor
from mindspore.train.model import Model
import mindspore.common.dtype as mstype
from mindspore.train.serialization import load_checkpoint, load_param_into_net
from mindspore.communication import management as MultiAscend
from mindspore.context import ParallelMode
from mindspore.parallel import set_algo_parameters
from src.cpm import CPMModel
from src.cpm_train import VirtualDatasetOneInputCell
from src.cpm_loss import Cross_entropy
from src.config import config_zero_shot_standalone, config_zero_shot_distrubute
from eval import create_ckpt_file_list
device_id = int(os.getenv("DEVICE_ID"))
context.set_context(mode=context.GRAPH_MODE,
save_graphs=False,
device_target="Ascend",
device_id=device_id)
class CPMForInfer(nn.Cell):
"""
Encapsulation class of CPM network infer.
Args:
network (nn.Cell): CPM model.
batch_size (int): Batch size of input dataset.
seq_length (int): Length of input tensor sequence.
vocab_size (int): Size of the dictionary of embeddings.
config: The config of networks.
Returns:
Tensor, losses.
"""
def __init__(self, network, batch_size, seq_length, vocab_size, config):
super(CPMForInfer, self).__init__(auto_prefix=False)
self.network = network
self.batch_size = batch_size
self.seq_length = seq_length
self.vocab_size = vocab_size
self.loss_net = Cross_entropy(batch_size=self.batch_size,
seq_length=self.seq_length,
vocab_size=self.vocab_size,
config=config)
def construct(self, input_ids, target, loss_mask):
"""Defines the computation performed."""
logist = self.network(input_ids)
loss = self.loss_net(logist, target, loss_mask)
return loss
def collate(sid, cid, input_ids, BatchInfo):
"""Collate operation for dataset."""
bs = len(sid)
max_size = np.size(input_ids, 1)
attn_mask = np.tril(np.ones(shape=(max_size, max_size),))
attention_mask = np.expand_dims(attn_mask, 0)
attention_mask = np.tile(attention_mask, (bs, 1, 1))
position_ids = np.expand_dims(np.arange(max_size * 1), 0)
position_ids = np.tile(position_ids, (bs, 1))
sids_list = np.zeros(bs, dtype=np.int64)
cids_list = np.zeros(bs, dtype=np.int64)
for i in range(bs):
sids_list[i] = sid[i]
cids_list[i] = cid[i]
return input_ids, attention_mask, position_ids, sids_list, cids_list
def _load_dataset(dataset_path, batch_size, rank_size=None, rank_id=None, shuffle=True, drop_remainder=True):
"""Loader for data."""
data = ds.MindDataset(dataset_file=dataset_path,
columns_list=["sid", "cid", "input_ids", "loss_mask", "labels", "size"],
shuffle=shuffle,
num_shards=rank_size,
shard_id=rank_id,
)
data = data.batch(batch_size,
num_parallel_workers=4,
drop_remainder=drop_remainder)
return data
def load_dataset(dataset, batch_size,
rank_size=None, rank_id=None,
shuffle=True,
drop_remainder=True):
"""
Load dataset.
Args:
dataset (class): Dataset.
batch_size (int): Batch size.
rank_size (int): Rank size.
rank_id (int): Rank index.
shuffle (bool): Whether shuffle dataset.
drop_remainder (bool): Determines whether or not to drop the last possibly incomplete batch.
Returns:
Dataset, dataset instance.
"""
return _load_dataset(dataset,
batch_size,
shuffle=shuffle,
drop_remainder=drop_remainder)
class CPM_LAYER(nn.Cell):
"""
CPM model training with loss function.
"""
def __init__(self, config_eval):
super(CPM_LAYER, self).__init__()
self.cpm_model = CPMModel(batch_size=config_eval.batch_size,
seq_length=config_eval.seq_length,
vocab_size=config_eval.vocab_size,
hidden_size=config_eval.hidden_size,
num_hidden_layers=config_eval.num_hidden_layers,
num_attention_heads=config_eval.num_attention_heads,
config=config_eval)
def construct(self, input_ids, position_ids=None, attention_mask=None):
output = self.cpm_model(input_ids, position_ids, attention_mask)
return output
def run_eval(args, config_eval, ckpt_file_list=None):
"""
Building infer pipeline
"""
truth_labels = np.loadtxt(args.truth_labels_path)
if args.distribute:
dataset = load_dataset(args.dataset, config_eval.batch_size,
rank_size=MultiAscend.get_group_size(),
rank_id=MultiAscend.get_rank(),
drop_remainder=False,
shuffle=False)
else:
dataset = load_dataset(args.dataset,
config_eval.batch_size,
drop_remainder=False,
shuffle=False)
cpm_model = CPM_LAYER(config_eval)
if args.distribute:
cpm_model = VirtualDatasetOneInputCell(cpm_model)
if not args.has_train_strategy:
weights = load_checkpoint(args.ckpt_path_doc)
can_be_loaded = {}
print("+++++++loading weights without train_strategy+++++")
for name, _ in weights.items():
if 'cpm_model.' not in name:
can_be_loaded['cpm_model.' + name] = weights[name]
else:
can_be_loaded[name] = weights[name]
load_param_into_net(cpm_model, parameter_dict=can_be_loaded)
infer_net = CPMForInfer(network=cpm_model,
batch_size=config_eval.batch_size,
seq_length=config_eval.seq_length,
vocab_size=config_eval.vocab_size,
config=config_eval)
model = Model(infer_net)
if args.has_train_strategy and not args.distribute:
load_distributed_checkpoint(infer_net, ckpt_file_list, None)
if args.has_train_strategy and args.distribute:
fake_input_ids = Tensor(np.ones((config_eval.batch_size, config_eval.seq_length)), mstype.int64)
fake_target = Tensor(np.random.randint(0, 10, [config_eval.batch_size, config_eval.seq_length]), mstype.int64)
fake_loss_mask = Tensor(np.random.randn(config_eval.batch_size, config_eval.seq_length), mstype.float16)
predict_layout = model.infer_predict_layout(fake_input_ids,
fake_target,
fake_loss_mask)
print("Start to load distributed checkpoint with train strategy.", flush=True)
load_distributed_checkpoint(infer_net, ckpt_file_list, predict_layout)
all_sids = []
all_losses = []
all_cids = []
steps_per_epoch = dataset.get_dataset_size()
print("++++++Dataset size", steps_per_epoch, flush=True)
for batch in dataset.create_dict_iterator(output_numpy=True, num_epochs=1):
input_ids = Tensor(batch['input_ids'], mstype.int64)
target = Tensor(batch['labels'], mstype.int64)
loss_mask = Tensor(batch['loss_mask'], mstype.float16)
sids = batch['sid']
cids = batch['cid']
loss = model.predict(input_ids,
target, loss_mask)
all_losses.append(loss.asnumpy())
all_cids.append(cids)
all_sids.append(sids)
print("+++++ ", int(round(time.time() * 1000)))
all_losses = np.stack(all_losses).reshape(-1)
all_sids = np.stack(all_sids).reshape(-1)
all_cids = np.stack(all_cids).reshape(-1)
print("++++ all_losses= \n", all_losses)
preds = [[] for _ in truth_labels]
for sid, cid, loss in zip(all_sids, all_cids, all_losses):
preds[sid].append((cid, loss))
preds = [min(p, key=lambda x: x[1])[0] for p in preds if len(p) > 0]
result = sum([int(p == l) for p, l in zip(preds, truth_labels)]) / len(truth_labels)
print("RESULT: ", result)
def set_parallel_env():
r"""
Parallel environment.
"""
context.reset_auto_parallel_context()
MultiAscend.init()
context.set_auto_parallel_context(parallel_mode=ParallelMode.SEMI_AUTO_PARALLEL,
device_num=MultiAscend.get_group_size(),
gradients_mean=True,
full_batch=True)
set_algo_parameters(elementwise_op_strategy_follow=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="CPM inference")
parser.add_argument('--dataset', type=str, default="", help="dataset path.")
parser.add_argument('--truth_labels_path', type=str, default="", help="truth_labels path.")
parser.add_argument('--ckpt_path_doc', type=str, default="", help="checkpoint path doc or checkpoint path.")
parser.add_argument("--distribute", type=ast.literal_eval, default=False, help='Whether distributed evaluation'
' with model parallel.')
parser.add_argument("--has_train_strategy", type=ast.literal_eval, default=False,
help='Whether the loaded checkpoints have distributed training strategy.')
parser.add_argument('--ckpt_partition', type=int, default=1, help="Number of checkpoint partition.")
args_parse = parser.parse_args()
ckpt_file_list_test = None
if args_parse.has_train_strategy:
# Get the checkpoint with train strategy.
train_strategy_list = create_ckpt_file_list(args_parse, train_strategy="train_strategy.ckpt")
context.set_auto_parallel_context(
strategy_ckpt_load_file=train_strategy_list[0]
)
ckpt_file_list_test = create_ckpt_file_list(args_parse)
print("Get checkpoint file lists++++", ckpt_file_list_test, flush=True)
if args_parse.distribute:
set_parallel_env()
print("Staring evaluating on 2 devices with model parallel.")
run_eval(args_parse, config_zero_shot_distrubute, ckpt_file_list_test)
else:
print("Staring evaluating on 1 device without model parallel.")
run_eval(args_parse, config_zero_shot_standalone, ckpt_file_list_test)