diff --git a/model_zoo/official/nlp/transformer/create_data.py b/model_zoo/official/nlp/transformer/create_data.py
index d8c4baeecba..4b1f64efd58 100644
--- a/model_zoo/official/nlp/transformer/create_data.py
+++ b/model_zoo/official/nlp/transformer/create_data.py
@@ -18,12 +18,11 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
-import argparse
-import ast
import collections
import logging
import numpy as np
import src.tokenization as tokenization
+from src.model_utils.config import config
from mindspore.mindrecord import FileWriter
class SampleInstance():
@@ -121,33 +120,17 @@ def create_training_instance(source_words, target_words, max_seq_length, clip_to
return instance
def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("--input_file", type=str, required=True,
- help='Input raw text file (or comma-separated list of files).')
- parser.add_argument("--output_file", type=str, required=True, help='Output MindRecord file.')
- parser.add_argument("--num_splits", type=int, default=16,
- help='The MindRecord file will be split into the number of partition.')
- parser.add_argument("--vocab_file", type=str, required=True,
- help='The vocabulary file that the Transformer model was trained on.')
- parser.add_argument("--clip_to_max_len", type=bool, default=False,
- help='clip sequences to maximum sequence length.')
- parser.add_argument("--max_seq_length", type=int, default=128, help='Maximum sequence length.')
- parser.add_argument("--bucket", type=ast.literal_eval, default=[16, 32, 48, 64, 128],
- help='bucket sequence length')
-
- args = parser.parse_args()
-
- tokenizer = tokenization.WhiteSpaceTokenizer(vocab_file=args.vocab_file)
+ tokenizer = tokenization.WhiteSpaceTokenizer(vocab_file=config.vocab_file)
input_files = []
- for input_pattern in args.input_file.split(","):
+ for input_pattern in config.input_file.split(","):
input_files.append(input_pattern)
logging.info("*** Read from input files ***")
for input_file in input_files:
logging.info(" %s", input_file)
- output_file = args.output_file
+ output_file = config.output_file
logging.info("*** Write to output files ***")
logging.info(" %s", output_file)
@@ -155,7 +138,7 @@ def main():
total_read = 0
feature_dict = {}
- for i in args.bucket:
+ for i in config.bucket:
feature_dict[i] = []
for input_file in input_files:
@@ -174,17 +157,17 @@ def main():
source_tokens = tokenizer.tokenize(source_line)
target_tokens = tokenizer.tokenize(target_line)
- if len(source_tokens) >= args.max_seq_length or len(target_tokens) >= args.max_seq_length:
+ if len(source_tokens) >= config.max_seq_length or len(target_tokens) >= config.max_seq_length:
logging.info("ignore long sentence!")
continue
- instance = create_training_instance(source_tokens, target_tokens, args.max_seq_length,
- clip_to_max_len=args.clip_to_max_len)
+ instance = create_training_instance(source_tokens, target_tokens, config.max_seq_length,
+ clip_to_max_len=config.clip_to_max_len)
if instance is None:
continue
- features, seq_max_bucket_length = get_instance_features(instance, tokenizer, args.max_seq_length,
- args.bucket)
+ features, seq_max_bucket_length = get_instance_features(instance, tokenizer, config.max_seq_length,
+ config.bucket)
for key in feature_dict:
if key == seq_max_bucket_length:
feature_dict[key].append(features)
@@ -200,12 +183,12 @@ def main():
feature = features[feature_name]
logging.info("%s: %s", feature_name, feature)
- for i in args.bucket:
- if args.num_splits == 1:
+ for i in config.bucket:
+ if config.num_splits == 1:
output_file_name = output_file
else:
output_file_name = output_file + '_' + str(i) + '_'
- writer = FileWriter(output_file_name, args.num_splits)
+ writer = FileWriter(output_file_name, config.num_splits)
data_schema = {"source_sos_ids": {"type": "int64", "shape": [-1]},
"source_sos_mask": {"type": "int64", "shape": [-1]},
"source_eos_ids": {"type": "int64", "shape": [-1]},
diff --git a/model_zoo/official/nlp/transformer/default_config.yaml b/model_zoo/official/nlp/transformer/default_config.yaml
new file mode 100644
index 00000000000..511ea602504
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/default_config.yaml
@@ -0,0 +1,133 @@
+# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing)
+enable_modelarts: False
+data_url: ""
+train_url: ""
+checkpoint_url: ""
+data_path: "/cache/data"
+output_path: "/cache/train"
+load_path: "/cache/checkpoint_path"
+checkpoint_path: ''
+device_target: Ascend
+enable_profiling: False
+
+# ==============================================================================
+# config/cfg edict
+transformer_network: 'base'
+init_loss_scale_value: 1024
+scale_factor: 2
+scale_window: 2000
+optimizer: 'Adam'
+optimizer_adam_beta2: 0.997
+# lr_schedule: edict({'learning_rate': 2.0, 'warmup_steps': 8000, 'start_decay_step': 16000, 'min_lr': 0.0,})
+
+# transformer_net_cfg
+batch_size: 96
+seq_length: 128
+vocab_size: 36560
+hidden_size: 512
+num_hidden_layers: 6
+num_attention_heads: 8
+intermediate_size: 2048
+hidden_act: "relu"
+hidden_dropout_prob: 0.2
+attention_probs_dropout_prob: 0.2
+max_position_embeddings: 128
+initializer_range: 0.02
+label_smoothing: 0.1
+dtype: mstype.float32
+compute_type: mstype.float16
+
+#eval_config/cfg edict
+data_file: '/cache/data'
+model_file: './transformer/transformer_trained.ckpt'
+output_file: './output_eval.txt'
+
+# transformer_net_cfg
+batch_size_ev: 1
+hidden_dropout_prob_ev: 0.0
+attention_probs_dropout_prob_ev: 0.0
+beam_width: 4
+max_decode_length: 80
+length_penalty_weight: 1.0
+
+# ==============================================================================
+# train.py / Argparse init.
+distribute: "false"
+epoch_size: 52
+device_id: 0
+device_num: 1
+enable_lossscale: "true"
+do_shuffle: "true"
+enable_save_ckpt: "true"
+save_checkpoint_steps: 2500
+save_checkpoint_num: 30
+save_checkpoint_path: "./"
+bucket_boundaries: [16, 32, 48, 64, 128]
+accumulation_steps: 1
+
+# export.py /eval_config - transformer export
+file_name: "transformer"
+file_format: 'AIR'
+
+#'postprocess / from eval_config'
+result_dir: "./result_Files"
+
+#'preprocess / from eval_config'
+result_path: "./preprocess_Result/"
+
+# src/process_output.py "recore nbest with smoothed sentence-level bleu."
+vocab_file: ""
+
+# create_data.py
+input_file: ''
+num_splits: 16
+clip_to_max_len: False
+max_seq_length: 128
+bucket: [16, 32, 48, 64, 128]
+
+---
+# Config description for each option
+enable_modelarts: 'Whether training on modelarts, default: False'
+data_url: 'Dataset url for obs'
+train_url: 'Training output url for obs'
+data_path: 'Dataset path for local, it is better to use absolute path'
+output_path: 'Training output path for local'
+ann_file: 'Ann file, default is val.json.'
+
+device_target: "device where the code will be implemented, default is Ascend"
+checkpoint_path: "Checkpoint file path"
+data_file: '/your/path/evaluation.mindrecord'
+model_file: '/your/path/checkpoint_file'
+output_file: './output_eval.txt'
+
+distribute: "Run distribute, default is false."
+epoch_size: "Epoch size, default is 52."
+device_id: "Device id, default is 0."
+device_num: "Use device nums, default is 1."
+enable_lossscale: "Use lossscale or not, default is true."
+do_shuffle: "Enable shuffle for dataset, default is true."
+enable_save_ckpt: "Enable save checkpoint, default is true."
+save_checkpoint_steps: "Save checkpoint steps, default is 2500."
+save_checkpoint_num: "Save checkpoint numbers, default is 30."
+save_checkpoint_path: "Save checkpoint file path"
+bucket_boundaries: "sequence length for different bucket"
+accumulation_steps: "Gradient accumulation steps, default is 1."
+
+file_name: "output file name."
+file_format: 'file format'
+result_dir: "./result_Files"
+result_path: "./preprocess_Result/"
+vocab_file: "vocab file path."
+input_file: 'Input raw text file (or comma-separated list of files).'
+num_splits: 'The MindRecord file will be split into the number of partition.'
+clip_to_max_len: 'clip sequences to maximum sequence length.'
+max_seq_length: 'Maximum sequence length.'
+bucket: 'bucket sequence length'
+
+---
+device_target: ["Ascend", "GPU", "CPU"]
+file_format: ["AIR", "ONNX", "MINDIR"]
+distribute: ['true', 'false']
+enable_lossscale: ['true', 'false']
+do_shuffle: ['true', 'false']
+enable_save_ckpt: ['true', 'false']
diff --git a/model_zoo/official/nlp/transformer/default_config_large.yaml b/model_zoo/official/nlp/transformer/default_config_large.yaml
new file mode 100644
index 00000000000..82d75585950
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/default_config_large.yaml
@@ -0,0 +1,134 @@
+# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing)
+enable_modelarts: False
+data_url: ""
+train_url: ""
+checkpoint_url: ""
+data_path: "/cache/data"
+output_path: "/cache/train"
+load_path: "/cache/checkpoint_path"
+checkpoint_path: ''
+device_target: Ascend
+enable_profiling: False
+
+# ==============================================================================
+# config/cfg edict
+transformer_network: 'large'
+init_loss_scale_value: 1024
+scale_factor: 2
+scale_window: 2000
+optimizer: 'Adam'
+optimizer_adam_beta2: 0.997
+#lr_schedule: edict({'learning_rate': 2.0, 'warmup_steps': 8000, 'start_decay_step': 16000, 'min_lr': 0.0,})
+
+# transformer_net_cfg
+batch_size: 96
+seq_length: 128
+vocab_size: 36560
+hidden_size: 1024
+num_hidden_layers: 6
+num_attention_heads: 16
+intermediate_size: 4096
+hidden_act: "relu"
+hidden_dropout_prob: 0.2
+attention_probs_dropout_prob: 0.2
+max_position_embeddings: 128
+initializer_range: 0.02
+label_smoothing: 0.1
+dtype: mstype.float32
+compute_type: mstype.float16
+
+#eval_config/cfg edict
+data_file: '/cache/data'
+model_file: './transformer/transformer_trained.ckpt'
+output_file: './output_eval.txt'
+
+# transformer_net_cfg
+batch_size_ev: 1
+hidden_dropout_prob_ev: 0.0
+attention_probs_dropout_prob_ev: 0.0
+beam_width: 4
+max_decode_length: 80
+length_penalty_weight: 1.0
+
+# ==============================================================================
+# train.py / Argparse init.
+distribute: "false"
+epoch_size: 52
+device_id: 0
+device_num: 1
+enable_lossscale: "true"
+do_shuffle: "true"
+enable_save_ckpt: "true"
+save_checkpoint_steps: 2500
+save_checkpoint_num: 30
+save_checkpoint_path: "./"
+bucket_boundaries: [16, 32, 48, 64, 128]
+accumulation_steps: 1
+
+# export.py /eval_config - transformer export
+file_name: "transformer"
+file_format: 'AIR'
+
+#'postprocess / from eval_config'
+result_dir: "./result_Files"
+
+#'preprocess / from eval_config'
+result_path: "./preprocess_Result/"
+
+# src/process_output.py "recore nbest with smoothed sentence-level bleu."
+vocab_file: ""
+
+# create_data.py
+input_file: ''
+num_splits: 16
+clip_to_max_len: False
+max_seq_length: 128
+bucket: [16, 32, 48, 64, 128]
+
+
+---
+# Config description for each option
+enable_modelarts: 'Whether training on modelarts, default: False'
+data_url: 'Dataset url for obs'
+train_url: 'Training output url for obs'
+data_path: 'Dataset path for local, it is better to use absolute path'
+output_path: 'Training output path for local'
+ann_file: 'Ann file, default is val.json.'
+
+device_target: "device where the code will be implemented, default is Ascend"
+checkpoint_path: "Checkpoint file path"
+data_file: '/your/path/evaluation.mindrecord'
+model_file: '/your/path/checkpoint_file'
+output_file: './output_eval.txt'
+
+distribute: "Run distribute, default is false."
+epoch_size: "Epoch size, default is 52."
+device_id: "Device id, default is 0."
+device_num: "Use device nums, default is 1."
+enable_lossscale: "Use lossscale or not, default is true."
+do_shuffle: "Enable shuffle for dataset, default is true."
+enable_save_ckpt: "Enable save checkpoint, default is true."
+save_checkpoint_steps: "Save checkpoint steps, default is 2500."
+save_checkpoint_num: "Save checkpoint numbers, default is 30."
+save_checkpoint_path: "Save checkpoint file path"
+bucket_boundaries: "sequence length for different bucket"
+accumulation_steps: "Gradient accumulation steps, default is 1."
+
+file_name: "output file name."
+file_format: 'file format'
+result_dir: "./result_Files"
+result_path: "./preprocess_Result/"
+vocab_file: "vocab file path."
+input_file: 'Input raw text file (or comma-separated list of files).'
+num_splits: 'The MindRecord file will be split into the number of partition.'
+clip_to_max_len: 'clip sequences to maximum sequence length.'
+max_seq_length: 'Maximum sequence length.'
+bucket: 'bucket sequence length'
+
+---
+device_target: ["Ascend", "GPU", "CPU"]
+file_format: ["AIR", "ONNX", "MINDIR"]
+distribute: ['true', 'false']
+enable_lossscale: ['true', 'false']
+do_shuffle: ['true', 'false']
+enable_save_ckpt: ['true', 'false']
diff --git a/model_zoo/official/nlp/transformer/default_config_large_gpu.yaml b/model_zoo/official/nlp/transformer/default_config_large_gpu.yaml
new file mode 100644
index 00000000000..b36ae8b7787
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/default_config_large_gpu.yaml
@@ -0,0 +1,134 @@
+# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing)
+enable_modelarts: False
+data_url: ""
+train_url: ""
+checkpoint_url: ""
+data_path: "/cache/data"
+output_path: "/cache/train"
+load_path: "/cache/checkpoint_path"
+checkpoint_path: ''
+device_target: GPU
+enable_profiling: False
+
+# ==============================================================================
+# config/cfg edict
+transformer_network: 'large'
+init_loss_scale_value: 1024
+scale_factor: 2
+scale_window: 2000
+optimizer: 'Adam'
+optimizer_adam_beta2: 0.997
+#lr_schedule: edict({'learning_rate': 2.0, 'warmup_steps': 8000, 'start_decay_step': 16000, 'min_lr': 0.0,})
+
+# transformer_net_cfg_gpu
+batch_size: 32
+seq_length: 128
+vocab_size: 36560
+hidden_size: 1024
+num_hidden_layers: 6
+num_attention_heads: 16
+intermediate_size: 4096
+hidden_act: "relu"
+hidden_dropout_prob: 0.2
+attention_probs_dropout_prob: 0.2
+max_position_embeddings: 128
+initializer_range: 0.02
+label_smoothing: 0.1
+dtype: mstype.float32
+compute_type: mstype.float16
+
+#eval_config/cfg edict
+data_file: '/cache/data'
+model_file: './transformer/transformer_trained.ckpt'
+output_file: './output_eval.txt'
+
+# transformer_net_cfg
+batch_size_ev: 1
+hidden_dropout_prob_ev: 0.0
+attention_probs_dropout_prob_ev: 0.0
+beam_width: 4
+max_decode_length: 80
+length_penalty_weight: 1.0
+
+# ==============================================================================
+# train.py / Argparse init.
+distribute: "false"
+epoch_size: 52
+device_id: 0
+device_num: 1
+enable_lossscale: "true"
+do_shuffle: "true"
+enable_save_ckpt: "true"
+save_checkpoint_steps: 2500
+save_checkpoint_num: 30
+save_checkpoint_path: "./"
+bucket_boundaries: [16, 32, 48, 64, 128]
+accumulation_steps: 1
+
+# export.py /eval_config - transformer export
+file_name: "transformer"
+file_format: 'AIR'
+
+#'postprocess / from eval_config'
+result_dir: "./result_Files"
+
+#'preprocess / from eval_config'
+result_path: "./preprocess_Result/"
+
+# src/process_output.py "recore nbest with smoothed sentence-level bleu."
+vocab_file: ""
+
+# create_data.py
+input_file: ''
+num_splits: 16
+clip_to_max_len: False
+max_seq_length: 128
+bucket: [16, 32, 48, 64, 128]
+
+
+---
+# Config description for each option
+enable_modelarts: 'Whether training on modelarts, default: False'
+data_url: 'Dataset url for obs'
+train_url: 'Training output url for obs'
+data_path: 'Dataset path for local, it is better to use absolute path'
+output_path: 'Training output path for local'
+ann_file: 'Ann file, default is val.json.'
+
+device_target: "device where the code will be implemented, default is Ascend"
+checkpoint_path: "Checkpoint file path"
+data_file: '/your/path/evaluation.mindrecord'
+model_file: '/your/path/checkpoint_file'
+output_file: './output_eval.txt'
+
+distribute: "Run distribute, default is false."
+epoch_size: "Epoch size, default is 52."
+device_id: "Device id, default is 0."
+device_num: "Use device nums, default is 1."
+enable_lossscale: "Use lossscale or not, default is true."
+do_shuffle: "Enable shuffle for dataset, default is true."
+enable_save_ckpt: "Enable save checkpoint, default is true."
+save_checkpoint_steps: "Save checkpoint steps, default is 2500."
+save_checkpoint_num: "Save checkpoint numbers, default is 30."
+save_checkpoint_path: "Save checkpoint file path"
+bucket_boundaries: "sequence length for different bucket"
+accumulation_steps: "Gradient accumulation steps, default is 1."
+
+file_name: "output file name."
+file_format: 'file format'
+result_dir: "./result_Files"
+result_path: "./preprocess_Result/"
+vocab_file: "vocab file path."
+input_file: 'Input raw text file (or comma-separated list of files).'
+num_splits: 'The MindRecord file will be split into the number of partition.'
+clip_to_max_len: 'clip sequences to maximum sequence length.'
+max_seq_length: 'Maximum sequence length.'
+bucket: 'bucket sequence length'
+
+---
+device_target: ["Ascend", "GPU", "CPU"]
+file_format: ["AIR", "ONNX", "MINDIR"]
+distribute: ['true', 'false']
+enable_lossscale: ['true', 'false']
+do_shuffle: ['true', 'false']
+enable_save_ckpt: ['true', 'false']
diff --git a/model_zoo/official/nlp/transformer/eval.py b/model_zoo/official/nlp/transformer/eval.py
index 825f3c67653..8c6b1c63f19 100644
--- a/model_zoo/official/nlp/transformer/eval.py
+++ b/model_zoo/official/nlp/transformer/eval.py
@@ -14,7 +14,7 @@
# ============================================================================
"""Transformer evaluation script."""
-import argparse
+import os
import numpy as np
import mindspore.nn as nn
@@ -28,8 +28,15 @@ import mindspore.dataset.transforms.c_transforms as deC
from mindspore import context
from src.transformer_model import TransformerModel
-from src.eval_config import cfg, transformer_net_cfg
+from src.model_utils.config import config
+from src.model_utils.moxing_adapter import moxing_wrapper
+from src.model_utils.device_adapter import get_device_id
+config.dtype = mstype.float32
+config.compute_type = mstype.float16
+config.batch_size = config.batch_size_ev
+config.hidden_dropout_prob = config.hidden_dropout_prob_ev
+config.attention_probs_dropout_prob = config.attention_probs_dropout_prob_ev
def load_test_data(batch_size=1, data_file=None):
"""
@@ -57,7 +64,6 @@ class TransformerInferCell(nn.Cell):
"""
Encapsulation class of transformer network infer.
"""
-
def __init__(self, network):
super(TransformerInferCell, self).__init__(auto_prefix=False)
self.network = network
@@ -98,23 +104,22 @@ def load_weights(model_path):
return parameter_dict
+def modelarts_pre_process():
+ config.output_file = os.path.join(config.output_path, config.output_file)
+ config.data_file = os.path.join(config.data_file, 'ende-l128-mindrecord_128_00')
+
+@moxing_wrapper(pre_process=modelarts_pre_process)
def run_transformer_eval():
"""
Transformer evaluation.
"""
- parser = argparse.ArgumentParser(description='tranformer')
- parser.add_argument("--device_target", type=str, default="Ascend",
- help="device where the code will be implemented, default is Ascend")
- parser.add_argument('--device_id', type=int, default=0, help='device id of GPU or Ascend, default is 0')
- args = parser.parse_args()
+ context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target, reserve_class_name_in_scope=False,
+ device_id=get_device_id())
- context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target, reserve_class_name_in_scope=False,
- device_id=args.device_id)
+ dataset = load_test_data(batch_size=config.batch_size, data_file=config.data_file)
+ tfm_model = TransformerModel(config=config, is_training=False, use_one_hot_embeddings=False)
- dataset = load_test_data(batch_size=transformer_net_cfg.batch_size, data_file=cfg.data_file)
- tfm_model = TransformerModel(config=transformer_net_cfg, is_training=False, use_one_hot_embeddings=False)
-
- parameter_dict = load_weights(cfg.model_file)
+ parameter_dict = load_weights(config.model_file)
load_param_into_net(tfm_model, parameter_dict)
tfm_infer = TransformerInferCell(tfm_model)
@@ -132,9 +137,9 @@ def run_transformer_eval():
predictions.append(predicted_ids.asnumpy())
# decode and write to file
- f = open(cfg.output_file, 'w')
+ f = open(config.output_file, 'w')
for batch_out in predictions:
- for i in range(transformer_net_cfg.batch_size):
+ for i in range(config.batch_size):
if batch_out.ndim == 3:
batch_out = batch_out[:, 0]
token_ids = [str(x) for x in batch_out[i].tolist()]
diff --git a/model_zoo/official/nlp/transformer/export.py b/model_zoo/official/nlp/transformer/export.py
index 6c0e30f1dcf..6c0f4d72986 100644
--- a/model_zoo/official/nlp/transformer/export.py
+++ b/model_zoo/official/nlp/transformer/export.py
@@ -14,35 +14,41 @@
# ============================================================================
""" export checkpoint file into models"""
-import argparse
import numpy as np
from mindspore import Tensor, context
from mindspore.train.serialization import load_param_into_net, export
from src.transformer_model import TransformerModel
-from src.eval_config import cfg, transformer_net_cfg
+from src.model_utils.config import config
+from src.model_utils.moxing_adapter import moxing_wrapper
+from src.model_utils.device_adapter import get_device_id
from eval import load_weights
-parser = argparse.ArgumentParser(description='transformer export')
-parser.add_argument("--device_id", type=int, default=0, help="Device id")
-parser.add_argument("--file_name", type=str, default="transformer", help="output file name.")
-parser.add_argument('--file_format', type=str, choices=["AIR", "ONNX", "MINDIR"], default='AIR', help='file format')
-parser.add_argument("--device_target", type=str, default="Ascend",
- choices=["Ascend", "GPU", "CPU"], help="device target (default: Ascend)")
-args = parser.parse_args()
-context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target)
-if args.device_target == "Ascend":
- context.set_context(device_id=args.device_id)
+config.batch_size = config.batch_size_ev
+config.hidden_dropout_prob = config.hidden_dropout_prob_ev
+config.attention_probs_dropout_prob = config.attention_probs_dropout_prob_ev
-if __name__ == '__main__':
- tfm_model = TransformerModel(config=transformer_net_cfg, is_training=False, use_one_hot_embeddings=False)
+context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target)
+if config.device_target == "Ascend":
+ context.set_context(device_id=get_device_id())
- parameter_dict = load_weights(cfg.model_file)
+def modelarts_pre_process():
+ pass
+
+@moxing_wrapper(pre_process=modelarts_pre_process)
+def export_transformer():
+ """ export_transformer """
+ tfm_model = TransformerModel(config=config, is_training=False, use_one_hot_embeddings=False)
+
+ parameter_dict = load_weights(config.model_file)
load_param_into_net(tfm_model, parameter_dict)
- source_ids = Tensor(np.ones((transformer_net_cfg.batch_size, transformer_net_cfg.seq_length)).astype(np.int32))
- source_mask = Tensor(np.ones((transformer_net_cfg.batch_size, transformer_net_cfg.seq_length)).astype(np.int32))
+ source_ids = Tensor(np.ones((config.batch_size, config.seq_length)).astype(np.int32))
+ source_mask = Tensor(np.ones((config.batch_size, config.seq_length)).astype(np.int32))
- export(tfm_model, source_ids, source_mask, file_name=args.file_name, file_format=args.file_format)
+ export(tfm_model, source_ids, source_mask, file_name=config.file_name, file_format=config.file_format)
+
+if __name__ == '__main__':
+ export_transformer()
diff --git a/model_zoo/official/nlp/transformer/postprocess.py b/model_zoo/official/nlp/transformer/postprocess.py
index 93c5f95ceb4..fa98e1020a9 100644
--- a/model_zoo/official/nlp/transformer/postprocess.py
+++ b/model_zoo/official/nlp/transformer/postprocess.py
@@ -15,31 +15,25 @@
"""Transformer evaluation script."""
import os
-import argparse
import numpy as np
+from src.model_utils.config import config
-from src.eval_config import cfg, transformer_net_cfg
-
-parser = argparse.ArgumentParser(description='postprocess')
-parser.add_argument("--result_dir", type=str, default="./result_Files",
- help="infer result path.")
-args = parser.parse_args()
def generate_output():
'''
Generate output.
'''
predictions = []
- file_num = len(os.listdir(args.result_dir))
+ file_num = len(os.listdir(config.result_dir))
for i in range(file_num):
- batch = "transformer_bs_" + str(transformer_net_cfg.batch_size) + "_" + str(i) + "_0.bin"
- pred = np.fromfile(os.path.join(args.result_dir, batch), np.int32)
- predictions.append(pred.reshape(1, 1, transformer_net_cfg.max_decode_length + 1))
+ batch = "transformer_bs_" + str(config.batch_size) + "_" + str(i) + "_0.bin"
+ pred = np.fromfile(os.path.join(config.result_dir, batch), np.int32)
+ predictions.append(pred.reshape(1, 1, config.max_decode_length + 1))
# decode and write to file
- f = open(cfg.output_file, 'w')
+ f = open(config.output_file, 'w')
for batch_out in predictions:
- for i in range(transformer_net_cfg.batch_size):
+ for i in range(config.batch_size):
if batch_out.ndim == 3:
batch_out = batch_out[:, 0]
token_ids = [str(x) for x in batch_out[i].tolist()]
diff --git a/model_zoo/official/nlp/transformer/preprocess.py b/model_zoo/official/nlp/transformer/preprocess.py
index ba163e943cc..3382234877b 100644
--- a/model_zoo/official/nlp/transformer/preprocess.py
+++ b/model_zoo/official/nlp/transformer/preprocess.py
@@ -15,23 +15,16 @@
"""Transformer evaluation script."""
import os
-import argparse
-
-from src.eval_config import cfg, transformer_net_cfg
+from src.model_utils.config import config
from eval import load_test_data
-parser = argparse.ArgumentParser(description='preprocess')
-parser.add_argument("--result_path", type=str, default="./preprocess_Result/",
- help="preprocess result path.")
-args = parser.parse_args()
-
def generate_bin():
'''
Generate bin files.
'''
- dataset = load_test_data(batch_size=transformer_net_cfg.batch_size, data_file=cfg.data_file)
- cur_dir = args.result_path
+ dataset = load_test_data(batch_size=config.batch_size, data_file=config.data_file)
+ cur_dir = config.result_path
source_eos_ids_path = os.path.join(cur_dir, "00_source_eos_ids")
source_eos_mask_path = os.path.join(cur_dir, "01_source_eos_mask")
@@ -41,7 +34,7 @@ def generate_bin():
if not os.path.isdir(source_eos_mask_path):
os.makedirs(source_eos_mask_path)
- batch_size = transformer_net_cfg.batch_size
+ batch_size = config.batch_size
for i, data in enumerate(dataset.create_dict_iterator(output_numpy=True, num_epochs=1)):
file_name = "transformer_bs_" + str(batch_size) + "_" + str(i) + ".bin"
diff --git a/model_zoo/official/nlp/transformer/scripts/process_output.sh b/model_zoo/official/nlp/transformer/scripts/process_output.sh
index 2ada1d13593..08f39229c13 100644
--- a/model_zoo/official/nlp/transformer/scripts/process_output.sh
+++ b/model_zoo/official/nlp/transformer/scripts/process_output.sh
@@ -28,7 +28,7 @@ eval_output=$2
vocab_file=$3
cat $eval_output \
- | python src/process_output.py --vocab_file $vocab_file \
+ | python src/process_output.py --config_path="./default_config_large.yaml" --vocab_file $vocab_file \
| sed 's/@@ //g' > ${eval_output}.processed
perl -ple 's/(\S)-(\S)/$1 #@#-#@# $2/g' < $ref_data | perl ${BASEDIR}/replace-quote.perl > ${ref_data}.forbleu
diff --git a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend.sh b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend.sh
index 4a40c2a2add..c2204c67f0a 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend.sh
@@ -13,11 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
-if [ $# != 4 ] ; then
+if [ $# != 5 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
-echo "sh run_distribute_pretrain.sh DEVICE_NUM EPOCH_SIZE DATA_PATH RANK_TABLE_FILE"
-echo "for example: sh run_distribute_pretrain.sh 8 52 /path/ende-l128-mindrecord00 /path/hccl.json"
+echo "sh run_distribute_pretrain.sh DEVICE_NUM EPOCH_SIZE DATA_PATH RANK_TABLE_FILE CONFIG_PATH"
+echo "for example: sh run_distribute_pretrain.sh 8 52 /path/ende-l128-mindrecord00 /path/hccl.json ./default_config_large.yaml"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
@@ -32,6 +32,7 @@ DATA_PATH=$3
export HCCL_CONNECT_TIMEOUT=600
export RANK_TABLE_FILE=$4
+export CONFIG_PATH=$5
export RANK_SIZE=$1
export HCCL_FLAG=1
export DEPLOY_MODE=0
@@ -43,11 +44,12 @@ do
export GE_USE_STATIC_MEMORY=1
mkdir helper$i
- cp -rf ../src/ ../train.py ./helper$i
+ cp -rf ../src/ ../train.py ../*.yaml ./helper$i
cd ./helper$i || exit
echo "start training for rank $i, device $DEVICE_ID"
env > env.log
python train.py \
+ --config_path=$CONFIG_PATH \
--distribute="true" \
--epoch_size=$EPOCH_SIZE \
--device_id=$DEVICE_ID \
@@ -58,8 +60,7 @@ do
--checkpoint_path="" \
--save_checkpoint_steps=2500 \
--save_checkpoint_num=30 \
- --data_path=$DATA_PATH \
- --bucket_boundaries=[16,32,48,64,128] > log.txt 2>&1 &
+ --data_path=$DATA_PATH > log.txt 2>&1 &
cd ../
done
cd ..
\ No newline at end of file
diff --git a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend_multi_machines.sh b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend_multi_machines.sh
index 5d54de49c62..47f3acdec32 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend_multi_machines.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_ascend_multi_machines.sh
@@ -13,11 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
-if [ $# != 5 ] ; then
+if [ $# != 6 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
-echo "sh run_distribute_train_ascend_multi_machines.sh DEVICE_NUM SERVER_ID EPOCH_SIZE DATA_PATH RANK_TABLE_FILE"
-echo "for example: sh run_distribute_train_ascend_multi_machines.sh 32 0 52 /path/ende-l128-mindrecord00 /path/hccl.json"
+echo "sh run_distribute_train_ascend_multi_machines.sh DEVICE_NUM SERVER_ID EPOCH_SIZE DATA_PATH RANK_TABLE_FILE CONFIG_PATH"
+echo "for example: sh run_distribute_train_ascend_multi_machines.sh 32 0 52 /path/ende-l128-mindrecord00 /path/hccl.json ./default_config_large.yaml"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
@@ -32,6 +32,7 @@ DATA_PATH=$4
export HCCL_CONNECT_TIMEOUT=600
export RANK_TABLE_FILE=$5
+export CONFIG_PATH=$6
export RANK_SIZE=$1
export SERVER_ID=$2
export DEVICE_NUM=8
@@ -47,11 +48,12 @@ do
export GE_USE_STATIC_MEMORY=1
mkdir helper$i
- cp -rf ../src/ ../train.py ./helper$i
+ cp -rf ../src/ ../train.py ../*.yaml ./helper$i
cd ./helper$i || exit
echo "start training for rank $i, device $DEVICE_ID"
env > env.log
python train.py \
+ --config_path=$CONFIG_PATH \
--distribute="true" \
--epoch_size=$EPOCH_SIZE \
--device_id=$DEVICE_ID \
diff --git a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_gpu.sh b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_gpu.sh
index 4e5d0447b8a..ef1337951e6 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_distribute_train_gpu.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_distribute_train_gpu.sh
@@ -13,11 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
-if [ $# != 3 ] ; then
+if [ $# != 4 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
-echo "sh run_distribute_train_gpu.sh DEVICE_NUM EPOCH_SIZE DATA_PATH"
-echo "for example: sh run_distribute_pretrain.sh 8 55 /path/ende-l128-mindrecord00"
+echo "sh run_distribute_train_gpu.sh DEVICE_NUM EPOCH_SIZE DATA_PATH CONFIG_PATH"
+echo "for example: sh run_distribute_pretrain.sh 8 55 /path/ende-l128-mindrecord00 ./default_config_large_gpu.yaml"
echo "It is better to use absolute path."
echo "=============================================================================================================="
exit 1;
@@ -25,16 +25,18 @@ fi
rm -rf run_distribute_train
mkdir run_distribute_train
-cp -rf ./src/ train.py ./run_distribute_train
+cp -rf ./src/ train.py ./*.yaml ./run_distribute_train
cd run_distribute_train || exit
export RANK_SIZE=$1
+export CONFIG_PATH=$4
EPOCH_SIZE=$2
DATA_PATH=$3
echo $RANK_SIZE
mpirun -n $RANK_SIZE \
python train.py \
+ --config_path=$CONFIG_PATH \
--distribute="true" \
--device_target="GPU" \
--epoch_size=$EPOCH_SIZE \
diff --git a/model_zoo/official/nlp/transformer/scripts/run_eval.sh b/model_zoo/official/nlp/transformer/scripts/run_eval.sh
index 9cb375a4c36..8a2d2a1fe2a 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_eval.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_eval.sh
@@ -13,19 +13,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
-if [ $# != 2 ] ; then
+if [ $# != 5 ] ; then
echo "=============================================================================================================="
echo "Please run the script as: "
-echo "sh run_eval.sh DEVICE_TARGET DEVICE_ID"
-echo "for example: sh run_eval.sh Ascend 0"
-echo "Note: set the checkpoint and dataset path in src/eval_config.py"
+echo "sh run_eval.sh DEVICE_TARGET DEVICE_ID MINDRECORD_DATA CKPT_PATH CONFIG_PATH"
+echo "for example: sh run_eval.sh Ascend 0 /your/path/evaluation.mindrecord /your/path/checkpoint_file ./default_config_large_gpu.yaml"
+echo "Note: set the checkpoint and dataset path in default_config.yaml"
echo "=============================================================================================================="
exit 1;
fi
export DEVICE_TARGET=$1
+export CONFIG_PATH=$5
DEVICE_ID=$2
+get_real_path(){
+ if [ "${1:0:1}" == "/" ]; then
+ echo "$1"
+ else
+ echo "$(realpath -m $PWD/$1)"
+ fi
+}
+
+PATH3=$(get_real_path $3)
+PATH4=$(get_real_path $4)
+echo $PATH3
+echo $PATH4
+
python eval.py \
+ --config_path=$CONFIG_PATH \
--device_target=$DEVICE_TARGET \
--device_id=$DEVICE_ID \
+ --data_file=$PATH3 \
+ --model_file=$PATH4 > log_eval.txt 2>&1 &
diff --git a/model_zoo/official/nlp/transformer/scripts/run_infer_310.sh b/model_zoo/official/nlp/transformer/scripts/run_infer_310.sh
index 5f6ded47426..6886030b9ed 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_infer_310.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_infer_310.sh
@@ -66,7 +66,7 @@ function preprocess_data()
rm -rf ./preprocess_Result
fi
mkdir preprocess_Result
- python3.7 ../preprocess.py --result_path=./preprocess_Result/
+ python3.7 ../preprocess.py --config_path="./default_config_large.yaml" --result_path=./preprocess_Result/
}
function compile_app()
@@ -93,7 +93,7 @@ function infer()
function cal_acc()
{
- python3.7 ../postprocess.py --result_dir=./result_Files &> acc.log
+ python3.7 ../postprocess.py --config_path="./default_config_large.yaml" --result_dir=./result_Files &> acc.log
}
if [ $need_preprocess == "y" ]; then
diff --git a/model_zoo/official/nlp/transformer/scripts/run_standalone_train.sh b/model_zoo/official/nlp/transformer/scripts/run_standalone_train.sh
index 9b82f7277bf..41980e36b7f 100644
--- a/model_zoo/official/nlp/transformer/scripts/run_standalone_train.sh
+++ b/model_zoo/official/nlp/transformer/scripts/run_standalone_train.sh
@@ -25,7 +25,7 @@ fi
rm -rf run_standalone_train
mkdir run_standalone_train
-cp -rf ./src/ train.py ./run_standalone_train
+cp -rf ./src/ train.py ./*.yaml ./run_standalone_train
cd run_standalone_train || exit
export DEVICE_TARGET=$1
@@ -36,6 +36,7 @@ DATA_PATH=$5
if [ $DEVICE_TARGET == 'Ascend' ];then
python train.py \
+ --config_path="./default_config_large.yaml" \
--distribute="false" \
--epoch_size=$EPOCH_SIZE \
--accumulation_steps=$GRADIENT_ACCUMULATE_STEP \
@@ -53,6 +54,7 @@ elif [ $DEVICE_TARGET == 'GPU' ];then
export CUDA_VISIBLE_DEVICES="$2"
python train.py \
+ --config_path="./default_config_large_gpu.yaml" \
--distribute="false" \
--epoch_size=$EPOCH_SIZE \
--device_target=$DEVICE_TARGET \
diff --git a/model_zoo/official/nlp/transformer/src/dataset.py b/model_zoo/official/nlp/transformer/src/dataset.py
index 75f9e42f674..e55da66029e 100644
--- a/model_zoo/official/nlp/transformer/src/dataset.py
+++ b/model_zoo/official/nlp/transformer/src/dataset.py
@@ -17,7 +17,8 @@
import mindspore.common.dtype as mstype
import mindspore.dataset as de
import mindspore.dataset.transforms.c_transforms as deC
-from .config import transformer_net_cfg, transformer_net_cfg_gpu
+from .model_utils.config import config
+
de.config.set_seed(1)
def create_transformer_dataset(epoch_count=1, rank_size=1, rank_id=0, do_shuffle="true", dataset_path=None,
bucket_boundaries=None, device_target="Ascend"):
@@ -38,11 +39,8 @@ def create_transformer_dataset(epoch_count=1, rank_size=1, rank_id=0, do_shuffle
ds = ds.map(operations=type_cast_op, input_columns="target_eos_mask")
# apply batch operations
- if device_target == "Ascend":
- ds = ds.batch(transformer_net_cfg.batch_size, drop_remainder=True)
- else:
- ds = ds.batch(transformer_net_cfg_gpu.batch_size, drop_remainder=True)
+ ds = ds.batch(config.batch_size, drop_remainder=True)
ds = ds.repeat(epoch_count)
return ds
diff --git a/model_zoo/official/nlp/transformer/src/model_utils/__init__.py b/model_zoo/official/nlp/transformer/src/model_utils/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/model_zoo/official/nlp/transformer/src/model_utils/config.py b/model_zoo/official/nlp/transformer/src/model_utils/config.py
new file mode 100644
index 00000000000..7f1ff6e2b8d
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/src/model_utils/config.py
@@ -0,0 +1,127 @@
+# 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.
+# ============================================================================
+
+"""Parse arguments"""
+
+import os
+import ast
+import argparse
+from pprint import pprint, pformat
+import yaml
+
+class Config:
+ """
+ Configuration namespace. Convert dictionary to members.
+ """
+ def __init__(self, cfg_dict):
+ for k, v in cfg_dict.items():
+ if isinstance(v, (list, tuple)):
+ setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v])
+ else:
+ setattr(self, k, Config(v) if isinstance(v, dict) else v)
+
+ def __str__(self):
+ return pformat(self.__dict__)
+
+ def __repr__(self):
+ return self.__str__()
+
+
+def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="default_config.yaml"):
+ """
+ Parse command line arguments to the configuration according to the default yaml.
+
+ Args:
+ parser: Parent parser.
+ cfg: Base configuration.
+ helper: Helper description.
+ cfg_path: Path to the default yaml config.
+ """
+ parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]",
+ parents=[parser])
+ helper = {} if helper is None else helper
+ choices = {} if choices is None else choices
+ for item in cfg:
+ if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict):
+ help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path)
+ choice = choices[item] if item in choices else None
+ if isinstance(cfg[item], bool):
+ parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice,
+ help=help_description)
+ else:
+ parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice,
+ help=help_description)
+ args = parser.parse_args()
+ return args
+
+
+def parse_yaml(yaml_path):
+ """
+ Parse the yaml config file.
+
+ Args:
+ yaml_path: Path to the yaml config.
+ """
+ with open(yaml_path, 'r') as fin:
+ try:
+ cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader)
+ cfgs = [x for x in cfgs]
+ if len(cfgs) == 1:
+ cfg_helper = {}
+ cfg = cfgs[0]
+ cfg_choices = {}
+ elif len(cfgs) == 2:
+ cfg, cfg_helper = cfgs
+ cfg_choices = {}
+ elif len(cfgs) == 3:
+ cfg, cfg_helper, cfg_choices = cfgs
+ else:
+ raise ValueError("At most 3 docs (config, description for help, choices) are supported in config yaml")
+ print(cfg_helper)
+ except:
+ raise ValueError("Failed to parse yaml")
+ return cfg, cfg_helper, cfg_choices
+
+
+def merge(args, cfg):
+ """
+ Merge the base config from yaml file and command line arguments.
+
+ Args:
+ args: Command line arguments.
+ cfg: Base configuration.
+ """
+ args_var = vars(args)
+ for item in args_var:
+ cfg[item] = args_var[item]
+ return cfg
+
+
+def get_config():
+ """
+ Get Config according to the yaml file and cli arguments.
+ """
+ parser = argparse.ArgumentParser(description="default name", add_help=False)
+ current_dir = os.path.dirname(os.path.abspath(__file__))
+ parser.add_argument("--config_path", type=str, default=os.path.join(current_dir, "../../default_config.yaml"),
+ help="Config file path")
+ path_args, _ = parser.parse_known_args()
+ default, helper, choices = parse_yaml(path_args.config_path)
+ pprint(default)
+ args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path)
+ final_config = merge(args, default)
+ return Config(final_config)
+
+config = get_config()
diff --git a/model_zoo/official/nlp/transformer/src/model_utils/device_adapter.py b/model_zoo/official/nlp/transformer/src/model_utils/device_adapter.py
new file mode 100644
index 00000000000..7c5d7f837dd
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/src/model_utils/device_adapter.py
@@ -0,0 +1,27 @@
+# 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.
+# ============================================================================
+
+"""Device adapter for ModelArts"""
+
+from .config import config
+
+if config.enable_modelarts:
+ from .moxing_adapter import get_device_id, get_device_num, get_rank_id, get_job_id
+else:
+ from .local_adapter import get_device_id, get_device_num, get_rank_id, get_job_id
+
+__all__ = [
+ "get_device_id", "get_device_num", "get_rank_id", "get_job_id"
+]
diff --git a/model_zoo/official/nlp/transformer/src/model_utils/local_adapter.py b/model_zoo/official/nlp/transformer/src/model_utils/local_adapter.py
new file mode 100644
index 00000000000..769fa6dc78e
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/src/model_utils/local_adapter.py
@@ -0,0 +1,36 @@
+# 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.
+# ============================================================================
+
+"""Local adapter"""
+
+import os
+
+def get_device_id():
+ device_id = os.getenv('DEVICE_ID', '0')
+ return int(device_id)
+
+
+def get_device_num():
+ device_num = os.getenv('RANK_SIZE', '1')
+ return int(device_num)
+
+
+def get_rank_id():
+ global_rank_id = os.getenv('RANK_ID', '0')
+ return int(global_rank_id)
+
+
+def get_job_id():
+ return "Local Job"
diff --git a/model_zoo/official/nlp/transformer/src/model_utils/moxing_adapter.py b/model_zoo/official/nlp/transformer/src/model_utils/moxing_adapter.py
new file mode 100644
index 00000000000..830d19a6fc9
--- /dev/null
+++ b/model_zoo/official/nlp/transformer/src/model_utils/moxing_adapter.py
@@ -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.
+# ============================================================================
+
+"""Moxing adapter for ModelArts"""
+
+import os
+import functools
+from mindspore import context
+from mindspore.profiler import Profiler
+from .config import config
+
+_global_sync_count = 0
+
+def get_device_id():
+ device_id = os.getenv('DEVICE_ID', '0')
+ return int(device_id)
+
+
+def get_device_num():
+ device_num = os.getenv('RANK_SIZE', '1')
+ return int(device_num)
+
+
+def get_rank_id():
+ global_rank_id = os.getenv('RANK_ID', '0')
+ return int(global_rank_id)
+
+
+def get_job_id():
+ job_id = os.getenv('JOB_ID')
+ job_id = job_id if job_id != "" else "default"
+ return job_id
+
+def sync_data(from_path, to_path):
+ """
+ Download data from remote obs to local directory if the first url is remote url and the second one is local path
+ Upload data from local directory to remote obs in contrast.
+ """
+ import moxing as mox
+ import time
+ global _global_sync_count
+ sync_lock = "/tmp/copy_sync.lock" + str(_global_sync_count)
+ _global_sync_count += 1
+
+ # Each server contains 8 devices as most.
+ if get_device_id() % min(get_device_num(), 8) == 0 and not os.path.exists(sync_lock):
+ print("from path: ", from_path)
+ print("to path: ", to_path)
+ mox.file.copy_parallel(from_path, to_path)
+ print("===finish data synchronization===")
+ try:
+ os.mknod(sync_lock)
+ except IOError:
+ pass
+ print("===save flag===")
+
+ while True:
+ if os.path.exists(sync_lock):
+ break
+ time.sleep(1)
+
+ print("Finish sync data from {} to {}.".format(from_path, to_path))
+
+
+def moxing_wrapper(pre_process=None, post_process=None):
+ """
+ Moxing wrapper to download dataset and upload outputs.
+ """
+ def wrapper(run_func):
+ @functools.wraps(run_func)
+ def wrapped_func(*args, **kwargs):
+ # Download data from data_url
+ if config.enable_modelarts:
+ if config.data_url:
+ sync_data(config.data_url, config.data_path)
+ print("Dataset downloaded: ", os.listdir(config.data_path))
+ if config.checkpoint_url:
+ sync_data(config.checkpoint_url, config.load_path)
+ print("Preload downloaded: ", os.listdir(config.load_path))
+ if config.train_url:
+ sync_data(config.train_url, config.output_path)
+ print("Workspace downloaded: ", os.listdir(config.output_path))
+
+ context.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id())))
+ config.device_num = get_device_num()
+ config.device_id = get_device_id()
+ if not os.path.exists(config.output_path):
+ os.makedirs(config.output_path)
+
+ if pre_process:
+ pre_process()
+
+ if config.enable_profiling:
+ profiler = Profiler()
+
+ run_func(*args, **kwargs)
+
+ if config.enable_profiling:
+ profiler.analyse()
+
+ # Upload data to train_url
+ if config.enable_modelarts:
+ if post_process:
+ post_process()
+
+ if config.train_url:
+ print("Start to copy output directory")
+ sync_data(config.output_path, config.train_url)
+ return wrapped_func
+ return wrapper
diff --git a/model_zoo/official/nlp/transformer/src/process_output.py b/model_zoo/official/nlp/transformer/src/process_output.py
index cccff02e090..0159f6540db 100644
--- a/model_zoo/official/nlp/transformer/src/process_output.py
+++ b/model_zoo/official/nlp/transformer/src/process_output.py
@@ -18,22 +18,18 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
-import argparse
-import sys
+import sys
import tokenization
+from .model_utils.config import config
# Explicitly set the encoding
sys.stdin = open(sys.stdin.fileno(), mode='r', encoding='utf-8', buffering=True)
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf-8', buffering=True)
def main():
- parser = argparse.ArgumentParser(
- description="recore nbest with smoothed sentence-level bleu.")
- parser.add_argument("--vocab_file", type=str, default="", required=True, help="vocab file path.")
- args = parser.parse_args()
- tokenizer = tokenization.WhiteSpaceTokenizer(vocab_file=args.vocab_file)
+ tokenizer = tokenization.WhiteSpaceTokenizer(vocab_file=config.vocab_file)
for line in sys.stdin:
token_ids = [int(x) for x in line.strip().split()]
diff --git a/model_zoo/official/nlp/transformer/train.py b/model_zoo/official/nlp/transformer/train.py
index 3bd06b244e3..a70a17ff782 100644
--- a/model_zoo/official/nlp/transformer/train.py
+++ b/model_zoo/official/nlp/transformer/train.py
@@ -16,9 +16,8 @@
import os
import time
-import argparse
-import ast
+from easydict import EasyDict as edict
import mindspore.common.dtype as mstype
from mindspore.common.tensor import Tensor
from mindspore.nn.optim import Adam
@@ -36,21 +35,30 @@ from mindspore.common import set_seed
from src.transformer_for_train import TransformerTrainOneStepCell, TransformerNetworkWithLoss, \
TransformerTrainOneStepWithLossScaleCell, \
TransformerTrainAccumulationAllReducePostWithLossScaleCell
-from src.config import cfg, transformer_net_cfg, transformer_net_cfg_gpu
from src.dataset import create_transformer_dataset
from src.lr_schedule import create_dynamic_lr
+from src.model_utils.config import config
+from src.model_utils.moxing_adapter import moxing_wrapper
+from src.model_utils.device_adapter import get_device_id
+
set_seed(1)
-
def get_ms_timestamp():
t = time.time()
return int(round(t * 1000))
-
time_stamp_init = False
time_stamp_first = 0
+config.dtype = mstype.float32
+config.compute_type = mstype.float16
+config.lr_schedule = edict({
+ 'learning_rate': 2.0,
+ 'warmup_steps': 8000,
+ 'start_decay_step': 16000,
+ 'min_lr': 0.0,
+ })
class LossCallBack(Callback):
"""
@@ -82,7 +90,12 @@ class LossCallBack(Callback):
cb_params.cur_epoch_num,
cb_params.cur_step_num,
str(cb_params.net_outputs)))
- with open("./loss_{}.log".format(self.rank_id), "a+") as f:
+
+ loss_file = "./loss_{}.log"
+ if config.enable_modelarts:
+ loss_file = "/cache/train/loss_{}.log"
+
+ with open(loss_file.format(self.rank_id), "a+") as f:
f.write("time: {}, epoch: {}, step: {}, loss: {}, overflow: {}, loss_scale: {}".format(
time_stamp_current - time_stamp_first,
cb_params.cur_epoch_num,
@@ -93,121 +106,90 @@ class LossCallBack(Callback):
f.write('\n')
-def argparse_init():
- """
- Argparse init.
- """
- parser = argparse.ArgumentParser(description='transformer')
- parser.add_argument("--distribute", type=str, default="false", choices=['true', 'false'],
- help="Run distribute, default is false.")
- parser.add_argument("--epoch_size", type=int, default=52, help="Epoch size, default is 52.")
- parser.add_argument("--device_target", type=str, default="Ascend",
- help="device where the code will be implemented, default is Ascend")
- parser.add_argument("--device_id", type=int, default=0, help="Device id, default is 0.")
- parser.add_argument("--device_num", type=int, default=1, help="Use device nums, default is 1.")
- parser.add_argument("--enable_lossscale", type=str, default="true", choices=['true', 'false'],
- help="Use lossscale or not, default is true.")
- parser.add_argument("--do_shuffle", type=str, default="true", choices=['true', 'false'],
- help="Enable shuffle for dataset, default is true.")
- parser.add_argument("--checkpoint_path", type=str, default="", help="Checkpoint file path")
- parser.add_argument("--enable_save_ckpt", type=str, default="true", choices=['true', 'false'],
- help="Enable save checkpoint, default is true.")
- parser.add_argument("--save_checkpoint_steps", type=int, default=2500, help="Save checkpoint steps, "
- "default is 2500.")
- parser.add_argument("--save_checkpoint_num", type=int, default=30, help="Save checkpoint numbers, default is 30.")
- parser.add_argument("--save_checkpoint_path", type=str, default="./", help="Save checkpoint file path")
- parser.add_argument("--data_path", type=str, default="", help="Data path, it is better to use absolute path")
- parser.add_argument("--bucket_boundaries", type=ast.literal_eval, default=[16, 32, 48, 64, 128],
- help="sequence length for different bucket")
- parser.add_argument("--accumulation_steps", type=int, default=1, help="Gradient accumulation steps, default is 1.")
-
- return parser
+def modelarts_pre_process():
+ config.save_checkpoint_path = config.output_path
+ config.data_path = os.path.join(config.data_path, 'ende-l128-mindrecord')
+@moxing_wrapper(pre_process=modelarts_pre_process)
def run_transformer_train():
"""
Transformer training.
"""
- parser = argparse_init()
- args, _ = parser.parse_known_args()
- if args.device_target == "Ascend":
- context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target, device_id=args.device_id)
+ if config.device_target == "Ascend":
+ context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target, device_id=get_device_id())
else:
- context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target)
+ context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target)
context.set_context(reserve_class_name_in_scope=False, enable_auto_mixed_precision=False)
- if args.device_target == "GPU":
+ if config.device_target == "GPU":
# Enable graph kernel
context.set_context(enable_graph_kernel=True, graph_kernel_flags="--enable_parallel_fusion")
- if args.distribute == "true":
- if args.device_target == "Ascend":
- device_num = args.device_num
+ if config.distribute == "true":
+ if config.device_target == "Ascend":
+ device_num = config.device_num
D.init('hccl')
else:
D.init('nccl')
device_num = D.get_group_size()
rank = get_rank()
- args.device_id = rank
+ config.device_id = rank
context.reset_auto_parallel_context()
context.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL, gradients_mean=True,
device_num=device_num)
- rank_id = args.device_id % device_num
- save_ckpt_path = os.path.join(args.save_checkpoint_path, 'ckpt_' + str(get_rank()) + '/')
+ rank_id = config.device_id % device_num
+ save_ckpt_path = os.path.join(config.save_checkpoint_path, 'ckpt_' + str(get_rank()) + '/')
else:
device_num = 1
rank_id = 0
- save_ckpt_path = os.path.join(args.save_checkpoint_path, 'ckpt_0/')
+ save_ckpt_path = os.path.join(config.save_checkpoint_path, 'ckpt_0/')
dataset = create_transformer_dataset(epoch_count=1, rank_size=device_num,
- rank_id=rank_id, do_shuffle=args.do_shuffle,
- dataset_path=args.data_path,
- bucket_boundaries=args.bucket_boundaries,
- device_target=args.device_target)
- if args.device_target == "Ascend":
- netwithloss = TransformerNetworkWithLoss(transformer_net_cfg, True)
- else:
- netwithloss = TransformerNetworkWithLoss(transformer_net_cfg_gpu, True)
+ rank_id=rank_id, do_shuffle=config.do_shuffle,
+ dataset_path=config.data_path,
+ bucket_boundaries=config.bucket_boundaries,
+ device_target=config.device_target)
- if args.checkpoint_path:
- parameter_dict = load_checkpoint(args.checkpoint_path)
+ netwithloss = TransformerNetworkWithLoss(config, True)
+
+ if config.checkpoint_path:
+ parameter_dict = load_checkpoint(config.checkpoint_path)
load_param_into_net(netwithloss, parameter_dict)
- hidden_size = transformer_net_cfg.hidden_size if args.device_target == "Ascend" \
- else transformer_net_cfg_gpu.hidden_size
- learning_rate = cfg.lr_schedule.learning_rate if args.device_target == "Ascend" \
- else 1.0
+ hidden_size = config.hidden_size
+ learning_rate = config.lr_schedule.learning_rate if config.device_target == "Ascend" else 1.0
lr = Tensor(create_dynamic_lr(schedule="constant*rsqrt_hidden*linear_warmup*rsqrt_decay",
- training_steps=dataset.get_dataset_size()*args.epoch_size,
+ training_steps=dataset.get_dataset_size()*config.epoch_size,
learning_rate=learning_rate,
- warmup_steps=cfg.lr_schedule.warmup_steps,
+ warmup_steps=config.lr_schedule.warmup_steps,
hidden_size=hidden_size,
- start_decay_step=cfg.lr_schedule.start_decay_step,
- min_lr=cfg.lr_schedule.min_lr), mstype.float32)
+ start_decay_step=config.lr_schedule.start_decay_step,
+ min_lr=config.lr_schedule.min_lr), mstype.float32)
- if args.device_target == "GPU" and cfg.transformer_network == "large":
- optimizer = Adam(netwithloss.trainable_params(), lr, beta2=cfg.optimizer_adam_beta2)
+ if config.device_target == "GPU" and config.transformer_network == "large":
+ optimizer = Adam(netwithloss.trainable_params(), lr, beta2=config.optimizer_adam_beta2)
else:
optimizer = Adam(netwithloss.trainable_params(), lr)
callbacks = [TimeMonitor(dataset.get_dataset_size()), LossCallBack(rank_id=rank_id)]
- if args.enable_save_ckpt == "true":
+ if config.enable_save_ckpt == "true":
if device_num == 1 or (device_num > 1 and rank_id == 0):
- if args.device_target == "Ascend":
- ckpt_config = CheckpointConfig(save_checkpoint_steps=args.save_checkpoint_steps,
- keep_checkpoint_max=args.save_checkpoint_num)
+ if config.device_target == "Ascend":
+ ckpt_config = CheckpointConfig(save_checkpoint_steps=config.save_checkpoint_steps,
+ keep_checkpoint_max=config.save_checkpoint_num)
else:
ckpt_config = CheckpointConfig(save_checkpoint_steps=dataset.get_dataset_size(),
- keep_checkpoint_max=args.save_checkpoint_num)
+ keep_checkpoint_max=config.save_checkpoint_num)
ckpoint_cb = ModelCheckpoint(prefix='transformer', directory=save_ckpt_path, config=ckpt_config)
callbacks.append(ckpoint_cb)
- if args.enable_lossscale == "true":
- scale_manager = DynamicLossScaleManager(init_loss_scale=cfg.init_loss_scale_value,
- scale_factor=cfg.scale_factor,
- scale_window=cfg.scale_window)
+ if config.enable_lossscale == "true":
+ scale_manager = DynamicLossScaleManager(init_loss_scale=config.init_loss_scale_value,
+ scale_factor=config.scale_factor,
+ scale_window=config.scale_window)
update_cell = scale_manager.get_update_cell()
- if args.accumulation_steps > 1:
+ if config.accumulation_steps > 1:
netwithgrads = TransformerTrainAccumulationAllReducePostWithLossScaleCell(netwithloss, optimizer,
update_cell,
- args.accumulation_steps)
+ config.accumulation_steps)
else:
netwithgrads = TransformerTrainOneStepWithLossScaleCell(netwithloss, optimizer=optimizer,
scale_update_cell=update_cell)
@@ -217,7 +199,7 @@ def run_transformer_train():
netwithgrads.set_train(True)
model = Model(netwithgrads)
- model.train(args.epoch_size, dataset, callbacks=callbacks, dataset_sink_mode=False)
+ model.train(config.epoch_size, dataset, callbacks=callbacks, dataset_sink_mode=False)
if __name__ == '__main__':
diff --git a/tests/st/model_zoo_tests/transformer/__init__.py b/tests/st/model_zoo_tests/transformer/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/st/model_zoo_tests/transformer/src/__init__.py b/tests/st/model_zoo_tests/transformer/src/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/st/model_zoo_tests/transformer/src/beam_search.py b/tests/st/model_zoo_tests/transformer/src/beam_search.py
new file mode 100644
index 00000000000..53c765f7223
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/beam_search.py
@@ -0,0 +1,281 @@
+# 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.
+# ============================================================================
+"""Transformer beam search module."""
+
+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
+
+INF = 1. * 1e9
+
+class LengthPenalty(nn.Cell):
+ """
+ Normalize scores of translations according to their length.
+
+ Args:
+ weight (float): Weight of length penalty. Default: 1.0.
+ compute_type (:class:`mindspore.dtype`): Compute type in Transformer. Default: mstype.float32.
+ """
+ def __init__(self,
+ weight=1.0,
+ compute_type=mstype.float32):
+ super(LengthPenalty, self).__init__()
+ self.weight = weight
+ self.add = P.Add()
+ self.pow = P.Pow()
+ self.div = P.RealDiv()
+ self.cast = P.Cast()
+ self.five = Tensor(5.0, mstype.float32)
+ self.six = Tensor(6.0, mstype.float32)
+
+ def construct(self, length_tensor):
+ length_tensor = self.cast(length_tensor, mstype.float32)
+ output = self.add(length_tensor, self.five)
+ output = self.div(output, self.six)
+ output = self.pow(output, self.weight)
+ return output
+
+
+class TileBeam(nn.Cell):
+ """
+ TileBeam.
+
+ Args:
+ beam_width (int): beam width setting. Default: 4.
+ compute_type (:class:`mindspore.dtype`): Compute type in Transformer. Default: mstype.float32.
+ """
+ def __init__(self,
+ beam_width,
+ compute_type=mstype.float32):
+ super(TileBeam, self).__init__()
+ self.beam_width = beam_width
+ self.expand = P.ExpandDims()
+ self.tile = P.Tile()
+ self.reshape = P.Reshape()
+ self.shape = P.Shape()
+
+ def construct(self, input_tensor):
+ """
+ input_tensor: shape [batch, dim1, dim2]
+ output_tensor: shape [batch*beam, dim1, dim2]
+ """
+ shape = self.shape(input_tensor)
+ input_tensor = self.expand(input_tensor, 1)
+ tile_shape = (1,) + (self.beam_width,)
+ for _ in range(len(shape)-1):
+ tile_shape = tile_shape + (1,)
+ output = self.tile(input_tensor, tile_shape)
+ out_shape = (shape[0]*self.beam_width,) + shape[1:]
+ output = self.reshape(output, out_shape)
+ return output
+
+
+class Mod(nn.Cell):
+ """
+ Mod function.
+
+ Args:
+ compute_type (:class:`mindspore.dtype`): Compute type in Transformer. Default: mstype.float32.
+ """
+ def __init__(self,
+ compute_type=mstype.float32):
+ super(Mod, self).__init__()
+ self.compute_type = compute_type
+ self.floor_div = P.FloorDiv()
+ self.sub = P.Sub()
+ self.multiply = P.Mul()
+
+ def construct(self, input_x, input_y):
+ x = self.floor_div(input_x, input_y)
+ x = self.multiply(x, input_y)
+ x = self.sub(input_x, x)
+ return x
+
+
+class BeamSearchDecoder(nn.Cell):
+ """
+ Beam search decoder.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ seq_length (int): Length of input sequence.
+ vocab_size (int): Size of vocabulary.
+ decoder (:class:`TransformerDecoderStep`): Decoder module.
+ beam_width (int): beam width setting. Default: 4.
+ length_penalty_weight (float): Weight of length penalty. Default: 1.0.
+ max_decode_length (int): max decode length. Default: 128.
+ sos_id (int): Id of sequence start token. Default: 1.
+ eos_id (int): Id of sequence end token. Default: 2.
+ compute_type (:class:`mindspore.dtype`): Compute type in Transformer. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ seq_length,
+ vocab_size,
+ decoder,
+ beam_width=4,
+ length_penalty_weight=1.0,
+ max_decode_length=128,
+ sos_id=1,
+ eos_id=2,
+ compute_type=mstype.float32):
+ super(BeamSearchDecoder, self).__init__(auto_prefix=False)
+ self.seq_length = seq_length
+ self.batch_size = batch_size
+ self.vocab_size = vocab_size
+ self.beam_width = beam_width
+ self.length_penalty_weight = length_penalty_weight
+ self.max_decode_length = max_decode_length
+ self.decoder = decoder
+
+ self.add = P.Add()
+ self.expand = P.ExpandDims()
+ self.reshape = P.Reshape()
+ self.shape_flat = (-1,)
+ self.shape = P.Shape()
+
+ self.zero_tensor = Tensor(np.zeros([batch_size, beam_width]), mstype.float32)
+ self.ninf_tensor = Tensor(np.full([batch_size, beam_width], -INF), mstype.float32)
+
+ self.select = P.Select()
+ self.flat_shape = (batch_size, beam_width * vocab_size)
+ self.topk = P.TopK(sorted=True)
+ self.floor_div = P.FloorDiv()
+ self.vocab_size_tensor = Tensor(self.vocab_size, mstype.int32)
+ self.real_div = P.RealDiv()
+ self.mod = Mod()
+ self.equal = P.Equal()
+ self.eos_ids = Tensor(np.full([batch_size, beam_width], eos_id), mstype.int32)
+
+ beam_ids = np.tile(np.arange(beam_width).reshape((1, beam_width)), [batch_size, 1])
+ self.beam_ids = Tensor(beam_ids, mstype.int32)
+ batch_ids = np.arange(batch_size*beam_width).reshape((batch_size, beam_width)) // beam_width
+ self.batch_ids = Tensor(batch_ids, mstype.int32)
+ self.concat = P.Concat(axis=-1)
+ self.gather_nd = P.GatherNd()
+
+ self.greater_equal = P.GreaterEqual()
+ self.sub = P.Sub()
+ self.cast = P.Cast()
+ self.zeroslike = P.ZerosLike()
+
+ # init inputs and states
+ self.start_ids = Tensor(np.full([batch_size * beam_width, 1], sos_id), mstype.int32)
+ self.init_seq = Tensor(np.full([batch_size, beam_width, 1], sos_id), mstype.int32)
+ init_scores = np.tile(np.array([[0.] + [-INF]*(beam_width-1)]), [batch_size, 1])
+ self.init_scores = Tensor(init_scores, mstype.float32)
+ self.init_finished = Tensor(np.zeros([batch_size, beam_width], dtype=np.bool))
+ self.init_length = Tensor(np.zeros([batch_size, beam_width], dtype=np.int32))
+ self.length_penalty = LengthPenalty(weight=length_penalty_weight)
+ self.one = Tensor(1, mstype.int32)
+
+ def one_step(self, cur_input_ids, enc_states, enc_attention_mask, state_log_probs,
+ state_seq, state_finished, state_length):
+ """
+ One step for decode
+ """
+ log_probs = self.decoder(cur_input_ids, enc_states, enc_attention_mask, self.seq_length)
+ log_probs = self.reshape(log_probs, (self.batch_size, self.beam_width, self.vocab_size))
+
+ # select topk indices
+ total_log_probs = self.add(log_probs, self.expand(state_log_probs, -1))
+
+ # mask finished beams
+ mask_tensor = self.select(state_finished, self.ninf_tensor, self.zero_tensor)
+ total_log_probs = self.add(total_log_probs, self.expand(mask_tensor, -1))
+
+ # reshape scores to [batch, beam*vocab]
+ flat_scores = self.reshape(total_log_probs, self.flat_shape)
+ # select topk
+ topk_scores, topk_indices = self.topk(flat_scores, self.beam_width)
+
+ temp = topk_indices
+ beam_indices = self.zeroslike(topk_indices)
+ for _ in range(self.beam_width - 1):
+ temp = self.sub(temp, self.vocab_size_tensor)
+ res = self.cast(self.greater_equal(temp, 0), mstype.int32)
+ beam_indices = beam_indices + res
+ word_indices = topk_indices - beam_indices * self.vocab_size_tensor
+ #======================================================================
+
+ # mask finished indices
+ beam_indices = self.select(state_finished, self.beam_ids, beam_indices)
+ word_indices = self.select(state_finished, self.eos_ids, word_indices)
+ topk_scores = self.select(state_finished, state_log_probs, topk_scores)
+
+ ###### put finished sequences to the end
+ # sort according to scores with -inf for finished beams
+ tmp_log_probs = self.select(
+ self.equal(word_indices, self.eos_ids),
+ self.ninf_tensor,
+ topk_scores)
+ _, tmp_indices = self.topk(tmp_log_probs, self.beam_width)
+ # update
+ tmp_gather_indices = self.concat((self.expand(self.batch_ids, -1), self.expand(tmp_indices, -1)))
+ beam_indices = self.gather_nd(beam_indices, tmp_gather_indices)
+ word_indices = self.gather_nd(word_indices, tmp_gather_indices)
+ topk_scores = self.gather_nd(topk_scores, tmp_gather_indices)
+
+ ###### generate new beam_search states
+ # gather indices for selecting alive beams
+ gather_indices = self.concat((self.expand(self.batch_ids, -1), self.expand(beam_indices, -1)))
+
+ # length add 1 if not finished in the previous step
+ length_add = self.add(state_length, self.one)
+ state_length = self.select(state_finished, state_length, length_add)
+ state_length = self.gather_nd(state_length, gather_indices)
+
+ # concat seq
+ seq = self.gather_nd(state_seq, gather_indices)
+ state_seq = self.concat((seq, self.expand(word_indices, -1)))
+
+ # new finished flag and log_probs
+ state_finished = self.equal(word_indices, self.eos_ids)
+ state_log_probs = topk_scores
+
+ ###### generate new inputs and decoder states
+ cur_input_ids = self.reshape(state_seq, (self.batch_size*self.beam_width, -1))
+ return cur_input_ids, state_log_probs, state_seq, state_finished, state_length
+
+ def construct(self, enc_states, enc_attention_mask):
+ """Get beam search result."""
+ cur_input_ids = self.start_ids
+ # beam search states
+ state_log_probs = self.init_scores
+ state_seq = self.init_seq
+ state_finished = self.init_finished
+ state_length = self.init_length
+
+ for _ in range(self.max_decode_length):
+ # run one step decoder to get outputs of the current step
+ # shape [batch*beam, 1, vocab]
+ cur_input_ids, state_log_probs, state_seq, state_finished, state_length = self.one_step(
+ cur_input_ids, enc_states, enc_attention_mask, state_log_probs, state_seq, state_finished, state_length)
+
+ # add length penalty scores
+ penalty_len = self.length_penalty(state_length)
+ # get penalty length
+ log_probs = self.real_div(state_log_probs, penalty_len)
+
+ # sort according to scores
+ _, top_beam_indices = self.topk(log_probs, self.beam_width)
+ gather_indices = self.concat((self.expand(self.batch_ids, -1), self.expand(top_beam_indices, -1)))
+ # sort sequence
+ predicted_ids = self.gather_nd(state_seq, gather_indices)
+ # take the first one
+ predicted_ids = predicted_ids[::, 0:1:1, ::]
+ return predicted_ids
diff --git a/model_zoo/official/nlp/transformer/src/config.py b/tests/st/model_zoo_tests/transformer/src/config.py
similarity index 98%
rename from model_zoo/official/nlp/transformer/src/config.py
rename to tests/st/model_zoo_tests/transformer/src/config.py
index 78771e018bd..58d5ee5f721 100644
--- a/model_zoo/official/nlp/transformer/src/config.py
+++ b/tests/st/model_zoo_tests/transformer/src/config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 Huawei Technologies Co., Ltd
+# 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.
diff --git a/tests/st/model_zoo_tests/transformer/src/dataset.py b/tests/st/model_zoo_tests/transformer/src/dataset.py
new file mode 100644
index 00000000000..b485fd7ddd6
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/dataset.py
@@ -0,0 +1,58 @@
+# 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.
+# ============================================================================
+"""Data operations, will be used in train.py."""
+
+import mindspore.common.dtype as mstype
+import mindspore.dataset as de
+import mindspore.dataset.transforms.c_transforms as deC
+from .config import transformer_net_cfg, transformer_net_cfg_gpu
+de.config.set_seed(1)
+def create_transformer_dataset(epoch_count=1, rank_size=1, rank_id=0, do_shuffle="true", dataset_path=None,
+ bucket_boundaries=None, device_target="Ascend"):
+ """create dataset"""
+ def batch_per_bucket(bucket_len, dataset_path):
+ dataset_path = dataset_path + "_" + str(bucket_len) + "_00"
+ ds = de.MindDataset(dataset_path,
+ columns_list=["source_eos_ids", "source_eos_mask",
+ "target_sos_ids", "target_sos_mask",
+ "target_eos_ids", "target_eos_mask"],
+ shuffle=(do_shuffle == "true"), num_shards=rank_size, shard_id=rank_id)
+ type_cast_op = deC.TypeCast(mstype.int32)
+ ds = ds.map(operations=type_cast_op, input_columns="source_eos_ids")
+ ds = ds.map(operations=type_cast_op, input_columns="source_eos_mask")
+ ds = ds.map(operations=type_cast_op, input_columns="target_sos_ids")
+ ds = ds.map(operations=type_cast_op, input_columns="target_sos_mask")
+ ds = ds.map(operations=type_cast_op, input_columns="target_eos_ids")
+ ds = ds.map(operations=type_cast_op, input_columns="target_eos_mask")
+
+ # apply batch operations
+ if device_target == "Ascend":
+ ds = ds.batch(transformer_net_cfg.batch_size, drop_remainder=True)
+ else:
+ ds = ds.batch(transformer_net_cfg_gpu.batch_size, drop_remainder=True)
+
+ ds = ds.repeat(epoch_count)
+ return ds
+
+ for i, _ in enumerate(bucket_boundaries):
+ bucket_len = bucket_boundaries[i]
+ ds_per = batch_per_bucket(bucket_len, dataset_path)
+ if i == 0:
+ ds = ds_per
+ else:
+ ds = ds + ds_per
+ ds = ds.shuffle(ds.get_dataset_size())
+ ds.channel_name = 'transformer'
+ return ds
diff --git a/model_zoo/official/nlp/transformer/src/eval_config.py b/tests/st/model_zoo_tests/transformer/src/eval_config.py
similarity index 97%
rename from model_zoo/official/nlp/transformer/src/eval_config.py
rename to tests/st/model_zoo_tests/transformer/src/eval_config.py
index e38ddb08b85..512e2c489a1 100644
--- a/model_zoo/official/nlp/transformer/src/eval_config.py
+++ b/tests/st/model_zoo_tests/transformer/src/eval_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 Huawei Technologies Co., Ltd
+# 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.
diff --git a/tests/st/model_zoo_tests/transformer/src/lr_schedule.py b/tests/st/model_zoo_tests/transformer/src/lr_schedule.py
new file mode 100644
index 00000000000..c246283478a
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/lr_schedule.py
@@ -0,0 +1,52 @@
+# 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 utilities."""
+
+def linear_warmup(warmup_steps, current_step):
+ return min([1.0, float(current_step)/float(warmup_steps)])
+
+def rsqrt_decay(warmup_steps, current_step):
+ return float(max([current_step, warmup_steps])) ** -0.5
+
+def rsqrt_hidden(hidden_size):
+ return float(hidden_size) ** -0.5
+
+def create_dynamic_lr(schedule, training_steps, learning_rate, warmup_steps, hidden_size,
+ start_decay_step=0, min_lr=0.):
+ """
+ Generate dynamic learning rate.
+ """
+ if start_decay_step < warmup_steps:
+ start_decay_step = warmup_steps
+ lr = []
+ for current_step in range(1, training_steps+1):
+ cur_lr = 1.0
+ for name in schedule.split("*"):
+ if name == "constant":
+ cur_lr *= float(learning_rate)
+ elif name == "rsqrt_hidden":
+ cur_lr *= rsqrt_hidden(hidden_size)
+ elif name == "linear_warmup":
+ cur_lr *= linear_warmup(warmup_steps, current_step)
+ elif name == "rsqrt_decay":
+ cur_lr *= rsqrt_decay(warmup_steps, current_step-start_decay_step+warmup_steps)
+ else:
+ raise ValueError("unknown learning rate schedule")
+ if warmup_steps < current_step < start_decay_step:
+ cur_lr = lr[-1]
+ if current_step > warmup_steps:
+ cur_lr = max([cur_lr, min_lr])
+ lr.append(cur_lr)
+ return lr
diff --git a/tests/st/model_zoo_tests/transformer/src/process_output.py b/tests/st/model_zoo_tests/transformer/src/process_output.py
new file mode 100644
index 00000000000..f69ea6a0d7d
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/process_output.py
@@ -0,0 +1,47 @@
+# 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.
+# ============================================================================
+"""Convert ids to tokens."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import sys
+
+import tokenization
+
+# Explicitly set the encoding
+sys.stdin = open(sys.stdin.fileno(), mode='r', encoding='utf-8', buffering=True)
+sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf-8', buffering=True)
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="recore nbest with smoothed sentence-level bleu.")
+ parser.add_argument("--vocab_file", type=str, default="", required=True, help="vocab file path.")
+ args = parser.parse_args()
+
+ tokenizer = tokenization.WhiteSpaceTokenizer(vocab_file=args.vocab_file)
+
+ for line in sys.stdin:
+ token_ids = [int(x) for x in line.strip().split()]
+ tokens = tokenizer.convert_ids_to_tokens(token_ids)
+ sent = " ".join(tokens)
+ sent = sent.split("")[-1]
+ sent = sent.split("")[0]
+ print(sent.strip())
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/st/model_zoo_tests/transformer/src/tokenization.py b/tests/st/model_zoo_tests/transformer/src/tokenization.py
new file mode 100644
index 00000000000..b4121f6c365
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/tokenization.py
@@ -0,0 +1,158 @@
+# 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 utilities."""
+
+import sys
+import collections
+import unicodedata
+
+def convert_to_printable(text):
+ """
+ Converts `text` to a printable coding format.
+ """
+ if sys.version_info[0] == 3:
+ if isinstance(text, str):
+ return text
+ if isinstance(text, bytes):
+ return text.decode("utf-8", "ignore")
+ raise ValueError("Only support type `str` or `bytes`, while text type is `%s`" % (type(text)))
+ if sys.version_info[0] == 2:
+ if isinstance(text, str):
+ return text
+ if isinstance(text, unicode):
+ return text.encode("utf-8")
+ raise ValueError("Only support type `str` or `unicode`, while text type is `%s`" % (type(text)))
+ raise ValueError("Only supported when running on Python2 or Python3.")
+
+
+def convert_to_unicode(text):
+ """
+ Converts `text` to Unicode format.
+ """
+ if sys.version_info[0] == 3:
+ if isinstance(text, str):
+ return text
+ if isinstance(text, bytes):
+ return text.decode("utf-8", "ignore")
+ raise ValueError("Only support type `str` or `bytes`, while text type is `%s`" % (type(text)))
+ if sys.version_info[0] == 2:
+ if isinstance(text, str):
+ return text.decode("utf-8", "ignore")
+ if isinstance(text, unicode):
+ return text
+ raise ValueError("Only support type `str` or `unicode`, while text type is `%s`" % (type(text)))
+ raise ValueError("Only supported when running on Python2 or Python3.")
+
+
+def load_vocab_file(vocab_file):
+ """
+ Loads a vocabulary file and turns into a {token:id} dictionary.
+ """
+ vocab_dict = collections.OrderedDict()
+ index = 0
+ with open(vocab_file, "r") as vocab:
+ while True:
+ token = convert_to_unicode(vocab.readline())
+ if not token:
+ break
+ token = token.strip()
+ vocab_dict[token] = index
+ index += 1
+ return vocab_dict
+
+
+def convert_by_vocab_dict(vocab_dict, items):
+ """
+ Converts a sequence of [tokens|ids] according to the vocab dict.
+ """
+ output = []
+ for item in items:
+ if item in vocab_dict:
+ output.append(vocab_dict[item])
+ else:
+ output.append(vocab_dict[""])
+ return output
+
+
+class WhiteSpaceTokenizer():
+ """
+ Whitespace tokenizer.
+ """
+ def __init__(self, vocab_file):
+ self.vocab_dict = load_vocab_file(vocab_file)
+ self.inv_vocab_dict = {index: token for token, index in self.vocab_dict.items()}
+
+ def _is_whitespace_char(self, char):
+ """
+ Checks if it is a whitespace character(regard "\t", "\n", "\r" as whitespace here).
+ """
+ if char in (" ", "\t", "\n", "\r"):
+ return True
+ uni = unicodedata.category(char)
+ if uni == "Zs":
+ return True
+ return False
+
+ def _is_control_char(self, char):
+ """
+ Checks if it is a control character.
+ """
+ if char in ("\t", "\n", "\r"):
+ return False
+ uni = unicodedata.category(char)
+ if uni in ("Cc", "Cf"):
+ return True
+ return False
+
+ def _clean_text(self, text):
+ """
+ Remove invalid characters and cleanup whitespace.
+ """
+ output = []
+ for char in text:
+ cp = ord(char)
+ if cp == 0 or cp == 0xfffd or self._is_control_char(char):
+ continue
+ if self._is_whitespace_char(char):
+ output.append(" ")
+ else:
+ output.append(char)
+ return "".join(output)
+
+ def _whitespace_tokenize(self, text):
+ """
+ Clean whitespace and split text into tokens.
+ """
+ text = text.strip()
+ if not text:
+ tokens = []
+ else:
+ tokens = text.split()
+ return tokens
+
+ def tokenize(self, text):
+ """
+ Tokenizes text.
+ """
+ text = convert_to_unicode(text)
+ text = self._clean_text(text)
+ tokens = self._whitespace_tokenize(text)
+ return tokens
+
+ def convert_tokens_to_ids(self, tokens):
+ return convert_by_vocab_dict(self.vocab_dict, tokens)
+
+ def convert_ids_to_tokens(self, ids):
+ return convert_by_vocab_dict(self.inv_vocab_dict, ids)
diff --git a/tests/st/model_zoo_tests/transformer/src/transformer_for_train.py b/tests/st/model_zoo_tests/transformer/src/transformer_for_train.py
new file mode 100644
index 00000000000..153a98d621f
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/transformer_for_train.py
@@ -0,0 +1,472 @@
+# 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.
+# ============================================================================
+"""Transformer for training."""
+import numpy as np
+
+from mindspore.common.initializer import initializer
+import mindspore.nn as nn
+from mindspore.ops import operations as P
+from mindspore.ops import functional as F
+from mindspore.ops import composite as C
+from mindspore.common.tensor import Tensor
+from mindspore.common.parameter import Parameter
+from mindspore.common import dtype as mstype
+from mindspore.nn.wrap.grad_reducer import DistributedGradReducer
+from mindspore.communication.management import get_group_size
+from mindspore.context import ParallelMode
+from mindspore import context
+
+from .transformer_model import TransformerModel
+
+GRADIENT_CLIP_TYPE = 1
+GRADIENT_CLIP_VALUE = 5.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 TransformerTrainingLoss(nn.Cell):
+ """
+ Provide transformer training loss.
+
+ Args:
+ config (TransformerConfig): The config of Transformer.
+
+ Returns:
+ Tensor, total loss.
+ """
+ def __init__(self, config):
+ super(TransformerTrainingLoss, self).__init__(auto_prefix=False)
+ self.vocab_size = config.vocab_size
+ self.onehot = P.OneHot()
+ self.on_value = Tensor(float(1 - config.label_smoothing), mstype.float32)
+ self.off_value = Tensor(config.label_smoothing / float(self.vocab_size - 1), mstype.float32)
+ self.reduce_sum = P.ReduceSum()
+ self.reduce_mean = P.ReduceMean()
+ self.reshape = P.Reshape()
+ self.last_idx = (-1,)
+ self.flatten = P.Flatten()
+ self.neg = P.Neg()
+ self.cast = P.Cast()
+ self.batch_size = config.batch_size
+
+ def construct(self, prediction_scores, label_ids, label_weights, seq_length):
+ """Defines the computation performed."""
+ flat_shape = (self.batch_size * seq_length,)
+ label_ids = self.reshape(label_ids, flat_shape)
+ label_weights = self.cast(self.reshape(label_weights, flat_shape), mstype.float32)
+ one_hot_labels = self.onehot(label_ids, self.vocab_size, self.on_value, self.off_value)
+
+ per_example_loss = self.neg(self.reduce_sum(prediction_scores * one_hot_labels, self.last_idx))
+ numerator = self.reduce_sum(label_weights * per_example_loss, ())
+ denominator = self.reduce_sum(label_weights, ()) + \
+ self.cast(F.tuple_to_array((1e-5,)), mstype.float32)
+ loss = numerator / denominator
+ return loss
+
+
+class TransformerNetworkWithLoss(nn.Cell):
+ """
+ Provide transformer training loss through network.
+
+ Args:
+ config (TransformerConfig): The config of Transformer.
+ is_training (bool): Specifies whether to use the training mode.
+ use_one_hot_embeddings (bool): Specifies whether to use one-hot for embeddings. Default: False.
+
+ Returns:
+ Tensor, the loss of the network.
+ """
+ def __init__(self, config, is_training, use_one_hot_embeddings=False):
+ super(TransformerNetworkWithLoss, self).__init__(auto_prefix=False)
+ self.transformer = TransformerModel(config, is_training, use_one_hot_embeddings)
+ self.loss = TransformerTrainingLoss(config)
+ self.cast = P.Cast()
+ self.shape = P.Shape()
+
+ def construct(self,
+ source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights):
+ """Transformer network with loss."""
+ prediction_scores = self.transformer(source_ids, source_mask, target_ids, target_mask)
+ seq_length = self.shape(source_ids)[1]
+ total_loss = self.loss(prediction_scores, label_ids, label_weights, seq_length)
+ return self.cast(total_loss, mstype.float32)
+
+
+class TransformerTrainOneStepCell(nn.TrainOneStepCell):
+ """
+ Encapsulation class of transformer 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.
+ sens (Number): The adjust parameter. Default: 1.0.
+ """
+ def __init__(self, network, optimizer, sens=1.0):
+ super(TransformerTrainOneStepCell, self).__init__(network, optimizer, sens)
+
+ self.cast = P.Cast()
+ self.hyper_map = C.HyperMap()
+
+ def set_sens(self, value):
+ self.sens = value
+
+ def construct(self,
+ source_eos_ids,
+ source_eos_mask,
+ target_sos_ids,
+ target_sos_mask,
+ target_eos_ids,
+ target_eos_mask,):
+ """Defines the computation performed."""
+ source_ids = source_eos_ids
+ source_mask = source_eos_mask
+ target_ids = target_sos_ids
+ target_mask = target_sos_mask
+ label_ids = target_eos_ids
+ label_weights = target_eos_mask
+
+ weights = self.weights
+ loss = self.network(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights)
+ grads = self.grad(self.network, weights)(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights,
+ self.cast(F.tuple_to_array((self.sens,)),
+ mstype.float32))
+ grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads)
+ # apply grad reducer on grads
+ grads = self.grad_reducer(grads)
+ succ = self.optimizer(grads)
+ return F.depend(loss, succ)
+
+
+grad_scale = C.MultitypeFuncGraph("grad_scale")
+reciprocal = P.Reciprocal()
+
+
+@grad_scale.register("Tensor", "Tensor")
+def tensor_grad_scale(scale, grad):
+ return grad * F.cast(reciprocal(scale), F.dtype(grad))
+
+_grad_overflow = C.MultitypeFuncGraph("_grad_overflow")
+grad_overflow = P.FloatStatus()
+
+@_grad_overflow.register("Tensor")
+def _tensor_grad_overflow(grad):
+ return grad_overflow(grad)
+
+class TransformerTrainOneStepWithLossScaleCell(nn.TrainOneStepWithLossScaleCell):
+ """
+ Encapsulation class of Transformer 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.
+ """
+ def __init__(self, network, optimizer, scale_update_cell=None):
+ super(TransformerTrainOneStepWithLossScaleCell, self).__init__(network, optimizer, scale_update_cell)
+ self.cast = P.Cast()
+ self.degree = 1
+ if self.reducer_flag:
+ self.degree = get_group_size()
+ self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree)
+
+ self.loss_scale = None
+ self.loss_scaling_manager = scale_update_cell
+ if scale_update_cell:
+ self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32))
+
+ def construct(self,
+ source_eos_ids,
+ source_eos_mask,
+ target_sos_ids,
+ target_sos_mask,
+ target_eos_ids,
+ target_eos_mask,
+ sens=None):
+ """Defines the computation performed."""
+ source_ids = source_eos_ids
+ source_mask = source_eos_mask
+ target_ids = target_sos_ids
+ target_mask = target_sos_mask
+ label_ids = target_eos_ids
+ label_weights = target_eos_mask
+
+ weights = self.weights
+ loss = self.network(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights)
+ if sens is None:
+ scaling_sens = self.loss_scale
+ else:
+ scaling_sens = sens
+ status, scaling_sens = self.start_overflow_check(loss, scaling_sens)
+ grads = self.grad(self.network, weights)(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights,
+ self.cast(scaling_sens,
+ mstype.float32))
+
+ # apply grad reducer on grads
+ grads = self.grad_reducer(grads)
+ grads = self.hyper_map(F.partial(grad_scale, scaling_sens * self.degree), grads)
+ grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads)
+
+ cond = self.get_overflow_status(status, grads)
+ overflow = cond
+ if sens is None:
+ overflow = self.loss_scaling_manager(self.loss_scale, cond)
+ if overflow:
+ succ = False
+ else:
+ succ = self.optimizer(grads)
+ ret = (loss, cond, scaling_sens)
+ return F.depend(ret, succ)
+
+
+cast = P.Cast()
+add_grads = C.MultitypeFuncGraph("add_grads")
+
+
+@add_grads.register("Tensor", "Tensor")
+def _add_grads(accu_grad, grad):
+ return accu_grad + cast(grad, mstype.float32)
+
+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(accu_grad, cast(grad, mstype.float32)))
+
+accumulate_accu_grads = C.MultitypeFuncGraph("accumulate_accu_grads")
+
+@accumulate_accu_grads.register("Tensor", "Tensor")
+def _accumulate_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 TransformerTrainAccumulationAllReducePostWithLossScaleCell(nn.Cell):
+ """
+ Encapsulation class of bert network training.
+
+ Append an optimizer to the training network after that the construct
+ function can be called to create the backward graph.
+
+ To mimic higher batch size, gradients are accumulated N times before weight update.
+
+ For distribution mode, allreduce will only be implemented in the weight updated step,
+ i.e. the sub-step after gradients accumulated N times.
+
+ 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.
+ accumulation_steps (int): Number of accumulation steps before gradient update. The global batch size =
+ batch_size * accumulation_steps. Default: 1.
+ """
+
+ def __init__(self, network, optimizer, scale_update_cell=None, accumulation_steps=8, enable_global_norm=False):
+ super(TransformerTrainAccumulationAllReducePostWithLossScaleCell, self).__init__(auto_prefix=False)
+ self.network = network
+ self.network.set_grad()
+ self.weights = optimizer.parameters
+ self.optimizer = optimizer
+ self.accumulation_steps = accumulation_steps
+ self.enable_global_norm = enable_global_norm
+ self.one = Tensor(np.array([1]).astype(np.int32))
+ self.zero = Tensor(np.array([0]).astype(np.int32))
+ self.local_step = Parameter(initializer(0, [1], mstype.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.grad = C.GradOperation(get_by_list=True, sens_param=True)
+ self.reducer_flag = False
+ self.parallel_mode = context.get_auto_parallel_context("parallel_mode")
+ if self.parallel_mode in [ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL]:
+ self.reducer_flag = True
+ self.grad_reducer = F.identity
+ self.degree = 1
+ if self.reducer_flag:
+ self.degree = get_group_size()
+ self.grad_reducer = DistributedGradReducer(optimizer.parameters, False, self.degree)
+ self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE)
+ self.overflow_reducer = F.identity
+ if self.is_distributed:
+ self.overflow_reducer = P.AllReduce()
+ self.cast = P.Cast()
+ self.alloc_status = P.NPUAllocFloatStatus()
+ self.get_status = P.NPUGetFloatStatus()
+ self.clear_status = P.NPUClearFloatStatus()
+ self.reduce_sum = P.ReduceSum(keep_dims=False)
+ self.base = Tensor(1, mstype.float32)
+ self.less_equal = P.LessEqual()
+ self.logical_or = P.LogicalOr()
+ self.not_equal = P.NotEqual()
+ self.select = P.Select()
+ self.reshape = P.Reshape()
+ self.hyper_map = C.HyperMap()
+ self.loss_scale = None
+ self.loss_scaling_manager = scale_update_cell
+ if scale_update_cell:
+ self.loss_scale = Parameter(Tensor(scale_update_cell.get_loss_scale(), dtype=mstype.float32))
+
+ def construct(self,
+ source_eos_ids,
+ source_eos_mask,
+ target_sos_ids,
+ target_sos_mask,
+ target_eos_ids,
+ target_eos_mask,
+ sens=None):
+ """Defines the computation performed."""
+ source_ids = source_eos_ids
+ source_mask = source_eos_mask
+ target_ids = target_sos_ids
+ target_mask = target_sos_mask
+ label_ids = target_eos_ids
+ label_weights = target_eos_mask
+
+ weights = self.weights
+ loss = self.network(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights)
+ if sens is None:
+ scaling_sens = self.loss_scale
+ else:
+ scaling_sens = sens
+ # alloc status and clear should be right before gradoperation
+ init = self.alloc_status()
+ init = F.depend(init, loss)
+ clear_status = self.clear_status(init)
+ scaling_sens = F.depend(scaling_sens, clear_status)
+ # update accumulation parameters
+ is_accu_step = self.not_equal(self.local_step, self.accumulation_steps)
+ self.local_step = self.select(is_accu_step, self.local_step + self.one, self.one)
+ self.accu_loss = self.select(is_accu_step, self.accu_loss + loss, loss)
+ mean_loss = self.accu_loss / self.local_step
+ is_accu_step = self.not_equal(self.local_step, self.accumulation_steps)
+
+ grads = self.grad(self.network, weights)(source_ids,
+ source_mask,
+ target_ids,
+ target_mask,
+ label_ids,
+ label_weights,
+ self.cast(scaling_sens,
+ mstype.float32))
+
+ accu_succ = self.hyper_map(accumulate_accu_grads, self.accu_grads, grads)
+ mean_loss = F.depend(mean_loss, accu_succ)
+
+ init = F.depend(init, mean_loss)
+ get_status = self.get_status(init)
+ init = F.depend(init, get_status)
+ flag_sum = self.reduce_sum(init, (0,))
+ overflow = self.less_equal(self.base, flag_sum)
+ overflow = self.logical_or(self.not_equal(self.accu_overflow, self.zero), overflow)
+ accu_overflow = self.select(overflow, self.one, self.zero)
+ self.accu_overflow = self.select(is_accu_step, accu_overflow, self.zero)
+
+ if is_accu_step:
+ succ = False
+ else:
+ # apply grad reducer on grads
+ grads = self.grad_reducer(self.accu_grads)
+ scaling = scaling_sens * self.degree * self.accumulation_steps
+ grads = self.hyper_map(F.partial(grad_scale, scaling), grads)
+ if self.enable_global_norm:
+ grads = C.clip_by_global_norm(grads, 1.0, None)
+ else:
+ grads = self.hyper_map(F.partial(clip_grad, GRADIENT_CLIP_TYPE, GRADIENT_CLIP_VALUE), grads)
+ accu_overflow = F.depend(accu_overflow, grads)
+ accu_overflow = self.overflow_reducer(accu_overflow)
+ overflow = self.less_equal(self.base, accu_overflow)
+ accu_succ = self.hyper_map(reset_accu_grads, self.accu_grads)
+ overflow = F.depend(overflow, accu_succ)
+ overflow = self.reshape(overflow, (()))
+ if sens is None:
+ overflow = self.loss_scaling_manager(self.loss_scale, overflow)
+ if overflow:
+ succ = False
+ else:
+ succ = self.optimizer(grads)
+
+ ret = (mean_loss, overflow, scaling_sens)
+ return F.depend(ret, succ)
diff --git a/tests/st/model_zoo_tests/transformer/src/transformer_model.py b/tests/st/model_zoo_tests/transformer/src/transformer_model.py
new file mode 100644
index 00000000000..5e0aa6aa5b7
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/transformer_model.py
@@ -0,0 +1,1153 @@
+# 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.
+# ============================================================================
+"""Transformer model."""
+
+import math
+import copy
+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 mindspore.common.parameter import Parameter
+from mindspore.ops.primitive import constexpr
+from .beam_search import BeamSearchDecoder, TileBeam
+from .weight_init import normal_weight, weight_variable
+
+class TransformerConfig:
+ """
+ Configuration for `Transformer`.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ seq_length (int): Length of input sequence. Default: 128.
+ vocab_size (int): The shape of each embedding vector. Default: 36560.
+ hidden_size (int): Size of the layers. Default: 1024.
+ num_hidden_layers (int): Number of hidden layers in the Transformer encoder/decoder
+ cell. Default: 6.
+ num_attention_heads (int): Number of attention heads in the Transformer
+ encoder/decoder cell. Default: 16.
+ intermediate_size (int): Size of intermediate layer in the Transformer
+ encoder/decoder cell. Default: 4096.
+ hidden_act (str): Activation function used in the Transformer encoder/decoder
+ cell. Default: "relu".
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.3.
+ attention_probs_dropout_prob (float): The dropout probability for
+ MultiheadAttention. Default: 0.3.
+ max_position_embeddings (int): Maximum length of sequences used in this
+ model. Default: 128.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ label_smoothing (float): label smoothing setting. Default: 0.1
+ beam_width (int): beam width setting. Default: 4
+ max_decode_length (int): max decode length in evaluation. Default: 80
+ length_penalty_weight (float): normalize scores of translations according to their length. Default: 1.0
+ dtype (:class:`mindspore.dtype`): Data type of the input. Default: mstype.float32.
+ compute_type (:class:`mindspore.dtype`): Compute type in Transformer. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ seq_length=128,
+ vocab_size=36560,
+ hidden_size=1024,
+ num_hidden_layers=6,
+ num_attention_heads=16,
+ intermediate_size=4096,
+ hidden_act="relu",
+ hidden_dropout_prob=0.3,
+ attention_probs_dropout_prob=0.3,
+ max_position_embeddings=128,
+ initializer_range=0.02,
+ label_smoothing=0.1,
+ beam_width=4,
+ max_decode_length=80,
+ length_penalty_weight=1.0,
+ dtype=mstype.float32,
+ compute_type=mstype.float32):
+ 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.hidden_act = hidden_act
+ self.intermediate_size = intermediate_size
+ self.hidden_dropout_prob = hidden_dropout_prob
+ self.attention_probs_dropout_prob = attention_probs_dropout_prob
+ self.max_position_embeddings = max_position_embeddings
+ self.initializer_range = initializer_range
+ self.label_smoothing = label_smoothing
+ self.beam_width = beam_width
+ self.max_decode_length = max_decode_length
+ self.length_penalty_weight = length_penalty_weight
+ self.dtype = dtype
+ self.compute_type = compute_type
+
+
+class EmbeddingLookup(nn.Cell):
+ """
+ A embeddings lookup table with a fixed dictionary and size.
+
+ Args:
+ vocab_size (int): Size of the dictionary of embeddings.
+ embedding_size (int): The size of each embedding vector.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ """
+ def __init__(self,
+ vocab_size,
+ embedding_size,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02):
+ super(EmbeddingLookup, self).__init__()
+ self.vocab_size = vocab_size
+ self.embedding_size = embedding_size
+ self.use_one_hot_embeddings = use_one_hot_embeddings
+ self.embedding_table = Parameter(normal_weight([vocab_size, embedding_size], embedding_size))
+ self.expand = P.ExpandDims()
+ self.shape_flat = (-1,)
+ self.gather = P.Gather()
+ self.one_hot = P.OneHot()
+ self.on_value = Tensor(1.0, mstype.float32)
+ self.off_value = Tensor(0.0, mstype.float32)
+ self.array_mul = P.MatMul()
+ self.reshape = P.Reshape()
+ self.shape = P.Shape()
+
+ def construct(self, input_ids):
+ """Get a embeddings lookup table with a fixed dictionary and size."""
+ input_shape = self.shape(input_ids)
+
+ flat_ids = self.reshape(input_ids, self.shape_flat)
+ if self.use_one_hot_embeddings:
+ one_hot_ids = self.one_hot(flat_ids, self.vocab_size, self.on_value, self.off_value)
+ output_for_reshape = self.array_mul(one_hot_ids, self.embedding_table)
+ else:
+ output_for_reshape = self.gather(self.embedding_table, flat_ids, 0)
+
+ out_shape = input_shape + (self.embedding_size,)
+ output = self.reshape(output_for_reshape, out_shape)
+ return output, self.embedding_table
+
+
+def position_encoding(length,
+ depth,
+ min_timescale=1,
+ max_timescale=1e4):
+ """
+ Create Tensor of sinusoids of different frequencies.
+
+ Args:
+ length (int): Length of the Tensor to create, i.e. Number of steps.
+ depth (int): Hidden size.
+ min_timescale (float): Default: 1.
+ max_timescale (float): Default: 10000.
+
+ Returns:
+ Tensor of shape (length, depth)
+ """
+ depth = depth // 2
+ positions = np.arange(length, dtype=np.float32)
+ log_timescale_increment = (np.log(max_timescale / min_timescale) / (depth - 1))
+ inv_timescales = min_timescale * np.exp(np.arange(depth, dtype=np.float32) * -log_timescale_increment)
+ scaled_time = np.expand_dims(positions, 1) * np.expand_dims(inv_timescales, 0)
+ x = np.concatenate([np.sin(scaled_time), np.cos(scaled_time)], axis=1)
+ return x
+
+
+class EmbeddingPostprocessor(nn.Cell):
+ """
+ Postprocessors apply positional embeddings to word embeddings.
+
+ Args:
+ embedding_size (int): The size of each embedding vector.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ max_position_embeddings (int): Maximum length of sequences used in this
+ model. Default: 128.
+ dropout_prob (float): The dropout probability. Default: 0.1.
+ """
+ def __init__(self,
+ embedding_size,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ max_position_embeddings=128,
+ dropout_prob=0.1):
+ super(EmbeddingPostprocessor, self).__init__()
+ self.scores_mul = Tensor([math.sqrt(float(embedding_size))], dtype=mstype.float32)
+ self.multiply = P.Mul()
+ self.add = P.Add()
+ self.dropout = nn.Dropout(1 - dropout_prob, dtype=mstype.float32)
+ self.use_dropout = dropout_prob > 0
+ self.expand_dims = P.ExpandDims()
+ self.position_embedding_table = Tensor(position_encoding(max_position_embeddings, embedding_size),
+ mstype.float32)
+ self.shape = P.Shape()
+
+ def construct(self, word_embeddings):
+ """Postprocessors apply positional embeddings to word embeddings."""
+ input_shape = self.shape(word_embeddings)
+ input_len = input_shape[1]
+
+ output = self.multiply(word_embeddings, self.scores_mul)
+
+ # add position embeddings
+ position_embeddings = self.position_embedding_table[0:input_len:1, ::]
+ position_embeddings = self.expand_dims(position_embeddings, 0)
+ output = self.add(output, position_embeddings)
+
+ if self.use_dropout:
+ output = self.dropout(output)
+ return output
+
+
+class CastWrapper(nn.Cell):
+ """
+ Cast wrapper.
+ """
+ def __init__(self, src_type=mstype.float32, dst_type=mstype.float32):
+ super(CastWrapper, self).__init__()
+ self.cast = P.Cast()
+ self.dst_type = dst_type
+
+ def construct(self, x):
+ return self.cast(x, self.dst_type)
+
+
+class LayerPreprocess(nn.Cell):
+ """
+ preprocess input of each layer.
+ """
+ def __init__(self,
+ in_channels=None):
+ super(LayerPreprocess, self).__init__()
+ self.layernorm = nn.LayerNorm((in_channels,))
+ self.cast = P.Cast()
+ self.get_dtype = P.DType()
+
+ def construct(self, input_tensor):
+ output = self.cast(input_tensor, mstype.float32)
+ output = self.layernorm(output)
+ output = self.cast(output, self.get_dtype(input_tensor))
+ return output
+
+
+class LayerPostprocess(nn.Cell):
+ """
+ postprocess output of each layer.
+ """
+ def __init__(self,
+ dropout_prob=0.1):
+ super(LayerPostprocess, self).__init__()
+ self.add = P.Add()
+ self.dropout = nn.Dropout(1 - dropout_prob)
+ self.use_dropout = dropout_prob > 0
+
+ def construct(self, hidden_tensor, input_tensor):
+ output = hidden_tensor
+ if self.use_dropout:
+ output = self.dropout(output)
+ output = self.add(output, input_tensor)
+ return output
+
+
+class MultiheadAttention(nn.Cell):
+ """
+ Apply multi-headed attention from "from_tensor" to "to_tensor".
+
+ Args:
+ batch_size (int): Batch size of input datasets.
+ from_tensor_width (int): Size of last dim of from_tensor.
+ to_tensor_width (int): Size of last dim of to_tensor.
+ from_seq_length (int): Length of from_tensor sequence.
+ to_seq_length (int): Length of to_tensor sequence.
+ num_attention_heads (int): Number of attention heads. Default: 1.
+ size_per_head (int): Size of each attention head. Default: 512.
+ query_act (str): Activation function for the query transform. Default: None.
+ key_act (str): Activation function for the key transform. Default: None.
+ value_act (str): Activation function for the value transform. Default: None.
+ has_attention_mask (bool): Specifies whether to use attention mask. Default: False.
+ attention_probs_dropout_prob (float): The dropout probability for
+ MultiheadAttention. Default: 0.0.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ do_return_2d_tensor (bool): True for return 2d tensor. False for return 3d
+ tensor. Default: False.
+ compute_type (:class:`mindspore.dtype`): Compute type in MultiheadAttention. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ from_tensor_width,
+ to_tensor_width,
+ out_tensor_width,
+ num_attention_heads=1,
+ size_per_head=512,
+ query_act=None,
+ key_act=None,
+ value_act=None,
+ out_act=None,
+ has_attention_mask=True,
+ attention_probs_dropout_prob=0.0,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ do_return_2d_tensor=True,
+ compute_type=mstype.float32):
+ super(MultiheadAttention, self).__init__()
+ self.batch_size = batch_size
+ self.num_attention_heads = num_attention_heads
+ self.size_per_head = size_per_head
+ self.has_attention_mask = has_attention_mask
+ assert has_attention_mask
+ self.use_one_hot_embeddings = use_one_hot_embeddings
+ self.initializer_range = initializer_range
+ self.do_return_2d_tensor = do_return_2d_tensor
+
+ self.scores_mul = Tensor([1.0 / math.sqrt(float(self.size_per_head))], dtype=compute_type)
+ self.reshape = P.Reshape()
+ self.shape_from_2d = (-1, from_tensor_width)
+ self.shape_to_2d = (-1, to_tensor_width)
+ units = num_attention_heads * size_per_head
+ self.query_layer = nn.Dense(from_tensor_width,
+ units,
+ activation=query_act,
+ has_bias=False,
+ weight_init=weight_variable([units, from_tensor_width])).to_float(compute_type)
+ self.key_layer = nn.Dense(to_tensor_width,
+ units,
+ activation=key_act,
+ has_bias=False,
+ weight_init=weight_variable([units, to_tensor_width])).to_float(compute_type)
+ self.value_layer = nn.Dense(to_tensor_width,
+ units,
+ activation=value_act,
+ has_bias=False,
+ weight_init=weight_variable([units, to_tensor_width])).to_float(compute_type)
+ self.out_layer = nn.Dense(units,
+ out_tensor_width,
+ activation=out_act,
+ has_bias=False,
+ weight_init=weight_variable([out_tensor_width, units])).to_float(compute_type)
+
+ self.matmul_trans_b = P.BatchMatMul(transpose_b=True)
+ self.multiply = P.Mul()
+ self.transpose = P.Transpose()
+ self.trans_shape = (0, 2, 1, 3)
+ self.trans_shape_relative = (2, 0, 1, 3)
+ self.trans_shape_position = (1, 2, 0, 3)
+ self.multiply_data = Tensor([-10000.0,], dtype=compute_type)
+ self.batch_num = batch_size * num_attention_heads
+ self.matmul = P.BatchMatMul()
+
+ self.softmax = nn.Softmax()
+ self.dropout = nn.Dropout(1 - attention_probs_dropout_prob)
+ self.use_dropout = attention_probs_dropout_prob > 0
+
+ if self.has_attention_mask:
+ self.expand_dims = P.ExpandDims()
+ self.sub = P.Sub()
+ self.add = P.Add()
+ self.cast = P.Cast()
+ self.get_dtype = P.DType()
+
+ self.cast_compute_type = CastWrapper(dst_type=compute_type)
+ self.softmax_cast = P.Cast()
+
+ def construct(self, from_tensor, to_tensor, seq_length, enc_seq_length, attention_mask=None):
+ """Apply multihead attention."""
+ from_seq_length = seq_length
+ to_seq_length = enc_seq_length
+ shape_from = (self.batch_size, from_seq_length, self.num_attention_heads, self.size_per_head)
+ shape_to = (self.batch_size, to_seq_length, self.num_attention_heads, self.size_per_head)
+ if self.do_return_2d_tensor:
+ shape_return = (self.batch_size * from_seq_length, self.num_attention_heads * self.size_per_head)
+ if from_seq_length == -1:
+ shape_return = (-1, self.num_attention_heads * self.size_per_head)
+ else:
+ shape_return = (self.batch_size, from_seq_length, self.num_attention_heads * self.size_per_head)
+
+ # reshape 2d/3d input tensors to 2d
+ from_tensor_2d = self.reshape(from_tensor, self.shape_from_2d)
+ to_tensor_2d = self.reshape(to_tensor, self.shape_to_2d)
+ query_out = self.query_layer(from_tensor_2d)
+ key_out = self.key_layer(to_tensor_2d)
+ value_out = self.value_layer(to_tensor_2d)
+
+ query_layer = self.reshape(query_out, shape_from)
+ query_layer = self.transpose(query_layer, self.trans_shape)
+ key_layer = self.reshape(key_out, shape_to)
+ key_layer = self.transpose(key_layer, self.trans_shape)
+
+ attention_scores = self.matmul_trans_b(query_layer, key_layer)
+ attention_scores = self.multiply(attention_scores, self.scores_mul)
+
+ if self.has_attention_mask:
+ 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)))
+ adder = self.multiply(multiply_out, self.multiply_data)
+ attention_scores = self.add(adder, attention_scores)
+
+ attention_scores = self.softmax_cast(attention_scores, mstype.float32)
+ attention_probs = self.softmax(attention_scores)
+ attention_probs = self.softmax_cast(attention_probs, self.get_dtype(key_layer))
+ if self.use_dropout:
+ attention_probs = self.dropout(attention_probs)
+
+ value_layer = self.reshape(value_out, shape_to)
+ value_layer = self.transpose(value_layer, self.trans_shape)
+ context_layer = self.matmul(attention_probs, value_layer)
+
+ context_layer = self.transpose(context_layer, self.trans_shape)
+ context_layer = self.reshape(context_layer, shape_return)
+ context_layer = self.out_layer(context_layer)
+ return context_layer
+
+
+class SelfAttention(nn.Cell):
+ """
+ Apply self-attention.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ from_seq_length (int): Length of query sequence.
+ to_seq_length (int): Length of memory sequence.
+ hidden_size (int): Size of attention layers.
+ num_attention_heads (int): Number of attention heads. Default: 16.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.1.
+ use_one_hot_embeddings (bool): Specifies whether to use one_hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ has_attention_mask (bool): Specifies whether has attention mask. Default: True.
+ is_encdec_att (bool): Specifies whether query sequence and memory sequence are different. Default: False.
+ compute_type (:class:`mindspore.dtype`): Compute type in MultiheadAttention. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size,
+ num_attention_heads=16,
+ attention_probs_dropout_prob=0.1,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ has_attention_mask=True,
+ is_encdec_att=False,
+ compute_type=mstype.float32):
+ super(SelfAttention, 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.size_per_head = int(hidden_size / num_attention_heads)
+ self.is_encdec_att = is_encdec_att
+
+ self.attention = MultiheadAttention(
+ batch_size=batch_size,
+ from_tensor_width=hidden_size,
+ to_tensor_width=hidden_size,
+ out_tensor_width=hidden_size,
+ num_attention_heads=num_attention_heads,
+ size_per_head=self.size_per_head,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ has_attention_mask=has_attention_mask,
+ do_return_2d_tensor=True,
+ compute_type=compute_type)
+
+ self.preprocess = LayerPreprocess(in_channels=hidden_size)
+ self.postprocess = LayerPostprocess(dropout_prob=hidden_dropout_prob)
+
+ self.reshape = P.Reshape()
+ self.shape = (-1, hidden_size)
+ def construct(self, input_tensor, memory_tensor, attention_mask, seq_length, enc_seq_length):
+ """Apply self-attention."""
+ input_tensor = self.reshape(input_tensor, self.shape)
+ memory_tensor = self.reshape(memory_tensor, self.shape)
+
+ output = self.preprocess(input_tensor)
+
+ if not self.is_encdec_att:
+ memory_tensor = output
+
+ attention_output = self.attention(output, memory_tensor, seq_length, enc_seq_length, attention_mask)
+ output = self.postprocess(attention_output, input_tensor)
+ return output
+
+
+class FeedForward(nn.Cell):
+ """
+ Apply two-layer feed forward
+
+ Args:
+ in_channels (int): Size of the input layer.
+ hidden_size (int): Size of the hidden layer.
+ out_channels (int): Size of the output layers.
+ hidden_act (str): name of the activation function. Default: relu
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ compute_type (:class:`mindspore.dtype`): Compute type in FeedForward. Default: mstype.float32.
+ """
+ def __init__(self,
+ in_channels,
+ hidden_size,
+ out_channels,
+ hidden_act="relu",
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ compute_type=mstype.float32):
+ super(FeedForward, self).__init__()
+
+ self.conv1 = nn.Dense(in_channels,
+ hidden_size,
+ activation=hidden_act,
+ weight_init=weight_variable([hidden_size, in_channels])).to_float(compute_type)
+ self.conv2 = nn.Dense(hidden_size,
+ out_channels,
+ weight_init=weight_variable([out_channels, hidden_size])).to_float(compute_type)
+
+ self.preprocess = LayerPreprocess(in_channels=in_channels)
+ self.postprocess = LayerPostprocess(dropout_prob=hidden_dropout_prob)
+
+ self.reshape = P.Reshape()
+ self.shape = (-1, in_channels)
+ self.dropout = nn.Dropout(1 - hidden_dropout_prob)
+ self.use_dropout = hidden_dropout_prob > 0
+
+ def construct(self, input_tensor):
+ input_tensor = self.reshape(input_tensor, self.shape)
+ output = self.preprocess(input_tensor)
+ output = self.conv1(output)
+ if self.use_dropout:
+ output = self.dropout(output)
+ output = self.conv2(output)
+ output = self.postprocess(output, input_tensor)
+ return output
+
+
+class EncoderCell(nn.Cell):
+ """
+ Encoder cells used in Transformer.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ hidden_size (int): Size of the encoder layers. Default: 1024.
+ seq_length (int): Length of input sequence. Default: 128.
+ num_attention_heads (int): Number of attention heads. Default: 16.
+ intermediate_size (int): Size of intermediate layer. Default: 4096.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.02.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.1.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ hidden_act (str): Activation function. Default: "relu".
+ compute_type (:class:`mindspore.dtype`): Compute type in attention. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size=1024,
+ num_attention_heads=16,
+ intermediate_size=4096,
+ attention_probs_dropout_prob=0.1,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ hidden_act="relu",
+ compute_type=mstype.float32):
+ super(EncoderCell, self).__init__()
+ self.attention = SelfAttention(
+ batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ is_encdec_att=False,
+ compute_type=compute_type)
+ self.feedforward = FeedForward(
+ in_channels=hidden_size,
+ hidden_size=intermediate_size,
+ out_channels=hidden_size,
+ hidden_act=hidden_act,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ compute_type=compute_type)
+
+ def construct(self, hidden_states, attention_mask, seq_length):
+ # self-attention with ln, res
+ attention_output = self.attention(hidden_states, hidden_states, attention_mask, seq_length, seq_length)
+ # feed forward with ln, res
+ output = self.feedforward(attention_output)
+ return output
+
+
+class TransformerEncoder(nn.Cell):
+ """
+ Multi-layer transformer encoder.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ hidden_size (int): Size of the encoder layers.
+ seq_length (int): Length of input sequence.
+ num_hidden_layers (int): Number of hidden layers in encoder cells.
+ num_attention_heads (int): Number of attention heads in encoder cells. Default: 16.
+ intermediate_size (int): Size of intermediate layer in encoder cells. Default: 4096.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.1.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1..
+ hidden_act (str): Activation function used in the encoder cells. Default: "gelu".
+ compute_type (:class:`mindspore.dtype`): Compute type. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size,
+ num_hidden_layers,
+ num_attention_heads=16,
+ intermediate_size=4096,
+ attention_probs_dropout_prob=0.1,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ hidden_act="relu",
+ compute_type=mstype.float32):
+ super(TransformerEncoder, self).__init__()
+ self.num_hidden_layers = num_hidden_layers
+ self.batch_size = batch_size
+ self.hidden_size = hidden_size
+
+ layers = []
+ for _ in range(num_hidden_layers):
+ layer = EncoderCell(batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ intermediate_size=intermediate_size,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ hidden_act=hidden_act,
+ compute_type=compute_type)
+ layers.append(layer)
+ self.layers = nn.CellList(layers)
+
+ self.layer_preprocess = LayerPreprocess(in_channels=hidden_size)
+
+ self.reshape = P.Reshape()
+ self.shape = (-1, hidden_size)
+
+ def construct(self, input_tensor, attention_mask, seq_length):
+ """Apply encoder."""
+ out_shape = (self.batch_size, seq_length, self.hidden_size)
+ prev_output = self.reshape(input_tensor, self.shape)
+
+ for layer_module in self.layers:
+ layer_output = layer_module(prev_output, attention_mask, seq_length)
+ prev_output = layer_output
+
+ prev_output = self.layer_preprocess(prev_output)
+ output = self.reshape(prev_output, out_shape)
+ return output
+
+
+class DecoderCell(nn.Cell):
+ """
+ decoder cells used in Transformer.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ hidden_size (int): Size of the Transformer decoder layers. Default: 1024.
+ seq_length (int): Length of input sequence. Default: 128.
+ enc_seq_length (int): Length of source sentences. Default:128
+ num_attention_heads (int): Number of attention heads. Default: 12.
+ intermediate_size (int): Size of intermediate layer. Default: 4096.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.02.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ hidden_act (str): Activation function. Default: "relu".
+ compute_type (:class:`mindspore.dtype`): Compute type in attention. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size=1024,
+ num_attention_heads=12,
+ intermediate_size=4096,
+ attention_probs_dropout_prob=0.02,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ hidden_act="relu",
+ compute_type=mstype.float32):
+ super(DecoderCell, self).__init__()
+ self.self_attention = SelfAttention(
+ batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ is_encdec_att=False,
+ hidden_dropout_prob=hidden_dropout_prob,
+ compute_type=compute_type)
+ self.cross_attention = SelfAttention(
+ batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ is_encdec_att=True,
+ hidden_dropout_prob=hidden_dropout_prob,
+ compute_type=compute_type)
+ self.feedforward = FeedForward(
+ in_channels=hidden_size,
+ hidden_size=intermediate_size,
+ out_channels=hidden_size,
+ hidden_act=hidden_act,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ compute_type=compute_type)
+
+ def construct(self, hidden_states, attention_mask, enc_states, enc_attention_mask, seq_length, enc_seq_length):
+ # self-attention with ln, res
+ attention_output = self.self_attention(hidden_states, hidden_states, attention_mask, seq_length, seq_length)
+ # cross-attention with ln, res
+ attention_output = self.cross_attention(attention_output, enc_states, enc_attention_mask,
+ seq_length, enc_seq_length)
+ # feed forward with ln, res
+ output = self.feedforward(attention_output)
+ return output
+
+
+class TransformerDecoder(nn.Cell):
+ """
+ Multi-layer transformer decoder.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ hidden_size (int): Size of the encoder layers.
+ seq_length (int): Length of input sequence.
+ enc_seq_length (int): Length of source sentences.
+ num_hidden_layers (int): Number of hidden layers in encoder cells.
+ num_attention_heads (int): Number of attention heads in encoder cells. Default: 16.
+ intermediate_size (int): Size of intermediate layer in encoder cells. Default: 4096.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.1.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ hidden_act (str): Activation function used in the encoder cells. Default: "gelu".
+ compute_type (:class:`mindspore.dtype`): Compute type. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size,
+ num_hidden_layers,
+ num_attention_heads=16,
+ intermediate_size=4096,
+ attention_probs_dropout_prob=0.1,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.1,
+ hidden_act="relu",
+ compute_type=mstype.float32):
+ super(TransformerDecoder, self).__init__()
+ self.num_hidden_layers = num_hidden_layers
+
+ layers = []
+ for _ in range(num_hidden_layers):
+ layer = DecoderCell(batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ intermediate_size=intermediate_size,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ hidden_act=hidden_act,
+ compute_type=compute_type)
+ layers.append(layer)
+ self.layers = nn.CellList(layers)
+
+ self.layer_preprocess = LayerPreprocess(in_channels=hidden_size)
+
+ self.reshape = P.Reshape()
+ self.shape = (-1, hidden_size)
+ self.hidden_size = hidden_size
+ self.batch_size = batch_size
+
+ def construct(self, input_tensor, attention_mask, enc_states, enc_attention_mask, seq_length, enc_seq_length):
+ """Apply decoder."""
+ out_shape = (self.batch_size, seq_length, self.hidden_size)
+ prev_output = self.reshape(input_tensor, self.shape)
+
+ for layer_module in self.layers:
+ layer_output = layer_module(prev_output, attention_mask, enc_states, enc_attention_mask,
+ seq_length, enc_seq_length)
+ prev_output = layer_output
+
+ prev_output = self.layer_preprocess(prev_output)
+ output = self.reshape(prev_output, out_shape)
+ return output
+
+
+class CreateAttentionMaskFromInputMask(nn.Cell):
+ """
+ Create attention mask according to input mask.
+
+ Args:
+ config (:class:`TransformerConfig`): Configuration for Transformer.
+ """
+ def __init__(self):
+ super(CreateAttentionMaskFromInputMask, self).__init__()
+ self.cast = P.Cast()
+ self.reshape = P.Reshape()
+ self.shape = P.Shape()
+ self.batch_matmul = P.BatchMatMul()
+
+ def construct(self, input_mask):
+ """Create attention mask according to input mask."""
+ input_shape = self.shape(input_mask)
+ shape_right = (input_shape[0], 1, input_shape[1])
+ shape_left = input_shape + (1,)
+
+ input_mask = self.cast(input_mask, mstype.float32)
+ mask_left = self.reshape(input_mask, shape_left)
+ mask_right = self.reshape(input_mask, shape_right)
+ attention_mask = self.batch_matmul(mask_left, mask_right)
+
+ return attention_mask
+
+
+class PredLogProbs(nn.Cell):
+ """
+ Get log probs.
+
+ Args:
+ batch_size (int): Batch size.
+ seq_length (int): Length of input sequence.
+ width (int): Hidden size.
+ compute_type (:class:`mindspore.dtype`): Compute type. Default: mstype.float32.
+ dtype (:class:`mindspore.dtype`): Compute type to compute log_softmax. Default: mstype.float32.
+ """
+ def __init__(self,
+ batch_size,
+ width,
+ compute_type=mstype.float32,
+ dtype=mstype.float32):
+ super(PredLogProbs, self).__init__()
+ self.batch_size = batch_size
+ self.width = width
+ self.compute_type = compute_type
+ self.dtype = dtype
+
+ self.reshape = P.Reshape()
+ self.matmul = P.MatMul(transpose_b=True)
+ self.log_softmax = nn.LogSoftmax(axis=-1)
+ self.cast = P.Cast()
+
+ def construct(self,
+ input_tensor,
+ output_weights,
+ seq_length):
+ """Get log probs."""
+ shape_flat_sequence_tensor = (self.batch_size * seq_length, self.width)
+
+ input_tensor = self.reshape(input_tensor, shape_flat_sequence_tensor)
+ input_tensor = self.cast(input_tensor, self.compute_type)
+ output_weights = self.cast(output_weights, self.compute_type)
+
+ logits = self.matmul(input_tensor, output_weights)
+ logits = self.cast(logits, self.dtype)
+
+ log_probs = self.log_softmax(logits)
+ return log_probs
+
+
+class TransformerDecoderStep(nn.Cell):
+ """
+ Multi-layer transformer decoder step.
+
+ Args:
+ batch_size (int): Batch size of input dataset.
+ hidden_size (int): Size of the encoder layers.
+ max_decode_length (int): Max decode length.
+ enc_seq_length (int): Length of source sentences.
+ num_hidden_layers (int): Number of hidden layers in encoder cells.
+ num_attention_heads (int): Number of attention heads in encoder cells. Default: 16.
+ intermediate_size (int): Size of intermediate layer in encoder cells. Default: 4096.
+ attention_probs_dropout_prob (float): The dropout probability for
+ SelfAttention. Default: 0.1.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ initializer_range (float): Initialization value of TruncatedNormal. Default: 0.02.
+ hidden_dropout_prob (float): The dropout probability for hidden outputs. Default: 0.1.
+ hidden_act (str): Activation function used in the encoder cells. Default: "gelu".
+ compute_type (:class:`mindspore.dtype`): Compute type. Default: mstype.float32.
+ embedding_lookup (:class:`EmbeddingLookup`): Embedding lookup module.
+ embedding_processor (:class:`EmbeddingPostprocessor`) Embedding postprocessor module.
+ projection (:class:`PredLogProbs`): PredLogProbs module
+ """
+ def __init__(self,
+ batch_size,
+ hidden_size,
+ max_decode_length,
+ num_hidden_layers,
+ num_attention_heads=16,
+ intermediate_size=4096,
+ attention_probs_dropout_prob=0.3,
+ use_one_hot_embeddings=False,
+ initializer_range=0.02,
+ hidden_dropout_prob=0.3,
+ hidden_act="relu",
+ compute_type=mstype.float32,
+ embedding_lookup=None,
+ embedding_processor=None,
+ projection=None):
+ super(TransformerDecoderStep, self).__init__(auto_prefix=False)
+ self.num_hidden_layers = num_hidden_layers
+
+ self.tfm_embedding_lookup = embedding_lookup
+ self.tfm_embedding_processor = embedding_processor
+ self.projection = projection
+
+ self.tfm_decoder = TransformerDecoder(
+ batch_size=batch_size,
+ hidden_size=hidden_size,
+ num_attention_heads=num_attention_heads,
+ num_hidden_layers=num_hidden_layers,
+ intermediate_size=intermediate_size,
+ attention_probs_dropout_prob=attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=initializer_range,
+ hidden_dropout_prob=hidden_dropout_prob,
+ hidden_act=hidden_act,
+ compute_type=compute_type)
+
+ self.ones_like = P.OnesLike()
+ self.shape = P.Shape()
+
+ self._create_attention_mask_from_input_mask = CreateAttentionMaskFromInputMask()
+ self.expand = P.ExpandDims()
+ self.multiply = P.Mul()
+
+ ones = np.ones(shape=(max_decode_length, max_decode_length))
+ self.future_mask = Tensor(np.tril(ones), dtype=mstype.float32)
+
+ self.cast_compute_type = CastWrapper(dst_type=compute_type)
+
+ def construct(self, input_ids, enc_states, enc_attention_mask, seq_length):
+ """
+ Multi-layer transformer decoder step.
+ input_ids: [batch_size * beam_width]
+ """
+ # process embedding
+ input_embedding, embedding_tables = self.tfm_embedding_lookup(input_ids)
+ input_embedding = self.tfm_embedding_processor(input_embedding)
+ input_embedding = self.cast_compute_type(input_embedding)
+
+ input_shape = self.shape(input_ids)
+ input_len = input_shape[1]
+ future_mask = self.future_mask[0:input_len:1, 0:input_len:1]
+
+ input_mask = self.ones_like(input_ids)
+ input_mask = self._create_attention_mask_from_input_mask(input_mask)
+ input_mask = self.multiply(input_mask, self.expand(future_mask, 0))
+ input_mask = self.cast_compute_type(input_mask)
+
+ enc_attention_mask = enc_attention_mask[::, 0:input_len:1, ::]
+
+ # call TransformerDecoder
+ decoder_output = self.tfm_decoder(input_embedding, input_mask, enc_states, enc_attention_mask, -1, seq_length)
+
+ # take the last step
+ decoder_output = decoder_output[::, input_len-1:input_len:1, ::]
+
+ # projection and log_prob
+ log_probs = self.projection(decoder_output, embedding_tables, 1)
+
+ return log_probs
+
+
+@constexpr
+def convert_np_to_tensor_encoder(seq_length):
+ ones = np.ones(shape=(seq_length, seq_length))
+ return Tensor(np.tril(ones), dtype=mstype.float32)
+
+
+class TransformerModel(nn.Cell):
+ """
+ Transformer with encoder and decoder.
+
+ Args:
+ config (Class): Configuration for Transformer.
+ is_training (bool): True for training mode. False for eval mode.
+ use_one_hot_embeddings (bool): Specifies whether to use one hot encoding form. Default: False.
+ """
+ def __init__(self,
+ config,
+ is_training,
+ use_one_hot_embeddings=False):
+ super(TransformerModel, self).__init__()
+ config = copy.deepcopy(config)
+ self.is_training = is_training
+ if not is_training:
+ config.hidden_dropout_prob = 0.0
+ config.attention_probs_dropout_prob = 0.0
+
+ self.batch_size = config.batch_size
+ self.hidden_size = config.hidden_size
+ self.num_hidden_layers = config.num_hidden_layers
+ self.embedding_size = config.hidden_size
+
+ self.last_idx = self.num_hidden_layers - 1
+ self.beam_width = config.beam_width
+ self.max_decode_length = config.max_decode_length
+
+ self.tfm_embedding_lookup = EmbeddingLookup(
+ vocab_size=config.vocab_size,
+ embedding_size=self.embedding_size,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=config.initializer_range)
+ self.tfm_embedding_postprocessor_for_encoder = EmbeddingPostprocessor(
+ embedding_size=self.embedding_size,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=0.02,
+ max_position_embeddings=config.max_position_embeddings,
+ dropout_prob=config.hidden_dropout_prob)
+ self.tfm_embedding_postprocessor_for_decoder = EmbeddingPostprocessor(
+ embedding_size=self.embedding_size,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=0.02,
+ max_position_embeddings=config.max_position_embeddings,
+ dropout_prob=config.hidden_dropout_prob)
+ self.tfm_encoder = TransformerEncoder(
+ batch_size=self.batch_size,
+ hidden_size=self.hidden_size,
+ num_attention_heads=config.num_attention_heads,
+ num_hidden_layers=self.num_hidden_layers,
+ intermediate_size=config.intermediate_size,
+ attention_probs_dropout_prob=config.attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=config.initializer_range,
+ hidden_dropout_prob=config.hidden_dropout_prob,
+ hidden_act=config.hidden_act,
+ compute_type=config.compute_type)
+
+ if is_training:
+ self.projection = PredLogProbs(
+ batch_size=self.batch_size,
+ width=self.hidden_size,
+ compute_type=config.compute_type,
+ dtype=config.dtype)
+ self.tfm_decoder = TransformerDecoder(
+ batch_size=self.batch_size,
+ hidden_size=self.hidden_size,
+ num_attention_heads=config.num_attention_heads,
+ num_hidden_layers=self.num_hidden_layers,
+ intermediate_size=config.intermediate_size,
+ attention_probs_dropout_prob=config.attention_probs_dropout_prob,
+ use_one_hot_embeddings=use_one_hot_embeddings,
+ initializer_range=config.initializer_range,
+ hidden_dropout_prob=config.hidden_dropout_prob,
+ hidden_act=config.hidden_act,
+ compute_type=config.compute_type)
+ else:
+ self.projection = PredLogProbs(
+ batch_size=self.batch_size * config.beam_width,
+ width=self.hidden_size,
+ compute_type=config.compute_type,
+ dtype=config.dtype)
+ self.tfm_decoder = TransformerDecoderStep(
+ batch_size=self.batch_size * config.beam_width,
+ hidden_size=self.hidden_size,
+ max_decode_length=config.max_decode_length,
+ num_hidden_layers=config.num_hidden_layers,
+ num_attention_heads=config.num_attention_heads,
+ intermediate_size=config.intermediate_size,
+ attention_probs_dropout_prob=config.attention_probs_dropout_prob,
+ use_one_hot_embeddings=False,
+ initializer_range=config.initializer_range,
+ hidden_dropout_prob=config.hidden_dropout_prob,
+ hidden_act=config.hidden_act,
+ compute_type=config.compute_type,
+ embedding_lookup=self.tfm_embedding_lookup,
+ embedding_processor=self.tfm_embedding_postprocessor_for_decoder,
+ projection=self.projection)
+ self.tfm_decoder = BeamSearchDecoder(
+ batch_size=config.batch_size,
+ seq_length=config.seq_length,
+ vocab_size=config.vocab_size,
+ decoder=self.tfm_decoder,
+ beam_width=config.beam_width,
+ length_penalty_weight=config.length_penalty_weight,
+ max_decode_length=config.max_decode_length)
+
+ self.tfm_decoder.add_flags(loop_can_unroll=True)
+ self.tile_beam = TileBeam(beam_width=self.beam_width)
+ ones = np.ones(shape=(self.batch_size, self.max_decode_length))
+ self.encdec_mask = Tensor(ones, mstype.float32)
+
+ self.cast = P.Cast()
+ self.dtype = config.dtype
+ self.cast_compute_type = CastWrapper(dst_type=config.compute_type)
+ self.expand = P.ExpandDims()
+ self.multiply = P.Mul()
+ self.shape = P.Shape()
+
+ self._create_attention_mask_from_input_mask = CreateAttentionMaskFromInputMask()
+
+ def construct(self, source_ids, source_mask, target_ids=None, target_mask=None):
+ """Transformer with encoder and decoder."""
+ seq_length = self.shape(source_ids)[1]
+
+ # process source sentence
+ src_word_embeddings, embedding_tables = self.tfm_embedding_lookup(source_ids)
+ src_embedding_output = self.tfm_embedding_postprocessor_for_encoder(src_word_embeddings)
+ # attention mask [batch_size, seq_length, seq_length]
+ enc_attention_mask = self._create_attention_mask_from_input_mask(source_mask)
+ # transformer encoder
+ encoder_output = self.tfm_encoder(self.cast_compute_type(src_embedding_output),
+ self.cast_compute_type(enc_attention_mask),
+ seq_length)
+
+ if self.is_training:
+ future_mask = convert_np_to_tensor_encoder(seq_length)
+ # process target sentence
+ tgt_word_embeddings, _ = self.tfm_embedding_lookup(target_ids)
+ tgt_embedding_output = self.tfm_embedding_postprocessor_for_decoder(tgt_word_embeddings)
+ # attention mask [batch_size, seq_length, seq_length]
+ tgt_attention_mask = self._create_attention_mask_from_input_mask(target_mask)
+ tgt_attention_mask = self.multiply(tgt_attention_mask, self.expand(future_mask, 0))
+ # transformer decoder
+ decoder_output = self.tfm_decoder(self.cast_compute_type(tgt_embedding_output),
+ self.cast_compute_type(tgt_attention_mask),
+ encoder_output, enc_attention_mask,
+ seq_length, seq_length)
+ # calculate logits and log_probs
+ log_probs = self.projection(decoder_output, embedding_tables, seq_length)
+ ret = log_probs
+ else:
+ beam_encoder_output = self.tile_beam(encoder_output)
+
+ enc_attention_mask = self.multiply(enc_attention_mask[::, 0:1:1, ::], self.expand(self.encdec_mask, -1))
+
+ beam_enc_attention_mask = self.tile_beam(enc_attention_mask)
+ beam_enc_attention_mask = self.cast_compute_type(beam_enc_attention_mask)
+ predicted_ids = self.tfm_decoder(beam_encoder_output, beam_enc_attention_mask)
+ ret = predicted_ids
+ return ret
diff --git a/tests/st/model_zoo_tests/transformer/src/weight_init.py b/tests/st/model_zoo_tests/transformer/src/weight_init.py
new file mode 100644
index 00000000000..460a1c67c43
--- /dev/null
+++ b/tests/st/model_zoo_tests/transformer/src/weight_init.py
@@ -0,0 +1,52 @@
+# 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.
+# ============================================================================
+"""Weight init utilities."""
+
+import math
+import numpy as np
+from mindspore.common.tensor import Tensor
+
+def _average_units(shape):
+ """
+ Average shape dim.
+ """
+ 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):
+ ones = np.ones(shape).astype(np.float32)
+ return Tensor(ones)
+
+def zero_weight(shape):
+ zeros = np.zeros(shape).astype(np.float32)
+ return Tensor(zeros)
+
+def normal_weight(shape, num_units):
+ norm = np.random.normal(0.0, num_units**-0.5, shape).astype(np.float32)
+ return Tensor(norm)
+
\ No newline at end of file
diff --git a/tests/st/model_zoo_tests/transformer/test_transformer.py b/tests/st/model_zoo_tests/transformer/test_transformer.py
index 1a981cc63ec..7ee8769944a 100644
--- a/tests/st/model_zoo_tests/transformer/test_transformer.py
+++ b/tests/st/model_zoo_tests/transformer/test_transformer.py
@@ -27,12 +27,10 @@ from mindspore.train.callback import Callback
import mindspore.dataset as ds
import mindspore.dataset.transforms.c_transforms as deC
from mindspore import context
-from model_zoo.official.nlp.transformer.src.transformer_model import TransformerConfig
-from model_zoo.official.nlp.transformer.src.transformer_for_train import TransformerNetworkWithLoss, \
- TransformerTrainOneStepWithLossScaleCell
-from model_zoo.official.nlp.transformer.src.config import cfg, transformer_net_cfg
-from model_zoo.official.nlp.transformer.src.lr_schedule import create_dynamic_lr
-
+from src.transformer_model import TransformerConfig
+from src.transformer_for_train import TransformerNetworkWithLoss, TransformerTrainOneStepWithLossScaleCell
+from src.config import cfg, transformer_net_cfg
+from src.lr_schedule import create_dynamic_lr
from tests.st.model_zoo_tests import utils
DATA_DIR = ["/home/workspace/mindspore_dataset/transformer/test-mindrecord"]
@@ -216,14 +214,14 @@ def test_transformer_export_mindir():
export_file = "transformer80_bs_0"
ckpt_path = os.path.join(utils.ckpt_root, "transformer/transformer_trained.ckpt")
print("ckpt_path:", ckpt_path)
- old_list = ["'model_file': '/your/path/checkpoint_file'"]
- new_list = ["'model_file': '{}'".format(ckpt_path)]
- utils.exec_sed_command(old_list, new_list, os.path.join(cur_model_path, "src/eval_config.py"))
- old_list = ["context.set_context(device_id=args.device_id)"]
+ old_list = ["model_file: './transformer/transformer_trained.ckpt'"]
+ new_list = ["model_file: '{}'".format(ckpt_path)]
+ utils.exec_sed_command(old_list, new_list, os.path.join(cur_model_path, "default_config_large.yaml"))
+ old_list = ["context.set_context(device_id=get_device_id())"]
new_list = ["context.set_context()"]
utils.exec_sed_command(old_list, new_list, os.path.join(cur_model_path, "export.py"))
exec_export_shell = "cd transformer; python -u export.py --file_name={}" \
- " --file_format=MINDIR".format(export_file)
+ " --file_format=MINDIR --config_path=./default_config_large.yaml".format(export_file)
os.system(exec_export_shell)
assert os.path.exists(os.path.join(cur_model_path, "{}.mindir".format(export_file)))