Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +7 -0
- audio-detection_core/model/logreg_margin_pruning_ALL_with_scaler_threshold.joblib +3 -0
- audio-detection_core/model/wav2vec2-xls-r-2b_truncated/model.safetensors +3 -0
- avh-align_core/00042_id04939_i4v2cXo9HIQ_faceswap.mp4 +3 -0
- avh-align_core/assets/lipreading.gif +3 -0
- avh-align_core/avhubert/preparation/data/vox-en.id.gz +3 -0
- avh-align_core/checkpoints/avh_sup/AVH_Sup_AV1M.ckpt +3 -0
- avh-align_core/checkpoints/avh_sup/AVH_Sup_FAVC.ckpt +3 -0
- avh-align_core/fairseq/benchmark/__init__.py +7 -0
- avh-align_core/fairseq/benchmark/dummy_dataset.py +36 -0
- avh-align_core/fairseq/benchmark/dummy_lm.py +83 -0
- avh-align_core/fairseq/benchmark/dummy_masked_lm.py +94 -0
- avh-align_core/fairseq/benchmark/dummy_model.py +96 -0
- avh-align_core/fairseq/benchmark/dummy_mt.py +119 -0
- avh-align_core/fairseq/config/__init__.py +4 -0
- avh-align_core/fairseq/config/config.yaml +18 -0
- avh-align_core/fairseq/data/data_utils_fast.cpython-310-x86_64-linux-gnu.so +3 -0
- avh-align_core/fairseq/data/token_block_utils_fast.cpython-310-x86_64-linux-gnu.so +3 -0
- avh-align_core/fairseq/distributed/__init__.py +21 -0
- avh-align_core/fairseq/distributed/distributed_timeout_wrapper.py +94 -0
- avh-align_core/fairseq/distributed/fully_sharded_data_parallel.py +135 -0
- avh-align_core/fairseq/distributed/legacy_distributed_data_parallel.py +165 -0
- avh-align_core/fairseq/distributed/module_proxy_wrapper.py +55 -0
- avh-align_core/fairseq/distributed/tpu_distributed_data_parallel.py +43 -0
- avh-align_core/fairseq/distributed/utils.py +805 -0
- avh-align_core/fairseq/logging/__init__.py +0 -0
- avh-align_core/fairseq/logging/meters.py +323 -0
- avh-align_core/fairseq/logging/metrics.py +314 -0
- avh-align_core/fairseq/logging/progress_bar.py +490 -0
- avh-align_core/fairseq/model_parallel/criterions/__init__.py +14 -0
- avh-align_core/fairseq/model_parallel/criterions/vocab_parallel_cross_entropy.py +87 -0
- avh-align_core/fairseq/model_parallel/models/__init__.py +20 -0
- avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/__init__.py +6 -0
- avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/layers.py +600 -0
- avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/model.py +767 -0
- avh-align_core/fairseq/model_parallel/models/roberta/__init__.py +6 -0
- avh-align_core/fairseq/model_parallel/models/roberta/model.py +225 -0
- avh-align_core/fairseq/model_parallel/models/transformer.py +121 -0
- avh-align_core/fairseq/model_parallel/models/transformer_lm.py +174 -0
- avh-align_core/fairseq/model_parallel/modules/__init__.py +17 -0
- avh-align_core/fairseq/model_parallel/modules/multihead_attention.py +349 -0
- avh-align_core/fairseq/model_parallel/modules/transformer_layer.py +78 -0
- avh-align_core/fairseq/models/__init__.py +225 -0
- avh-align_core/fairseq/models/bart/__init__.py +7 -0
- avh-align_core/fairseq/models/bart/hub_interface.py +208 -0
- avh-align_core/fairseq/models/bart/model.py +384 -0
- avh-align_core/fairseq/models/composite_encoder.py +57 -0
- avh-align_core/fairseq/models/distributed_fairseq_model.py +145 -0
- avh-align_core/fairseq/models/fairseq_decoder.py +105 -0
- avh-align_core/fairseq/models/fairseq_encoder.py +92 -0
.gitattributes
CHANGED
|
@@ -34,3 +34,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
avh-align_core/final_podcast.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
avh-align_core/final_podcast.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
video-deepfake-detection_core/model/models--MCG-NJU--videomae-large/blobs/243706355bfd0f9399af7121df22a6d75286d5e03eaf820cad5b6c5570b3efe3 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
avh-align_core/00042_id04939_i4v2cXo9HIQ_faceswap.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
avh-align_core/temp_data/temp.wav filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
avh-align_core/model/shape_predictor_68_face_landmarks.dat filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
avh-align_core/assets/lipreading.gif filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
avh-align_core/fairseq/data/data_utils_fast.cpython-310-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
avh-align_core/fairseq/data/token_block_utils_fast.cpython-310-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
|
audio-detection_core/model/logreg_margin_pruning_ALL_with_scaler_threshold.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:dbdc1bd864d2b997fc873499f6485891998d5f829af4746064426e942ba580dc
|
| 3 |
+
size 16779
|
audio-detection_core/model/wav2vec2-xls-r-2b_truncated/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d8c01b238f4ff6e53e52677c208fb119f21e665817d7bd11fcc8e33ea5cea3e6
|
| 3 |
+
size 1732225016
|
avh-align_core/00042_id04939_i4v2cXo9HIQ_faceswap.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:71a545b2bb07b9e491a2da74d04dbbb62c6aa40f060a34063aa6bd33f3b29cc1
|
| 3 |
+
size 118526
|
avh-align_core/assets/lipreading.gif
ADDED
|
Git LFS Details
|
avh-align_core/avhubert/preparation/data/vox-en.id.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:be537900d6341087b1fd06208f8a5a0dbdfe365dde61033b9156f7a2ef461262
|
| 3 |
+
size 3101791
|
avh-align_core/checkpoints/avh_sup/AVH_Sup_AV1M.ckpt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f4095aea94a1d846663995c653a2b0d1321d5112065507bdbe7de1fe0016d5ab
|
| 3 |
+
size 6969066
|
avh-align_core/checkpoints/avh_sup/AVH_Sup_FAVC.ckpt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ff6071ac565e0c3f2c711dce0dbe50bef2f5df3f972fc6f6f53790a75458ed7d
|
| 3 |
+
size 6969066
|
avh-align_core/fairseq/benchmark/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
# import models/tasks to register them
|
| 7 |
+
from . import dummy_dataset, dummy_lm, dummy_masked_lm, dummy_model, dummy_mt # noqa
|
avh-align_core/fairseq/benchmark/dummy_dataset.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from fairseq.data import FairseqDataset
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class DummyDataset(FairseqDataset):
|
| 6 |
+
def __init__(self, batch, num_items, item_size):
|
| 7 |
+
super().__init__()
|
| 8 |
+
self.batch = batch
|
| 9 |
+
self.num_items = num_items
|
| 10 |
+
self.item_size = item_size
|
| 11 |
+
|
| 12 |
+
def __getitem__(self, index):
|
| 13 |
+
return index
|
| 14 |
+
|
| 15 |
+
def __len__(self):
|
| 16 |
+
return self.num_items
|
| 17 |
+
|
| 18 |
+
def collater(self, samples):
|
| 19 |
+
return self.batch
|
| 20 |
+
|
| 21 |
+
@property
|
| 22 |
+
def sizes(self):
|
| 23 |
+
return np.array([self.item_size] * self.num_items)
|
| 24 |
+
|
| 25 |
+
def num_tokens(self, index):
|
| 26 |
+
return self.item_size
|
| 27 |
+
|
| 28 |
+
def size(self, index):
|
| 29 |
+
return self.item_size
|
| 30 |
+
|
| 31 |
+
def ordered_indices(self):
|
| 32 |
+
return np.arange(self.num_items)
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def supports_prefetch(self):
|
| 36 |
+
return False
|
avh-align_core/fairseq/benchmark/dummy_lm.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
from dataclasses import dataclass, field
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from .dummy_dataset import DummyDataset
|
| 12 |
+
from fairseq.data import Dictionary
|
| 13 |
+
from fairseq.dataclass import FairseqDataclass
|
| 14 |
+
from fairseq.tasks import FairseqTask, register_task
|
| 15 |
+
from omegaconf import II
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class DummyLMConfig(FairseqDataclass):
|
| 23 |
+
dict_size: int = 49996
|
| 24 |
+
dataset_size: int = 100000
|
| 25 |
+
tokens_per_sample: int = field(
|
| 26 |
+
default=512, metadata={"help": "max sequence length"}
|
| 27 |
+
)
|
| 28 |
+
add_bos_token: bool = False
|
| 29 |
+
batch_size: Optional[int] = II("dataset.batch_size")
|
| 30 |
+
max_tokens: Optional[int] = II("dataset.max_tokens")
|
| 31 |
+
max_target_positions: int = II("task.tokens_per_sample")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@register_task("dummy_lm", dataclass=DummyLMConfig)
|
| 35 |
+
class DummyLMTask(FairseqTask):
|
| 36 |
+
def __init__(self, cfg: DummyLMConfig):
|
| 37 |
+
super().__init__(cfg)
|
| 38 |
+
|
| 39 |
+
# load dictionary
|
| 40 |
+
self.dictionary = Dictionary()
|
| 41 |
+
for i in range(cfg.dict_size):
|
| 42 |
+
self.dictionary.add_symbol("word{}".format(i))
|
| 43 |
+
self.dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 44 |
+
logger.info("dictionary: {} types".format(len(self.dictionary)))
|
| 45 |
+
|
| 46 |
+
seq = torch.arange(cfg.tokens_per_sample + 1) + self.dictionary.pad() + 1
|
| 47 |
+
|
| 48 |
+
self.dummy_src = seq[:-1]
|
| 49 |
+
self.dummy_tgt = seq[1:]
|
| 50 |
+
|
| 51 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 52 |
+
"""Load a given dataset split.
|
| 53 |
+
Args:
|
| 54 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 55 |
+
"""
|
| 56 |
+
if self.cfg.batch_size is not None:
|
| 57 |
+
bsz = self.cfg.batch_size
|
| 58 |
+
else:
|
| 59 |
+
bsz = max(1, self.cfg.max_tokens // self.cfg.tokens_per_sample)
|
| 60 |
+
self.datasets[split] = DummyDataset(
|
| 61 |
+
{
|
| 62 |
+
"id": 1,
|
| 63 |
+
"net_input": {
|
| 64 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 65 |
+
"src_lengths": torch.full(
|
| 66 |
+
(bsz,), self.cfg.tokens_per_sample, dtype=torch.long
|
| 67 |
+
),
|
| 68 |
+
},
|
| 69 |
+
"target": torch.stack([self.dummy_tgt for _ in range(bsz)]),
|
| 70 |
+
"nsentences": bsz,
|
| 71 |
+
"ntokens": bsz * self.cfg.tokens_per_sample,
|
| 72 |
+
},
|
| 73 |
+
num_items=self.cfg.dataset_size,
|
| 74 |
+
item_size=self.cfg.tokens_per_sample,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
@property
|
| 78 |
+
def source_dictionary(self):
|
| 79 |
+
return self.dictionary
|
| 80 |
+
|
| 81 |
+
@property
|
| 82 |
+
def target_dictionary(self):
|
| 83 |
+
return self.dictionary
|
avh-align_core/fairseq/benchmark/dummy_masked_lm.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
from dataclasses import dataclass, field
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
from omegaconf import II
|
| 12 |
+
|
| 13 |
+
from .dummy_dataset import DummyDataset
|
| 14 |
+
from fairseq.data import Dictionary
|
| 15 |
+
from fairseq.dataclass import FairseqDataclass
|
| 16 |
+
from fairseq.tasks import FairseqTask, register_task
|
| 17 |
+
|
| 18 |
+
logger = logging.getLogger(__name__)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class DummyMaskedLMConfig(FairseqDataclass):
|
| 23 |
+
dict_size: int = 49996
|
| 24 |
+
dataset_size: int = 100000
|
| 25 |
+
tokens_per_sample: int = field(
|
| 26 |
+
default=512,
|
| 27 |
+
metadata={
|
| 28 |
+
"help": "max number of total tokens over all"
|
| 29 |
+
" segments per sample for BERT dataset"
|
| 30 |
+
},
|
| 31 |
+
)
|
| 32 |
+
batch_size: Optional[int] = II("dataset.batch_size")
|
| 33 |
+
max_tokens: Optional[int] = II("dataset.max_tokens")
|
| 34 |
+
max_target_positions: int = II("task.tokens_per_sample")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@register_task("dummy_masked_lm", dataclass=DummyMaskedLMConfig)
|
| 38 |
+
class DummyMaskedLMTask(FairseqTask):
|
| 39 |
+
def __init__(self, cfg: DummyMaskedLMConfig):
|
| 40 |
+
super().__init__(cfg)
|
| 41 |
+
|
| 42 |
+
self.dictionary = Dictionary()
|
| 43 |
+
for i in range(cfg.dict_size):
|
| 44 |
+
self.dictionary.add_symbol("word{}".format(i))
|
| 45 |
+
logger.info("dictionary: {} types".format(len(self.dictionary)))
|
| 46 |
+
# add mask token
|
| 47 |
+
self.mask_idx = self.dictionary.add_symbol("<mask>")
|
| 48 |
+
self.dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 49 |
+
|
| 50 |
+
mask_idx = 0
|
| 51 |
+
pad_idx = 1
|
| 52 |
+
seq = torch.arange(cfg.tokens_per_sample) + pad_idx + 1
|
| 53 |
+
mask = torch.arange(2, cfg.tokens_per_sample, 7) # ~15%
|
| 54 |
+
src = seq.clone()
|
| 55 |
+
src[mask] = mask_idx
|
| 56 |
+
tgt = torch.full_like(seq, pad_idx)
|
| 57 |
+
tgt[mask] = seq[mask]
|
| 58 |
+
|
| 59 |
+
self.dummy_src = src
|
| 60 |
+
self.dummy_tgt = tgt
|
| 61 |
+
|
| 62 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 63 |
+
"""Load a given dataset split.
|
| 64 |
+
Args:
|
| 65 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 66 |
+
"""
|
| 67 |
+
if self.cfg.batch_size is not None:
|
| 68 |
+
bsz = self.cfg.batch_size
|
| 69 |
+
else:
|
| 70 |
+
bsz = max(1, self.cfg.max_tokens // self.cfg.tokens_per_sample)
|
| 71 |
+
self.datasets[split] = DummyDataset(
|
| 72 |
+
{
|
| 73 |
+
"id": 1,
|
| 74 |
+
"net_input": {
|
| 75 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 76 |
+
"src_lengths": torch.full(
|
| 77 |
+
(bsz,), self.cfg.tokens_per_sample, dtype=torch.long
|
| 78 |
+
),
|
| 79 |
+
},
|
| 80 |
+
"target": torch.stack([self.dummy_tgt for _ in range(bsz)]),
|
| 81 |
+
"nsentences": bsz,
|
| 82 |
+
"ntokens": bsz * self.cfg.tokens_per_sample,
|
| 83 |
+
},
|
| 84 |
+
num_items=self.cfg.dataset_size,
|
| 85 |
+
item_size=self.cfg.tokens_per_sample,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
@property
|
| 89 |
+
def source_dictionary(self):
|
| 90 |
+
return self.dictionary
|
| 91 |
+
|
| 92 |
+
@property
|
| 93 |
+
def target_dictionary(self):
|
| 94 |
+
return self.dictionary
|
avh-align_core/fairseq/benchmark/dummy_model.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import torch.nn.functional as F
|
| 8 |
+
from fairseq.data import Dictionary
|
| 9 |
+
from fairseq.models import (
|
| 10 |
+
FairseqDecoder,
|
| 11 |
+
FairseqLanguageModel,
|
| 12 |
+
register_model,
|
| 13 |
+
register_model_architecture,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@register_model("dummy_model")
|
| 18 |
+
class DummyModel(FairseqLanguageModel):
|
| 19 |
+
def __init__(self, args, encoder):
|
| 20 |
+
super().__init__(encoder)
|
| 21 |
+
self.args = args
|
| 22 |
+
|
| 23 |
+
@staticmethod
|
| 24 |
+
def add_args(parser):
|
| 25 |
+
parser.add_argument("--num-layers", type=int, default=24)
|
| 26 |
+
parser.add_argument("--embed-dim", type=int, default=1024)
|
| 27 |
+
|
| 28 |
+
@classmethod
|
| 29 |
+
def build_model(cls, args, task):
|
| 30 |
+
encoder = DummyEncoder(
|
| 31 |
+
num_embed=len(task.target_dictionary),
|
| 32 |
+
embed_dim=args.embed_dim,
|
| 33 |
+
num_layers=args.num_layers,
|
| 34 |
+
)
|
| 35 |
+
return cls(args, encoder)
|
| 36 |
+
|
| 37 |
+
def forward(self, src_tokens, masked_tokens=None, **kwargs):
|
| 38 |
+
return self.decoder(src_tokens, masked_tokens=masked_tokens)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class DummyEncoder(FairseqDecoder):
|
| 42 |
+
def __init__(self, num_embed=50000, embed_dim=1024, num_layers=24):
|
| 43 |
+
super().__init__(Dictionary())
|
| 44 |
+
self.embed = nn.Embedding(
|
| 45 |
+
num_embeddings=num_embed, embedding_dim=embed_dim, padding_idx=0
|
| 46 |
+
)
|
| 47 |
+
self.layers_a = nn.ModuleList(
|
| 48 |
+
[
|
| 49 |
+
nn.Sequential(
|
| 50 |
+
nn.LayerNorm(embed_dim),
|
| 51 |
+
nn.Linear(embed_dim, 3 * embed_dim), # q, k, v input projection
|
| 52 |
+
nn.Linear(3 * embed_dim, embed_dim), # skip self-attention
|
| 53 |
+
nn.Linear(embed_dim, embed_dim), # output projection
|
| 54 |
+
nn.Dropout(),
|
| 55 |
+
)
|
| 56 |
+
for i in range(num_layers)
|
| 57 |
+
]
|
| 58 |
+
)
|
| 59 |
+
self.layers_b = nn.ModuleList(
|
| 60 |
+
[
|
| 61 |
+
nn.Sequential(
|
| 62 |
+
nn.LayerNorm(embed_dim),
|
| 63 |
+
nn.Linear(embed_dim, 4 * embed_dim), # FFN
|
| 64 |
+
nn.ReLU(),
|
| 65 |
+
nn.Linear(4 * embed_dim, embed_dim), # FFN
|
| 66 |
+
nn.Dropout(0.1),
|
| 67 |
+
)
|
| 68 |
+
for i in range(num_layers)
|
| 69 |
+
]
|
| 70 |
+
)
|
| 71 |
+
self.out_proj = nn.Linear(embed_dim, num_embed)
|
| 72 |
+
|
| 73 |
+
def forward(self, tokens, masked_tokens=None):
|
| 74 |
+
x = self.embed(tokens)
|
| 75 |
+
for layer_a, layer_b in zip(self.layers_a, self.layers_b):
|
| 76 |
+
x = x + layer_a(x)
|
| 77 |
+
x = x + layer_b(x)
|
| 78 |
+
x = self.out_proj(x)
|
| 79 |
+
if masked_tokens is not None:
|
| 80 |
+
x = x[masked_tokens]
|
| 81 |
+
return (x,)
|
| 82 |
+
|
| 83 |
+
def max_positions(self):
|
| 84 |
+
return 1024
|
| 85 |
+
|
| 86 |
+
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
| 87 |
+
logits = net_output[0].float()
|
| 88 |
+
if log_probs:
|
| 89 |
+
return F.log_softmax(logits, dim=-1)
|
| 90 |
+
else:
|
| 91 |
+
return F.softmax(logits, dim=-1)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@register_model_architecture("dummy_model", "dummy_model")
|
| 95 |
+
def base_architecture(args):
|
| 96 |
+
pass
|
avh-align_core/fairseq/benchmark/dummy_mt.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
import torch
|
| 10 |
+
from fairseq.data import Dictionary, FairseqDataset
|
| 11 |
+
from fairseq.tasks import LegacyFairseqTask, register_task
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@register_task("dummy_mt")
|
| 18 |
+
class DummyMTTask(LegacyFairseqTask):
|
| 19 |
+
@staticmethod
|
| 20 |
+
def add_args(parser):
|
| 21 |
+
"""Add task-specific arguments to the parser."""
|
| 22 |
+
parser.add_argument("--dict-size", default=49996, type=int)
|
| 23 |
+
parser.add_argument("--dataset-size", default=100000, type=int)
|
| 24 |
+
parser.add_argument("--src-len", default=30, type=int)
|
| 25 |
+
parser.add_argument("--tgt-len", default=30, type=int)
|
| 26 |
+
|
| 27 |
+
def __init__(self, args, dictionary):
|
| 28 |
+
super().__init__(args)
|
| 29 |
+
self.dictionary = dictionary
|
| 30 |
+
self.seed = args.seed
|
| 31 |
+
|
| 32 |
+
dictionary.pad_to_multiple_(8) # often faster if divisible by 8
|
| 33 |
+
|
| 34 |
+
self.dummy_src = torch.arange(args.src_len + 1) + dictionary.pad() + 1
|
| 35 |
+
self.dummy_tgt = torch.arange(args.tgt_len + 1) + dictionary.pad() + 1
|
| 36 |
+
|
| 37 |
+
@classmethod
|
| 38 |
+
def setup_task(cls, args, **kwargs):
|
| 39 |
+
"""Setup the task. """
|
| 40 |
+
dictionary = Dictionary()
|
| 41 |
+
for i in range(args.dict_size):
|
| 42 |
+
dictionary.add_symbol("word{}".format(i))
|
| 43 |
+
logger.info("dictionary: {} types".format(len(dictionary)))
|
| 44 |
+
|
| 45 |
+
args.max_source_positions = args.src_len + dictionary.pad() + 2
|
| 46 |
+
args.max_target_positions = args.tgt_len + dictionary.pad() + 2
|
| 47 |
+
|
| 48 |
+
return cls(args, dictionary)
|
| 49 |
+
|
| 50 |
+
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
|
| 51 |
+
"""Load a given dataset split.
|
| 52 |
+
Args:
|
| 53 |
+
split (str): name of the split (e.g., train, valid, test)
|
| 54 |
+
"""
|
| 55 |
+
item_size = max(self.args.src_len, self.args.tgt_len)
|
| 56 |
+
if self.args.batch_size is not None:
|
| 57 |
+
bsz = self.args.batch_size
|
| 58 |
+
else:
|
| 59 |
+
bsz = max(1, self.args.max_tokens // item_size)
|
| 60 |
+
tgt = torch.stack([self.dummy_tgt for _ in range(bsz)])
|
| 61 |
+
self.datasets[split] = DummyDataset(
|
| 62 |
+
{
|
| 63 |
+
"id": 1,
|
| 64 |
+
"net_input": {
|
| 65 |
+
"src_tokens": torch.stack([self.dummy_src for _ in range(bsz)]),
|
| 66 |
+
"src_lengths": torch.full(
|
| 67 |
+
(bsz,), self.args.src_len, dtype=torch.long
|
| 68 |
+
),
|
| 69 |
+
"prev_output_tokens": tgt.clone(),
|
| 70 |
+
},
|
| 71 |
+
"target": tgt,
|
| 72 |
+
"nsentences": bsz,
|
| 73 |
+
"ntokens": bsz * self.args.tgt_len,
|
| 74 |
+
},
|
| 75 |
+
num_items=self.args.dataset_size,
|
| 76 |
+
item_size=item_size,
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
@property
|
| 80 |
+
def source_dictionary(self):
|
| 81 |
+
return self.dictionary
|
| 82 |
+
|
| 83 |
+
@property
|
| 84 |
+
def target_dictionary(self):
|
| 85 |
+
return self.dictionary
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class DummyDataset(FairseqDataset):
|
| 89 |
+
def __init__(self, batch, num_items, item_size):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.batch = batch
|
| 92 |
+
self.num_items = num_items
|
| 93 |
+
self.item_size = item_size
|
| 94 |
+
|
| 95 |
+
def __getitem__(self, index):
|
| 96 |
+
return index
|
| 97 |
+
|
| 98 |
+
def __len__(self):
|
| 99 |
+
return self.num_items
|
| 100 |
+
|
| 101 |
+
def collater(self, samples):
|
| 102 |
+
return self.batch
|
| 103 |
+
|
| 104 |
+
@property
|
| 105 |
+
def sizes(self):
|
| 106 |
+
return np.array([self.item_size] * self.num_items)
|
| 107 |
+
|
| 108 |
+
def num_tokens(self, index):
|
| 109 |
+
return self.item_size
|
| 110 |
+
|
| 111 |
+
def size(self, index):
|
| 112 |
+
return self.item_size
|
| 113 |
+
|
| 114 |
+
def ordered_indices(self):
|
| 115 |
+
return np.arange(self.num_items)
|
| 116 |
+
|
| 117 |
+
@property
|
| 118 |
+
def supports_prefetch(self):
|
| 119 |
+
return False
|
avh-align_core/fairseq/config/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
avh-align_core/fairseq/config/config.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# @package _group_
|
| 2 |
+
|
| 3 |
+
hydra:
|
| 4 |
+
run:
|
| 5 |
+
dir: .
|
| 6 |
+
|
| 7 |
+
defaults:
|
| 8 |
+
- task: null
|
| 9 |
+
- model: null
|
| 10 |
+
- criterion: cross_entropy
|
| 11 |
+
- optimizer: null
|
| 12 |
+
- lr_scheduler: fixed
|
| 13 |
+
- bpe: null
|
| 14 |
+
- tokenizer: null
|
| 15 |
+
- scoring: null
|
| 16 |
+
- generation: null
|
| 17 |
+
- common_eval: null
|
| 18 |
+
- eval_lm: null
|
avh-align_core/fairseq/data/data_utils_fast.cpython-310-x86_64-linux-gnu.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:69558d8f04e3b4faf3dd6e2cd5afa87575eda8706300bdb9190c85a0d063bc24
|
| 3 |
+
size 262128
|
avh-align_core/fairseq/data/token_block_utils_fast.cpython-310-x86_64-linux-gnu.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:181413dbe125f7b3f485407d0cfa313aa720456a86a3b4a0e761d370fc9baca2
|
| 3 |
+
size 280360
|
avh-align_core/fairseq/distributed/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .distributed_timeout_wrapper import DistributedTimeoutWrapper
|
| 7 |
+
from .fully_sharded_data_parallel import fsdp_enable_wrap, fsdp_wrap, FullyShardedDataParallel
|
| 8 |
+
from .legacy_distributed_data_parallel import LegacyDistributedDataParallel
|
| 9 |
+
from .module_proxy_wrapper import ModuleProxyWrapper
|
| 10 |
+
from .tpu_distributed_data_parallel import TPUDistributedDataParallel
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
"DistributedTimeoutWrapper",
|
| 15 |
+
"fsdp_enable_wrap",
|
| 16 |
+
"fsdp_wrap",
|
| 17 |
+
"FullyShardedDataParallel",
|
| 18 |
+
"LegacyDistributedDataParallel",
|
| 19 |
+
"ModuleProxyWrapper",
|
| 20 |
+
"TPUDistributedDataParallel",
|
| 21 |
+
]
|
avh-align_core/fairseq/distributed/distributed_timeout_wrapper.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
import os
|
| 8 |
+
import signal
|
| 9 |
+
import threading
|
| 10 |
+
|
| 11 |
+
from torch import nn
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class DistributedTimeoutWrapper(nn.Module):
|
| 18 |
+
"""
|
| 19 |
+
A wrapper that kills the process if no progress is made within a given
|
| 20 |
+
*timeout*. The timer is reset every time :func:`forward` is called.
|
| 21 |
+
|
| 22 |
+
Usage::
|
| 23 |
+
|
| 24 |
+
module = DistributedTimeoutWrapper(module, timeout=30)
|
| 25 |
+
x = module(input)
|
| 26 |
+
time.sleep(20) # safe
|
| 27 |
+
x = module(input)
|
| 28 |
+
time.sleep(45) # job will be killed before this returns
|
| 29 |
+
|
| 30 |
+
Args:
|
| 31 |
+
module (nn.Module): module to wrap
|
| 32 |
+
timeout (int): number of seconds before killing the process
|
| 33 |
+
(set to a value <= 0 to disable the timeout)
|
| 34 |
+
signal (Optional): signal to send once timeout is triggered
|
| 35 |
+
"""
|
| 36 |
+
def __init__(self, module: nn.Module, timeout: int, signal=signal.SIGINT):
|
| 37 |
+
super().__init__()
|
| 38 |
+
self.module = module
|
| 39 |
+
self.timeout = timeout
|
| 40 |
+
self.signal = signal
|
| 41 |
+
|
| 42 |
+
if timeout > 0:
|
| 43 |
+
self._heartbeat = threading.Event()
|
| 44 |
+
self._heartbeat_thread = threading.Thread(
|
| 45 |
+
target=self._check_heartbeat,
|
| 46 |
+
args=(os.getpid(),),
|
| 47 |
+
daemon=True,
|
| 48 |
+
)
|
| 49 |
+
self._heartbeat_thread.start()
|
| 50 |
+
self._terminated = False
|
| 51 |
+
else:
|
| 52 |
+
self._heartbeat = None
|
| 53 |
+
self._heartbeat_thread = None
|
| 54 |
+
|
| 55 |
+
def __del__(self):
|
| 56 |
+
self.stop_timeout()
|
| 57 |
+
|
| 58 |
+
def __getattr__(self, name):
|
| 59 |
+
"""Forward missing attributes to wrapped module."""
|
| 60 |
+
try:
|
| 61 |
+
return super().__getattr__(name) # defer to nn.Module's logic
|
| 62 |
+
except AttributeError:
|
| 63 |
+
return getattr(self.module, name)
|
| 64 |
+
|
| 65 |
+
def stop_timeout(self):
|
| 66 |
+
if self._heartbeat_thread is not None:
|
| 67 |
+
self._terminated = True
|
| 68 |
+
self._heartbeat_thread.join()
|
| 69 |
+
|
| 70 |
+
def state_dict(self, *args, **kwargs):
|
| 71 |
+
return self.module.state_dict(*args, **kwargs)
|
| 72 |
+
|
| 73 |
+
def load_state_dict(self, *args, **kwargs):
|
| 74 |
+
return self.module.load_state_dict(*args, **kwargs)
|
| 75 |
+
|
| 76 |
+
def forward(self, *args, **kwargs):
|
| 77 |
+
if self._heartbeat is not None:
|
| 78 |
+
self._heartbeat.set()
|
| 79 |
+
return self.module(*args, **kwargs)
|
| 80 |
+
|
| 81 |
+
def _check_heartbeat(self, parent_pid):
|
| 82 |
+
self._heartbeat.wait() # wait for the first forward pass
|
| 83 |
+
while True:
|
| 84 |
+
self._heartbeat.clear()
|
| 85 |
+
success = self._heartbeat.wait(timeout=self.timeout)
|
| 86 |
+
if self._terminated:
|
| 87 |
+
break
|
| 88 |
+
elif not success:
|
| 89 |
+
logger.error((
|
| 90 |
+
"Killing job for not making progress in {} seconds. "
|
| 91 |
+
"Set --heartbeat-timeout=-1 to disable this timeout."
|
| 92 |
+
).format(int(self.timeout)))
|
| 93 |
+
os.kill(parent_pid, self.signal)
|
| 94 |
+
return
|
avh-align_core/fairseq/distributed/fully_sharded_data_parallel.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import contextlib
|
| 7 |
+
from typing import Optional
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
from fairseq.dataclass.configs import DistributedTrainingConfig
|
| 11 |
+
from fairseq.distributed import utils as dist_utils
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
from fairscale.nn.data_parallel import FullyShardedDataParallel as FSDP
|
| 16 |
+
|
| 17 |
+
has_FSDP = True
|
| 18 |
+
except ImportError:
|
| 19 |
+
FSDP = torch.nn.Module
|
| 20 |
+
has_FSDP = False
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class FullyShardedDataParallel(FSDP):
|
| 24 |
+
"""
|
| 25 |
+
A small wrapper around fairscale's FullyShardedDataParallel (FSDP) with some
|
| 26 |
+
fairseq-specific checkpoint saving/loading logic.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
use_sharded_state (bool): if True, then ``state_dict`` will return
|
| 30 |
+
``FSDP.local_state_dict`` and ``load_state_dict`` will call
|
| 31 |
+
``FSDP.load_local_state_dict``. Otherwise, ``state_dict`` will
|
| 32 |
+
return the full model weights on data parallel rank 0 (empty on
|
| 33 |
+
other ranks) and ``load_state_dict`` will broadcast model weights
|
| 34 |
+
from rank 0 to other ranks.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(self, *args, use_sharded_state: bool = False, **kwargs):
|
| 38 |
+
if not has_FSDP:
|
| 39 |
+
raise ImportError(
|
| 40 |
+
"Cannot find FullyShardedDataParallel. "
|
| 41 |
+
"Please install fairscale with: pip install fairscale"
|
| 42 |
+
)
|
| 43 |
+
super().__init__(*args, **kwargs)
|
| 44 |
+
self.use_sharded_state = use_sharded_state
|
| 45 |
+
|
| 46 |
+
@property
|
| 47 |
+
def unwrapped_module(self) -> torch.nn.Module:
|
| 48 |
+
if self.flatten_parameters:
|
| 49 |
+
return self.module.module
|
| 50 |
+
else:
|
| 51 |
+
return self.module
|
| 52 |
+
|
| 53 |
+
def state_dict(self, destination=None, prefix="", keep_vars=False):
|
| 54 |
+
if self.use_sharded_state:
|
| 55 |
+
return super().local_state_dict(
|
| 56 |
+
destination=destination, prefix=prefix, keep_vars=keep_vars
|
| 57 |
+
)
|
| 58 |
+
else:
|
| 59 |
+
if self.rank == 0:
|
| 60 |
+
return super().state_dict(
|
| 61 |
+
destination=destination, prefix=prefix, keep_vars=keep_vars
|
| 62 |
+
)
|
| 63 |
+
else:
|
| 64 |
+
# We must call state_dict() due to use of communication
|
| 65 |
+
# primitives. But we don't use the result.
|
| 66 |
+
super().state_dict()
|
| 67 |
+
return destination or {}
|
| 68 |
+
|
| 69 |
+
def load_state_dict(self, state_dict, strict=True, model_cfg=None):
|
| 70 |
+
if self.use_sharded_state:
|
| 71 |
+
return super().load_local_state_dict(state_dict, strict=strict)
|
| 72 |
+
else:
|
| 73 |
+
state_dict = dist_utils.broadcast_object(
|
| 74 |
+
state_dict, src_rank=0, group=self.process_group
|
| 75 |
+
)
|
| 76 |
+
return super().load_state_dict(state_dict, strict=strict)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
@contextlib.contextmanager
|
| 80 |
+
def fsdp_enable_wrap(cfg: DistributedTrainingConfig):
|
| 81 |
+
try:
|
| 82 |
+
from fairscale.nn import enable_wrap
|
| 83 |
+
except ImportError:
|
| 84 |
+
raise ImportError(
|
| 85 |
+
"Cannot find FullyShardedDataParallel. "
|
| 86 |
+
"Please install fairscale with: pip install fairscale"
|
| 87 |
+
)
|
| 88 |
+
if cfg.memory_efficient_fp16:
|
| 89 |
+
assert cfg.fp16 # memory_efficient_fp16 should imply fp16
|
| 90 |
+
group = dist_utils.get_data_parallel_group()
|
| 91 |
+
if group is None and cfg.distributed_world_size == 1:
|
| 92 |
+
from fairscale.utils.testing import DummyProcessGroup
|
| 93 |
+
|
| 94 |
+
group = DummyProcessGroup(rank=0, size=1)
|
| 95 |
+
fsdp_config = {
|
| 96 |
+
"process_group": group,
|
| 97 |
+
"reshard_after_forward": not cfg.no_reshard_after_forward,
|
| 98 |
+
"mixed_precision": cfg.fp16 and not cfg.memory_efficient_fp16,
|
| 99 |
+
"fp32_reduce_scatter": cfg.fp32_reduce_scatter,
|
| 100 |
+
"flatten_parameters": True,
|
| 101 |
+
"cpu_offload": cfg.cpu_offload,
|
| 102 |
+
"compute_dtype": torch.float16 if cfg.fp16 else torch.float32,
|
| 103 |
+
"bucket_cap_mb": cfg.bucket_cap_mb,
|
| 104 |
+
"state_dict_device": torch.device("cpu"), # reduce GPU mem usage
|
| 105 |
+
}
|
| 106 |
+
with enable_wrap(
|
| 107 |
+
wrapper_cls=FullyShardedDataParallel,
|
| 108 |
+
use_sharded_state=cfg.use_sharded_state,
|
| 109 |
+
**fsdp_config,
|
| 110 |
+
):
|
| 111 |
+
yield
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def fsdp_wrap(module, min_num_params: Optional[int] = None, **kwargs):
|
| 115 |
+
"""
|
| 116 |
+
Helper to wrap layers/modules in FSDP. This falls back to a no-op if
|
| 117 |
+
fairscale is not available.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
module (nn.Module): module to (maybe) wrap
|
| 121 |
+
min_num_params (int, Optional): minimum number of layer params to wrap
|
| 122 |
+
"""
|
| 123 |
+
try:
|
| 124 |
+
from fairscale.nn import wrap
|
| 125 |
+
|
| 126 |
+
if min_num_params is not None:
|
| 127 |
+
num_params = sum(p.numel() for p in module.parameters())
|
| 128 |
+
if num_params >= min_num_params:
|
| 129 |
+
return wrap(module, **kwargs)
|
| 130 |
+
else:
|
| 131 |
+
return module
|
| 132 |
+
else:
|
| 133 |
+
return wrap(module, **kwargs)
|
| 134 |
+
except ImportError:
|
| 135 |
+
return module
|
avh-align_core/fairseq/distributed/legacy_distributed_data_parallel.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
"""
|
| 7 |
+
A modified version of the legacy DistributedDataParallel module that uses c10d
|
| 8 |
+
communication primitives. This version is simpler than the latest PyTorch
|
| 9 |
+
version and is useful for debugging. Notably it does not overlap gradient
|
| 10 |
+
communication with the backward pass, which makes it slower but more robust
|
| 11 |
+
than the PyTorch version.
|
| 12 |
+
|
| 13 |
+
This version also supports the *no_sync* context manager, which allows faster
|
| 14 |
+
training with `--update-freq`.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
from collections import OrderedDict
|
| 18 |
+
from contextlib import contextmanager
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from torch import nn
|
| 22 |
+
|
| 23 |
+
from fairseq.distributed import utils
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class LegacyDistributedDataParallel(nn.Module):
|
| 27 |
+
"""Implements distributed data parallelism at the module level.
|
| 28 |
+
|
| 29 |
+
A simplified version of :class:`torch.nn.parallel.DistributedDataParallel`.
|
| 30 |
+
This version uses a c10d process group for communication and does not
|
| 31 |
+
broadcast buffers.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
module (~torch.nn.Module): module to be parallelized
|
| 35 |
+
process_group: the c10d process group to be used for distributed data
|
| 36 |
+
parallel all-reduction.
|
| 37 |
+
buffer_size (int, optional): number of elements to buffer before
|
| 38 |
+
performing all-reduce (default: 256M).
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
def __init__(self, module, process_group, buffer_size=2 ** 28):
|
| 42 |
+
super().__init__()
|
| 43 |
+
|
| 44 |
+
self.module = module
|
| 45 |
+
self.process_group = process_group
|
| 46 |
+
self.world_size = utils.get_world_size(self.process_group)
|
| 47 |
+
|
| 48 |
+
# Never use a bigger buffer than the number of model params
|
| 49 |
+
self.buffer_size = min(buffer_size, sum(p.numel() for p in module.parameters()))
|
| 50 |
+
self.buffer = None
|
| 51 |
+
|
| 52 |
+
# We can also forcibly accumulate grads locally and only do the
|
| 53 |
+
# all-reduce at some later time
|
| 54 |
+
self.accumulate_grads = False
|
| 55 |
+
|
| 56 |
+
# make per-device lists of parameters
|
| 57 |
+
paramlists = OrderedDict()
|
| 58 |
+
for param in self.module.parameters():
|
| 59 |
+
device = param.device
|
| 60 |
+
if paramlists.get(device) is None:
|
| 61 |
+
paramlists[device] = []
|
| 62 |
+
paramlists[device] += [param]
|
| 63 |
+
self.per_device_params = list(paramlists.values())
|
| 64 |
+
|
| 65 |
+
@contextmanager
|
| 66 |
+
def no_sync(self):
|
| 67 |
+
"""A context manager to disable gradient synchronization."""
|
| 68 |
+
old_accumulate_grads = self.accumulate_grads
|
| 69 |
+
self.accumulate_grads = True
|
| 70 |
+
yield
|
| 71 |
+
self.accumulate_grads = old_accumulate_grads
|
| 72 |
+
|
| 73 |
+
def forward(self, *inputs, **kwargs):
|
| 74 |
+
return self.module(*inputs, **kwargs)
|
| 75 |
+
|
| 76 |
+
def all_reduce_grads(self):
|
| 77 |
+
"""
|
| 78 |
+
This function must be called explicitly after backward to reduce
|
| 79 |
+
gradients. There is no automatic hook like c10d.
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
def all_reduce_params(params):
|
| 83 |
+
buffer = self.buffer
|
| 84 |
+
nonzero_buffer = False
|
| 85 |
+
if len(params) > 1:
|
| 86 |
+
offset = 0
|
| 87 |
+
for p in params:
|
| 88 |
+
sz = p.numel()
|
| 89 |
+
if p.grad is not None:
|
| 90 |
+
buffer[offset : offset + sz].copy_(p.grad.data.view(-1))
|
| 91 |
+
nonzero_buffer = True
|
| 92 |
+
else:
|
| 93 |
+
buffer[offset : offset + sz].zero_()
|
| 94 |
+
offset += sz
|
| 95 |
+
else:
|
| 96 |
+
# we only have a single grad to all-reduce
|
| 97 |
+
p = params[0]
|
| 98 |
+
if p.grad is not None:
|
| 99 |
+
buffer = p.grad.data
|
| 100 |
+
nonzero_buffer = True
|
| 101 |
+
elif p.numel() <= self.buffer.numel():
|
| 102 |
+
buffer = buffer[: p.numel()]
|
| 103 |
+
buffer.zero_()
|
| 104 |
+
else:
|
| 105 |
+
buffer = torch.zeros_like(p)
|
| 106 |
+
|
| 107 |
+
if nonzero_buffer:
|
| 108 |
+
buffer.div_(self.world_size)
|
| 109 |
+
|
| 110 |
+
utils.all_reduce(buffer, self.process_group)
|
| 111 |
+
|
| 112 |
+
# copy all-reduced grads back into their original place
|
| 113 |
+
offset = 0
|
| 114 |
+
for p in params:
|
| 115 |
+
sz = p.numel()
|
| 116 |
+
if p.grad is not None:
|
| 117 |
+
p.grad.data.copy_(buffer[offset : offset + sz].view_as(p))
|
| 118 |
+
else:
|
| 119 |
+
p.grad = buffer[offset : offset + sz].view_as(p).clone()
|
| 120 |
+
offset += sz
|
| 121 |
+
|
| 122 |
+
def reduction_fn():
|
| 123 |
+
# This function only needs to be called once
|
| 124 |
+
if self.accumulate_grads:
|
| 125 |
+
return
|
| 126 |
+
|
| 127 |
+
if self.buffer is None:
|
| 128 |
+
self.buffer = next(self.module.parameters()).new(self.buffer_size)
|
| 129 |
+
|
| 130 |
+
for params in self.per_device_params:
|
| 131 |
+
# All-reduce the gradients in buckets
|
| 132 |
+
offset = 0
|
| 133 |
+
buffered_params = []
|
| 134 |
+
for param in params:
|
| 135 |
+
if not param.requires_grad:
|
| 136 |
+
continue
|
| 137 |
+
if param.grad is None:
|
| 138 |
+
param.grad = torch.zeros_like(param)
|
| 139 |
+
|
| 140 |
+
if hasattr(param, 'expert'):
|
| 141 |
+
# Skip gradient sync for unshared parameters
|
| 142 |
+
continue
|
| 143 |
+
|
| 144 |
+
if param.grad.requires_grad:
|
| 145 |
+
raise RuntimeError(
|
| 146 |
+
"DistributedDataParallel only works "
|
| 147 |
+
"with gradients that don't require "
|
| 148 |
+
"grad"
|
| 149 |
+
)
|
| 150 |
+
sz = param.numel()
|
| 151 |
+
if sz > self.buffer.numel():
|
| 152 |
+
# all-reduce big params directly
|
| 153 |
+
all_reduce_params([param])
|
| 154 |
+
else:
|
| 155 |
+
if offset + sz > self.buffer.numel():
|
| 156 |
+
all_reduce_params(buffered_params)
|
| 157 |
+
offset = 0
|
| 158 |
+
buffered_params.clear()
|
| 159 |
+
buffered_params.append(param)
|
| 160 |
+
offset += sz
|
| 161 |
+
|
| 162 |
+
if len(buffered_params) > 0:
|
| 163 |
+
all_reduce_params(buffered_params)
|
| 164 |
+
|
| 165 |
+
reduction_fn()
|
avh-align_core/fairseq/distributed/module_proxy_wrapper.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from torch import nn
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ModuleProxyWrapper(nn.Module):
|
| 10 |
+
"""
|
| 11 |
+
Wrap a DistributedDataParallel module and forward requests for missing
|
| 12 |
+
attributes to the module wrapped by DDP (the twice-wrapped module).
|
| 13 |
+
Also forward calls to :func:`state_dict` and :func:`load_state_dict`.
|
| 14 |
+
|
| 15 |
+
Usage::
|
| 16 |
+
|
| 17 |
+
module.xyz = "hello world"
|
| 18 |
+
wrapped_module = DistributedDataParallel(module, **ddp_args)
|
| 19 |
+
wrapped_module = ModuleProxyWrapper(wrapped_module)
|
| 20 |
+
assert wrapped_module.xyz == "hello world"
|
| 21 |
+
assert wrapped_module.state_dict().keys() == module.state_dict().keys()
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
module (nn.Module): module to wrap
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
def __init__(self, module: nn.Module):
|
| 28 |
+
super().__init__()
|
| 29 |
+
assert hasattr(module, "module"), \
|
| 30 |
+
"ModuleProxyWrapper expects input to wrap another module"
|
| 31 |
+
self.module = module
|
| 32 |
+
|
| 33 |
+
def __getattr__(self, name):
|
| 34 |
+
"""Forward missing attributes to twice-wrapped module."""
|
| 35 |
+
try:
|
| 36 |
+
# defer to nn.Module's logic
|
| 37 |
+
return super().__getattr__(name)
|
| 38 |
+
except AttributeError:
|
| 39 |
+
try:
|
| 40 |
+
# forward to the once-wrapped module
|
| 41 |
+
return getattr(self.module, name)
|
| 42 |
+
except AttributeError:
|
| 43 |
+
# forward to the twice-wrapped module
|
| 44 |
+
return getattr(self.module.module, name)
|
| 45 |
+
|
| 46 |
+
def state_dict(self, *args, **kwargs):
|
| 47 |
+
"""Forward to the twice-wrapped module."""
|
| 48 |
+
return self.module.module.state_dict(*args, **kwargs)
|
| 49 |
+
|
| 50 |
+
def load_state_dict(self, *args, **kwargs):
|
| 51 |
+
"""Forward to the twice-wrapped module."""
|
| 52 |
+
return self.module.module.load_state_dict(*args, **kwargs)
|
| 53 |
+
|
| 54 |
+
def forward(self, *args, **kwargs):
|
| 55 |
+
return self.module(*args, **kwargs)
|
avh-align_core/fairseq/distributed/tpu_distributed_data_parallel.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from torch import nn
|
| 8 |
+
|
| 9 |
+
from fairseq.distributed import utils
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class TPUDistributedDataParallel(nn.Module):
|
| 13 |
+
|
| 14 |
+
def __init__(self, module, process_group):
|
| 15 |
+
super().__init__()
|
| 16 |
+
self.module = module
|
| 17 |
+
self.process_group = process_group
|
| 18 |
+
self.world_size = utils.get_world_size(self.process_group)
|
| 19 |
+
|
| 20 |
+
def forward(self, *inputs, **kwargs):
|
| 21 |
+
return self.module(*inputs, **kwargs)
|
| 22 |
+
|
| 23 |
+
def all_reduce_grads(self):
|
| 24 |
+
gradients = []
|
| 25 |
+
for p in self.parameters():
|
| 26 |
+
if not p.requires_grad:
|
| 27 |
+
continue
|
| 28 |
+
if p.grad is None:
|
| 29 |
+
p.grad = torch.zeros_like(p)
|
| 30 |
+
if p.grad.requires_grad:
|
| 31 |
+
raise RuntimeError(
|
| 32 |
+
"TPUDistributedDataParallel only works with gradients that don't "
|
| 33 |
+
"require grad"
|
| 34 |
+
)
|
| 35 |
+
gradients.append(p.grad)
|
| 36 |
+
|
| 37 |
+
import torch_xla.core.xla_model as xm
|
| 38 |
+
xm.all_reduce(
|
| 39 |
+
'sum',
|
| 40 |
+
gradients,
|
| 41 |
+
scale=1. / self.world_size,
|
| 42 |
+
groups=self.process_group[1],
|
| 43 |
+
)
|
avh-align_core/fairseq/distributed/utils.py
ADDED
|
@@ -0,0 +1,805 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import io
|
| 7 |
+
import logging
|
| 8 |
+
import os
|
| 9 |
+
import pickle
|
| 10 |
+
import random
|
| 11 |
+
import socket
|
| 12 |
+
import struct
|
| 13 |
+
import subprocess
|
| 14 |
+
import warnings
|
| 15 |
+
from argparse import Namespace
|
| 16 |
+
from collections import OrderedDict
|
| 17 |
+
from dataclasses import dataclass
|
| 18 |
+
from typing import Any, Dict, List, Mapping, Optional
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
import torch.distributed as dist
|
| 22 |
+
from fairseq.dataclass.configs import DistributedTrainingConfig, FairseqConfig
|
| 23 |
+
from omegaconf import open_dict
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
import torch_xla.core.xla_model as xm
|
| 27 |
+
except ImportError:
|
| 28 |
+
xm = None
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
# Flag to indicate if we're using Megatron
|
| 32 |
+
# NOTE: this is a temporary hack until we move away from Megatron's model parallel init
|
| 33 |
+
_USE_MEGATRON = False
|
| 34 |
+
|
| 35 |
+
# Whether to use XLA ops (e.g., on TPUs) instead of CUDA ops.
|
| 36 |
+
_USE_XLA = False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
logger = logging.getLogger(__name__)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def is_master(cfg: DistributedTrainingConfig):
|
| 43 |
+
return cfg.distributed_rank == 0
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def infer_init_method(cfg: DistributedTrainingConfig, force_distributed=False):
|
| 47 |
+
if cfg.distributed_init_method is not None or cfg.tpu:
|
| 48 |
+
return
|
| 49 |
+
|
| 50 |
+
num_pipelines_per_node = None
|
| 51 |
+
if cfg.pipeline_model_parallel:
|
| 52 |
+
num_pipeline_devices, num_pipelines_per_node = _pipeline_parallel_pre_init(cfg)
|
| 53 |
+
|
| 54 |
+
if all(
|
| 55 |
+
key in os.environ
|
| 56 |
+
for key in ["MASTER_ADDR", "MASTER_PORT", "WORLD_SIZE", "RANK"]
|
| 57 |
+
):
|
| 58 |
+
# support torch.distributed.launch
|
| 59 |
+
_infer_torch_distributed_launch_init(cfg)
|
| 60 |
+
elif cfg.distributed_port > 0:
|
| 61 |
+
# we can determine the init method automatically for Slurm
|
| 62 |
+
_infer_slurm_init(cfg, num_pipelines_per_node)
|
| 63 |
+
elif cfg.distributed_world_size > 1 or force_distributed:
|
| 64 |
+
# fallback for single node with multiple GPUs
|
| 65 |
+
_infer_single_node_init(cfg)
|
| 66 |
+
|
| 67 |
+
if cfg.pipeline_model_parallel:
|
| 68 |
+
_pipeline_parallel_post_init(cfg, num_pipeline_devices, num_pipelines_per_node)
|
| 69 |
+
elif not cfg.distributed_no_spawn:
|
| 70 |
+
with open_dict(cfg):
|
| 71 |
+
cfg.distributed_num_procs = min(
|
| 72 |
+
torch.cuda.device_count(), cfg.distributed_world_size
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _infer_torch_distributed_launch_init(cfg: DistributedTrainingConfig):
|
| 77 |
+
cfg.distributed_init_method = "env://"
|
| 78 |
+
cfg.distributed_world_size = int(os.environ["WORLD_SIZE"])
|
| 79 |
+
cfg.distributed_rank = int(os.environ["RANK"])
|
| 80 |
+
# processes are created by torch.distributed.launch
|
| 81 |
+
cfg.distributed_no_spawn = True
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _infer_slurm_init(cfg: DistributedTrainingConfig, num_pipelines_per_node):
|
| 85 |
+
node_list = os.environ.get("SLURM_STEP_NODELIST")
|
| 86 |
+
if node_list is None:
|
| 87 |
+
node_list = os.environ.get("SLURM_JOB_NODELIST")
|
| 88 |
+
if node_list is not None:
|
| 89 |
+
try:
|
| 90 |
+
hostnames = subprocess.check_output(
|
| 91 |
+
["scontrol", "show", "hostnames", node_list]
|
| 92 |
+
)
|
| 93 |
+
cfg.distributed_init_method = "tcp://{host}:{port}".format(
|
| 94 |
+
host=hostnames.split()[0].decode("utf-8"),
|
| 95 |
+
port=cfg.distributed_port,
|
| 96 |
+
)
|
| 97 |
+
nnodes = int(os.environ.get("SLURM_NNODES"))
|
| 98 |
+
ntasks_per_node = os.environ.get("SLURM_NTASKS_PER_NODE")
|
| 99 |
+
if ntasks_per_node is not None:
|
| 100 |
+
ntasks_per_node = int(ntasks_per_node)
|
| 101 |
+
else:
|
| 102 |
+
ntasks = int(os.environ.get("SLURM_NTASKS"))
|
| 103 |
+
nnodes = int(os.environ.get("SLURM_NNODES"))
|
| 104 |
+
assert ntasks % nnodes == 0
|
| 105 |
+
ntasks_per_node = int(ntasks / nnodes)
|
| 106 |
+
if ntasks_per_node == 1:
|
| 107 |
+
gpus_per_node = torch.cuda.device_count()
|
| 108 |
+
node_id = int(os.environ.get("SLURM_NODEID"))
|
| 109 |
+
cfg.distributed_rank = node_id * gpus_per_node
|
| 110 |
+
cfg.distributed_world_size = nnodes * gpus_per_node
|
| 111 |
+
elif cfg.pipeline_model_parallel:
|
| 112 |
+
assert ntasks_per_node == num_pipelines_per_node, (
|
| 113 |
+
"SLURM --ntasks-per-node must match number of pipelines per "
|
| 114 |
+
"node (={})".format(num_pipelines_per_node)
|
| 115 |
+
)
|
| 116 |
+
cfg.distributed_no_spawn = True
|
| 117 |
+
# For 4-way MP on nodes with 8 GPUs, ranks will be [0, 1] on
|
| 118 |
+
# the first node, [1, 2] on the second node, etc. This
|
| 119 |
+
# matches torch.distributed.launch.
|
| 120 |
+
node_id = int(os.environ.get("SLURM_NODEID"))
|
| 121 |
+
local_id = int(os.environ.get("SLURM_LOCALID"))
|
| 122 |
+
cfg.distributed_rank = node_id * num_pipelines_per_node + local_id
|
| 123 |
+
# In the above example, device_id will always be in [0, 1],
|
| 124 |
+
# which also matches torch.distributed.launch.
|
| 125 |
+
cfg.device_id = local_id
|
| 126 |
+
# We also want to set distributed_world_size to be the total
|
| 127 |
+
# number of pipelines across all nodes.
|
| 128 |
+
cfg.distributed_world_size = nnodes * num_pipelines_per_node
|
| 129 |
+
else:
|
| 130 |
+
assert ntasks_per_node == cfg.distributed_world_size // nnodes
|
| 131 |
+
cfg.distributed_no_spawn = True
|
| 132 |
+
cfg.distributed_rank = int(os.environ.get("SLURM_PROCID"))
|
| 133 |
+
cfg.device_id = int(os.environ.get("SLURM_LOCALID"))
|
| 134 |
+
except subprocess.CalledProcessError as e: # scontrol failed
|
| 135 |
+
raise e
|
| 136 |
+
except FileNotFoundError: # Slurm is not installed
|
| 137 |
+
pass
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def _infer_single_node_init(cfg: DistributedTrainingConfig):
|
| 141 |
+
assert (
|
| 142 |
+
cfg.distributed_world_size <= torch.cuda.device_count()
|
| 143 |
+
), f"world size is {cfg.distributed_world_size} but have {torch.cuda.device_count()} available devices"
|
| 144 |
+
port = random.randint(10000, 20000)
|
| 145 |
+
cfg.distributed_init_method = "tcp://localhost:{port}".format(port=port)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def _pipeline_parallel_pre_init(cfg: DistributedTrainingConfig):
|
| 149 |
+
from fairseq import utils
|
| 150 |
+
|
| 151 |
+
balance_exists = (
|
| 152 |
+
cfg.pipeline_balance is not None
|
| 153 |
+
or cfg.pipeline_encoder_balance is not None
|
| 154 |
+
or cfg.pipeline_decoder_balance is not None
|
| 155 |
+
)
|
| 156 |
+
devices_exist = (
|
| 157 |
+
cfg.pipeline_devices is not None
|
| 158 |
+
or cfg.pipeline_encoder_devices is not None
|
| 159 |
+
or cfg.pipeline_decoder_devices is not None
|
| 160 |
+
)
|
| 161 |
+
if not balance_exists:
|
| 162 |
+
raise ValueError(
|
| 163 |
+
"--pipeline-balance is currently required for pipeline model parallelism"
|
| 164 |
+
)
|
| 165 |
+
if not devices_exist:
|
| 166 |
+
raise ValueError(
|
| 167 |
+
"--pipeline-devices is currently required for pipeline model parallelism"
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
cfg.pipeline_balance = utils.eval_str_list(cfg.pipeline_balance, type=int)
|
| 171 |
+
if cfg.pipeline_devices is not None:
|
| 172 |
+
cfg.pipeline_devices = utils.eval_str_list(cfg.pipeline_devices, type=int)
|
| 173 |
+
num_pipeline_devices = len(set(cfg.pipeline_devices))
|
| 174 |
+
else:
|
| 175 |
+
cfg.pipeline_encoder_devices = utils.eval_str_list(
|
| 176 |
+
cfg.pipeline_encoder_devices, type=int
|
| 177 |
+
)
|
| 178 |
+
cfg.pipeline_decoder_devices = utils.eval_str_list(
|
| 179 |
+
cfg.pipeline_decoder_devices, type=int
|
| 180 |
+
)
|
| 181 |
+
num_pipeline_devices = len(
|
| 182 |
+
set(cfg.pipeline_encoder_devices + cfg.pipeline_decoder_devices)
|
| 183 |
+
)
|
| 184 |
+
gpus_per_node = torch.cuda.device_count()
|
| 185 |
+
assert (
|
| 186 |
+
gpus_per_node >= num_pipeline_devices
|
| 187 |
+
and gpus_per_node % num_pipeline_devices == 0
|
| 188 |
+
), (
|
| 189 |
+
"the number of unique device IDs in --pipeline-devices must evenly divide "
|
| 190 |
+
"the number of GPUs per node (multi-node pipelining is not yet supported)"
|
| 191 |
+
)
|
| 192 |
+
num_pipelines_per_node = gpus_per_node // num_pipeline_devices
|
| 193 |
+
return num_pipeline_devices, num_pipelines_per_node
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def _pipeline_parallel_post_init(
|
| 197 |
+
cfg: DistributedTrainingConfig, num_pipeline_devices, num_pipelines_per_node
|
| 198 |
+
):
|
| 199 |
+
if not cfg.distributed_no_spawn:
|
| 200 |
+
# When distributed_no_spawn is False, we expect distributed_rank and
|
| 201 |
+
# distributed_world_size to be based on the total number of GPUs, so
|
| 202 |
+
# we need to correct them to be based on the number of pipelines.
|
| 203 |
+
assert cfg.distributed_world_size % num_pipeline_devices == 0
|
| 204 |
+
cfg.distributed_world_size = (
|
| 205 |
+
cfg.distributed_world_size // num_pipeline_devices
|
| 206 |
+
)
|
| 207 |
+
# In the case of 4-way MP on nodes with 8 GPUs, we want
|
| 208 |
+
# distributed_rank to be the starting GPU index for each pipeline
|
| 209 |
+
# i.e., 0, 2, ...
|
| 210 |
+
gpus_per_node = torch.cuda.device_count()
|
| 211 |
+
assert cfg.distributed_rank % gpus_per_node == 0
|
| 212 |
+
assert cfg.distributed_rank % num_pipeline_devices == 0
|
| 213 |
+
|
| 214 |
+
with open_dict(cfg):
|
| 215 |
+
cfg.distributed_rank = cfg.distributed_rank // num_pipeline_devices
|
| 216 |
+
# launch one process per pipeline
|
| 217 |
+
cfg.distributed_num_procs = num_pipelines_per_node
|
| 218 |
+
|
| 219 |
+
# if we have 4-way MP on a node with 8 GPUs, we want device_ids to be 0
|
| 220 |
+
# and 4, indicating the starting device IDs for each pipeline
|
| 221 |
+
cfg.device_id *= num_pipeline_devices
|
| 222 |
+
|
| 223 |
+
if cfg.device_id > 0:
|
| 224 |
+
# if there's multiple pipelines on a node (e.g., 4-way MP on an 8
|
| 225 |
+
# GPU node), we need to adjust pipeline_devices accordingly
|
| 226 |
+
logger.debug(
|
| 227 |
+
"setting CUDA device={} on rank {}".format(
|
| 228 |
+
cfg.device_id, cfg.distributed_rank
|
| 229 |
+
)
|
| 230 |
+
)
|
| 231 |
+
torch.cuda.set_device(cfg.device_id)
|
| 232 |
+
with open_dict(cfg):
|
| 233 |
+
cfg.pipeline_devices = [cfg.device_id + d for d in cfg.pipeline_devices]
|
| 234 |
+
logger.info(
|
| 235 |
+
"setting pipeline_devices={} on rank {}".format(
|
| 236 |
+
cfg.pipeline_devices, cfg.distributed_rank
|
| 237 |
+
)
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def distributed_init(cfg: FairseqConfig):
|
| 242 |
+
if isinstance(cfg, Namespace):
|
| 243 |
+
from fairseq.dataclass.utils import convert_namespace_to_omegaconf
|
| 244 |
+
|
| 245 |
+
cfg = convert_namespace_to_omegaconf(cfg)
|
| 246 |
+
|
| 247 |
+
if not cfg.common.tpu:
|
| 248 |
+
if torch.distributed.is_available() and torch.distributed.is_initialized():
|
| 249 |
+
warnings.warn(
|
| 250 |
+
"Distributed is already initialized, cannot initialize twice!"
|
| 251 |
+
)
|
| 252 |
+
else:
|
| 253 |
+
logger.info(
|
| 254 |
+
"distributed init (rank {}): {}".format(
|
| 255 |
+
cfg.distributed_training.distributed_rank,
|
| 256 |
+
cfg.distributed_training.distributed_init_method,
|
| 257 |
+
)
|
| 258 |
+
)
|
| 259 |
+
dist.init_process_group(
|
| 260 |
+
backend=cfg.distributed_training.distributed_backend,
|
| 261 |
+
init_method=cfg.distributed_training.distributed_init_method,
|
| 262 |
+
world_size=cfg.distributed_training.distributed_world_size,
|
| 263 |
+
rank=cfg.distributed_training.distributed_rank,
|
| 264 |
+
)
|
| 265 |
+
logger.info(
|
| 266 |
+
"initialized host {} as rank {}".format(
|
| 267 |
+
socket.gethostname(),
|
| 268 |
+
cfg.distributed_training.distributed_rank,
|
| 269 |
+
)
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
# perform a dummy all-reduce to initialize the NCCL communicator
|
| 273 |
+
if torch.cuda.is_available():
|
| 274 |
+
dist.all_reduce(torch.zeros(1).cuda())
|
| 275 |
+
|
| 276 |
+
cfg.distributed_training.distributed_rank = torch.distributed.get_rank()
|
| 277 |
+
else:
|
| 278 |
+
assert xm.xrt_world_size() == cfg.distributed_training.distributed_world_size
|
| 279 |
+
global _USE_XLA
|
| 280 |
+
_USE_XLA = True
|
| 281 |
+
cfg.distributed_training.device_id = xm.get_local_ordinal()
|
| 282 |
+
cfg.distributed_training.distributed_rank = xm.get_ordinal()
|
| 283 |
+
xm.rendezvous("distributed_init") # wait for all workers
|
| 284 |
+
|
| 285 |
+
if is_master(cfg.distributed_training):
|
| 286 |
+
logging.getLogger().setLevel(logging.INFO)
|
| 287 |
+
else:
|
| 288 |
+
logging.getLogger().setLevel(logging.WARNING)
|
| 289 |
+
|
| 290 |
+
if cfg.common.model_parallel_size > 1:
|
| 291 |
+
try:
|
| 292 |
+
from fairseq.model_parallel.megatron.mpu import (
|
| 293 |
+
initialize_model_parallel,
|
| 294 |
+
model_parallel_cuda_manual_seed,
|
| 295 |
+
)
|
| 296 |
+
except ImportError:
|
| 297 |
+
raise ImportError(
|
| 298 |
+
"\n\nPlease install the megatron submodule:"
|
| 299 |
+
"\n\n git submodule update --init "
|
| 300 |
+
"fairseq/model_parallel/megatron"
|
| 301 |
+
)
|
| 302 |
+
global _USE_MEGATRON
|
| 303 |
+
_USE_MEGATRON = True
|
| 304 |
+
initialize_model_parallel(cfg.common.model_parallel_size)
|
| 305 |
+
model_parallel_cuda_manual_seed(cfg.common.seed)
|
| 306 |
+
model_part_number = get_model_parallel_rank()
|
| 307 |
+
cfg.checkpoint.checkpoint_suffix += "-model_part-{0}".format(model_part_number)
|
| 308 |
+
|
| 309 |
+
if hasattr(cfg, "model") and getattr(cfg.model, "base_layers", 0) > 0:
|
| 310 |
+
cfg.checkpoint.checkpoint_suffix = f"-rank-{cfg.distributed_training.distributed_rank}"
|
| 311 |
+
|
| 312 |
+
return cfg.distributed_training.distributed_rank
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def distributed_main(i, main, cfg: FairseqConfig, kwargs):
|
| 316 |
+
cfg.distributed_training.device_id = i
|
| 317 |
+
if torch.cuda.is_available() and not cfg.common.cpu and not cfg.common.tpu:
|
| 318 |
+
torch.cuda.set_device(cfg.distributed_training.device_id)
|
| 319 |
+
if cfg.distributed_training.distributed_rank is None: # torch.multiprocessing.spawn
|
| 320 |
+
cfg.distributed_training.distributed_rank = kwargs.pop("start_rank", 0) + i
|
| 321 |
+
|
| 322 |
+
cfg.distributed_training.distributed_rank = distributed_init(cfg)
|
| 323 |
+
|
| 324 |
+
after_distributed_init_fn = kwargs.pop("after_distributed_init_fn", None)
|
| 325 |
+
if after_distributed_init_fn:
|
| 326 |
+
cfg = after_distributed_init_fn(cfg)
|
| 327 |
+
|
| 328 |
+
main(cfg, **kwargs)
|
| 329 |
+
|
| 330 |
+
if torch.distributed.is_initialized():
|
| 331 |
+
torch.distributed.barrier(get_global_group())
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def call_main(cfg: FairseqConfig, main, **kwargs):
|
| 335 |
+
if cfg.distributed_training.distributed_init_method is None:
|
| 336 |
+
infer_init_method(cfg.distributed_training)
|
| 337 |
+
|
| 338 |
+
if cfg.distributed_training.distributed_init_method is not None:
|
| 339 |
+
# distributed training
|
| 340 |
+
if not cfg.distributed_training.distributed_no_spawn:
|
| 341 |
+
start_rank = cfg.distributed_training.distributed_rank
|
| 342 |
+
cfg.distributed_training.distributed_rank = None # assign automatically
|
| 343 |
+
kwargs["start_rank"] = start_rank
|
| 344 |
+
torch.multiprocessing.spawn(
|
| 345 |
+
fn=distributed_main,
|
| 346 |
+
args=(main, cfg, kwargs),
|
| 347 |
+
nprocs=min(
|
| 348 |
+
torch.cuda.device_count(),
|
| 349 |
+
cfg.distributed_training.distributed_world_size,
|
| 350 |
+
),
|
| 351 |
+
join=True,
|
| 352 |
+
)
|
| 353 |
+
else:
|
| 354 |
+
distributed_main(cfg.distributed_training.device_id, main, cfg, kwargs)
|
| 355 |
+
elif cfg.common.tpu and cfg.distributed_training.distributed_world_size > 1:
|
| 356 |
+
import torch_xla.distributed.xla_multiprocessing as xmp
|
| 357 |
+
|
| 358 |
+
torch.multiprocessing.set_sharing_strategy("file_system")
|
| 359 |
+
xmp.spawn(
|
| 360 |
+
fn=distributed_main,
|
| 361 |
+
args=(main, cfg, kwargs),
|
| 362 |
+
# tpu-comment:
|
| 363 |
+
# 8 devices in one TPU VM, is the max processes to be spawned.
|
| 364 |
+
# The rest is driven by xm.distributed.xla_dist
|
| 365 |
+
nprocs=min(cfg.distributed_training.distributed_world_size, 8),
|
| 366 |
+
)
|
| 367 |
+
else:
|
| 368 |
+
# single GPU main
|
| 369 |
+
main(cfg, **kwargs)
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
def use_xla():
|
| 373 |
+
global _USE_XLA
|
| 374 |
+
return _USE_XLA
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
def new_groups(grouped_ranks: List[List[int]]):
|
| 378 |
+
if use_xla():
|
| 379 |
+
return ("tpu", grouped_ranks)
|
| 380 |
+
else:
|
| 381 |
+
groups = [dist.new_group(g) for g in grouped_ranks]
|
| 382 |
+
my_group_idx = _find_my_group_index(grouped_ranks)
|
| 383 |
+
return groups[my_group_idx]
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def _find_my_group_index(grouped_ranks):
|
| 387 |
+
my_rank = get_global_rank()
|
| 388 |
+
for i, group in enumerate(grouped_ranks):
|
| 389 |
+
if my_rank in group:
|
| 390 |
+
return i
|
| 391 |
+
raise RuntimeError
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def _find_my_group(grouped_ranks):
|
| 395 |
+
index = _find_my_group_index(grouped_ranks)
|
| 396 |
+
return grouped_ranks[index]
|
| 397 |
+
|
| 398 |
+
|
| 399 |
+
def get_rank(group):
|
| 400 |
+
if use_xla():
|
| 401 |
+
assert group[0] == "tpu"
|
| 402 |
+
my_group = _find_my_group(group[1])
|
| 403 |
+
return my_group.index(get_global_rank())
|
| 404 |
+
else:
|
| 405 |
+
return dist.get_rank(group=group)
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def get_world_size(group):
|
| 409 |
+
if use_xla():
|
| 410 |
+
assert group[0] == "tpu"
|
| 411 |
+
my_group = _find_my_group(group[1])
|
| 412 |
+
return len(my_group)
|
| 413 |
+
elif torch.distributed.is_initialized():
|
| 414 |
+
return dist.get_world_size(group=group)
|
| 415 |
+
else:
|
| 416 |
+
return 1
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def get_global_group():
|
| 420 |
+
if use_xla():
|
| 421 |
+
return new_groups([list(range(get_global_world_size()))])
|
| 422 |
+
elif torch.distributed.is_initialized():
|
| 423 |
+
if not hasattr(get_global_group, "_global_group"):
|
| 424 |
+
# ideally we could use torch.distributed.group.WORLD, but it seems
|
| 425 |
+
# to cause random NCCL hangs in some cases
|
| 426 |
+
get_global_group._global_group = dist.new_group()
|
| 427 |
+
return get_global_group._global_group
|
| 428 |
+
else:
|
| 429 |
+
return None
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
def get_global_rank():
|
| 433 |
+
if use_xla():
|
| 434 |
+
return xm.get_ordinal()
|
| 435 |
+
elif torch.distributed.is_initialized():
|
| 436 |
+
return torch.distributed.get_rank()
|
| 437 |
+
else:
|
| 438 |
+
return 0
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
def get_global_world_size():
|
| 442 |
+
if use_xla():
|
| 443 |
+
return xm.xrt_world_size()
|
| 444 |
+
elif torch.distributed.is_initialized():
|
| 445 |
+
return torch.distributed.get_world_size()
|
| 446 |
+
else:
|
| 447 |
+
return 1
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
def get_data_parallel_group():
|
| 451 |
+
"""Get the data parallel group the caller rank belongs to."""
|
| 452 |
+
global _USE_MEGATRON
|
| 453 |
+
if _USE_MEGATRON:
|
| 454 |
+
from fairseq.model_parallel.megatron import mpu
|
| 455 |
+
|
| 456 |
+
return mpu.get_data_parallel_group()
|
| 457 |
+
else:
|
| 458 |
+
return get_global_group()
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
def get_data_parallel_rank():
|
| 462 |
+
"""Return my rank for the data parallel group."""
|
| 463 |
+
return get_rank(get_data_parallel_group())
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
def get_data_parallel_world_size():
|
| 467 |
+
"""Return world size for the data parallel group."""
|
| 468 |
+
return get_world_size(get_data_parallel_group())
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
def get_model_parallel_group():
|
| 472 |
+
global _USE_MEGATRON
|
| 473 |
+
if _USE_MEGATRON:
|
| 474 |
+
from fairseq.model_parallel.megatron import mpu
|
| 475 |
+
|
| 476 |
+
return mpu.get_model_parallel_group()
|
| 477 |
+
else:
|
| 478 |
+
return None
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
def get_model_parallel_rank():
|
| 482 |
+
"""Return my rank for the model parallel group."""
|
| 483 |
+
return get_rank(get_model_parallel_group())
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def get_model_parallel_world_size():
|
| 487 |
+
"""Return world size for the model parallel group."""
|
| 488 |
+
return get_world_size(get_model_parallel_group())
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
def all_reduce(tensor, group, op="sum"):
|
| 492 |
+
if use_xla():
|
| 493 |
+
assert isinstance(group, tuple) and group[0] == "tpu"
|
| 494 |
+
tensor = [tensor] # wrap in a list to make xm.all_reduce in-place
|
| 495 |
+
return xm.all_reduce(op, tensor, groups=group[1])[0]
|
| 496 |
+
else:
|
| 497 |
+
if op == "sum":
|
| 498 |
+
op = dist.ReduceOp.SUM
|
| 499 |
+
elif op == "max":
|
| 500 |
+
op = dist.ReduceOp.MAX
|
| 501 |
+
else:
|
| 502 |
+
raise NotImplementedError
|
| 503 |
+
dist.all_reduce(tensor, op=op, group=group)
|
| 504 |
+
return tensor
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
def broadcast(tensor, src, group):
|
| 508 |
+
if use_xla():
|
| 509 |
+
# XLA doesn't support broadcast, hack it with all_reduce
|
| 510 |
+
if get_rank(group) != src:
|
| 511 |
+
tensor.zero_()
|
| 512 |
+
all_reduce(tensor, group)
|
| 513 |
+
else:
|
| 514 |
+
dist.broadcast(tensor, src=src, group=group)
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def all_to_all(tensor, group):
|
| 518 |
+
"""Perform an all-to-all operation on a 1D Tensor."""
|
| 519 |
+
assert tensor.dim() == 1
|
| 520 |
+
split_count = get_world_size(group=group)
|
| 521 |
+
assert tensor.numel() % split_count == 0
|
| 522 |
+
if use_xla():
|
| 523 |
+
assert isinstance(group, tuple) and group[0] == "tpu"
|
| 524 |
+
return xm.all_to_all(
|
| 525 |
+
tensor,
|
| 526 |
+
split_dimension=0,
|
| 527 |
+
concat_dimension=0,
|
| 528 |
+
split_count=split_count,
|
| 529 |
+
groups=group[1],
|
| 530 |
+
)
|
| 531 |
+
else:
|
| 532 |
+
output = torch.zeros_like(tensor)
|
| 533 |
+
dist.all_to_all_single(output, tensor, group=group)
|
| 534 |
+
return output
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def all_gather(tensor, group, return_tensor=False):
|
| 538 |
+
"""Perform an all-gather operation."""
|
| 539 |
+
if use_xla():
|
| 540 |
+
result = xm.all_gather(tensor, groups=group[1])
|
| 541 |
+
world_size = get_world_size(group=group)
|
| 542 |
+
result = result.view(world_size, *tensor.size())
|
| 543 |
+
if return_tensor:
|
| 544 |
+
return result
|
| 545 |
+
else:
|
| 546 |
+
return [result[i] for i in range(world_size)]
|
| 547 |
+
else:
|
| 548 |
+
world_size = get_world_size(group=group)
|
| 549 |
+
rank = get_rank(group=group)
|
| 550 |
+
tensor_list = [
|
| 551 |
+
tensor if i == rank else torch.empty_like(tensor) for i in range(world_size)
|
| 552 |
+
]
|
| 553 |
+
dist.all_gather(tensor_list, tensor, group=group)
|
| 554 |
+
if return_tensor:
|
| 555 |
+
return torch.stack(tensor_list, dim=0)
|
| 556 |
+
else:
|
| 557 |
+
return tensor_list
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
def all_gather_list(data, group=None, max_size=16384):
|
| 561 |
+
"""Gathers arbitrary data from all nodes into a list.
|
| 562 |
+
|
| 563 |
+
Similar to :func:`~torch.distributed.all_gather` but for arbitrary Python
|
| 564 |
+
data. Note that *data* must be picklable and any CUDA tensors will be moved
|
| 565 |
+
to CPU and returned on CPU as well.
|
| 566 |
+
|
| 567 |
+
Args:
|
| 568 |
+
data (Any): data from the local worker to be gathered on other workers
|
| 569 |
+
group: group of the collective
|
| 570 |
+
max_size (int, optional): maximum size of the data to be gathered
|
| 571 |
+
across workers
|
| 572 |
+
"""
|
| 573 |
+
from fairseq import utils
|
| 574 |
+
|
| 575 |
+
if group is None:
|
| 576 |
+
group = get_global_group()
|
| 577 |
+
rank = get_rank(group=group)
|
| 578 |
+
world_size = get_world_size(group=group)
|
| 579 |
+
|
| 580 |
+
buffer_size = max_size * world_size
|
| 581 |
+
if (
|
| 582 |
+
not hasattr(all_gather_list, "_buffer")
|
| 583 |
+
or all_gather_list._buffer.numel() < buffer_size
|
| 584 |
+
):
|
| 585 |
+
all_gather_list._buffer = torch.cuda.ByteTensor(buffer_size)
|
| 586 |
+
all_gather_list._cpu_buffer = torch.ByteTensor(max_size).pin_memory()
|
| 587 |
+
buffer = all_gather_list._buffer
|
| 588 |
+
buffer.zero_()
|
| 589 |
+
cpu_buffer = all_gather_list._cpu_buffer
|
| 590 |
+
|
| 591 |
+
data = utils.move_to_cpu(data)
|
| 592 |
+
enc = pickle.dumps(data)
|
| 593 |
+
enc_size = len(enc)
|
| 594 |
+
header_size = 4 # size of header that contains the length of the encoded data
|
| 595 |
+
size = header_size + enc_size
|
| 596 |
+
if size > max_size:
|
| 597 |
+
raise ValueError(
|
| 598 |
+
"encoded data size ({}) exceeds max_size ({})".format(size, max_size)
|
| 599 |
+
)
|
| 600 |
+
|
| 601 |
+
header = struct.pack(">I", enc_size)
|
| 602 |
+
cpu_buffer[:size] = torch.ByteTensor(list(header + enc))
|
| 603 |
+
start = rank * max_size
|
| 604 |
+
buffer[start : start + size].copy_(cpu_buffer[:size])
|
| 605 |
+
|
| 606 |
+
all_reduce(buffer, group=group)
|
| 607 |
+
|
| 608 |
+
buffer = buffer.cpu()
|
| 609 |
+
try:
|
| 610 |
+
result = []
|
| 611 |
+
for i in range(world_size):
|
| 612 |
+
out_buffer = buffer[i * max_size : (i + 1) * max_size]
|
| 613 |
+
(enc_size,) = struct.unpack(">I", bytes(out_buffer[:header_size].tolist()))
|
| 614 |
+
if enc_size > 0:
|
| 615 |
+
result.append(
|
| 616 |
+
pickle.loads(
|
| 617 |
+
bytes(out_buffer[header_size : header_size + enc_size].tolist())
|
| 618 |
+
)
|
| 619 |
+
)
|
| 620 |
+
return result
|
| 621 |
+
except pickle.UnpicklingError:
|
| 622 |
+
raise Exception(
|
| 623 |
+
"Unable to unpickle data from other workers. all_gather_list requires all "
|
| 624 |
+
"workers to enter the function together, so this error usually indicates "
|
| 625 |
+
"that the workers have fallen out of sync somehow. Workers can fall out of "
|
| 626 |
+
"sync if one of them runs out of memory, or if there are other conditions "
|
| 627 |
+
"in your training script that can cause one worker to finish an epoch "
|
| 628 |
+
"while other workers are still iterating over their portions of the data. "
|
| 629 |
+
"Try rerunning with --ddp-backend=legacy_ddp and see if that helps."
|
| 630 |
+
)
|
| 631 |
+
|
| 632 |
+
|
| 633 |
+
def all_reduce_dict(data: Mapping[str, Any], device, group) -> Dict[str, Any]:
|
| 634 |
+
"""
|
| 635 |
+
AllReduce a dictionary of values across workers. We separately
|
| 636 |
+
reduce items that are already on the device and items on CPU for
|
| 637 |
+
better performance.
|
| 638 |
+
|
| 639 |
+
Args:
|
| 640 |
+
data (Mapping[str, Any]): dictionary of data to all-reduce, but
|
| 641 |
+
cannot be a nested dictionary
|
| 642 |
+
device (torch.device): device for the reduction
|
| 643 |
+
group: group of the collective
|
| 644 |
+
"""
|
| 645 |
+
data_keys = list(data.keys())
|
| 646 |
+
|
| 647 |
+
# We want to separately reduce items that are already on the
|
| 648 |
+
# device and items on CPU for performance reasons.
|
| 649 |
+
cpu_data = OrderedDict()
|
| 650 |
+
device_data = OrderedDict()
|
| 651 |
+
for k in data_keys:
|
| 652 |
+
t = data[k]
|
| 653 |
+
if not torch.is_tensor(t):
|
| 654 |
+
cpu_data[k] = torch.tensor(t, dtype=torch.double)
|
| 655 |
+
elif t.device.type != device.type:
|
| 656 |
+
cpu_data[k] = t.to(dtype=torch.double)
|
| 657 |
+
else:
|
| 658 |
+
device_data[k] = t.to(dtype=torch.double)
|
| 659 |
+
|
| 660 |
+
def _all_reduce_dict(data: OrderedDict):
|
| 661 |
+
if len(data) == 0:
|
| 662 |
+
return data
|
| 663 |
+
buf = torch.cat([t.view(-1) for t in data.values()]).to(device=device)
|
| 664 |
+
all_reduce(buf, group=group)
|
| 665 |
+
split_buf = torch.split(buf, [t.numel() for t in data.values()])
|
| 666 |
+
reduced_data = [t.view_as(orig) for t, orig in zip(split_buf, data.values())]
|
| 667 |
+
return OrderedDict(zip(data.keys(), reduced_data))
|
| 668 |
+
|
| 669 |
+
cpu_data = _all_reduce_dict(cpu_data)
|
| 670 |
+
device_data = _all_reduce_dict(device_data)
|
| 671 |
+
|
| 672 |
+
def get_from_stack(key):
|
| 673 |
+
if key in cpu_data:
|
| 674 |
+
return cpu_data[key]
|
| 675 |
+
elif key in device_data:
|
| 676 |
+
return device_data[key]
|
| 677 |
+
raise KeyError
|
| 678 |
+
|
| 679 |
+
return OrderedDict([(key, get_from_stack(key)) for key in data_keys])
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
def broadcast_tensors(
|
| 683 |
+
tensors: Optional[List[torch.Tensor]],
|
| 684 |
+
src_rank: int,
|
| 685 |
+
group: object,
|
| 686 |
+
dist_device: Optional[torch.device] = None,
|
| 687 |
+
) -> List[torch.Tensor]:
|
| 688 |
+
"""
|
| 689 |
+
Broadcasts a list of tensors without other (non-src) ranks needing to know
|
| 690 |
+
the dtypes/shapes of the tensors.
|
| 691 |
+
"""
|
| 692 |
+
if dist_device is None:
|
| 693 |
+
if torch.distributed.get_backend(group) == "nccl":
|
| 694 |
+
dist_device = torch.device("cuda")
|
| 695 |
+
else:
|
| 696 |
+
dist_device = torch.device("cpu")
|
| 697 |
+
|
| 698 |
+
# share metadata first to simplify transfer
|
| 699 |
+
is_src_rank = (get_rank(group) == src_rank)
|
| 700 |
+
if is_src_rank:
|
| 701 |
+
metadata = [
|
| 702 |
+
{"size": t.size(), "dtype": t.dtype, "device": t.device} for t in tensors
|
| 703 |
+
]
|
| 704 |
+
metadata = _broadcast_object_slow(metadata, src_rank, group, dist_device)
|
| 705 |
+
else:
|
| 706 |
+
metadata = _broadcast_object_slow(None, src_rank, group, dist_device)
|
| 707 |
+
|
| 708 |
+
out_tensors = []
|
| 709 |
+
for i, meta in enumerate(metadata):
|
| 710 |
+
if is_src_rank:
|
| 711 |
+
tensor = tensors[i]
|
| 712 |
+
broadcast(tensors[i].to(dist_device), src=src_rank, group=group)
|
| 713 |
+
else:
|
| 714 |
+
tensor = torch.zeros(
|
| 715 |
+
[meta["size"].numel()], dtype=meta["dtype"], device=dist_device
|
| 716 |
+
)
|
| 717 |
+
broadcast(tensor, src=src_rank, group=group)
|
| 718 |
+
tensor = tensor.view(meta["size"]).to(meta["device"])
|
| 719 |
+
out_tensors.append(tensor)
|
| 720 |
+
return out_tensors
|
| 721 |
+
|
| 722 |
+
|
| 723 |
+
def broadcast_object(
|
| 724 |
+
obj: Any,
|
| 725 |
+
src_rank: int,
|
| 726 |
+
group: object,
|
| 727 |
+
dist_device: Optional[torch.device] = None,
|
| 728 |
+
) -> Any:
|
| 729 |
+
"""Broadcast an arbitrary Python object to other workers."""
|
| 730 |
+
if dist_device is None:
|
| 731 |
+
if torch.distributed.get_backend(group) == "nccl":
|
| 732 |
+
dist_device = torch.device("cuda")
|
| 733 |
+
else:
|
| 734 |
+
dist_device = torch.device("cpu")
|
| 735 |
+
|
| 736 |
+
if get_rank(group) == src_rank:
|
| 737 |
+
# split the tensors from the non-tensors so we can broadcast them
|
| 738 |
+
# directly, avoiding unnecessary serialization/deserialization
|
| 739 |
+
tensors = []
|
| 740 |
+
obj = _split_tensors_from_obj(obj, tensors)
|
| 741 |
+
obj = _broadcast_object_slow(obj, src_rank, group, dist_device)
|
| 742 |
+
tensors = broadcast_tensors(tensors, src_rank, group, dist_device)
|
| 743 |
+
else:
|
| 744 |
+
obj = _broadcast_object_slow(None, src_rank, group, dist_device)
|
| 745 |
+
tensors = broadcast_tensors(None, src_rank, group, dist_device)
|
| 746 |
+
return _put_tensors_in_obj(obj, tensors)
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
def _broadcast_object_slow(
|
| 750 |
+
obj: Any, src_rank: int, group: object, dist_device: torch.device,
|
| 751 |
+
) -> Any:
|
| 752 |
+
if get_rank(group) == src_rank:
|
| 753 |
+
# Emit data
|
| 754 |
+
buffer = io.BytesIO()
|
| 755 |
+
torch.save(obj, buffer)
|
| 756 |
+
buffer = torch.ByteTensor(buffer.getbuffer()).to(dist_device)
|
| 757 |
+
length = torch.LongTensor([len(buffer)]).to(dist_device)
|
| 758 |
+
broadcast(length, src=src_rank, group=group)
|
| 759 |
+
broadcast(buffer, src=src_rank, group=group)
|
| 760 |
+
else:
|
| 761 |
+
# Fetch from the source
|
| 762 |
+
length = torch.LongTensor([0]).to(dist_device)
|
| 763 |
+
broadcast(length, src=src_rank, group=group)
|
| 764 |
+
buffer = torch.ByteTensor(int(length.item())).to(dist_device)
|
| 765 |
+
broadcast(buffer, src=src_rank, group=group)
|
| 766 |
+
buffer = io.BytesIO(buffer.cpu().numpy())
|
| 767 |
+
obj = torch.load(buffer, map_location="cpu")
|
| 768 |
+
return obj
|
| 769 |
+
|
| 770 |
+
|
| 771 |
+
@dataclass(frozen=True)
|
| 772 |
+
class _TensorPlaceholder:
|
| 773 |
+
index: int
|
| 774 |
+
|
| 775 |
+
|
| 776 |
+
def _split_tensors_from_obj(obj: Any, tensors: List[torch.Tensor]) -> Any:
|
| 777 |
+
if torch.is_tensor(obj):
|
| 778 |
+
placeholder = _TensorPlaceholder(index=len(tensors))
|
| 779 |
+
tensors.append(obj)
|
| 780 |
+
return placeholder
|
| 781 |
+
elif isinstance(obj, dict):
|
| 782 |
+
return {k: _split_tensors_from_obj(v, tensors) for k, v in obj.items()}
|
| 783 |
+
elif isinstance(obj, list):
|
| 784 |
+
return [_split_tensors_from_obj(v, tensors) for v in obj]
|
| 785 |
+
elif isinstance(obj, tuple):
|
| 786 |
+
return tuple(_split_tensors_from_obj(v, tensors) for v in obj)
|
| 787 |
+
elif isinstance(obj, set):
|
| 788 |
+
return {_split_tensors_from_obj(v, tensors) for v in obj}
|
| 789 |
+
else:
|
| 790 |
+
return obj
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
def _put_tensors_in_obj(obj: Any, tensors: List[torch.Tensor]) -> Any:
|
| 794 |
+
if isinstance(obj, _TensorPlaceholder):
|
| 795 |
+
return tensors[obj.index]
|
| 796 |
+
elif isinstance(obj, dict):
|
| 797 |
+
return {k: _put_tensors_in_obj(v, tensors) for k, v in obj.items()}
|
| 798 |
+
elif isinstance(obj, list):
|
| 799 |
+
return [_put_tensors_in_obj(v, tensors) for v in obj]
|
| 800 |
+
elif isinstance(obj, tuple):
|
| 801 |
+
return tuple(_put_tensors_in_obj(v, tensors) for v in obj)
|
| 802 |
+
elif isinstance(obj, set):
|
| 803 |
+
return {_put_tensors_in_obj(v, tensors) for v in obj}
|
| 804 |
+
else:
|
| 805 |
+
return obj
|
avh-align_core/fairseq/logging/__init__.py
ADDED
|
File without changes
|
avh-align_core/fairseq/logging/meters.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import bisect
|
| 7 |
+
import time
|
| 8 |
+
from collections import OrderedDict
|
| 9 |
+
from typing import Dict, Optional
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
import torch
|
| 14 |
+
|
| 15 |
+
def type_as(a, b):
|
| 16 |
+
if torch.is_tensor(a) and torch.is_tensor(b):
|
| 17 |
+
return a.to(b)
|
| 18 |
+
else:
|
| 19 |
+
return a
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
except ImportError:
|
| 23 |
+
torch = None
|
| 24 |
+
|
| 25 |
+
def type_as(a, b):
|
| 26 |
+
return a
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
import numpy as np
|
| 31 |
+
except ImportError:
|
| 32 |
+
np = None
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class Meter(object):
|
| 36 |
+
"""Base class for Meters."""
|
| 37 |
+
|
| 38 |
+
def __init__(self):
|
| 39 |
+
pass
|
| 40 |
+
|
| 41 |
+
def state_dict(self):
|
| 42 |
+
return {}
|
| 43 |
+
|
| 44 |
+
def load_state_dict(self, state_dict):
|
| 45 |
+
pass
|
| 46 |
+
|
| 47 |
+
def reset(self):
|
| 48 |
+
raise NotImplementedError
|
| 49 |
+
|
| 50 |
+
@property
|
| 51 |
+
def smoothed_value(self) -> float:
|
| 52 |
+
"""Smoothed value used for logging."""
|
| 53 |
+
raise NotImplementedError
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def safe_round(number, ndigits):
|
| 57 |
+
if hasattr(number, "__round__"):
|
| 58 |
+
return round(number, ndigits)
|
| 59 |
+
elif torch is not None and torch.is_tensor(number) and number.numel() == 1:
|
| 60 |
+
return safe_round(number.item(), ndigits)
|
| 61 |
+
elif np is not None and np.ndim(number) == 0 and hasattr(number, "item"):
|
| 62 |
+
return safe_round(number.item(), ndigits)
|
| 63 |
+
else:
|
| 64 |
+
return number
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class AverageMeter(Meter):
|
| 68 |
+
"""Computes and stores the average and current value"""
|
| 69 |
+
|
| 70 |
+
def __init__(self, round: Optional[int] = None):
|
| 71 |
+
self.round = round
|
| 72 |
+
self.reset()
|
| 73 |
+
|
| 74 |
+
def reset(self):
|
| 75 |
+
self.val = None # most recent update
|
| 76 |
+
self.sum = 0 # sum from all updates
|
| 77 |
+
self.count = 0 # total n from all updates
|
| 78 |
+
|
| 79 |
+
def update(self, val, n=1):
|
| 80 |
+
if val is not None:
|
| 81 |
+
self.val = val
|
| 82 |
+
if n > 0:
|
| 83 |
+
self.sum = type_as(self.sum, val) + (val * n)
|
| 84 |
+
self.count = type_as(self.count, n) + n
|
| 85 |
+
|
| 86 |
+
def state_dict(self):
|
| 87 |
+
return {
|
| 88 |
+
"val": self.val,
|
| 89 |
+
"sum": self.sum,
|
| 90 |
+
"count": self.count,
|
| 91 |
+
"round": self.round,
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
def load_state_dict(self, state_dict):
|
| 95 |
+
self.val = state_dict["val"]
|
| 96 |
+
self.sum = state_dict["sum"]
|
| 97 |
+
self.count = state_dict["count"]
|
| 98 |
+
self.round = state_dict.get("round", None)
|
| 99 |
+
|
| 100 |
+
@property
|
| 101 |
+
def avg(self):
|
| 102 |
+
return self.sum / self.count if self.count > 0 else self.val
|
| 103 |
+
|
| 104 |
+
@property
|
| 105 |
+
def smoothed_value(self) -> float:
|
| 106 |
+
val = self.avg
|
| 107 |
+
if self.round is not None and val is not None:
|
| 108 |
+
val = safe_round(val, self.round)
|
| 109 |
+
return val
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class SumMeter(Meter):
|
| 113 |
+
"""Computes and stores the sum"""
|
| 114 |
+
|
| 115 |
+
def __init__(self, round: Optional[int] = None):
|
| 116 |
+
self.round = round
|
| 117 |
+
self.reset()
|
| 118 |
+
|
| 119 |
+
def reset(self):
|
| 120 |
+
self.sum = 0 # sum from all updates
|
| 121 |
+
|
| 122 |
+
def update(self, val):
|
| 123 |
+
if val is not None:
|
| 124 |
+
self.sum = type_as(self.sum, val) + val
|
| 125 |
+
|
| 126 |
+
def state_dict(self):
|
| 127 |
+
return {
|
| 128 |
+
"sum": self.sum,
|
| 129 |
+
"round": self.round,
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
def load_state_dict(self, state_dict):
|
| 133 |
+
self.sum = state_dict["sum"]
|
| 134 |
+
self.round = state_dict.get("round", None)
|
| 135 |
+
|
| 136 |
+
@property
|
| 137 |
+
def smoothed_value(self) -> float:
|
| 138 |
+
val = self.sum
|
| 139 |
+
if self.round is not None and val is not None:
|
| 140 |
+
val = safe_round(val, self.round)
|
| 141 |
+
return val
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class TimeMeter(Meter):
|
| 145 |
+
"""Computes the average occurrence of some event per second"""
|
| 146 |
+
|
| 147 |
+
def __init__(
|
| 148 |
+
self,
|
| 149 |
+
init: int = 0,
|
| 150 |
+
n: int = 0,
|
| 151 |
+
round: Optional[int] = None,
|
| 152 |
+
):
|
| 153 |
+
self.round = round
|
| 154 |
+
self.reset(init, n)
|
| 155 |
+
|
| 156 |
+
def reset(self, init=0, n=0):
|
| 157 |
+
self.init = init
|
| 158 |
+
self.start = time.perf_counter()
|
| 159 |
+
self.n = n
|
| 160 |
+
self.i = 0
|
| 161 |
+
|
| 162 |
+
def update(self, val=1):
|
| 163 |
+
self.n = type_as(self.n, val) + val
|
| 164 |
+
self.i += 1
|
| 165 |
+
|
| 166 |
+
def state_dict(self):
|
| 167 |
+
return {
|
| 168 |
+
"init": self.elapsed_time,
|
| 169 |
+
"n": self.n,
|
| 170 |
+
"round": self.round,
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
def load_state_dict(self, state_dict):
|
| 174 |
+
if "start" in state_dict:
|
| 175 |
+
# backwards compatibility for old state_dicts
|
| 176 |
+
self.reset(init=state_dict["init"])
|
| 177 |
+
else:
|
| 178 |
+
self.reset(init=state_dict["init"], n=state_dict["n"])
|
| 179 |
+
self.round = state_dict.get("round", None)
|
| 180 |
+
|
| 181 |
+
@property
|
| 182 |
+
def avg(self):
|
| 183 |
+
return self.n / self.elapsed_time
|
| 184 |
+
|
| 185 |
+
@property
|
| 186 |
+
def elapsed_time(self):
|
| 187 |
+
return self.init + (time.perf_counter() - self.start)
|
| 188 |
+
|
| 189 |
+
@property
|
| 190 |
+
def smoothed_value(self) -> float:
|
| 191 |
+
val = self.avg
|
| 192 |
+
if self.round is not None and val is not None:
|
| 193 |
+
val = safe_round(val, self.round)
|
| 194 |
+
return val
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
class StopwatchMeter(Meter):
|
| 198 |
+
"""Computes the sum/avg duration of some event in seconds"""
|
| 199 |
+
|
| 200 |
+
def __init__(self, round: Optional[int] = None):
|
| 201 |
+
self.round = round
|
| 202 |
+
self.sum = 0
|
| 203 |
+
self.n = 0
|
| 204 |
+
self.start_time = None
|
| 205 |
+
|
| 206 |
+
def start(self):
|
| 207 |
+
self.start_time = time.perf_counter()
|
| 208 |
+
|
| 209 |
+
def stop(self, n=1, prehook=None):
|
| 210 |
+
if self.start_time is not None:
|
| 211 |
+
if prehook is not None:
|
| 212 |
+
prehook()
|
| 213 |
+
delta = time.perf_counter() - self.start_time
|
| 214 |
+
self.sum = self.sum + delta
|
| 215 |
+
self.n = type_as(self.n, n) + n
|
| 216 |
+
|
| 217 |
+
def reset(self):
|
| 218 |
+
self.sum = 0 # cumulative time during which stopwatch was active
|
| 219 |
+
self.n = 0 # total n across all start/stop
|
| 220 |
+
self.start()
|
| 221 |
+
|
| 222 |
+
def state_dict(self):
|
| 223 |
+
return {
|
| 224 |
+
"sum": self.sum,
|
| 225 |
+
"n": self.n,
|
| 226 |
+
"round": self.round,
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
def load_state_dict(self, state_dict):
|
| 230 |
+
self.sum = state_dict["sum"]
|
| 231 |
+
self.n = state_dict["n"]
|
| 232 |
+
self.start_time = None
|
| 233 |
+
self.round = state_dict.get("round", None)
|
| 234 |
+
|
| 235 |
+
@property
|
| 236 |
+
def avg(self):
|
| 237 |
+
return self.sum / self.n if self.n > 0 else self.sum
|
| 238 |
+
|
| 239 |
+
@property
|
| 240 |
+
def elapsed_time(self):
|
| 241 |
+
if self.start_time is None:
|
| 242 |
+
return 0.0
|
| 243 |
+
return time.perf_counter() - self.start_time
|
| 244 |
+
|
| 245 |
+
@property
|
| 246 |
+
def smoothed_value(self) -> float:
|
| 247 |
+
val = self.avg if self.sum > 0 else self.elapsed_time
|
| 248 |
+
if self.round is not None and val is not None:
|
| 249 |
+
val = safe_round(val, self.round)
|
| 250 |
+
return val
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
class MetersDict(OrderedDict):
|
| 254 |
+
"""A sorted dictionary of :class:`Meters`.
|
| 255 |
+
|
| 256 |
+
Meters are sorted according to a priority that is given when the
|
| 257 |
+
meter is first added to the dictionary.
|
| 258 |
+
"""
|
| 259 |
+
|
| 260 |
+
def __init__(self, *args, **kwargs):
|
| 261 |
+
super().__init__(*args, **kwargs)
|
| 262 |
+
self.priorities = []
|
| 263 |
+
|
| 264 |
+
def __setitem__(self, key, value):
|
| 265 |
+
assert key not in self, "MetersDict doesn't support reassignment"
|
| 266 |
+
priority, value = value
|
| 267 |
+
bisect.insort(self.priorities, (priority, len(self.priorities), key))
|
| 268 |
+
super().__setitem__(key, value)
|
| 269 |
+
for _, _, key in self.priorities: # reorder dict to match priorities
|
| 270 |
+
self.move_to_end(key)
|
| 271 |
+
|
| 272 |
+
def add_meter(self, key, meter, priority):
|
| 273 |
+
self.__setitem__(key, (priority, meter))
|
| 274 |
+
|
| 275 |
+
def state_dict(self):
|
| 276 |
+
return [
|
| 277 |
+
(pri, key, self[key].__class__.__name__, self[key].state_dict())
|
| 278 |
+
for pri, _, key in self.priorities
|
| 279 |
+
# can't serialize DerivedMeter instances
|
| 280 |
+
if not isinstance(self[key], MetersDict._DerivedMeter)
|
| 281 |
+
]
|
| 282 |
+
|
| 283 |
+
def load_state_dict(self, state_dict):
|
| 284 |
+
self.clear()
|
| 285 |
+
self.priorities.clear()
|
| 286 |
+
for pri, key, meter_cls, meter_state in state_dict:
|
| 287 |
+
meter = globals()[meter_cls]()
|
| 288 |
+
meter.load_state_dict(meter_state)
|
| 289 |
+
self.add_meter(key, meter, pri)
|
| 290 |
+
|
| 291 |
+
def get_smoothed_value(self, key: str) -> float:
|
| 292 |
+
"""Get a single smoothed value."""
|
| 293 |
+
meter = self[key]
|
| 294 |
+
if isinstance(meter, MetersDict._DerivedMeter):
|
| 295 |
+
return meter.fn(self)
|
| 296 |
+
else:
|
| 297 |
+
return meter.smoothed_value
|
| 298 |
+
|
| 299 |
+
def get_smoothed_values(self) -> Dict[str, float]:
|
| 300 |
+
"""Get all smoothed values."""
|
| 301 |
+
return OrderedDict(
|
| 302 |
+
[
|
| 303 |
+
(key, self.get_smoothed_value(key))
|
| 304 |
+
for key in self.keys()
|
| 305 |
+
if not key.startswith("_")
|
| 306 |
+
]
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
def reset(self):
|
| 310 |
+
"""Reset Meter instances."""
|
| 311 |
+
for meter in self.values():
|
| 312 |
+
if isinstance(meter, MetersDict._DerivedMeter):
|
| 313 |
+
continue
|
| 314 |
+
meter.reset()
|
| 315 |
+
|
| 316 |
+
class _DerivedMeter(Meter):
|
| 317 |
+
"""A Meter whose values are derived from other Meters."""
|
| 318 |
+
|
| 319 |
+
def __init__(self, fn):
|
| 320 |
+
self.fn = fn
|
| 321 |
+
|
| 322 |
+
def reset(self):
|
| 323 |
+
pass
|
avh-align_core/fairseq/logging/metrics.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""
|
| 6 |
+
A standalone module for aggregating metrics.
|
| 7 |
+
|
| 8 |
+
Metrics can be logged from anywhere using the `log_*` functions defined
|
| 9 |
+
in this module. The logged values will be aggregated dynamically based
|
| 10 |
+
on the aggregation context in which the logging occurs. See the
|
| 11 |
+
:func:`aggregate` context manager for more details.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import contextlib
|
| 15 |
+
import uuid
|
| 16 |
+
from collections import defaultdict
|
| 17 |
+
from typing import Callable, List, Optional
|
| 18 |
+
|
| 19 |
+
from .meters import *
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# Aggregation contexts are considered "active" when inside the scope
|
| 23 |
+
# created by the :func:`aggregate` context manager.
|
| 24 |
+
_aggregators = OrderedDict()
|
| 25 |
+
_active_aggregators = OrderedDict()
|
| 26 |
+
_active_aggregators_cnt = defaultdict(lambda: 0)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def reset() -> None:
|
| 30 |
+
"""Reset all metrics aggregators."""
|
| 31 |
+
_aggregators.clear()
|
| 32 |
+
_active_aggregators.clear()
|
| 33 |
+
_active_aggregators_cnt.clear()
|
| 34 |
+
|
| 35 |
+
# The "default" aggregator observes all logged values.
|
| 36 |
+
_aggregators["default"] = MetersDict()
|
| 37 |
+
_active_aggregators["default"] = _aggregators["default"]
|
| 38 |
+
_active_aggregators_cnt["default"] = 1
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
reset()
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@contextlib.contextmanager
|
| 45 |
+
def aggregate(name: Optional[str] = None, new_root: bool = False):
|
| 46 |
+
"""Context manager to aggregate metrics under a given name.
|
| 47 |
+
|
| 48 |
+
Aggregations can be nested. If *new_root* is ``False``, then logged
|
| 49 |
+
metrics will be recorded along the entire stack of nested
|
| 50 |
+
aggregators, including a global "default" aggregator. If *new_root*
|
| 51 |
+
is ``True``, then this aggregator will be the root of a new
|
| 52 |
+
aggregation stack, thus bypassing any parent aggregators.
|
| 53 |
+
|
| 54 |
+
Note that aggregation contexts are uniquely identified by their
|
| 55 |
+
*name* (e.g., train, valid). Creating a context with an existing
|
| 56 |
+
name will reuse the corresponding :class:`MetersDict` instance.
|
| 57 |
+
If no name is given, then a temporary aggregator will be created.
|
| 58 |
+
|
| 59 |
+
Usage::
|
| 60 |
+
|
| 61 |
+
with metrics.aggregate("train"):
|
| 62 |
+
for step, batch in enumerate(epoch):
|
| 63 |
+
with metrics.aggregate("train_inner") as agg:
|
| 64 |
+
metrics.log_scalar("loss", get_loss(batch))
|
| 65 |
+
if step % log_interval == 0:
|
| 66 |
+
print(agg.get_smoothed_value("loss"))
|
| 67 |
+
agg.reset()
|
| 68 |
+
print(metrics.get_smoothed_values("train")["loss"])
|
| 69 |
+
|
| 70 |
+
Args:
|
| 71 |
+
name (str): name of the aggregation. Defaults to a
|
| 72 |
+
random/temporary name if not given explicitly.
|
| 73 |
+
new_root (bool): make this aggregation the root of a new
|
| 74 |
+
aggregation stack.
|
| 75 |
+
"""
|
| 76 |
+
if name is None:
|
| 77 |
+
# generate a temporary name
|
| 78 |
+
name = str(uuid.uuid4())
|
| 79 |
+
assert name not in _aggregators
|
| 80 |
+
agg = MetersDict()
|
| 81 |
+
else:
|
| 82 |
+
assert name != "default"
|
| 83 |
+
agg = _aggregators.setdefault(name, MetersDict())
|
| 84 |
+
|
| 85 |
+
if new_root:
|
| 86 |
+
backup_aggregators = _active_aggregators.copy()
|
| 87 |
+
_active_aggregators.clear()
|
| 88 |
+
backup_aggregators_cnt = _active_aggregators_cnt.copy()
|
| 89 |
+
_active_aggregators_cnt.clear()
|
| 90 |
+
|
| 91 |
+
_active_aggregators[name] = agg
|
| 92 |
+
_active_aggregators_cnt[name] += 1
|
| 93 |
+
|
| 94 |
+
yield agg
|
| 95 |
+
|
| 96 |
+
_active_aggregators_cnt[name] -= 1
|
| 97 |
+
if _active_aggregators_cnt[name] == 0 and name in _active_aggregators:
|
| 98 |
+
del _active_aggregators[name]
|
| 99 |
+
|
| 100 |
+
if new_root:
|
| 101 |
+
_active_aggregators.clear()
|
| 102 |
+
_active_aggregators.update(backup_aggregators)
|
| 103 |
+
_active_aggregators_cnt.clear()
|
| 104 |
+
_active_aggregators_cnt.update(backup_aggregators_cnt)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def get_active_aggregators() -> List[MetersDict]:
|
| 108 |
+
return list(_active_aggregators.values())
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def log_scalar(
|
| 112 |
+
key: str,
|
| 113 |
+
value: float,
|
| 114 |
+
weight: float = 1,
|
| 115 |
+
priority: int = 10,
|
| 116 |
+
round: Optional[int] = None,
|
| 117 |
+
):
|
| 118 |
+
"""Log a scalar value.
|
| 119 |
+
|
| 120 |
+
Args:
|
| 121 |
+
key (str): name of the field to log
|
| 122 |
+
value (float): value to log
|
| 123 |
+
weight (float): weight that this value contributes to the average.
|
| 124 |
+
A weight of 0 will always log the latest value.
|
| 125 |
+
priority (int): smaller values are logged earlier in the output
|
| 126 |
+
round (Optional[int]): number of digits to round to when displaying
|
| 127 |
+
"""
|
| 128 |
+
for agg in get_active_aggregators():
|
| 129 |
+
if key not in agg:
|
| 130 |
+
agg.add_meter(key, AverageMeter(round=round), priority)
|
| 131 |
+
agg[key].update(value, weight)
|
| 132 |
+
|
| 133 |
+
def log_scalar_sum(
|
| 134 |
+
key: str,
|
| 135 |
+
value: float,
|
| 136 |
+
priority: int = 10,
|
| 137 |
+
round: Optional[int] = None,
|
| 138 |
+
):
|
| 139 |
+
"""Log a scalar value that is summed for reporting.
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
key (str): name of the field to log
|
| 143 |
+
value (float): value to log
|
| 144 |
+
priority (int): smaller values are logged earlier in the output
|
| 145 |
+
round (Optional[int]): number of digits to round to when displaying
|
| 146 |
+
"""
|
| 147 |
+
for agg in get_active_aggregators():
|
| 148 |
+
if key not in agg:
|
| 149 |
+
agg.add_meter(key, SumMeter(round=round), priority)
|
| 150 |
+
agg[key].update(value)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def log_derived(key: str, fn: Callable[[MetersDict], float], priority: int = 20):
|
| 154 |
+
"""Log a scalar value derived from other meters.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
key (str): name of the field to log
|
| 158 |
+
fn (Callable[[MetersDict], float]): function that takes a single
|
| 159 |
+
argument *meters* and returns the derived value
|
| 160 |
+
priority (int): smaller values are logged earlier in the output
|
| 161 |
+
"""
|
| 162 |
+
for agg in get_active_aggregators():
|
| 163 |
+
if key not in agg:
|
| 164 |
+
agg.add_meter(key, MetersDict._DerivedMeter(fn), priority)
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def log_speed(
|
| 168 |
+
key: str,
|
| 169 |
+
value: float,
|
| 170 |
+
priority: int = 30,
|
| 171 |
+
round: Optional[int] = None,
|
| 172 |
+
):
|
| 173 |
+
"""Log the rate of some quantity per second.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
key (str): name of the field to log
|
| 177 |
+
value (float): value to log
|
| 178 |
+
priority (int): smaller values are logged earlier in the output
|
| 179 |
+
round (Optional[int]): number of digits to round to when displaying
|
| 180 |
+
"""
|
| 181 |
+
for agg in get_active_aggregators():
|
| 182 |
+
if key not in agg:
|
| 183 |
+
agg.add_meter(key, TimeMeter(round=round), priority)
|
| 184 |
+
agg[key].reset() # reset meter on the first call
|
| 185 |
+
else:
|
| 186 |
+
agg[key].update(value)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def log_start_time(key: str, priority: int = 40, round: Optional[int] = None):
|
| 190 |
+
"""Log the duration of some event in seconds.
|
| 191 |
+
|
| 192 |
+
The duration will be computed once :func:`log_stop_time` is called.
|
| 193 |
+
|
| 194 |
+
Args:
|
| 195 |
+
key (str): name of the field to log
|
| 196 |
+
priority (int): smaller values are logged earlier in the output
|
| 197 |
+
round (Optional[int]): number of digits to round to when displaying
|
| 198 |
+
"""
|
| 199 |
+
for agg in get_active_aggregators():
|
| 200 |
+
if key not in agg:
|
| 201 |
+
agg.add_meter(key, StopwatchMeter(round=round), priority)
|
| 202 |
+
agg[key].start()
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def log_stop_time(key: str, weight: float = 0.0, prehook=None):
|
| 206 |
+
"""Log the duration of some event in seconds.
|
| 207 |
+
|
| 208 |
+
The duration will be computed since :func:`log_start_time` was called.
|
| 209 |
+
Set weight > 0 to report the average time instead of the sum.
|
| 210 |
+
|
| 211 |
+
Args:
|
| 212 |
+
key (str): name of the field to log
|
| 213 |
+
weight (float): weight that this time contributes to the average
|
| 214 |
+
prehook (function, no arguments): will be called before the timer
|
| 215 |
+
is stopped. For example, use prehook=torch.cuda.synchronize to
|
| 216 |
+
make sure all gpu operations are done before timer is stopped.
|
| 217 |
+
"""
|
| 218 |
+
for agg in get_active_aggregators():
|
| 219 |
+
if key in agg:
|
| 220 |
+
agg[key].stop(weight, prehook)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def log_custom(
|
| 224 |
+
new_meter_fn: Callable[[], Meter],
|
| 225 |
+
key: str,
|
| 226 |
+
*args,
|
| 227 |
+
priority: int = 50,
|
| 228 |
+
**kwargs,
|
| 229 |
+
):
|
| 230 |
+
"""Log using a custom Meter.
|
| 231 |
+
|
| 232 |
+
Any extra *args* or *kwargs* will be passed through to the Meter's
|
| 233 |
+
*update* method.
|
| 234 |
+
|
| 235 |
+
Args:
|
| 236 |
+
new_meter_fn (Callable[[], Meter]): function that returns a new
|
| 237 |
+
Meter instance
|
| 238 |
+
key (str): name of the field to log
|
| 239 |
+
priority (int): smaller values are logged earlier in the output
|
| 240 |
+
"""
|
| 241 |
+
for agg in get_active_aggregators():
|
| 242 |
+
if key not in agg:
|
| 243 |
+
agg.add_meter(key, new_meter_fn(), priority)
|
| 244 |
+
agg[key].update(*args, **kwargs)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def reset_meter(name: str, key: str) -> None:
|
| 248 |
+
"""Reset Meter instance aggregated under a given *name* and *key*."""
|
| 249 |
+
meter = get_meter(name, key)
|
| 250 |
+
if meter is not None:
|
| 251 |
+
meter.reset()
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def reset_meters(name: str) -> None:
|
| 255 |
+
"""Reset Meter instances aggregated under a given *name*."""
|
| 256 |
+
meters = get_meters(name)
|
| 257 |
+
if meters is not None:
|
| 258 |
+
meters.reset()
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def get_meter(name: str, key: str) -> Meter:
|
| 262 |
+
"""Get a single Meter instance aggregated under *name* and *key*.
|
| 263 |
+
|
| 264 |
+
Returns:
|
| 265 |
+
Meter or None if no metrics have been logged under *name* and *key*.
|
| 266 |
+
"""
|
| 267 |
+
if name not in _aggregators:
|
| 268 |
+
return None
|
| 269 |
+
return _aggregators[name].get(key, None)
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def get_meters(name: str) -> MetersDict:
|
| 273 |
+
"""Get Meter instances aggregated under a given *name*.
|
| 274 |
+
|
| 275 |
+
Returns:
|
| 276 |
+
MetersDict or None if no metrics have been logged under *name*.
|
| 277 |
+
"""
|
| 278 |
+
return _aggregators.get(name, None)
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def get_smoothed_value(name: str, key: str) -> float:
|
| 282 |
+
"""Get a single smoothed value.
|
| 283 |
+
|
| 284 |
+
Raises:
|
| 285 |
+
KeyError: if no metrics have been logged under *name* and *key*.
|
| 286 |
+
"""
|
| 287 |
+
return _aggregators[name].get_smoothed_value(key)
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def get_smoothed_values(name: str) -> Dict[str, float]:
|
| 291 |
+
"""Get smoothed values aggregated under a given *name*.
|
| 292 |
+
|
| 293 |
+
Raises:
|
| 294 |
+
KeyError: if no metrics have been logged under *name*.
|
| 295 |
+
"""
|
| 296 |
+
return _aggregators[name].get_smoothed_values()
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def state_dict():
|
| 300 |
+
return OrderedDict([(name, agg.state_dict()) for name, agg in _aggregators.items()])
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def load_state_dict(state_dict):
|
| 304 |
+
for name, agg_state in state_dict.items():
|
| 305 |
+
_aggregators[name] = MetersDict()
|
| 306 |
+
_aggregators[name].load_state_dict(agg_state)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def xla_metrics_report():
|
| 310 |
+
try:
|
| 311 |
+
import torch_xla.debug.metrics as met
|
| 312 |
+
print(met.metrics_report())
|
| 313 |
+
except ImportError:
|
| 314 |
+
return
|
avh-align_core/fairseq/logging/progress_bar.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
"""
|
| 7 |
+
Wrapper around various loggers and progress bars (e.g., tqdm).
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import atexit
|
| 11 |
+
import json
|
| 12 |
+
import logging
|
| 13 |
+
import os
|
| 14 |
+
import sys
|
| 15 |
+
from collections import OrderedDict
|
| 16 |
+
from contextlib import contextmanager
|
| 17 |
+
from numbers import Number
|
| 18 |
+
from typing import Optional
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
|
| 22 |
+
from .meters import AverageMeter, StopwatchMeter, TimeMeter
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
logger = logging.getLogger(__name__)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def progress_bar(
|
| 29 |
+
iterator,
|
| 30 |
+
log_format: Optional[str] = None,
|
| 31 |
+
log_interval: int = 100,
|
| 32 |
+
log_file: Optional[str] = None,
|
| 33 |
+
epoch: Optional[int] = None,
|
| 34 |
+
prefix: Optional[str] = None,
|
| 35 |
+
tensorboard_logdir: Optional[str] = None,
|
| 36 |
+
default_log_format: str = "tqdm",
|
| 37 |
+
wandb_project: Optional[str] = None,
|
| 38 |
+
wandb_run_name: Optional[str] = None,
|
| 39 |
+
azureml_logging: Optional[bool] = False,
|
| 40 |
+
):
|
| 41 |
+
if log_format is None:
|
| 42 |
+
log_format = default_log_format
|
| 43 |
+
if log_file is not None:
|
| 44 |
+
handler = logging.FileHandler(filename=log_file)
|
| 45 |
+
logger.addHandler(handler)
|
| 46 |
+
|
| 47 |
+
if log_format == "tqdm" and not sys.stderr.isatty():
|
| 48 |
+
log_format = "simple"
|
| 49 |
+
|
| 50 |
+
if log_format == "json":
|
| 51 |
+
bar = JsonProgressBar(iterator, epoch, prefix, log_interval)
|
| 52 |
+
elif log_format == "none":
|
| 53 |
+
bar = NoopProgressBar(iterator, epoch, prefix)
|
| 54 |
+
elif log_format == "simple":
|
| 55 |
+
bar = SimpleProgressBar(iterator, epoch, prefix, log_interval)
|
| 56 |
+
elif log_format == "tqdm":
|
| 57 |
+
bar = TqdmProgressBar(iterator, epoch, prefix)
|
| 58 |
+
else:
|
| 59 |
+
raise ValueError("Unknown log format: {}".format(log_format))
|
| 60 |
+
|
| 61 |
+
if tensorboard_logdir:
|
| 62 |
+
try:
|
| 63 |
+
# [FB only] custom wrapper for TensorBoard
|
| 64 |
+
import palaas # noqa
|
| 65 |
+
from .fb_tbmf_wrapper import FbTbmfWrapper
|
| 66 |
+
|
| 67 |
+
bar = FbTbmfWrapper(bar, log_interval)
|
| 68 |
+
except ImportError:
|
| 69 |
+
bar = TensorboardProgressBarWrapper(bar, tensorboard_logdir)
|
| 70 |
+
|
| 71 |
+
if wandb_project:
|
| 72 |
+
bar = WandBProgressBarWrapper(bar, wandb_project, run_name=wandb_run_name)
|
| 73 |
+
|
| 74 |
+
if azureml_logging:
|
| 75 |
+
bar = AzureMLProgressBarWrapper(bar)
|
| 76 |
+
|
| 77 |
+
return bar
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def build_progress_bar(
|
| 81 |
+
args,
|
| 82 |
+
iterator,
|
| 83 |
+
epoch: Optional[int] = None,
|
| 84 |
+
prefix: Optional[str] = None,
|
| 85 |
+
default: str = "tqdm",
|
| 86 |
+
no_progress_bar: str = "none",
|
| 87 |
+
):
|
| 88 |
+
"""Legacy wrapper that takes an argparse.Namespace."""
|
| 89 |
+
if getattr(args, "no_progress_bar", False):
|
| 90 |
+
default = no_progress_bar
|
| 91 |
+
if getattr(args, "distributed_rank", 0) == 0:
|
| 92 |
+
tensorboard_logdir = getattr(args, "tensorboard_logdir", None)
|
| 93 |
+
else:
|
| 94 |
+
tensorboard_logdir = None
|
| 95 |
+
return progress_bar(
|
| 96 |
+
iterator,
|
| 97 |
+
log_format=args.log_format,
|
| 98 |
+
log_interval=args.log_interval,
|
| 99 |
+
epoch=epoch,
|
| 100 |
+
prefix=prefix,
|
| 101 |
+
tensorboard_logdir=tensorboard_logdir,
|
| 102 |
+
default_log_format=default,
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def format_stat(stat):
|
| 107 |
+
if isinstance(stat, Number):
|
| 108 |
+
stat = "{:g}".format(stat)
|
| 109 |
+
elif isinstance(stat, AverageMeter):
|
| 110 |
+
stat = "{:.3f}".format(stat.avg)
|
| 111 |
+
elif isinstance(stat, TimeMeter):
|
| 112 |
+
stat = "{:g}".format(round(stat.avg))
|
| 113 |
+
elif isinstance(stat, StopwatchMeter):
|
| 114 |
+
stat = "{:g}".format(round(stat.sum))
|
| 115 |
+
elif torch.is_tensor(stat):
|
| 116 |
+
stat = stat.tolist()
|
| 117 |
+
return stat
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class BaseProgressBar(object):
|
| 121 |
+
"""Abstract class for progress bars."""
|
| 122 |
+
|
| 123 |
+
def __init__(self, iterable, epoch=None, prefix=None):
|
| 124 |
+
self.iterable = iterable
|
| 125 |
+
self.n = getattr(iterable, "n", 0)
|
| 126 |
+
self.epoch = epoch
|
| 127 |
+
self.prefix = ""
|
| 128 |
+
if epoch is not None:
|
| 129 |
+
self.prefix += "epoch {:03d}".format(epoch)
|
| 130 |
+
if prefix is not None:
|
| 131 |
+
self.prefix += (" | " if self.prefix != "" else "") + prefix
|
| 132 |
+
|
| 133 |
+
def __len__(self):
|
| 134 |
+
return len(self.iterable)
|
| 135 |
+
|
| 136 |
+
def __enter__(self):
|
| 137 |
+
return self
|
| 138 |
+
|
| 139 |
+
def __exit__(self, *exc):
|
| 140 |
+
return False
|
| 141 |
+
|
| 142 |
+
def __iter__(self):
|
| 143 |
+
raise NotImplementedError
|
| 144 |
+
|
| 145 |
+
def log(self, stats, tag=None, step=None):
|
| 146 |
+
"""Log intermediate stats according to log_interval."""
|
| 147 |
+
raise NotImplementedError
|
| 148 |
+
|
| 149 |
+
def print(self, stats, tag=None, step=None):
|
| 150 |
+
"""Print end-of-epoch stats."""
|
| 151 |
+
raise NotImplementedError
|
| 152 |
+
|
| 153 |
+
def update_config(self, config):
|
| 154 |
+
"""Log latest configuration."""
|
| 155 |
+
pass
|
| 156 |
+
|
| 157 |
+
def _str_commas(self, stats):
|
| 158 |
+
return ", ".join(key + "=" + stats[key].strip() for key in stats.keys())
|
| 159 |
+
|
| 160 |
+
def _str_pipes(self, stats):
|
| 161 |
+
return " | ".join(key + " " + stats[key].strip() for key in stats.keys())
|
| 162 |
+
|
| 163 |
+
def _format_stats(self, stats):
|
| 164 |
+
postfix = OrderedDict(stats)
|
| 165 |
+
# Preprocess stats according to datatype
|
| 166 |
+
for key in postfix.keys():
|
| 167 |
+
postfix[key] = str(format_stat(postfix[key]))
|
| 168 |
+
return postfix
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@contextmanager
|
| 172 |
+
def rename_logger(logger, new_name):
|
| 173 |
+
old_name = logger.name
|
| 174 |
+
if new_name is not None:
|
| 175 |
+
logger.name = new_name
|
| 176 |
+
yield logger
|
| 177 |
+
logger.name = old_name
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class JsonProgressBar(BaseProgressBar):
|
| 181 |
+
"""Log output in JSON format."""
|
| 182 |
+
|
| 183 |
+
def __init__(self, iterable, epoch=None, prefix=None, log_interval=1000):
|
| 184 |
+
super().__init__(iterable, epoch, prefix)
|
| 185 |
+
self.log_interval = log_interval
|
| 186 |
+
self.i = None
|
| 187 |
+
self.size = None
|
| 188 |
+
|
| 189 |
+
def __iter__(self):
|
| 190 |
+
self.size = len(self.iterable)
|
| 191 |
+
for i, obj in enumerate(self.iterable, start=self.n):
|
| 192 |
+
self.i = i
|
| 193 |
+
yield obj
|
| 194 |
+
|
| 195 |
+
def log(self, stats, tag=None, step=None):
|
| 196 |
+
"""Log intermediate stats according to log_interval."""
|
| 197 |
+
step = step or self.i or 0
|
| 198 |
+
if step > 0 and self.log_interval is not None and step % self.log_interval == 0:
|
| 199 |
+
update = (
|
| 200 |
+
self.epoch - 1 + (self.i + 1) / float(self.size)
|
| 201 |
+
if self.epoch is not None
|
| 202 |
+
else None
|
| 203 |
+
)
|
| 204 |
+
stats = self._format_stats(stats, epoch=self.epoch, update=update)
|
| 205 |
+
with rename_logger(logger, tag):
|
| 206 |
+
logger.info(json.dumps(stats))
|
| 207 |
+
|
| 208 |
+
def print(self, stats, tag=None, step=None):
|
| 209 |
+
"""Print end-of-epoch stats."""
|
| 210 |
+
self.stats = stats
|
| 211 |
+
if tag is not None:
|
| 212 |
+
self.stats = OrderedDict(
|
| 213 |
+
[(tag + "_" + k, v) for k, v in self.stats.items()]
|
| 214 |
+
)
|
| 215 |
+
stats = self._format_stats(self.stats, epoch=self.epoch)
|
| 216 |
+
with rename_logger(logger, tag):
|
| 217 |
+
logger.info(json.dumps(stats))
|
| 218 |
+
|
| 219 |
+
def _format_stats(self, stats, epoch=None, update=None):
|
| 220 |
+
postfix = OrderedDict()
|
| 221 |
+
if epoch is not None:
|
| 222 |
+
postfix["epoch"] = epoch
|
| 223 |
+
if update is not None:
|
| 224 |
+
postfix["update"] = round(update, 3)
|
| 225 |
+
# Preprocess stats according to datatype
|
| 226 |
+
for key in stats.keys():
|
| 227 |
+
postfix[key] = format_stat(stats[key])
|
| 228 |
+
return postfix
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class NoopProgressBar(BaseProgressBar):
|
| 232 |
+
"""No logging."""
|
| 233 |
+
|
| 234 |
+
def __init__(self, iterable, epoch=None, prefix=None):
|
| 235 |
+
super().__init__(iterable, epoch, prefix)
|
| 236 |
+
|
| 237 |
+
def __iter__(self):
|
| 238 |
+
for obj in self.iterable:
|
| 239 |
+
yield obj
|
| 240 |
+
|
| 241 |
+
def log(self, stats, tag=None, step=None):
|
| 242 |
+
"""Log intermediate stats according to log_interval."""
|
| 243 |
+
pass
|
| 244 |
+
|
| 245 |
+
def print(self, stats, tag=None, step=None):
|
| 246 |
+
"""Print end-of-epoch stats."""
|
| 247 |
+
pass
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
class SimpleProgressBar(BaseProgressBar):
|
| 251 |
+
"""A minimal logger for non-TTY environments."""
|
| 252 |
+
|
| 253 |
+
def __init__(self, iterable, epoch=None, prefix=None, log_interval=1000):
|
| 254 |
+
super().__init__(iterable, epoch, prefix)
|
| 255 |
+
self.log_interval = log_interval
|
| 256 |
+
self.i = None
|
| 257 |
+
self.size = None
|
| 258 |
+
|
| 259 |
+
def __iter__(self):
|
| 260 |
+
self.size = len(self.iterable)
|
| 261 |
+
for i, obj in enumerate(self.iterable, start=self.n):
|
| 262 |
+
self.i = i
|
| 263 |
+
yield obj
|
| 264 |
+
|
| 265 |
+
def log(self, stats, tag=None, step=None):
|
| 266 |
+
"""Log intermediate stats according to log_interval."""
|
| 267 |
+
step = step or self.i or 0
|
| 268 |
+
if step > 0 and self.log_interval is not None and step % self.log_interval == 0:
|
| 269 |
+
stats = self._format_stats(stats)
|
| 270 |
+
postfix = self._str_commas(stats)
|
| 271 |
+
with rename_logger(logger, tag):
|
| 272 |
+
logger.info(
|
| 273 |
+
"{}: {:5d} / {:d} {}".format(
|
| 274 |
+
self.prefix, self.i + 1, self.size, postfix
|
| 275 |
+
)
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
def print(self, stats, tag=None, step=None):
|
| 279 |
+
"""Print end-of-epoch stats."""
|
| 280 |
+
postfix = self._str_pipes(self._format_stats(stats))
|
| 281 |
+
with rename_logger(logger, tag):
|
| 282 |
+
logger.info("{} | {}".format(self.prefix, postfix))
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
class TqdmProgressBar(BaseProgressBar):
|
| 286 |
+
"""Log to tqdm."""
|
| 287 |
+
|
| 288 |
+
def __init__(self, iterable, epoch=None, prefix=None):
|
| 289 |
+
super().__init__(iterable, epoch, prefix)
|
| 290 |
+
from tqdm import tqdm
|
| 291 |
+
|
| 292 |
+
self.tqdm = tqdm(
|
| 293 |
+
iterable,
|
| 294 |
+
self.prefix,
|
| 295 |
+
leave=False,
|
| 296 |
+
disable=(logger.getEffectiveLevel() > logging.INFO),
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
def __iter__(self):
|
| 300 |
+
return iter(self.tqdm)
|
| 301 |
+
|
| 302 |
+
def log(self, stats, tag=None, step=None):
|
| 303 |
+
"""Log intermediate stats according to log_interval."""
|
| 304 |
+
self.tqdm.set_postfix(self._format_stats(stats), refresh=False)
|
| 305 |
+
|
| 306 |
+
def print(self, stats, tag=None, step=None):
|
| 307 |
+
"""Print end-of-epoch stats."""
|
| 308 |
+
postfix = self._str_pipes(self._format_stats(stats))
|
| 309 |
+
with rename_logger(logger, tag):
|
| 310 |
+
logger.info("{} | {}".format(self.prefix, postfix))
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
try:
|
| 314 |
+
_tensorboard_writers = {}
|
| 315 |
+
from torch.utils.tensorboard import SummaryWriter
|
| 316 |
+
except ImportError:
|
| 317 |
+
try:
|
| 318 |
+
from tensorboardX import SummaryWriter
|
| 319 |
+
except ImportError:
|
| 320 |
+
SummaryWriter = None
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def _close_writers():
|
| 324 |
+
for w in _tensorboard_writers.values():
|
| 325 |
+
w.close()
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
atexit.register(_close_writers)
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
class TensorboardProgressBarWrapper(BaseProgressBar):
|
| 332 |
+
"""Log to tensorboard."""
|
| 333 |
+
|
| 334 |
+
def __init__(self, wrapped_bar, tensorboard_logdir):
|
| 335 |
+
self.wrapped_bar = wrapped_bar
|
| 336 |
+
self.tensorboard_logdir = tensorboard_logdir
|
| 337 |
+
|
| 338 |
+
if SummaryWriter is None:
|
| 339 |
+
logger.warning(
|
| 340 |
+
"tensorboard not found, please install with: pip install tensorboard"
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
def _writer(self, key):
|
| 344 |
+
if SummaryWriter is None:
|
| 345 |
+
return None
|
| 346 |
+
_writers = _tensorboard_writers
|
| 347 |
+
if key not in _writers:
|
| 348 |
+
_writers[key] = SummaryWriter(os.path.join(self.tensorboard_logdir, key))
|
| 349 |
+
_writers[key].add_text("sys.argv", " ".join(sys.argv))
|
| 350 |
+
return _writers[key]
|
| 351 |
+
|
| 352 |
+
def __iter__(self):
|
| 353 |
+
return iter(self.wrapped_bar)
|
| 354 |
+
|
| 355 |
+
def log(self, stats, tag=None, step=None):
|
| 356 |
+
"""Log intermediate stats to tensorboard."""
|
| 357 |
+
self._log_to_tensorboard(stats, tag, step)
|
| 358 |
+
self.wrapped_bar.log(stats, tag=tag, step=step)
|
| 359 |
+
|
| 360 |
+
def print(self, stats, tag=None, step=None):
|
| 361 |
+
"""Print end-of-epoch stats."""
|
| 362 |
+
self._log_to_tensorboard(stats, tag, step)
|
| 363 |
+
self.wrapped_bar.print(stats, tag=tag, step=step)
|
| 364 |
+
|
| 365 |
+
def update_config(self, config):
|
| 366 |
+
"""Log latest configuration."""
|
| 367 |
+
# TODO add hparams to Tensorboard
|
| 368 |
+
self.wrapped_bar.update_config(config)
|
| 369 |
+
|
| 370 |
+
def _log_to_tensorboard(self, stats, tag=None, step=None):
|
| 371 |
+
writer = self._writer(tag or "")
|
| 372 |
+
if writer is None:
|
| 373 |
+
return
|
| 374 |
+
if step is None:
|
| 375 |
+
step = stats["num_updates"]
|
| 376 |
+
for key in stats.keys() - {"num_updates"}:
|
| 377 |
+
if isinstance(stats[key], AverageMeter):
|
| 378 |
+
writer.add_scalar(key, stats[key].val, step)
|
| 379 |
+
elif isinstance(stats[key], Number):
|
| 380 |
+
writer.add_scalar(key, stats[key], step)
|
| 381 |
+
elif torch.is_tensor(stats[key]) and stats[key].numel() == 1:
|
| 382 |
+
writer.add_scalar(key, stats[key].item(), step)
|
| 383 |
+
writer.flush()
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
try:
|
| 387 |
+
import wandb
|
| 388 |
+
except ImportError:
|
| 389 |
+
wandb = None
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
class WandBProgressBarWrapper(BaseProgressBar):
|
| 393 |
+
"""Log to Weights & Biases."""
|
| 394 |
+
|
| 395 |
+
def __init__(self, wrapped_bar, wandb_project, run_name=None):
|
| 396 |
+
self.wrapped_bar = wrapped_bar
|
| 397 |
+
if wandb is None:
|
| 398 |
+
logger.warning("wandb not found, pip install wandb")
|
| 399 |
+
return
|
| 400 |
+
|
| 401 |
+
# reinit=False to ensure if wandb.init() is called multiple times
|
| 402 |
+
# within one process it still references the same run
|
| 403 |
+
wandb.init(project=wandb_project, reinit=False, name=run_name)
|
| 404 |
+
|
| 405 |
+
def __iter__(self):
|
| 406 |
+
return iter(self.wrapped_bar)
|
| 407 |
+
|
| 408 |
+
def log(self, stats, tag=None, step=None):
|
| 409 |
+
"""Log intermediate stats to tensorboard."""
|
| 410 |
+
self._log_to_wandb(stats, tag, step)
|
| 411 |
+
self.wrapped_bar.log(stats, tag=tag, step=step)
|
| 412 |
+
|
| 413 |
+
def print(self, stats, tag=None, step=None):
|
| 414 |
+
"""Print end-of-epoch stats."""
|
| 415 |
+
self._log_to_wandb(stats, tag, step)
|
| 416 |
+
self.wrapped_bar.print(stats, tag=tag, step=step)
|
| 417 |
+
|
| 418 |
+
def update_config(self, config):
|
| 419 |
+
"""Log latest configuration."""
|
| 420 |
+
if wandb is not None:
|
| 421 |
+
wandb.config.update(config)
|
| 422 |
+
self.wrapped_bar.update_config(config)
|
| 423 |
+
|
| 424 |
+
def _log_to_wandb(self, stats, tag=None, step=None):
|
| 425 |
+
if wandb is None:
|
| 426 |
+
return
|
| 427 |
+
if step is None:
|
| 428 |
+
step = stats["num_updates"]
|
| 429 |
+
|
| 430 |
+
prefix = "" if tag is None else tag + "/"
|
| 431 |
+
|
| 432 |
+
for key in stats.keys() - {"num_updates"}:
|
| 433 |
+
if isinstance(stats[key], AverageMeter):
|
| 434 |
+
wandb.log({prefix + key: stats[key].val}, step=step)
|
| 435 |
+
elif isinstance(stats[key], Number):
|
| 436 |
+
wandb.log({prefix + key: stats[key]}, step=step)
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
try:
|
| 440 |
+
from azureml.core import Run
|
| 441 |
+
except ImportError:
|
| 442 |
+
Run = None
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
class AzureMLProgressBarWrapper(BaseProgressBar):
|
| 446 |
+
"""Log to Azure ML"""
|
| 447 |
+
|
| 448 |
+
def __init__(self, wrapped_bar):
|
| 449 |
+
self.wrapped_bar = wrapped_bar
|
| 450 |
+
if Run is None:
|
| 451 |
+
logger.warning("azureml.core not found, pip install azureml-core")
|
| 452 |
+
return
|
| 453 |
+
self.run = Run.get_context()
|
| 454 |
+
|
| 455 |
+
def __exit__(self, *exc):
|
| 456 |
+
if Run is not None:
|
| 457 |
+
self.run.complete()
|
| 458 |
+
return False
|
| 459 |
+
|
| 460 |
+
def __iter__(self):
|
| 461 |
+
return iter(self.wrapped_bar)
|
| 462 |
+
|
| 463 |
+
def log(self, stats, tag=None, step=None):
|
| 464 |
+
"""Log intermediate stats to AzureML"""
|
| 465 |
+
self._log_to_azureml(stats, tag, step)
|
| 466 |
+
self.wrapped_bar.log(stats, tag=tag, step=step)
|
| 467 |
+
|
| 468 |
+
def print(self, stats, tag=None, step=None):
|
| 469 |
+
"""Print end-of-epoch stats"""
|
| 470 |
+
self._log_to_azureml(stats, tag, step)
|
| 471 |
+
self.wrapped_bar.print(stats, tag=tag, step=step)
|
| 472 |
+
|
| 473 |
+
def update_config(self, config):
|
| 474 |
+
"""Log latest configuration."""
|
| 475 |
+
self.wrapped_bar.update_config(config)
|
| 476 |
+
|
| 477 |
+
def _log_to_azureml(self, stats, tag=None, step=None):
|
| 478 |
+
if Run is None:
|
| 479 |
+
return
|
| 480 |
+
if step is None:
|
| 481 |
+
step = stats["num_updates"]
|
| 482 |
+
|
| 483 |
+
prefix = "" if tag is None else tag + "/"
|
| 484 |
+
|
| 485 |
+
for key in stats.keys() - {"num_updates"}:
|
| 486 |
+
name = prefix + key
|
| 487 |
+
if isinstance(stats[key], AverageMeter):
|
| 488 |
+
self.run.log_row(name=name, **{"step": step, key: stats[key].val})
|
| 489 |
+
elif isinstance(stats[key], Number):
|
| 490 |
+
self.run.log_row(name=name, **{"step": step, key: stats[key]})
|
avh-align_core/fairseq/model_parallel/criterions/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import importlib
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# automatically import any Python files in the criterions/ directory
|
| 11 |
+
for file in sorted(os.listdir(os.path.dirname(__file__))):
|
| 12 |
+
if file.endswith(".py") and not file.startswith("_"):
|
| 13 |
+
module = file[: file.find(".py")]
|
| 14 |
+
importlib.import_module("fairseq.model_parallel.criterions." + module)
|
avh-align_core/fairseq/model_parallel/criterions/vocab_parallel_cross_entropy.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
from fairseq import metrics, utils
|
| 9 |
+
from fairseq.criterions import FairseqCriterion, register_criterion
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
from fairseq.model_parallel.megatron.mpu.cross_entropy import (
|
| 14 |
+
vocab_parallel_cross_entropy,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
has_megatron_submodule = True
|
| 18 |
+
except (ImportError, ModuleNotFoundError):
|
| 19 |
+
has_megatron_submodule = False
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@register_criterion("vocab_parallel_cross_entropy")
|
| 23 |
+
class VocabParallelCrossEntropyCriterion(FairseqCriterion):
|
| 24 |
+
def __init__(self, task, sentence_avg):
|
| 25 |
+
super().__init__(task)
|
| 26 |
+
self.sentence_avg = sentence_avg
|
| 27 |
+
if not has_megatron_submodule:
|
| 28 |
+
raise ImportError(
|
| 29 |
+
"\n\nPlease install the megatron submodule:"
|
| 30 |
+
"\n\n git submodule update --init "
|
| 31 |
+
"fairseq/model_parallel/megatron"
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
def forward(self, model, sample, reduce=True):
|
| 35 |
+
"""Compute the loss for the given sample.
|
| 36 |
+
|
| 37 |
+
Returns a tuple with three elements:
|
| 38 |
+
1) the loss
|
| 39 |
+
2) the sample size, which is used as the denominator for the gradient
|
| 40 |
+
3) logging outputs to display while training
|
| 41 |
+
"""
|
| 42 |
+
net_output = model(**sample["net_input"])
|
| 43 |
+
target = sample["target"]
|
| 44 |
+
|
| 45 |
+
loss = vocab_parallel_cross_entropy(net_output[0].float(), target)
|
| 46 |
+
loss = (loss * (target != self.padding_idx)).sum()
|
| 47 |
+
sample_size = (
|
| 48 |
+
sample["target"].size(0) if self.sentence_avg else sample["ntokens"]
|
| 49 |
+
)
|
| 50 |
+
logging_output = {
|
| 51 |
+
"loss": utils.item(loss.data) if reduce else loss.data,
|
| 52 |
+
"ntokens": sample["ntokens"],
|
| 53 |
+
"nsentences": sample["target"].size(0),
|
| 54 |
+
"sample_size": sample_size,
|
| 55 |
+
}
|
| 56 |
+
return loss, sample_size, logging_output
|
| 57 |
+
|
| 58 |
+
@staticmethod
|
| 59 |
+
def reduce_metrics(logging_outputs) -> None:
|
| 60 |
+
"""Aggregate logging outputs from data parallel training."""
|
| 61 |
+
loss_sum = sum(log.get("loss", 0) for log in logging_outputs)
|
| 62 |
+
ntokens = sum(log.get("ntokens", 0) for log in logging_outputs)
|
| 63 |
+
sample_size = sum(log.get("sample_size", 0) for log in logging_outputs)
|
| 64 |
+
|
| 65 |
+
metrics.log_scalar(
|
| 66 |
+
"loss", loss_sum / sample_size / math.log(2), sample_size, round=3
|
| 67 |
+
)
|
| 68 |
+
if sample_size != ntokens:
|
| 69 |
+
metrics.log_scalar(
|
| 70 |
+
"nll_loss", loss_sum / ntokens / math.log(2), ntokens, round=3
|
| 71 |
+
)
|
| 72 |
+
metrics.log_derived(
|
| 73 |
+
"ppl", lambda meters: utils.get_perplexity(meters["nll_loss"].avg)
|
| 74 |
+
)
|
| 75 |
+
else:
|
| 76 |
+
metrics.log_derived(
|
| 77 |
+
"ppl", lambda meters: utils.get_perplexity(meters["loss"].avg)
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
@staticmethod
|
| 81 |
+
def logging_outputs_can_be_summed() -> bool:
|
| 82 |
+
"""
|
| 83 |
+
Whether the logging outputs returned by `forward` can be summed
|
| 84 |
+
across workers prior to calling `reduce_metrics`. Setting this
|
| 85 |
+
to True will improves distributed training speed.
|
| 86 |
+
"""
|
| 87 |
+
return True
|
avh-align_core/fairseq/model_parallel/models/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import importlib
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# automatically import any Python files in the models/ directory
|
| 11 |
+
models_dir = os.path.dirname(__file__)
|
| 12 |
+
for file in os.listdir(models_dir):
|
| 13 |
+
path = os.path.join(models_dir, file)
|
| 14 |
+
if (
|
| 15 |
+
not file.startswith("_")
|
| 16 |
+
and not file.startswith(".")
|
| 17 |
+
and (file.endswith(".py") or os.path.isdir(path))
|
| 18 |
+
):
|
| 19 |
+
model_name = file[: file.find(".py")] if file.endswith(".py") else file
|
| 20 |
+
module = importlib.import_module("fairseq.model_parallel.models." + model_name)
|
avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .model import * # noqa
|
avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/layers.py
ADDED
|
@@ -0,0 +1,600 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
from collections import namedtuple
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from fairseq import options, utils
|
| 13 |
+
from fairseq.modules import (
|
| 14 |
+
AdaptiveSoftmax,
|
| 15 |
+
LayerNorm,
|
| 16 |
+
MultiheadAttention,
|
| 17 |
+
PositionalEmbedding,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
EncoderOut = namedtuple(
|
| 22 |
+
"TransformerEncoderOut",
|
| 23 |
+
[
|
| 24 |
+
"encoder_out", # T x B x C
|
| 25 |
+
"encoder_padding_mask", # B x T
|
| 26 |
+
"encoder_embedding", # B x T x C
|
| 27 |
+
"encoder_states", # List[T x B x C]
|
| 28 |
+
],
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class TransformerEncoderEmbedding(nn.Module):
|
| 33 |
+
""" Encoder Embedding + Positional Embedding """
|
| 34 |
+
|
| 35 |
+
def __init__(self, args, embed_tokens):
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.dropout = args.dropout
|
| 38 |
+
self.max_source_positions = args.max_source_positions
|
| 39 |
+
self.embed_tokens = embed_tokens
|
| 40 |
+
if isinstance(embed_tokens, nn.ModuleList):
|
| 41 |
+
self.padding_idx = embed_tokens[0].padding_idx
|
| 42 |
+
embed_dim = sum(e.embedding_dim for e in embed_tokens)
|
| 43 |
+
else:
|
| 44 |
+
self.padding_idx = embed_tokens.padding_idx
|
| 45 |
+
embed_dim = embed_tokens.embedding_dim
|
| 46 |
+
self.embed_scale = math.sqrt(embed_dim)
|
| 47 |
+
self.embed_positions = (
|
| 48 |
+
PositionalEmbedding(
|
| 49 |
+
args.max_source_positions,
|
| 50 |
+
embed_dim,
|
| 51 |
+
self.padding_idx,
|
| 52 |
+
learned=args.encoder_learned_pos,
|
| 53 |
+
)
|
| 54 |
+
if not args.no_token_positional_embeddings
|
| 55 |
+
else None
|
| 56 |
+
)
|
| 57 |
+
if getattr(args, "layernorm_embedding", False):
|
| 58 |
+
self.layernorm_embedding = LayerNorm(embed_dim)
|
| 59 |
+
else:
|
| 60 |
+
self.layernorm_embedding = None
|
| 61 |
+
|
| 62 |
+
def forward(self, input):
|
| 63 |
+
# embed tokens and positions
|
| 64 |
+
src_tokens = input[0]
|
| 65 |
+
prev_output_tokens = input[2]
|
| 66 |
+
if isinstance(self.embed_tokens, nn.ModuleList):
|
| 67 |
+
x_embed_list = []
|
| 68 |
+
for embed_tokens_part in self.embed_tokens:
|
| 69 |
+
x_embed_list.append(embed_tokens_part(src_tokens))
|
| 70 |
+
|
| 71 |
+
embedded = torch.cat(x_embed_list, dim=-1)
|
| 72 |
+
else:
|
| 73 |
+
embedded = self.embed_tokens(src_tokens)
|
| 74 |
+
x = embed = self.embed_scale * embedded
|
| 75 |
+
if self.embed_positions is not None:
|
| 76 |
+
x = embed + self.embed_positions(src_tokens)
|
| 77 |
+
if self.layernorm_embedding:
|
| 78 |
+
x = self.layernorm_embedding(x)
|
| 79 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 80 |
+
# B x T x C -> T x B x C
|
| 81 |
+
x = x.transpose(0, 1)
|
| 82 |
+
|
| 83 |
+
# compute padding mask
|
| 84 |
+
encoder_padding_mask = src_tokens.eq(self.padding_idx)
|
| 85 |
+
return (x, encoder_padding_mask, prev_output_tokens)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class TransformerEncoderLayerNorm(nn.Module):
|
| 89 |
+
"""
|
| 90 |
+
Layer norm at the the end of all encoder layers if
|
| 91 |
+
args.encoder_enormalize_before = True
|
| 92 |
+
"""
|
| 93 |
+
|
| 94 |
+
def __init__(self, args, embed_dim):
|
| 95 |
+
super().__init__()
|
| 96 |
+
if args.encoder_normalize_before:
|
| 97 |
+
self.layer_norm = LayerNorm(embed_dim)
|
| 98 |
+
else:
|
| 99 |
+
self.layer_norm = None
|
| 100 |
+
|
| 101 |
+
def forward(self, input):
|
| 102 |
+
x = input[0]
|
| 103 |
+
encoder_padding_mask = input[1]
|
| 104 |
+
prev_output_tokens = input[2]
|
| 105 |
+
if self.layer_norm:
|
| 106 |
+
x = self.layer_norm(x)
|
| 107 |
+
# keeping track of the incremental_state is not supported yet
|
| 108 |
+
return (x, encoder_padding_mask, prev_output_tokens)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class TransformerDecoderEmbedding(nn.Module):
|
| 112 |
+
""" Decoder Embedding + Positional Embedding """
|
| 113 |
+
|
| 114 |
+
def __init__(self, args, embed_tokens):
|
| 115 |
+
super().__init__()
|
| 116 |
+
self.dropout = args.dropout
|
| 117 |
+
self.share_input_output_embed = args.share_decoder_input_output_embed
|
| 118 |
+
input_embed_dim = (
|
| 119 |
+
sum(e.embedding_dim for e in embed_tokens)
|
| 120 |
+
if isinstance(embed_tokens, nn.ModuleList)
|
| 121 |
+
else embed_tokens.embedding_dim
|
| 122 |
+
)
|
| 123 |
+
embed_dim = args.decoder_embed_dim
|
| 124 |
+
self.output_embed_dim = args.decoder_output_dim
|
| 125 |
+
|
| 126 |
+
padding_idx = (
|
| 127 |
+
embed_tokens[0].padding_idx
|
| 128 |
+
if isinstance(embed_tokens, nn.ModuleList)
|
| 129 |
+
else embed_tokens.padding_idx
|
| 130 |
+
)
|
| 131 |
+
self.max_target_positions = args.max_target_positions
|
| 132 |
+
|
| 133 |
+
self.embed_tokens = embed_tokens
|
| 134 |
+
self.embed_scale = math.sqrt(embed_dim) # todo: try with input_embed_dim
|
| 135 |
+
|
| 136 |
+
self.project_in_dim = (
|
| 137 |
+
Linear(input_embed_dim, embed_dim, bias=False)
|
| 138 |
+
if embed_dim != input_embed_dim
|
| 139 |
+
else None
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
self.embed_positions = (
|
| 143 |
+
PositionalEmbedding(
|
| 144 |
+
args.max_target_positions,
|
| 145 |
+
embed_dim,
|
| 146 |
+
padding_idx,
|
| 147 |
+
learned=args.decoder_learned_pos,
|
| 148 |
+
)
|
| 149 |
+
if not args.no_token_positional_embeddings
|
| 150 |
+
else None
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
def forward(self, input):
|
| 154 |
+
mt_task = False
|
| 155 |
+
if isinstance(input, tuple):
|
| 156 |
+
if len(input) == 3:
|
| 157 |
+
encoder_out = input[0]
|
| 158 |
+
encoder_padding_mask = input[1]
|
| 159 |
+
prev_output_tokens = input[2]
|
| 160 |
+
incremental_state = None # Hardcoding to avoid passing of None objects
|
| 161 |
+
mt_task = True
|
| 162 |
+
else:
|
| 163 |
+
# HACK for now, need to fix (TODO sidgoyal)
|
| 164 |
+
prev_output_tokens = input[0]
|
| 165 |
+
# discard "src_lengths"
|
| 166 |
+
encoder_out = None
|
| 167 |
+
encoder_padding_mask = None
|
| 168 |
+
incremental_state = None
|
| 169 |
+
|
| 170 |
+
else:
|
| 171 |
+
prev_output_tokens = input
|
| 172 |
+
encoder_out = None
|
| 173 |
+
encoder_padding_mask = None
|
| 174 |
+
incremental_state = None
|
| 175 |
+
|
| 176 |
+
positions = (
|
| 177 |
+
self.embed_positions(
|
| 178 |
+
prev_output_tokens,
|
| 179 |
+
incremental_state=incremental_state,
|
| 180 |
+
)
|
| 181 |
+
if self.embed_positions is not None
|
| 182 |
+
else None
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
if incremental_state is not None:
|
| 186 |
+
prev_output_tokens = prev_output_tokens[:, -1:]
|
| 187 |
+
if positions is not None:
|
| 188 |
+
positions = positions[:, -1:]
|
| 189 |
+
|
| 190 |
+
# embed tokens and positions
|
| 191 |
+
|
| 192 |
+
if isinstance(self.embed_tokens, nn.ModuleList):
|
| 193 |
+
x_embed_list = []
|
| 194 |
+
for embed_tokens_part in self.embed_tokens:
|
| 195 |
+
x_embed_list.append(embed_tokens_part(prev_output_tokens))
|
| 196 |
+
|
| 197 |
+
x = self.embed_scale * torch.cat(x_embed_list, dim=-1)
|
| 198 |
+
else:
|
| 199 |
+
x = self.embed_scale * self.embed_tokens(prev_output_tokens)
|
| 200 |
+
|
| 201 |
+
if self.project_in_dim is not None:
|
| 202 |
+
x = self.project_in_dim(x)
|
| 203 |
+
|
| 204 |
+
if positions is not None:
|
| 205 |
+
x += positions
|
| 206 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 207 |
+
|
| 208 |
+
# B x T x C -> T x B x C
|
| 209 |
+
x = x.transpose(0, 1)
|
| 210 |
+
if mt_task:
|
| 211 |
+
return (x, encoder_out, encoder_padding_mask)
|
| 212 |
+
return x
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
class TransformerDecoderOutputLayer(nn.Module):
|
| 216 |
+
def __init__(self, args, embed_tokens, dictionary):
|
| 217 |
+
super().__init__()
|
| 218 |
+
self.share_input_output_embed = args.share_decoder_input_output_embed
|
| 219 |
+
self.embed_tokens = embed_tokens
|
| 220 |
+
self.output_embed_dim = args.decoder_output_dim
|
| 221 |
+
embed_dim = args.decoder_embed_dim
|
| 222 |
+
|
| 223 |
+
self.project_out_dim = (
|
| 224 |
+
Linear(embed_dim, self.output_embed_dim, bias=False)
|
| 225 |
+
if embed_dim != self.output_embed_dim and not args.tie_adaptive_weights
|
| 226 |
+
else None
|
| 227 |
+
)
|
| 228 |
+
self.adaptive_softmax = None
|
| 229 |
+
if args.adaptive_softmax_cutoff is not None:
|
| 230 |
+
assert not isinstance(embed_tokens, nn.ModuleList)
|
| 231 |
+
self.adaptive_softmax = AdaptiveSoftmax(
|
| 232 |
+
len(dictionary),
|
| 233 |
+
self.output_embed_dim,
|
| 234 |
+
options.eval_str_list(args.adaptive_softmax_cutoff, type=int),
|
| 235 |
+
dropout=args.adaptive_softmax_dropout,
|
| 236 |
+
adaptive_inputs=embed_tokens if args.tie_adaptive_weights else None,
|
| 237 |
+
factor=args.adaptive_softmax_factor,
|
| 238 |
+
tie_proj=args.tie_adaptive_proj,
|
| 239 |
+
)
|
| 240 |
+
elif not self.share_input_output_embed:
|
| 241 |
+
self.embed_tokens = nn.Parameter(
|
| 242 |
+
torch.Tensor(len(dictionary), self.output_embed_dim)
|
| 243 |
+
)
|
| 244 |
+
nn.init.normal_(
|
| 245 |
+
self.embed_tokens, mean=0, std=self.output_embed_dim ** -0.5
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
if args.decoder_normalize_before and not getattr(
|
| 249 |
+
args, "no_decoder_final_norm", False
|
| 250 |
+
):
|
| 251 |
+
self.layer_norm = LayerNorm(embed_dim)
|
| 252 |
+
else:
|
| 253 |
+
self.layer_norm = None
|
| 254 |
+
|
| 255 |
+
def forward(self, input, apply_final_proj=True):
|
| 256 |
+
if isinstance(input, tuple):
|
| 257 |
+
x = input[0]
|
| 258 |
+
else:
|
| 259 |
+
x = input
|
| 260 |
+
|
| 261 |
+
if self.layer_norm:
|
| 262 |
+
x = self.layer_norm(x)
|
| 263 |
+
|
| 264 |
+
# T x B x C -> B x T x C
|
| 265 |
+
x = x.transpose(0, 1)
|
| 266 |
+
|
| 267 |
+
if self.project_out_dim is not None:
|
| 268 |
+
x = self.project_out_dim(x)
|
| 269 |
+
if apply_final_proj:
|
| 270 |
+
x = self.output_layer(x)
|
| 271 |
+
return x
|
| 272 |
+
|
| 273 |
+
def output_layer(self, features, **kwargs):
|
| 274 |
+
"""Project features to the vocabulary size."""
|
| 275 |
+
if self.adaptive_softmax is None:
|
| 276 |
+
# project back to size of vocabulary
|
| 277 |
+
if self.share_input_output_embed:
|
| 278 |
+
if isinstance(self.embed_tokens, nn.ModuleList):
|
| 279 |
+
output = None
|
| 280 |
+
for i, emb in enumerate(self.embed_tokens):
|
| 281 |
+
sidx = i * emb.embedding_dim
|
| 282 |
+
eidx = (i + 1) * emb.embedding_dim
|
| 283 |
+
if output is None:
|
| 284 |
+
output = F.linear(features[:, :, sidx:eidx], emb.weight)
|
| 285 |
+
else:
|
| 286 |
+
output += F.linear(features[:, :, sidx:eidx], emb.weight)
|
| 287 |
+
|
| 288 |
+
return output
|
| 289 |
+
else:
|
| 290 |
+
return F.linear(features, self.embed_tokens.weight)
|
| 291 |
+
else:
|
| 292 |
+
return F.linear(features, self.embed_tokens)
|
| 293 |
+
else:
|
| 294 |
+
return features
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class TransformerEncoderLayer(nn.Module):
|
| 298 |
+
"""Encoder layer block.
|
| 299 |
+
In the original paper each operation (multi-head attention or FFN) is
|
| 300 |
+
postprocessed with: `dropout -> add residual -> layernorm`. In the
|
| 301 |
+
tensor2tensor code they suggest that learning is more robust when
|
| 302 |
+
preprocessing each layer with layernorm and postprocessing with:
|
| 303 |
+
`dropout -> add residual`. We default to the approach in the paper, but the
|
| 304 |
+
tensor2tensor approach can be enabled by setting
|
| 305 |
+
*args.encoder_normalize_before* to ``True``.
|
| 306 |
+
|
| 307 |
+
Args:
|
| 308 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 309 |
+
"""
|
| 310 |
+
|
| 311 |
+
def __init__(self, args):
|
| 312 |
+
super().__init__()
|
| 313 |
+
self.embed_dim = args.encoder_embed_dim
|
| 314 |
+
self.self_attn = MultiheadAttention(
|
| 315 |
+
self.embed_dim,
|
| 316 |
+
args.encoder_attention_heads,
|
| 317 |
+
dropout=args.attention_dropout,
|
| 318 |
+
self_attention=True,
|
| 319 |
+
)
|
| 320 |
+
self.self_attn_layer_norm = LayerNorm(self.embed_dim)
|
| 321 |
+
self.dropout = args.dropout
|
| 322 |
+
self.activation_fn = utils.get_activation_fn(
|
| 323 |
+
activation=getattr(args, "activation_fn", "relu")
|
| 324 |
+
)
|
| 325 |
+
self.activation_dropout = getattr(args, "activation_dropout", 0)
|
| 326 |
+
if self.activation_dropout == 0:
|
| 327 |
+
# for backwards compatibility with models that use args.relu_dropout
|
| 328 |
+
self.activation_dropout = getattr(args, "relu_dropout", 0)
|
| 329 |
+
self.normalize_before = args.encoder_normalize_before
|
| 330 |
+
self.fc1 = Linear(self.embed_dim, args.encoder_ffn_embed_dim)
|
| 331 |
+
self.fc2 = Linear(args.encoder_ffn_embed_dim, self.embed_dim)
|
| 332 |
+
self.final_layer_norm = LayerNorm(self.embed_dim)
|
| 333 |
+
|
| 334 |
+
def upgrade_state_dict_named(self, state_dict, name):
|
| 335 |
+
"""
|
| 336 |
+
Rename layer norm states from `...layer_norms.0.weight` to
|
| 337 |
+
`...self_attn_layer_norm.weight` and `...layer_norms.1.weight` to
|
| 338 |
+
`...final_layer_norm.weight`
|
| 339 |
+
"""
|
| 340 |
+
layer_norm_map = {"0": "self_attn_layer_norm", "1": "final_layer_norm"}
|
| 341 |
+
for old, new in layer_norm_map.items():
|
| 342 |
+
for m in ("weight", "bias"):
|
| 343 |
+
k = "{}.layer_norms.{}.{}".format(name, old, m)
|
| 344 |
+
if k in state_dict:
|
| 345 |
+
state_dict["{}.{}.{}".format(name, new, m)] = state_dict[k]
|
| 346 |
+
del state_dict[k]
|
| 347 |
+
|
| 348 |
+
def forward(self, input):
|
| 349 |
+
"""
|
| 350 |
+
Args:
|
| 351 |
+
input (Tuple):
|
| 352 |
+
input[0] (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)`
|
| 353 |
+
input[1] (ByteTensor/FloatTensor): encoder padding mask -
|
| 354 |
+
binary ByteTensor of shape `(batch, src_len)` where padding elements
|
| 355 |
+
are indicated by ``1``.
|
| 356 |
+
input[2] (LongTensor): previous decoder outputs of shape
|
| 357 |
+
`(batch, tgt_len)`, for teacher forcing)
|
| 358 |
+
Returns:
|
| 359 |
+
output (Tuple):
|
| 360 |
+
output[0] (Tensor): encoded output of shape `(batch, src_len, embed_dim)`
|
| 361 |
+
output[1] (ByteTensor/FloatTensor): encoder padding mask
|
| 362 |
+
output[2] (LongTensor): previous decoder outputs
|
| 363 |
+
"""
|
| 364 |
+
x = input[0]
|
| 365 |
+
encoder_padding_mask = input[1]
|
| 366 |
+
prev_output_tokens = input[2]
|
| 367 |
+
residual = x
|
| 368 |
+
x = self.maybe_layer_norm(self.self_attn_layer_norm, x, before=True)
|
| 369 |
+
x, _ = self.self_attn(
|
| 370 |
+
query=x, key=x, value=x, key_padding_mask=encoder_padding_mask
|
| 371 |
+
)
|
| 372 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 373 |
+
x = residual + x
|
| 374 |
+
x = self.maybe_layer_norm(self.self_attn_layer_norm, x, after=True)
|
| 375 |
+
|
| 376 |
+
residual = x
|
| 377 |
+
x = self.maybe_layer_norm(self.final_layer_norm, x, before=True)
|
| 378 |
+
x = self.activation_fn(self.fc1(x))
|
| 379 |
+
x = F.dropout(x, p=self.activation_dropout, training=self.training)
|
| 380 |
+
x = self.fc2(x)
|
| 381 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 382 |
+
x = residual + x
|
| 383 |
+
x = self.maybe_layer_norm(self.final_layer_norm, x, after=True)
|
| 384 |
+
return (x, encoder_padding_mask, prev_output_tokens)
|
| 385 |
+
|
| 386 |
+
def maybe_layer_norm(self, layer_norm, x, before=False, after=False):
|
| 387 |
+
assert before ^ after
|
| 388 |
+
if after ^ self.normalize_before:
|
| 389 |
+
return layer_norm(x)
|
| 390 |
+
else:
|
| 391 |
+
return x
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
class TransformerDecoderLayer(nn.Module):
|
| 395 |
+
"""Decoder layer block.
|
| 396 |
+
|
| 397 |
+
In the original paper each operation (multi-head attention, encoder
|
| 398 |
+
attention or FFN) is postprocessed with: `dropout -> add residual ->
|
| 399 |
+
layernorm`. In the tensor2tensor code they suggest that learning is more
|
| 400 |
+
robust when preprocessing each layer with layernorm and postprocessing with:
|
| 401 |
+
`dropout -> add residual`. We default to the approach in the paper, but the
|
| 402 |
+
tensor2tensor approach can be enabled by setting
|
| 403 |
+
*args.decoder_normalize_before* to ``True``.
|
| 404 |
+
|
| 405 |
+
Args:
|
| 406 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 407 |
+
no_encoder_attn (bool, optional): whether to attend to encoder outputs
|
| 408 |
+
(default: False).
|
| 409 |
+
"""
|
| 410 |
+
|
| 411 |
+
def __init__(
|
| 412 |
+
self, args, no_encoder_attn=False, add_bias_kv=False, add_zero_attn=False
|
| 413 |
+
):
|
| 414 |
+
super().__init__()
|
| 415 |
+
self.embed_dim = args.decoder_embed_dim
|
| 416 |
+
self.self_attn = MultiheadAttention(
|
| 417 |
+
embed_dim=self.embed_dim,
|
| 418 |
+
num_heads=args.decoder_attention_heads,
|
| 419 |
+
dropout=args.attention_dropout,
|
| 420 |
+
add_bias_kv=add_bias_kv,
|
| 421 |
+
add_zero_attn=add_zero_attn,
|
| 422 |
+
self_attention=True,
|
| 423 |
+
)
|
| 424 |
+
self.dropout = args.dropout
|
| 425 |
+
self.activation_fn = utils.get_activation_fn(
|
| 426 |
+
activation=getattr(args, "activation_fn", "relu")
|
| 427 |
+
)
|
| 428 |
+
self.activation_dropout = getattr(args, "activation_dropout", 0)
|
| 429 |
+
if self.activation_dropout == 0:
|
| 430 |
+
# for backwards compatibility with models that use args.relu_dropout
|
| 431 |
+
self.activation_dropout = getattr(args, "relu_dropout", 0)
|
| 432 |
+
self.normalize_before = args.decoder_normalize_before
|
| 433 |
+
|
| 434 |
+
# use layerNorm rather than FusedLayerNorm for exporting.
|
| 435 |
+
# char_inputs can be used to determint this.
|
| 436 |
+
# TODO remove this once we update apex with the fix
|
| 437 |
+
export = getattr(args, "char_inputs", False)
|
| 438 |
+
self.self_attn_layer_norm = LayerNorm(self.embed_dim, export=export)
|
| 439 |
+
|
| 440 |
+
if no_encoder_attn:
|
| 441 |
+
self.encoder_attn = None
|
| 442 |
+
self.encoder_attn_layer_norm = None
|
| 443 |
+
else:
|
| 444 |
+
self.encoder_attn = MultiheadAttention(
|
| 445 |
+
self.embed_dim,
|
| 446 |
+
args.decoder_attention_heads,
|
| 447 |
+
kdim=getattr(args, "encoder_embed_dim", None),
|
| 448 |
+
vdim=getattr(args, "encoder_embed_dim", None),
|
| 449 |
+
dropout=args.attention_dropout,
|
| 450 |
+
encoder_decoder_attention=True,
|
| 451 |
+
)
|
| 452 |
+
self.encoder_attn_layer_norm = LayerNorm(self.embed_dim, export=export)
|
| 453 |
+
|
| 454 |
+
self.fc1 = Linear(self.embed_dim, args.decoder_ffn_embed_dim)
|
| 455 |
+
self.fc2 = Linear(args.decoder_ffn_embed_dim, self.embed_dim)
|
| 456 |
+
|
| 457 |
+
self.final_layer_norm = LayerNorm(self.embed_dim, export=export)
|
| 458 |
+
self.need_attn = True
|
| 459 |
+
|
| 460 |
+
self.onnx_trace = False
|
| 461 |
+
|
| 462 |
+
def prepare_for_onnx_export_(self):
|
| 463 |
+
self.onnx_trace = True
|
| 464 |
+
|
| 465 |
+
def forward(self, input):
|
| 466 |
+
"""
|
| 467 |
+
Args:
|
| 468 |
+
input (Tuple):
|
| 469 |
+
input[0] (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)`
|
| 470 |
+
input[1] (Tensor): encoder output of shape `(batch, src_len, embed_dim)`
|
| 471 |
+
input[2] (ByteTensor/FloatTensor): encoder padding mask -
|
| 472 |
+
binary ByteTensor of shape `(batch, src_len)` where padding elements
|
| 473 |
+
are indicated by ``1``.
|
| 474 |
+
Returns:
|
| 475 |
+
output (Tuple):
|
| 476 |
+
output[0] (Tensor): encoded output of shape `(batch, src_len, embed_dim)`
|
| 477 |
+
output[1] (ByteTensor/FloatTensor): encoder padding mask
|
| 478 |
+
output[2] (LongTensor): previous decoder outputs
|
| 479 |
+
"""
|
| 480 |
+
# Note: incremental state is not yet supported
|
| 481 |
+
mt_task = False
|
| 482 |
+
if isinstance(input, tuple):
|
| 483 |
+
x = input[0]
|
| 484 |
+
encoder_out = input[1]
|
| 485 |
+
encoder_padding_mask = input[2]
|
| 486 |
+
incremental_state = None
|
| 487 |
+
mt_task = True
|
| 488 |
+
else:
|
| 489 |
+
x = input
|
| 490 |
+
encoder_out = None
|
| 491 |
+
encoder_padding_mask = None
|
| 492 |
+
incremental_state = None
|
| 493 |
+
|
| 494 |
+
if incremental_state is None:
|
| 495 |
+
self_attn_mask = self.buffered_future_mask(x)
|
| 496 |
+
else:
|
| 497 |
+
self_attn_mask = None
|
| 498 |
+
|
| 499 |
+
# TODO: add back prev_self_attn_state, prev_attn_state,
|
| 500 |
+
# self_attn_padding_mask
|
| 501 |
+
prev_self_attn_state = None
|
| 502 |
+
prev_attn_state = None
|
| 503 |
+
self_attn_padding_mask = None
|
| 504 |
+
|
| 505 |
+
residual = x
|
| 506 |
+
x = self.maybe_layer_norm(self.self_attn_layer_norm, x, before=True)
|
| 507 |
+
if prev_self_attn_state is not None:
|
| 508 |
+
if incremental_state is None:
|
| 509 |
+
incremental_state = {}
|
| 510 |
+
prev_key, prev_value = prev_self_attn_state
|
| 511 |
+
saved_state = {"prev_key": prev_key, "prev_value": prev_value}
|
| 512 |
+
self.self_attn._set_input_buffer(incremental_state, saved_state)
|
| 513 |
+
x, attn = self.self_attn(
|
| 514 |
+
query=x,
|
| 515 |
+
key=x,
|
| 516 |
+
value=x,
|
| 517 |
+
key_padding_mask=self_attn_padding_mask,
|
| 518 |
+
incremental_state=incremental_state,
|
| 519 |
+
need_weights=False,
|
| 520 |
+
attn_mask=self_attn_mask,
|
| 521 |
+
)
|
| 522 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 523 |
+
x = residual + x
|
| 524 |
+
x = self.maybe_layer_norm(self.self_attn_layer_norm, x, after=True)
|
| 525 |
+
|
| 526 |
+
if self.encoder_attn is not None:
|
| 527 |
+
residual = x
|
| 528 |
+
x = self.maybe_layer_norm(self.encoder_attn_layer_norm, x, before=True)
|
| 529 |
+
if prev_attn_state is not None:
|
| 530 |
+
if incremental_state is None:
|
| 531 |
+
incremental_state = {}
|
| 532 |
+
prev_key, prev_value = prev_attn_state
|
| 533 |
+
saved_state = {"prev_key": prev_key, "prev_value": prev_value}
|
| 534 |
+
self.encoder_attn._set_input_buffer(incremental_state, saved_state)
|
| 535 |
+
x, attn = self.encoder_attn(
|
| 536 |
+
query=x,
|
| 537 |
+
key=encoder_out,
|
| 538 |
+
value=encoder_out,
|
| 539 |
+
key_padding_mask=encoder_padding_mask,
|
| 540 |
+
incremental_state=incremental_state,
|
| 541 |
+
static_kv=True,
|
| 542 |
+
need_weights=(not self.training and self.need_attn),
|
| 543 |
+
)
|
| 544 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 545 |
+
x = residual + x
|
| 546 |
+
x = self.maybe_layer_norm(self.encoder_attn_layer_norm, x, after=True)
|
| 547 |
+
|
| 548 |
+
residual = x
|
| 549 |
+
x = self.maybe_layer_norm(self.final_layer_norm, x, before=True)
|
| 550 |
+
x = self.activation_fn(self.fc1(x))
|
| 551 |
+
x = F.dropout(x, p=self.activation_dropout, training=self.training)
|
| 552 |
+
x = self.fc2(x)
|
| 553 |
+
x = F.dropout(x, p=self.dropout, training=self.training)
|
| 554 |
+
x = residual + x
|
| 555 |
+
x = self.maybe_layer_norm(self.final_layer_norm, x, after=True)
|
| 556 |
+
|
| 557 |
+
if mt_task:
|
| 558 |
+
return (x, encoder_out, encoder_padding_mask)
|
| 559 |
+
return x
|
| 560 |
+
|
| 561 |
+
def buffered_future_mask(self, tensor):
|
| 562 |
+
dim = tensor.size(0)
|
| 563 |
+
if (
|
| 564 |
+
not hasattr(self, "_future_mask")
|
| 565 |
+
or self._future_mask is None
|
| 566 |
+
or self._future_mask.device != tensor.device
|
| 567 |
+
):
|
| 568 |
+
self._future_mask = torch.triu(
|
| 569 |
+
utils.fill_with_neg_inf(tensor.new(dim, dim)), 1
|
| 570 |
+
)
|
| 571 |
+
if self._future_mask.size(0) < dim:
|
| 572 |
+
self._future_mask = torch.triu(
|
| 573 |
+
utils.fill_with_neg_inf(self._future_mask.resize_(dim, dim)), 1
|
| 574 |
+
)
|
| 575 |
+
return self._future_mask[:dim, :dim]
|
| 576 |
+
|
| 577 |
+
def maybe_layer_norm(self, layer_norm, x, before=False, after=False):
|
| 578 |
+
assert before ^ after
|
| 579 |
+
if after ^ self.normalize_before:
|
| 580 |
+
return layer_norm(x)
|
| 581 |
+
else:
|
| 582 |
+
return x
|
| 583 |
+
|
| 584 |
+
def make_generation_fast_(self, need_attn=False, **kwargs):
|
| 585 |
+
self.need_attn = need_attn
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
def Embedding(num_embeddings, embedding_dim, padding_idx):
|
| 589 |
+
m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
|
| 590 |
+
nn.init.normal_(m.weight, mean=0, std=embedding_dim ** -0.5)
|
| 591 |
+
nn.init.constant_(m.weight[padding_idx], 0)
|
| 592 |
+
return m
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
def Linear(in_features, out_features, bias=True):
|
| 596 |
+
m = nn.Linear(in_features, out_features, bias)
|
| 597 |
+
nn.init.xavier_uniform_(m.weight)
|
| 598 |
+
if bias:
|
| 599 |
+
nn.init.constant_(m.bias, 0.0)
|
| 600 |
+
return m
|
avh-align_core/fairseq/model_parallel/models/pipeline_parallel_transformer/model.py
ADDED
|
@@ -0,0 +1,767 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from fairseq import utils
|
| 12 |
+
from fairseq.model_parallel.models.pipeline_parallel_transformer.layers import (
|
| 13 |
+
Embedding,
|
| 14 |
+
TransformerDecoderEmbedding,
|
| 15 |
+
TransformerDecoderLayer,
|
| 16 |
+
TransformerDecoderOutputLayer,
|
| 17 |
+
TransformerEncoderEmbedding,
|
| 18 |
+
TransformerEncoderLayer,
|
| 19 |
+
TransformerEncoderLayerNorm,
|
| 20 |
+
)
|
| 21 |
+
from fairseq.models import (
|
| 22 |
+
BaseFairseqModel,
|
| 23 |
+
FairseqDecoder,
|
| 24 |
+
FairseqEncoder,
|
| 25 |
+
register_model,
|
| 26 |
+
register_model_architecture,
|
| 27 |
+
)
|
| 28 |
+
from fairseq.models.fairseq_encoder import EncoderOut
|
| 29 |
+
from fairseq.models.transformer import (
|
| 30 |
+
base_architecture,
|
| 31 |
+
transformer_iwslt_de_en,
|
| 32 |
+
transformer_wmt_en_de_big,
|
| 33 |
+
)
|
| 34 |
+
from fairseq.modules import SinusoidalPositionalEmbedding
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
logger = logging.getLogger(__name__)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
DEFAULT_MAX_SOURCE_POSITIONS = 1024
|
| 41 |
+
DEFAULT_MAX_TARGET_POSITIONS = 1024
|
| 42 |
+
TORCH_PIPE = False
|
| 43 |
+
RPC_INIT = False
|
| 44 |
+
|
| 45 |
+
def import_pipe():
|
| 46 |
+
global TORCH_PIPE
|
| 47 |
+
global RPC_INIT
|
| 48 |
+
try:
|
| 49 |
+
from torch.distributed.pipeline.sync import Pipe # noqa
|
| 50 |
+
global Pipe
|
| 51 |
+
from torch.distributed.pipeline.sync.utils import partition_model
|
| 52 |
+
global partition_model
|
| 53 |
+
from torch.distributed import rpc
|
| 54 |
+
import tempfile
|
| 55 |
+
TORCH_PIPE = True
|
| 56 |
+
# Initialize single process RPC agent since TORCH_PIPE requires
|
| 57 |
+
# RRef. RRef depends on RPC being initialized and as a result we initialize
|
| 58 |
+
# RPC with a single node.
|
| 59 |
+
tmpfile = tempfile.NamedTemporaryFile()
|
| 60 |
+
if not RPC_INIT:
|
| 61 |
+
rpc.init_rpc(
|
| 62 |
+
name="worker",
|
| 63 |
+
rank=0,
|
| 64 |
+
world_size=1,
|
| 65 |
+
rpc_backend_options=rpc.TensorPipeRpcBackendOptions(
|
| 66 |
+
init_method="file://{}".format(tmpfile.name),
|
| 67 |
+
)
|
| 68 |
+
)
|
| 69 |
+
RPC_INIT = True
|
| 70 |
+
logger.info('Using torch pipe')
|
| 71 |
+
except ImportError:
|
| 72 |
+
try:
|
| 73 |
+
from fairscale.nn import Pipe # noqa
|
| 74 |
+
logger.info('Using fairscale pipe')
|
| 75 |
+
except ImportError:
|
| 76 |
+
raise ImportError("Please install fairscale with: pip install fairscale")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
@register_model("pipeline_parallel_transformer")
|
| 80 |
+
class PipelineParallelTransformerModel(BaseFairseqModel):
|
| 81 |
+
def __init__(self, encoder, decoder, balance, devices, chunks, checkpoint):
|
| 82 |
+
import_pipe()
|
| 83 |
+
super().__init__()
|
| 84 |
+
assert isinstance(encoder, FairseqEncoder)
|
| 85 |
+
assert isinstance(decoder, FairseqDecoder)
|
| 86 |
+
encoder_module_list = (
|
| 87 |
+
[encoder.embedding_layer]
|
| 88 |
+
+ list(encoder.encoder_layers)
|
| 89 |
+
+ [encoder.final_layer_norm]
|
| 90 |
+
)
|
| 91 |
+
self.num_encoder_modules = len(encoder_module_list)
|
| 92 |
+
decoder_module_list = (
|
| 93 |
+
[decoder.embedding_layer]
|
| 94 |
+
+ list(decoder.decoder_layers)
|
| 95 |
+
+ [decoder.decoder_output_layer]
|
| 96 |
+
)
|
| 97 |
+
self.num_decoder_modules = len(decoder_module_list)
|
| 98 |
+
module_list = encoder_module_list + decoder_module_list
|
| 99 |
+
self.devices = devices
|
| 100 |
+
if TORCH_PIPE:
|
| 101 |
+
self.model = Pipe(
|
| 102 |
+
partition_model(nn.Sequential(*module_list), balance, devices),
|
| 103 |
+
chunks=chunks,
|
| 104 |
+
checkpoint=checkpoint,
|
| 105 |
+
)
|
| 106 |
+
else:
|
| 107 |
+
self.model = Pipe(
|
| 108 |
+
nn.Sequential(*module_list),
|
| 109 |
+
balance=balance,
|
| 110 |
+
devices=devices,
|
| 111 |
+
chunks=chunks,
|
| 112 |
+
checkpoint=checkpoint,
|
| 113 |
+
)
|
| 114 |
+
self.encoder_max_positions = self.max_positions_helper(
|
| 115 |
+
encoder.embedding_layer, "max_source_positions"
|
| 116 |
+
)
|
| 117 |
+
self.decoder_max_positions = self.max_positions_helper(
|
| 118 |
+
decoder.embedding_layer, "max_target_positions"
|
| 119 |
+
)
|
| 120 |
+
self.adaptive_softmax = getattr(decoder, "adaptive_softmax", None)
|
| 121 |
+
# Note: To be populated during inference
|
| 122 |
+
self.encoder = None
|
| 123 |
+
self.decoder = None
|
| 124 |
+
|
| 125 |
+
def forward(self, src_tokens, src_lengths, prev_output_tokens):
|
| 126 |
+
if self.training:
|
| 127 |
+
input_lst = [src_tokens, src_lengths, prev_output_tokens]
|
| 128 |
+
input = tuple(i.to(self.devices[0], non_blocking=True) for i in input_lst)
|
| 129 |
+
if TORCH_PIPE:
|
| 130 |
+
return self.model(input).local_value()
|
| 131 |
+
else:
|
| 132 |
+
return self.model(input)
|
| 133 |
+
else:
|
| 134 |
+
assert self.encoder is not None and self.decoder is not None, (
|
| 135 |
+
"encoder and decoder need to be initialized by "
|
| 136 |
+
+ "calling the `prepare_for_inference_()` method"
|
| 137 |
+
)
|
| 138 |
+
encoder_output_tuple = self.encoder(input)
|
| 139 |
+
return self.decoder(encoder_output_tuple)
|
| 140 |
+
|
| 141 |
+
def prepare_for_inference_(self, cfg):
|
| 142 |
+
if self.encoder is not None and self.decoder is not None:
|
| 143 |
+
logger.info("Encoder and Decoder already initialized")
|
| 144 |
+
return
|
| 145 |
+
encoder_module_list = []
|
| 146 |
+
decoder_module_list = []
|
| 147 |
+
module_count = 0
|
| 148 |
+
for partition in self.model.partitions:
|
| 149 |
+
for module in partition:
|
| 150 |
+
if module_count < self.num_encoder_modules:
|
| 151 |
+
encoder_module_list.append(module)
|
| 152 |
+
else:
|
| 153 |
+
decoder_module_list.append(module)
|
| 154 |
+
module_count += 1
|
| 155 |
+
self.model = None
|
| 156 |
+
self.encoder = TransformerEncoder(cfg.distributed_training, None, None, encoder_module_list)
|
| 157 |
+
self.decoder = TransformerDecoder(
|
| 158 |
+
cfg.distributed_training, None, None, decoder_module_list=decoder_module_list
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
@staticmethod
|
| 162 |
+
def add_args(parser):
|
| 163 |
+
"""Add model-specific arguments to the parser."""
|
| 164 |
+
# fmt: off
|
| 165 |
+
parser.add_argument('--activation-fn',
|
| 166 |
+
choices=utils.get_available_activation_fns(),
|
| 167 |
+
help='activation function to use')
|
| 168 |
+
parser.add_argument('--dropout', type=float, metavar='D',
|
| 169 |
+
help='dropout probability')
|
| 170 |
+
parser.add_argument('--attention-dropout', type=float, metavar='D',
|
| 171 |
+
help='dropout probability for attention weights')
|
| 172 |
+
parser.add_argument('--activation-dropout', '--relu-dropout', type=float, metavar='D',
|
| 173 |
+
help='dropout probability after activation in FFN.')
|
| 174 |
+
parser.add_argument('--encoder-embed-path', type=str, metavar='STR',
|
| 175 |
+
help='path to pre-trained encoder embedding')
|
| 176 |
+
parser.add_argument('--encoder-embed-dim', type=int, metavar='N',
|
| 177 |
+
help='encoder embedding dimension')
|
| 178 |
+
parser.add_argument('--encoder-ffn-embed-dim', type=int, metavar='N',
|
| 179 |
+
help='encoder embedding dimension for FFN')
|
| 180 |
+
parser.add_argument('--encoder-layers', type=int, metavar='N',
|
| 181 |
+
help='num encoder layers')
|
| 182 |
+
parser.add_argument('--encoder-attention-heads', type=int, metavar='N',
|
| 183 |
+
help='num encoder attention heads')
|
| 184 |
+
parser.add_argument('--encoder-normalize-before', action='store_true',
|
| 185 |
+
help='apply layernorm before each encoder block')
|
| 186 |
+
parser.add_argument('--encoder-learned-pos', action='store_true',
|
| 187 |
+
help='use learned positional embeddings in the encoder')
|
| 188 |
+
parser.add_argument('--decoder-embed-path', type=str, metavar='STR',
|
| 189 |
+
help='path to pre-trained decoder embedding')
|
| 190 |
+
parser.add_argument('--decoder-embed-dim', type=int, metavar='N',
|
| 191 |
+
help='decoder embedding dimension')
|
| 192 |
+
parser.add_argument('--decoder-ffn-embed-dim', type=int, metavar='N',
|
| 193 |
+
help='decoder embedding dimension for FFN')
|
| 194 |
+
parser.add_argument('--decoder-layers', type=int, metavar='N',
|
| 195 |
+
help='num decoder layers')
|
| 196 |
+
parser.add_argument('--decoder-attention-heads', type=int, metavar='N',
|
| 197 |
+
help='num decoder attention heads')
|
| 198 |
+
parser.add_argument('--decoder-learned-pos', action='store_true',
|
| 199 |
+
help='use learned positional embeddings in the decoder')
|
| 200 |
+
parser.add_argument('--decoder-normalize-before', action='store_true',
|
| 201 |
+
help='apply layernorm before each decoder block')
|
| 202 |
+
parser.add_argument('--share-decoder-input-output-embed', action='store_true',
|
| 203 |
+
help='share decoder input and output embeddings')
|
| 204 |
+
parser.add_argument('--share-all-embeddings', action='store_true',
|
| 205 |
+
help='share encoder, decoder and output embeddings'
|
| 206 |
+
' (requires shared dictionary and embed dim)')
|
| 207 |
+
parser.add_argument('--no-token-positional-embeddings', default=False, action='store_true',
|
| 208 |
+
help='if set, disables positional embeddings (outside self attention)')
|
| 209 |
+
parser.add_argument('--adaptive-softmax-cutoff', metavar='EXPR',
|
| 210 |
+
help='comma separated list of adaptive softmax cutoff points. '
|
| 211 |
+
'Must be used with adaptive_loss criterion'),
|
| 212 |
+
parser.add_argument('--adaptive-softmax-dropout', type=float, metavar='D',
|
| 213 |
+
help='sets adaptive softmax dropout for the tail projections')
|
| 214 |
+
parser.add_argument('--num-embedding-chunks', type=int, metavar='N', default=1,
|
| 215 |
+
help='Number of embedding layer chunks (enables more even distribution'
|
| 216 |
+
'of optimizer states across data parallel nodes'
|
| 217 |
+
'when using optimizer state sharding and'
|
| 218 |
+
'a big embedding vocabulary)')
|
| 219 |
+
# fmt: on
|
| 220 |
+
|
| 221 |
+
@classmethod
|
| 222 |
+
def build_model_base(cls, args, task):
|
| 223 |
+
"""Build a new model instance."""
|
| 224 |
+
|
| 225 |
+
# make sure all arguments are present in older models
|
| 226 |
+
base_architecture(args)
|
| 227 |
+
|
| 228 |
+
if not hasattr(args, "max_source_positions"):
|
| 229 |
+
args.max_source_positions = DEFAULT_MAX_SOURCE_POSITIONS
|
| 230 |
+
if not hasattr(args, "max_target_positions"):
|
| 231 |
+
args.max_target_positions = DEFAULT_MAX_TARGET_POSITIONS
|
| 232 |
+
|
| 233 |
+
src_dict, tgt_dict = task.source_dictionary, task.target_dictionary
|
| 234 |
+
|
| 235 |
+
def build_embedding(dictionary, embed_dim, path=None, num_embed_chunks=1):
|
| 236 |
+
assert embed_dim % num_embed_chunks == 0, (
|
| 237 |
+
f"Number of embedding chunks = {num_embed_chunks} should be "
|
| 238 |
+
+ f"divisible by the embedding dimension = {embed_dim}"
|
| 239 |
+
)
|
| 240 |
+
assert path is None or num_embed_chunks == 1, (
|
| 241 |
+
"Loading embedding from a path with number of embedding chunks > 1"
|
| 242 |
+
+ " is not yet supported"
|
| 243 |
+
)
|
| 244 |
+
num_embeddings = len(dictionary)
|
| 245 |
+
padding_idx = dictionary.pad()
|
| 246 |
+
# if provided, load from preloaded dictionaries
|
| 247 |
+
if path:
|
| 248 |
+
emb = Embedding(num_embeddings, embed_dim, padding_idx)
|
| 249 |
+
embed_dict = utils.parse_embedding(path)
|
| 250 |
+
utils.load_embedding(embed_dict, dictionary, emb)
|
| 251 |
+
else:
|
| 252 |
+
embed_chunk_dim = embed_dim // num_embed_chunks
|
| 253 |
+
emb = nn.ModuleList()
|
| 254 |
+
for i in range(num_embed_chunks):
|
| 255 |
+
emb.append(Embedding(num_embeddings, embed_chunk_dim, padding_idx))
|
| 256 |
+
return emb
|
| 257 |
+
|
| 258 |
+
num_embed_chunks = args.num_embedding_chunks
|
| 259 |
+
if args.share_all_embeddings:
|
| 260 |
+
if src_dict != tgt_dict:
|
| 261 |
+
raise ValueError("--share-all-embeddings requires a joined dictionary")
|
| 262 |
+
if args.encoder_embed_dim != args.decoder_embed_dim:
|
| 263 |
+
raise ValueError(
|
| 264 |
+
"--share-all-embeddings requires --encoder-embed-dim to match --decoder-embed-dim"
|
| 265 |
+
)
|
| 266 |
+
if args.decoder_embed_path and (
|
| 267 |
+
args.decoder_embed_path != args.encoder_embed_path
|
| 268 |
+
):
|
| 269 |
+
raise ValueError(
|
| 270 |
+
"--share-all-embeddings not compatible with --decoder-embed-path"
|
| 271 |
+
)
|
| 272 |
+
encoder_embed_tokens = build_embedding(
|
| 273 |
+
src_dict,
|
| 274 |
+
args.encoder_embed_dim,
|
| 275 |
+
args.encoder_embed_path,
|
| 276 |
+
num_embed_chunks,
|
| 277 |
+
)
|
| 278 |
+
decoder_embed_tokens = encoder_embed_tokens
|
| 279 |
+
args.share_decoder_input_output_embed = True
|
| 280 |
+
else:
|
| 281 |
+
assert args.share_decoder_input_output_embed or num_embed_chunks == 1, (
|
| 282 |
+
"Not sharing decoder I/O embeddings is not yet supported with number of "
|
| 283 |
+
+ "embedding chunks > 1"
|
| 284 |
+
)
|
| 285 |
+
encoder_embed_tokens = build_embedding(
|
| 286 |
+
src_dict,
|
| 287 |
+
args.encoder_embed_dim,
|
| 288 |
+
args.encoder_embed_path,
|
| 289 |
+
num_embed_chunks,
|
| 290 |
+
)
|
| 291 |
+
decoder_embed_tokens = build_embedding(
|
| 292 |
+
tgt_dict,
|
| 293 |
+
args.decoder_embed_dim,
|
| 294 |
+
args.decoder_embed_path,
|
| 295 |
+
num_embed_chunks,
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
encoder = cls.build_encoder(args, src_dict, encoder_embed_tokens)
|
| 299 |
+
decoder = cls.build_decoder(args, tgt_dict, decoder_embed_tokens)
|
| 300 |
+
return (encoder, decoder)
|
| 301 |
+
|
| 302 |
+
@classmethod
|
| 303 |
+
def build_encoder(cls, args, src_dict, embed_tokens):
|
| 304 |
+
return TransformerEncoder(args, src_dict, embed_tokens)
|
| 305 |
+
|
| 306 |
+
@classmethod
|
| 307 |
+
def build_decoder(cls, args, tgt_dict, embed_tokens):
|
| 308 |
+
return TransformerDecoder(args, tgt_dict, embed_tokens)
|
| 309 |
+
|
| 310 |
+
@classmethod
|
| 311 |
+
def build_model(cls, args, task):
|
| 312 |
+
encoder, decoder = cls.build_model_base(args, task)
|
| 313 |
+
return PipelineParallelTransformerModel(
|
| 314 |
+
encoder=encoder,
|
| 315 |
+
decoder=decoder,
|
| 316 |
+
balance=utils.eval_str_list(args.pipeline_balance, type=int),
|
| 317 |
+
devices=utils.eval_str_list(args.pipeline_devices, type=int),
|
| 318 |
+
chunks=args.pipeline_chunks,
|
| 319 |
+
checkpoint=args.pipeline_checkpoint,
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
def output_layer(self, features, **kwargs):
|
| 323 |
+
"""Project features to the default output size (typically vocabulary size)."""
|
| 324 |
+
return self.decoder.output_layer(features, **kwargs)
|
| 325 |
+
|
| 326 |
+
def max_positions(self):
|
| 327 |
+
"""Maximum length supported by the model."""
|
| 328 |
+
return (self.encoder_max_positions, self.decoder_max_positions)
|
| 329 |
+
|
| 330 |
+
def max_positions_helper(
|
| 331 |
+
self, embedding_layer, max_positions_field="max_source_positions"
|
| 332 |
+
):
|
| 333 |
+
"""Maximum input length supported by the encoder or decoder."""
|
| 334 |
+
if embedding_layer.embed_positions is None:
|
| 335 |
+
return getattr(embedding_layer, max_positions_field)
|
| 336 |
+
return min(
|
| 337 |
+
getattr(embedding_layer, max_positions_field),
|
| 338 |
+
embedding_layer.embed_positions.max_positions,
|
| 339 |
+
)
|
| 340 |
+
|
| 341 |
+
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
| 342 |
+
"""Get normalized probabilities (or log probs) from a net's output."""
|
| 343 |
+
|
| 344 |
+
if hasattr(self, "adaptive_softmax") and self.adaptive_softmax is not None:
|
| 345 |
+
if sample is not None:
|
| 346 |
+
assert "target" in sample
|
| 347 |
+
target = sample["target"]
|
| 348 |
+
else:
|
| 349 |
+
target = None
|
| 350 |
+
out = self.adaptive_softmax.get_log_prob(net_output, target=target)
|
| 351 |
+
return out.exp_() if not log_probs else out
|
| 352 |
+
|
| 353 |
+
# A Pipe() module returns a tuple of tensors as the output.
|
| 354 |
+
# In this case, the tuple has one element - the output tensor of logits
|
| 355 |
+
logits = net_output if isinstance(net_output, torch.Tensor) else net_output[0]
|
| 356 |
+
if log_probs:
|
| 357 |
+
return utils.log_softmax(logits, dim=-1, onnx_trace=False)
|
| 358 |
+
else:
|
| 359 |
+
return utils.softmax(logits, dim=-1, onnx_trace=False)
|
| 360 |
+
|
| 361 |
+
def max_decoder_positions(self):
|
| 362 |
+
"""Maximum length supported by the decoder."""
|
| 363 |
+
return self.decoder_max_positions
|
| 364 |
+
|
| 365 |
+
def load_state_dict(self, state_dict, strict=True, model_cfg=None):
|
| 366 |
+
"""Copies parameters and buffers from *state_dict* into this module and
|
| 367 |
+
its descendants.
|
| 368 |
+
|
| 369 |
+
Overrides the method in :class:`nn.Module`. Compared with that method
|
| 370 |
+
this additionally "upgrades" *state_dicts* from old checkpoints.
|
| 371 |
+
"""
|
| 372 |
+
self.upgrade_state_dict(state_dict)
|
| 373 |
+
is_regular_transformer = not any("model.partitions" in k for k in state_dict)
|
| 374 |
+
if is_regular_transformer:
|
| 375 |
+
state_dict = self.convert_to_pipeline_parallel_state_dict(state_dict)
|
| 376 |
+
return super().load_state_dict(state_dict, strict)
|
| 377 |
+
|
| 378 |
+
def convert_to_pipeline_parallel_state_dict(self, state_dict):
|
| 379 |
+
new_state_dict = self.state_dict()
|
| 380 |
+
encoder_layer_idx = 0
|
| 381 |
+
decoder_layer_idx = 0
|
| 382 |
+
encoder_key_suffixes = [
|
| 383 |
+
"self_attn.k_proj.weight",
|
| 384 |
+
"self_attn.k_proj.bias",
|
| 385 |
+
"self_attn.v_proj.weight",
|
| 386 |
+
"self_attn.v_proj.bias",
|
| 387 |
+
"self_attn.q_proj.weight",
|
| 388 |
+
"self_attn.q_proj.bias",
|
| 389 |
+
"self_attn.out_proj.weight",
|
| 390 |
+
"self_attn.out_proj.bias",
|
| 391 |
+
"self_attn_layer_norm.weight",
|
| 392 |
+
"self_attn_layer_norm.bias",
|
| 393 |
+
"fc1.weight",
|
| 394 |
+
"fc1.bias",
|
| 395 |
+
"fc2.weight",
|
| 396 |
+
"fc2.bias",
|
| 397 |
+
"final_layer_norm.weight",
|
| 398 |
+
"final_layer_norm.bias",
|
| 399 |
+
]
|
| 400 |
+
decoder_key_suffixes = [
|
| 401 |
+
"self_attn.k_proj.weight",
|
| 402 |
+
"self_attn.k_proj.bias",
|
| 403 |
+
"self_attn.v_proj.weight",
|
| 404 |
+
"self_attn.v_proj.bias",
|
| 405 |
+
"self_attn.q_proj.weight",
|
| 406 |
+
"self_attn.q_proj.bias",
|
| 407 |
+
"self_attn.out_proj.weight",
|
| 408 |
+
"self_attn.out_proj.bias",
|
| 409 |
+
"self_attn_layer_norm.weight",
|
| 410 |
+
"self_attn_layer_norm.bias",
|
| 411 |
+
"encoder_attn.k_proj.weight",
|
| 412 |
+
"encoder_attn.k_proj.bias",
|
| 413 |
+
"encoder_attn.v_proj.weight",
|
| 414 |
+
"encoder_attn.v_proj.bias",
|
| 415 |
+
"encoder_attn.q_proj.weight",
|
| 416 |
+
"encoder_attn.q_proj.bias",
|
| 417 |
+
"encoder_attn.out_proj.weight",
|
| 418 |
+
"encoder_attn.out_proj.bias",
|
| 419 |
+
"encoder_attn_layer_norm.weight",
|
| 420 |
+
"encoder_attn_layer_norm.bias",
|
| 421 |
+
"fc1.weight",
|
| 422 |
+
"fc1.bias",
|
| 423 |
+
"fc2.weight",
|
| 424 |
+
"fc2.bias",
|
| 425 |
+
"final_layer_norm.weight",
|
| 426 |
+
"final_layer_norm.bias",
|
| 427 |
+
]
|
| 428 |
+
for pid, partition in enumerate(self.model.partitions):
|
| 429 |
+
logger.info(f"Begin Partition {pid}")
|
| 430 |
+
for mid, module in enumerate(partition):
|
| 431 |
+
# fmt: off
|
| 432 |
+
if isinstance(module, TransformerEncoderEmbedding):
|
| 433 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.embed_tokens.weight'] = state_dict['encoder.embed_tokens.weight']
|
| 434 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.embed_positions._float_tensor'] = state_dict['encoder.embed_positions._float_tensor']
|
| 435 |
+
if isinstance(module, TransformerEncoderLayer):
|
| 436 |
+
for suffix in encoder_key_suffixes:
|
| 437 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.{suffix}'] = state_dict[f'encoder.layers.{encoder_layer_idx}.{suffix}']
|
| 438 |
+
encoder_layer_idx += 1
|
| 439 |
+
if isinstance(module, TransformerDecoderLayer):
|
| 440 |
+
for suffix in decoder_key_suffixes:
|
| 441 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.{suffix}'] = state_dict[f'decoder.layers.{decoder_layer_idx}.{suffix}']
|
| 442 |
+
decoder_layer_idx += 1
|
| 443 |
+
if isinstance(module, TransformerEncoderLayerNorm):
|
| 444 |
+
if 'encoder.layer_norm.weight' in state_dict:
|
| 445 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.layer_norm.weight'] = state_dict['encoder.layer_norm.weight']
|
| 446 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.layer_norm.bias'] = state_dict['encoder.layer_norm.bias']
|
| 447 |
+
if isinstance(module, TransformerDecoderEmbedding):
|
| 448 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.embed_tokens.weight'] = state_dict['decoder.embed_tokens.weight']
|
| 449 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.embed_positions._float_tensor'] = state_dict['decoder.embed_positions._float_tensor']
|
| 450 |
+
if isinstance(module, TransformerDecoderOutputLayer):
|
| 451 |
+
new_state_dict[f'model.partitions.{pid}.{mid}.output_projection.weight'] = state_dict['decoder.output_projection.weight']
|
| 452 |
+
# fmt: on
|
| 453 |
+
return new_state_dict
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
class TransformerEncoder(FairseqEncoder):
|
| 457 |
+
"""
|
| 458 |
+
Transformer encoder consisting of *args.encoder_layers* layers. Each layer
|
| 459 |
+
is a :class:`TransformerEncoderLayer`.
|
| 460 |
+
|
| 461 |
+
Args:
|
| 462 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 463 |
+
dictionary (~fairseq.data.Dictionary): encoding dictionary
|
| 464 |
+
embed_tokens (torch.nn.Embedding): input embedding
|
| 465 |
+
"""
|
| 466 |
+
|
| 467 |
+
def __init__(self, args, dictionary, embed_tokens, encoder_module_list=None):
|
| 468 |
+
super().__init__(dictionary)
|
| 469 |
+
self.register_buffer("version", torch.Tensor([3]))
|
| 470 |
+
import_pipe()
|
| 471 |
+
self.use_pipeline = encoder_module_list is not None
|
| 472 |
+
if not self.use_pipeline:
|
| 473 |
+
self.embedding_layer = TransformerEncoderEmbedding(args, embed_tokens)
|
| 474 |
+
self.encoder_layers = nn.Sequential(*[TransformerEncoderLayer(args) for i in range(args.encoder_layers)])
|
| 475 |
+
if isinstance(embed_tokens, nn.ModuleList):
|
| 476 |
+
emb_dim = sum(e.embedding_dim for e in embed_tokens)
|
| 477 |
+
else:
|
| 478 |
+
emb_dim = embed_tokens.embedding_dim
|
| 479 |
+
self.final_layer_norm = TransformerEncoderLayerNorm(args, emb_dim)
|
| 480 |
+
else:
|
| 481 |
+
encoder_balance = utils.eval_str_list(
|
| 482 |
+
args.pipeline_encoder_balance, type=int
|
| 483 |
+
)
|
| 484 |
+
encoder_devices = utils.eval_str_list(
|
| 485 |
+
args.pipeline_encoder_devices, type=int
|
| 486 |
+
)
|
| 487 |
+
assert sum(encoder_balance) == len(encoder_module_list), (
|
| 488 |
+
f"Sum of encoder_balance={encoder_balance} is not equal "
|
| 489 |
+
+ f"to num_encoder_modules={len(encoder_module_list)}"
|
| 490 |
+
)
|
| 491 |
+
if TORCH_PIPE:
|
| 492 |
+
self.model = Pipe(
|
| 493 |
+
module=partition_model(nn.Sequential(*encoder_module_list), encoder_balance, encoder_devices),
|
| 494 |
+
chunks=args.pipeline_chunks,
|
| 495 |
+
checkpoint=args.pipeline_checkpoint,
|
| 496 |
+
)
|
| 497 |
+
else:
|
| 498 |
+
self.model = Pipe(
|
| 499 |
+
module=nn.Sequential(*encoder_module_list),
|
| 500 |
+
balance=encoder_balance,
|
| 501 |
+
devices=encoder_devices,
|
| 502 |
+
chunks=args.pipeline_chunks,
|
| 503 |
+
checkpoint=args.pipeline_checkpoint,
|
| 504 |
+
)
|
| 505 |
+
|
| 506 |
+
def forward(self, src_tokens, src_lengths):
|
| 507 |
+
"""
|
| 508 |
+
Args:
|
| 509 |
+
input_tuple(
|
| 510 |
+
src_tokens (LongTensor): tokens in the source language of shape
|
| 511 |
+
`(batch, src_len)`
|
| 512 |
+
src_lengths (torch.LongTensor): lengths of each source sentence of
|
| 513 |
+
shape `(batch)`
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
Returns:
|
| 517 |
+
output_tuple(
|
| 518 |
+
- **encoder_out** (Tensor): the last encoder layer's output of
|
| 519 |
+
shape `(src_len, batch, embed_dim)`
|
| 520 |
+
- **encoder_padding_mask** (ByteTensor): the positions of
|
| 521 |
+
padding elements of shape `(batch, src_len)`
|
| 522 |
+
- prev_output_tokens
|
| 523 |
+
- **encoder_states** (List[Tensor]): all intermediate
|
| 524 |
+
hidden states of shape `(src_len, batch, embed_dim)`.
|
| 525 |
+
Only populated if *return_all_hiddens* is True.
|
| 526 |
+
)
|
| 527 |
+
"""
|
| 528 |
+
dummy_prev_output_tokens = torch.zeros(
|
| 529 |
+
1, dtype=src_tokens.dtype, device=src_tokens.device
|
| 530 |
+
)
|
| 531 |
+
input_tuple = (src_tokens, src_lengths, dummy_prev_output_tokens)
|
| 532 |
+
if self.use_pipeline:
|
| 533 |
+
input_tuple = tuple(i.to(self.model.devices[0]) for i in input_tuple)
|
| 534 |
+
if TORCH_PIPE:
|
| 535 |
+
encoder_out = self.model(input_tuple).local_value()
|
| 536 |
+
else:
|
| 537 |
+
encoder_out = self.model(input_tuple)
|
| 538 |
+
else:
|
| 539 |
+
encoder_embed_output_tuple = self.embedding_layer(input_tuple)
|
| 540 |
+
encoder_layers_output = self.encoder_layers(encoder_embed_output_tuple)
|
| 541 |
+
encoder_out = self.final_layer_norm(encoder_layers_output)
|
| 542 |
+
# first element is the encoder output
|
| 543 |
+
# second element is the encoder padding mask
|
| 544 |
+
# the remaining elements of EncoderOut are not computed by
|
| 545 |
+
# the PipelineParallelTransformer
|
| 546 |
+
return EncoderOut(encoder_out[0], encoder_out[1], None, None, None, None)
|
| 547 |
+
|
| 548 |
+
def reorder_encoder_out(self, encoder_out, new_order):
|
| 549 |
+
"""
|
| 550 |
+
Reorder encoder output according to *new_order*.
|
| 551 |
+
|
| 552 |
+
Args:
|
| 553 |
+
encoder_out: output from the ``forward()`` method
|
| 554 |
+
new_order (LongTensor): desired order
|
| 555 |
+
|
| 556 |
+
Returns:
|
| 557 |
+
*encoder_out* rearranged according to *new_order*
|
| 558 |
+
"""
|
| 559 |
+
if encoder_out.encoder_out is not None:
|
| 560 |
+
encoder_out = encoder_out._replace(
|
| 561 |
+
encoder_out=encoder_out.encoder_out.index_select(1, new_order)
|
| 562 |
+
)
|
| 563 |
+
if encoder_out.encoder_padding_mask is not None:
|
| 564 |
+
encoder_out = encoder_out._replace(
|
| 565 |
+
encoder_padding_mask=encoder_out.encoder_padding_mask.index_select(
|
| 566 |
+
0, new_order
|
| 567 |
+
)
|
| 568 |
+
)
|
| 569 |
+
if encoder_out.encoder_embedding is not None:
|
| 570 |
+
encoder_out = encoder_out._replace(
|
| 571 |
+
encoder_embedding=encoder_out.encoder_embedding.index_select(
|
| 572 |
+
0, new_order
|
| 573 |
+
)
|
| 574 |
+
)
|
| 575 |
+
if encoder_out.encoder_states is not None:
|
| 576 |
+
for idx, state in enumerate(encoder_out.encoder_states):
|
| 577 |
+
encoder_out.encoder_states[idx] = state.index_select(1, new_order)
|
| 578 |
+
return encoder_out
|
| 579 |
+
|
| 580 |
+
def max_positions(self):
|
| 581 |
+
"""Maximum input length supported by the encoder."""
|
| 582 |
+
if self.embedding_layer.embed_positions is None:
|
| 583 |
+
return self.embedding_layer.max_source_positions
|
| 584 |
+
return min(
|
| 585 |
+
self.embedding_layer.max_source_positions,
|
| 586 |
+
self.embedding_layer.embed_positions.max_positions,
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
class TransformerDecoder(FairseqDecoder):
|
| 591 |
+
"""
|
| 592 |
+
Transformer decoder consisting of *args.decoder_layers* layers. Each layer
|
| 593 |
+
is a :class:`TransformerDecoderLayer`.
|
| 594 |
+
|
| 595 |
+
Args:
|
| 596 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 597 |
+
dictionary (~fairseq.data.Dictionary): decoding dictionary
|
| 598 |
+
embed_tokens (torch.nn.Embedding): output embedding
|
| 599 |
+
no_encoder_attn (bool, optional): whether to attend to encoder outputs
|
| 600 |
+
(default: False).
|
| 601 |
+
"""
|
| 602 |
+
|
| 603 |
+
def __init__(
|
| 604 |
+
self,
|
| 605 |
+
args,
|
| 606 |
+
dictionary,
|
| 607 |
+
embed_tokens,
|
| 608 |
+
no_encoder_attn=False,
|
| 609 |
+
decoder_module_list=None,
|
| 610 |
+
):
|
| 611 |
+
super().__init__(dictionary)
|
| 612 |
+
self.register_buffer("version", torch.Tensor([3]))
|
| 613 |
+
import_pipe()
|
| 614 |
+
self.use_pipeline = decoder_module_list is not None
|
| 615 |
+
if not self.use_pipeline:
|
| 616 |
+
self.embedding_layer = TransformerDecoderEmbedding(args, embed_tokens)
|
| 617 |
+
self.decoder_layers = nn.Sequential(*[
|
| 618 |
+
TransformerDecoderLayer(args, no_encoder_attn)
|
| 619 |
+
for _ in range(args.decoder_layers)
|
| 620 |
+
])
|
| 621 |
+
self.decoder_output_layer = TransformerDecoderOutputLayer(
|
| 622 |
+
args, embed_tokens, dictionary
|
| 623 |
+
)
|
| 624 |
+
else:
|
| 625 |
+
decoder_balance = utils.eval_str_list(
|
| 626 |
+
args.pipeline_decoder_balance, type=int
|
| 627 |
+
)
|
| 628 |
+
decoder_devices = utils.eval_str_list(
|
| 629 |
+
args.pipeline_decoder_devices, type=int
|
| 630 |
+
)
|
| 631 |
+
assert sum(decoder_balance) == len(decoder_module_list), (
|
| 632 |
+
f"Sum of decoder_balance={decoder_balance} is not equal "
|
| 633 |
+
+ f"to num_decoder_modules={len(decoder_module_list)}"
|
| 634 |
+
)
|
| 635 |
+
if TORCH_PIPE:
|
| 636 |
+
self.model = Pipe(
|
| 637 |
+
module=partition_model(nn.Sequential(*decoder_module_list), decoder_balance, decoder_devices),
|
| 638 |
+
chunks=args.pipeline_chunks,
|
| 639 |
+
checkpoint=args.pipeline_checkpoint,
|
| 640 |
+
)
|
| 641 |
+
else:
|
| 642 |
+
self.model = Pipe(
|
| 643 |
+
module=nn.Sequential(*decoder_module_list),
|
| 644 |
+
balance=decoder_balance,
|
| 645 |
+
devices=decoder_devices,
|
| 646 |
+
chunks=args.pipeline_chunks,
|
| 647 |
+
checkpoint=args.pipeline_checkpoint,
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
def forward(
|
| 651 |
+
self,
|
| 652 |
+
prev_output_tokens,
|
| 653 |
+
encoder_out=None,
|
| 654 |
+
):
|
| 655 |
+
"""
|
| 656 |
+
Args:
|
| 657 |
+
prev_output_tokens (LongTensor): previous decoder outputs of shape
|
| 658 |
+
`(batch, tgt_len)`, for teacher forcing
|
| 659 |
+
encoder_out (optional): output from the encoder, used for
|
| 660 |
+
encoder-side attention
|
| 661 |
+
incremental_state (dict): dictionary used for storing state during
|
| 662 |
+
:ref:`Incremental decoding`
|
| 663 |
+
features_only (bool, optional): only return features without
|
| 664 |
+
applying output layer (default: False).
|
| 665 |
+
|
| 666 |
+
Returns:
|
| 667 |
+
tuple:
|
| 668 |
+
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
| 669 |
+
- a dictionary with any model-specific outputs
|
| 670 |
+
"""
|
| 671 |
+
input_tuple = (
|
| 672 |
+
encoder_out.encoder_out,
|
| 673 |
+
encoder_out.encoder_padding_mask,
|
| 674 |
+
prev_output_tokens,
|
| 675 |
+
)
|
| 676 |
+
if self.use_pipeline:
|
| 677 |
+
input_tuple = tuple(i.to(self.model.devices[0]) for i in input_tuple)
|
| 678 |
+
if TORCH_PIPE:
|
| 679 |
+
return (self.model(input_tuple).local_value(),)
|
| 680 |
+
else:
|
| 681 |
+
return (self.model(input_tuple),)
|
| 682 |
+
else:
|
| 683 |
+
embed_layer_output = self.embedding_layer(input_tuple)
|
| 684 |
+
state = self.decoder_layers(embed_layer_output)
|
| 685 |
+
return (self.decoder_output_layer(state),)
|
| 686 |
+
|
| 687 |
+
def output_layer(self, features, **kwargs):
|
| 688 |
+
"""Project features to the vocabulary size."""
|
| 689 |
+
if self.adaptive_softmax is None:
|
| 690 |
+
# project back to size of vocabulary
|
| 691 |
+
if self.share_input_output_embed:
|
| 692 |
+
return F.linear(features, self.embed_tokens.weight)
|
| 693 |
+
else:
|
| 694 |
+
return F.linear(features, self.embed_out)
|
| 695 |
+
else:
|
| 696 |
+
return features
|
| 697 |
+
|
| 698 |
+
def max_positions(self):
|
| 699 |
+
"""Maximum output length supported by the decoder."""
|
| 700 |
+
if self.embedding_layer.embed_positions is None:
|
| 701 |
+
return self.embedding_layer.max_target_positions
|
| 702 |
+
return min(
|
| 703 |
+
self.embedding_layer.max_target_positions,
|
| 704 |
+
self.embedding_layer.embed_positions.max_positions,
|
| 705 |
+
)
|
| 706 |
+
|
| 707 |
+
def buffered_future_mask(self, tensor):
|
| 708 |
+
dim = tensor.size(0)
|
| 709 |
+
if (
|
| 710 |
+
not hasattr(self, "_future_mask")
|
| 711 |
+
or self._future_mask is None
|
| 712 |
+
or self._future_mask.device != tensor.device
|
| 713 |
+
or self._future_mask.size(0) < dim
|
| 714 |
+
):
|
| 715 |
+
self._future_mask = torch.triu(
|
| 716 |
+
utils.fill_with_neg_inf(tensor.new(dim, dim)), 1
|
| 717 |
+
)
|
| 718 |
+
return self._future_mask[:dim, :dim]
|
| 719 |
+
|
| 720 |
+
def upgrade_state_dict_named(self, state_dict, name):
|
| 721 |
+
"""Upgrade a (possibly old) state dict for new versions of fairseq."""
|
| 722 |
+
if isinstance(self.embed_positions, SinusoidalPositionalEmbedding):
|
| 723 |
+
weights_key = "{}.embed_positions.weights".format(name)
|
| 724 |
+
if weights_key in state_dict:
|
| 725 |
+
del state_dict[weights_key]
|
| 726 |
+
state_dict[
|
| 727 |
+
"{}.embed_positions._float_tensor".format(name)
|
| 728 |
+
] = torch.FloatTensor(1)
|
| 729 |
+
|
| 730 |
+
for i in range(len(self.layers)):
|
| 731 |
+
# update layer norms
|
| 732 |
+
layer_norm_map = {
|
| 733 |
+
"0": "self_attn_layer_norm",
|
| 734 |
+
"1": "encoder_attn_layer_norm",
|
| 735 |
+
"2": "final_layer_norm",
|
| 736 |
+
}
|
| 737 |
+
for old, new in layer_norm_map.items():
|
| 738 |
+
for m in ("weight", "bias"):
|
| 739 |
+
k = "{}.layers.{}.layer_norms.{}.{}".format(name, i, old, m)
|
| 740 |
+
if k in state_dict:
|
| 741 |
+
state_dict[
|
| 742 |
+
"{}.layers.{}.{}.{}".format(name, i, new, m)
|
| 743 |
+
] = state_dict[k]
|
| 744 |
+
del state_dict[k]
|
| 745 |
+
|
| 746 |
+
version_key = "{}.version".format(name)
|
| 747 |
+
if utils.item(state_dict.get(version_key, torch.Tensor([1]))[0]) <= 2:
|
| 748 |
+
# earlier checkpoints did not normalize after the stack of layers
|
| 749 |
+
self.layer_norm = None
|
| 750 |
+
self.normalize = False
|
| 751 |
+
state_dict[version_key] = torch.Tensor([1])
|
| 752 |
+
|
| 753 |
+
return state_dict
|
| 754 |
+
|
| 755 |
+
|
| 756 |
+
@register_model_architecture(
|
| 757 |
+
"pipeline_parallel_transformer", "transformer_iwslt_de_en_pipeline_parallel"
|
| 758 |
+
)
|
| 759 |
+
def transformer_iwslt_de_en_dist(args):
|
| 760 |
+
transformer_iwslt_de_en(args)
|
| 761 |
+
|
| 762 |
+
|
| 763 |
+
@register_model_architecture(
|
| 764 |
+
"pipeline_parallel_transformer", "transformer_wmt_en_de_big_pipeline_parallel"
|
| 765 |
+
)
|
| 766 |
+
def transformer_wmt_en_de_big_dist(args):
|
| 767 |
+
transformer_wmt_en_de_big(args)
|
avh-align_core/fairseq/model_parallel/models/roberta/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .model import * # noqa
|
avh-align_core/fairseq/model_parallel/models/roberta/model.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""
|
| 6 |
+
RoBERTa: A Robustly Optimized BERT Pretraining Approach.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import logging
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
from fairseq import utils
|
| 15 |
+
from fairseq.model_parallel.models.transformer import ModelParallelTransformerEncoder
|
| 16 |
+
from fairseq.models import register_model, register_model_architecture
|
| 17 |
+
from fairseq.models.roberta import (
|
| 18 |
+
roberta_base_architecture,
|
| 19 |
+
roberta_prenorm_architecture,
|
| 20 |
+
RobertaEncoder,
|
| 21 |
+
RobertaModel,
|
| 22 |
+
)
|
| 23 |
+
from fairseq.modules import LayerNorm
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
from fairseq.model_parallel.megatron.mpu import (
|
| 28 |
+
copy_to_model_parallel_region,
|
| 29 |
+
gather_from_model_parallel_region,
|
| 30 |
+
ColumnParallelLinear,
|
| 31 |
+
VocabParallelEmbedding,
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
has_megatron_submodule = True
|
| 35 |
+
except (ImportError, ModuleNotFoundError):
|
| 36 |
+
has_megatron_submodule = False
|
| 37 |
+
|
| 38 |
+
logger = logging.getLogger(__name__)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@register_model("model_parallel_roberta")
|
| 42 |
+
class ModelParallelRobertaModel(RobertaModel):
|
| 43 |
+
def __init__(self, args, encoder):
|
| 44 |
+
super().__init__(args, encoder)
|
| 45 |
+
|
| 46 |
+
self.classification_heads = nn.ModuleDict()
|
| 47 |
+
|
| 48 |
+
@staticmethod
|
| 49 |
+
def add_args(parser):
|
| 50 |
+
RobertaModel.add_args(parser)
|
| 51 |
+
parser.add_argument(
|
| 52 |
+
"--no-final-layer-norm",
|
| 53 |
+
action="store_true",
|
| 54 |
+
help=(
|
| 55 |
+
"don't add final layernorm (only applicable when "
|
| 56 |
+
"--encoder-normalize-before=True"
|
| 57 |
+
),
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
@classmethod
|
| 61 |
+
def build_model(cls, args, task):
|
| 62 |
+
"""Build a new model instance."""
|
| 63 |
+
|
| 64 |
+
# make sure all arguments are present
|
| 65 |
+
base_architecture(args)
|
| 66 |
+
|
| 67 |
+
task.source_dictionary.pad_to_multiple_(args.model_parallel_size * 8)
|
| 68 |
+
task.target_dictionary.pad_to_multiple_(args.model_parallel_size * 8)
|
| 69 |
+
|
| 70 |
+
if not hasattr(args, "max_positions"):
|
| 71 |
+
args.max_positions = args.tokens_per_sample
|
| 72 |
+
|
| 73 |
+
if getattr(args, "untie_weights_roberta", False):
|
| 74 |
+
raise NotImplementedError(
|
| 75 |
+
"--untie-weights-roberta is not supported in model parallel mode"
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
encoder = ModelParallelRobertaEncoder(args, task.source_dictionary)
|
| 79 |
+
return cls(args, encoder)
|
| 80 |
+
|
| 81 |
+
def forward(
|
| 82 |
+
self,
|
| 83 |
+
src_tokens,
|
| 84 |
+
features_only=False,
|
| 85 |
+
return_all_hiddens=False,
|
| 86 |
+
classification_head_name=None,
|
| 87 |
+
**kwargs
|
| 88 |
+
):
|
| 89 |
+
if classification_head_name is not None:
|
| 90 |
+
features_only = True
|
| 91 |
+
|
| 92 |
+
x, extra = self.encoder(src_tokens, features_only, return_all_hiddens, **kwargs)
|
| 93 |
+
|
| 94 |
+
if classification_head_name is not None:
|
| 95 |
+
x = self.classification_heads[classification_head_name](x)
|
| 96 |
+
return x, extra
|
| 97 |
+
|
| 98 |
+
def register_classification_head(
|
| 99 |
+
self, name, num_classes=None, inner_dim=None, **kwargs
|
| 100 |
+
):
|
| 101 |
+
"""Register a classification head."""
|
| 102 |
+
if name in self.classification_heads:
|
| 103 |
+
prev_num_classes = self.classification_heads[name].out_proj.out_features
|
| 104 |
+
prev_inner_dim = self.classification_heads[name].dense.out_features
|
| 105 |
+
if num_classes != prev_num_classes or inner_dim != prev_inner_dim:
|
| 106 |
+
logger.warning(
|
| 107 |
+
're-registering head "{}" with num_classes {} (prev: {}) '
|
| 108 |
+
"and inner_dim {} (prev: {})".format(
|
| 109 |
+
name, num_classes, prev_num_classes, inner_dim, prev_inner_dim
|
| 110 |
+
)
|
| 111 |
+
)
|
| 112 |
+
self.classification_heads[name] = ModelParallelRobertaClassificationHead(
|
| 113 |
+
self.args.encoder_embed_dim,
|
| 114 |
+
inner_dim or self.args.encoder_embed_dim,
|
| 115 |
+
num_classes,
|
| 116 |
+
self.args.pooler_activation_fn,
|
| 117 |
+
self.args.pooler_dropout,
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class ModelParallelRobertaLMHead(nn.Module):
|
| 122 |
+
"""Head for masked language modeling."""
|
| 123 |
+
|
| 124 |
+
def __init__(self, embed_dim, output_dim, activation_fn, weight=None):
|
| 125 |
+
super().__init__()
|
| 126 |
+
self.dense = ColumnParallelLinear(embed_dim, embed_dim, gather_output=True)
|
| 127 |
+
self.activation_fn = utils.get_activation_fn(activation_fn)
|
| 128 |
+
self.layer_norm = LayerNorm(embed_dim)
|
| 129 |
+
|
| 130 |
+
if weight is None:
|
| 131 |
+
weight = nn.Linear(embed_dim, output_dim, bias=False).weight
|
| 132 |
+
self.weight = weight
|
| 133 |
+
self.bias = nn.Parameter(torch.zeros(output_dim))
|
| 134 |
+
|
| 135 |
+
def forward(self, features, masked_tokens=None, **kwargs):
|
| 136 |
+
# Only project the unmasked tokens while training,
|
| 137 |
+
# saves both memory and computation
|
| 138 |
+
if masked_tokens is not None:
|
| 139 |
+
features = features[masked_tokens, :]
|
| 140 |
+
|
| 141 |
+
x = self.dense(features)
|
| 142 |
+
x = self.activation_fn(x)
|
| 143 |
+
x = self.layer_norm(x)
|
| 144 |
+
|
| 145 |
+
x = copy_to_model_parallel_region(x)
|
| 146 |
+
# project back to size of vocabulary with bias
|
| 147 |
+
x = F.linear(x, self.weight)
|
| 148 |
+
x = gather_from_model_parallel_region(x).contiguous()
|
| 149 |
+
x = x + self.bias
|
| 150 |
+
return x
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class ModelParallelRobertaClassificationHead(nn.Module):
|
| 154 |
+
"""Head for sentence-level classification tasks."""
|
| 155 |
+
|
| 156 |
+
def __init__(
|
| 157 |
+
self, input_dim, inner_dim, num_classes, activation_fn, pooler_dropout
|
| 158 |
+
):
|
| 159 |
+
super().__init__()
|
| 160 |
+
self.dense = ColumnParallelLinear(input_dim, inner_dim, gather_output=True)
|
| 161 |
+
self.activation_fn = utils.get_activation_fn(activation_fn)
|
| 162 |
+
self.dropout = nn.Dropout(p=pooler_dropout)
|
| 163 |
+
self.out_proj = nn.Linear(inner_dim, num_classes)
|
| 164 |
+
|
| 165 |
+
def forward(self, features, **kwargs):
|
| 166 |
+
x = features[:, 0, :] # take <s> token (equiv. to [CLS])
|
| 167 |
+
x = self.dropout(x)
|
| 168 |
+
x = self.dense(x)
|
| 169 |
+
x = self.activation_fn(x)
|
| 170 |
+
x = self.dropout(x)
|
| 171 |
+
x = self.out_proj(x)
|
| 172 |
+
return x
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class ModelParallelRobertaEncoder(RobertaEncoder):
|
| 176 |
+
"""RoBERTa encoder."""
|
| 177 |
+
|
| 178 |
+
def __init__(self, args, dictionary):
|
| 179 |
+
super().__init__(args, dictionary)
|
| 180 |
+
assert not self.args.untie_weights_roberta
|
| 181 |
+
|
| 182 |
+
def build_embedding(self, vocab_size, embedding_dim, padding_idx):
|
| 183 |
+
return VocabParallelEmbedding(vocab_size, embedding_dim, padding_idx)
|
| 184 |
+
|
| 185 |
+
def build_encoder(self, args, dictionary, embed_tokens):
|
| 186 |
+
return ModelParallelTransformerEncoder(args, dictionary, embed_tokens)
|
| 187 |
+
|
| 188 |
+
def build_lm_head(self, embed_dim, output_dim, activation_fn, weight):
|
| 189 |
+
return ModelParallelRobertaLMHead(embed_dim, output_dim, activation_fn, weight)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
@register_model_architecture("model_parallel_roberta", "model_parallel_roberta")
|
| 193 |
+
def base_architecture(args):
|
| 194 |
+
args.no_final_layer_norm = getattr(args, "no_final_layer_norm", False)
|
| 195 |
+
# model parallel RoBERTa defaults to "Pre-LN" formulation
|
| 196 |
+
roberta_prenorm_architecture(args)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
# earlier versions of model parallel RoBERTa removed the final layer norm
|
| 200 |
+
@register_model_architecture("model_parallel_roberta", "model_parallel_roberta_v1")
|
| 201 |
+
def model_parallel_roberta_v1_architecture(args):
|
| 202 |
+
args.no_final_layer_norm = getattr(args, "no_final_layer_norm", True)
|
| 203 |
+
base_architecture(args)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
@register_model_architecture(
|
| 207 |
+
"model_parallel_roberta", "model_parallel_roberta_postnorm"
|
| 208 |
+
)
|
| 209 |
+
def model_parallel_roberta_postnorm_architecture(args):
|
| 210 |
+
# the original BERT/RoBERTa uses the "Post-LN" formulation
|
| 211 |
+
roberta_base_architecture(args)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
@register_model_architecture("model_parallel_roberta", "model_parallel_roberta_base")
|
| 215 |
+
def model_parallel_roberta_base_architecture(args):
|
| 216 |
+
base_architecture(args)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
@register_model_architecture("model_parallel_roberta", "model_parallel_roberta_large")
|
| 220 |
+
def model_parallel_roberta_large_architecture(args):
|
| 221 |
+
args.encoder_layers = getattr(args, "encoder_layers", 24)
|
| 222 |
+
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
| 223 |
+
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4096)
|
| 224 |
+
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
| 225 |
+
base_architecture(args)
|
avh-align_core/fairseq/model_parallel/models/transformer.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from fairseq.model_parallel.modules import (
|
| 10 |
+
ModelParallelTransformerDecoderLayer,
|
| 11 |
+
ModelParallelTransformerEncoderLayer,
|
| 12 |
+
)
|
| 13 |
+
from fairseq.models import register_model
|
| 14 |
+
from fairseq.models.transformer import (
|
| 15 |
+
TransformerDecoder,
|
| 16 |
+
TransformerEncoder,
|
| 17 |
+
TransformerModel,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
from fairseq.model_parallel.megatron.mpu import (
|
| 23 |
+
copy_to_model_parallel_region,
|
| 24 |
+
gather_from_model_parallel_region,
|
| 25 |
+
VocabParallelEmbedding,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
has_megatron_submodule = True
|
| 29 |
+
except (ImportError, ModuleNotFoundError):
|
| 30 |
+
has_megatron_submodule = False
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logger = logging.getLogger(__name__)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@register_model("model_parallel_transformer")
|
| 37 |
+
class ModelParallelTransformerModel(TransformerModel):
|
| 38 |
+
"""
|
| 39 |
+
Model parallel Transformer model.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
@classmethod
|
| 43 |
+
def build_embedding(cls, args, dictionary, embed_dim, path=None):
|
| 44 |
+
if not has_megatron_submodule:
|
| 45 |
+
raise ImportError(
|
| 46 |
+
"\n\nPlease install the megatron submodule:"
|
| 47 |
+
"\n\n git submodule update --init "
|
| 48 |
+
"fairseq/model_parallel/megatron"
|
| 49 |
+
)
|
| 50 |
+
dictionary.pad_to_multiple_(args.model_parallel_size * 8)
|
| 51 |
+
num_embeddings = len(dictionary)
|
| 52 |
+
padding_idx = dictionary.pad()
|
| 53 |
+
|
| 54 |
+
def _vocab_init(tensor, **kwargs):
|
| 55 |
+
nn.init.normal_(tensor, mean=0, std=num_embeddings ** -0.5)
|
| 56 |
+
nn.init.constant_(tensor[1], 0)
|
| 57 |
+
|
| 58 |
+
emb = VocabParallelEmbedding(
|
| 59 |
+
num_embeddings, embed_dim, padding_idx, init_method=_vocab_init
|
| 60 |
+
)
|
| 61 |
+
# if provided, load from preloaded dictionaries
|
| 62 |
+
if path:
|
| 63 |
+
raise NotImplementedError(
|
| 64 |
+
"Loading of embedding from path is not supported for model parallel"
|
| 65 |
+
)
|
| 66 |
+
return emb
|
| 67 |
+
|
| 68 |
+
@classmethod
|
| 69 |
+
def build_encoder(cls, args, src_dict, embed_tokens):
|
| 70 |
+
return ModelParallelTransformerEncoder(args, src_dict, embed_tokens)
|
| 71 |
+
|
| 72 |
+
@classmethod
|
| 73 |
+
def build_decoder(cls, args, tgt_dict, embed_tokens):
|
| 74 |
+
return ModelParallelTransformerDecoder(
|
| 75 |
+
args,
|
| 76 |
+
tgt_dict,
|
| 77 |
+
embed_tokens,
|
| 78 |
+
no_encoder_attn=getattr(args, "no_cross_attention", False),
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class ModelParallelTransformerEncoder(TransformerEncoder):
|
| 83 |
+
"""
|
| 84 |
+
Model parallel Transformer encoder consisting of *args.encoder_layers* layers. Each layer
|
| 85 |
+
is a :class:`ModelParallelTransformerEncoderLayer`.
|
| 86 |
+
"""
|
| 87 |
+
|
| 88 |
+
def __init__(self, args, dictionary, embed_tokens):
|
| 89 |
+
super().__init__(args, dictionary, embed_tokens)
|
| 90 |
+
|
| 91 |
+
if args.no_final_layer_norm:
|
| 92 |
+
self.layer_norm = None
|
| 93 |
+
|
| 94 |
+
def build_encoder_layer(self, args):
|
| 95 |
+
return ModelParallelTransformerEncoderLayer(args)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class ModelParallelTransformerDecoder(TransformerDecoder):
|
| 99 |
+
"""
|
| 100 |
+
Model Parallel Transformer decoder consisting of *args.decoder_layers* layers. Each layer
|
| 101 |
+
is a :class:`ModelParallelTransformerDecoderLayer`.
|
| 102 |
+
"""
|
| 103 |
+
|
| 104 |
+
def build_decoder_layer(self, args, no_encoder_attn=False):
|
| 105 |
+
return ModelParallelTransformerDecoderLayer(args, no_encoder_attn)
|
| 106 |
+
|
| 107 |
+
def output_layer(self, features, **kwargs):
|
| 108 |
+
"""Project features to the vocabulary size."""
|
| 109 |
+
if not self.share_input_output_embed:
|
| 110 |
+
raise NotImplementedError(
|
| 111 |
+
"Model parallel training currently requires --share-decoder-input-output-embed"
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
features = copy_to_model_parallel_region(features)
|
| 115 |
+
|
| 116 |
+
# project back to size of vocabulary
|
| 117 |
+
x = self.output_projection(features)
|
| 118 |
+
|
| 119 |
+
if getattr(self.args, "criterion") != "vocab_parallel_cross_entropy":
|
| 120 |
+
x = gather_from_model_parallel_region(x).contiguous()
|
| 121 |
+
return x
|
avh-align_core/fairseq/model_parallel/models/transformer_lm.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
from fairseq.model_parallel.models.transformer import ModelParallelTransformerDecoder
|
| 8 |
+
from fairseq.models import register_model, register_model_architecture
|
| 9 |
+
from fairseq.models.transformer_lm import TransformerLanguageModel
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
from fairseq.model_parallel.megatron.mpu import VocabParallelEmbedding
|
| 14 |
+
|
| 15 |
+
has_megatron_submodule = True
|
| 16 |
+
except (ImportError, ModuleNotFoundError):
|
| 17 |
+
has_megatron_submodule = False
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
DEFAULT_MAX_TARGET_POSITIONS = 1024
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@register_model("model_parallel_transformer_lm")
|
| 24 |
+
class ModelParallelTransformerLanguageModel(TransformerLanguageModel):
|
| 25 |
+
|
| 26 |
+
@staticmethod
|
| 27 |
+
def add_args(parser):
|
| 28 |
+
TransformerLanguageModel.add_args(parser)
|
| 29 |
+
|
| 30 |
+
@classmethod
|
| 31 |
+
def build_model(cls, args, task):
|
| 32 |
+
"""Build a new model instance."""
|
| 33 |
+
if not has_megatron_submodule:
|
| 34 |
+
raise ImportError(
|
| 35 |
+
"\n\nPlease install the megatron submodule:"
|
| 36 |
+
"\n\n git submodule update --init "
|
| 37 |
+
"fairseq/model_parallel/megatron"
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
# make sure all arguments are present in older models
|
| 41 |
+
base_lm_architecture(args)
|
| 42 |
+
|
| 43 |
+
task.source_dictionary.pad_to_multiple_(args.model_parallel_size * 8)
|
| 44 |
+
task.target_dictionary.pad_to_multiple_(args.model_parallel_size * 8)
|
| 45 |
+
|
| 46 |
+
if args.decoder_layers_to_keep:
|
| 47 |
+
args.decoder_layers = len(args.decoder_layers_to_keep.split(","))
|
| 48 |
+
|
| 49 |
+
if getattr(args, "max_target_positions", None) is None:
|
| 50 |
+
args.max_target_positions = getattr(
|
| 51 |
+
args, "tokens_per_sample", DEFAULT_MAX_TARGET_POSITIONS
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
if args.character_embeddings:
|
| 55 |
+
raise NotImplementedError(
|
| 56 |
+
"Character embeddings is not supported for model parallel"
|
| 57 |
+
)
|
| 58 |
+
elif args.adaptive_input:
|
| 59 |
+
raise NotImplementedError(
|
| 60 |
+
"Adaptive input is not supported for model parallel"
|
| 61 |
+
)
|
| 62 |
+
else:
|
| 63 |
+
embed_tokens = cls.build_embedding(
|
| 64 |
+
args, task.source_dictionary, args.decoder_input_dim
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
decoder = ModelParallelTransformerDecoder(
|
| 68 |
+
args,
|
| 69 |
+
task.target_dictionary,
|
| 70 |
+
embed_tokens,
|
| 71 |
+
no_encoder_attn=True,
|
| 72 |
+
)
|
| 73 |
+
return cls(decoder)
|
| 74 |
+
|
| 75 |
+
@staticmethod
|
| 76 |
+
def add_args(parser):
|
| 77 |
+
TransformerLanguageModel.add_args(parser)
|
| 78 |
+
|
| 79 |
+
@classmethod
|
| 80 |
+
def build_embedding(cls, args, dictionary, embed_dim, path=None):
|
| 81 |
+
def _vocab_init(tensor, **kwargs):
|
| 82 |
+
nn.init.normal_(tensor, mean=0, std=embed_dim ** -0.5)
|
| 83 |
+
nn.init.constant_(tensor[1], 0)
|
| 84 |
+
|
| 85 |
+
embed_tokens = VocabParallelEmbedding(
|
| 86 |
+
len(dictionary), embed_dim, dictionary.pad(), init_method=_vocab_init
|
| 87 |
+
)
|
| 88 |
+
return embed_tokens
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def base_lm_architecture(args):
|
| 92 |
+
# backward compatibility for older model checkpoints
|
| 93 |
+
if hasattr(args, "no_tie_adaptive_proj"):
|
| 94 |
+
# previous models defined --no-tie-adaptive-proj, so use the existence of
|
| 95 |
+
# that option to determine if this is an "old" model checkpoint
|
| 96 |
+
args.no_decoder_final_norm = True # old models always set this to True
|
| 97 |
+
if args.no_tie_adaptive_proj is False:
|
| 98 |
+
args.tie_adaptive_proj = True
|
| 99 |
+
if hasattr(args, "decoder_final_norm"):
|
| 100 |
+
args.no_decoder_final_norm = not args.decoder_final_norm
|
| 101 |
+
|
| 102 |
+
args.activation_fn = getattr(args, "activation_fn", "relu")
|
| 103 |
+
args.dropout = getattr(args, "dropout", 0.1)
|
| 104 |
+
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
| 105 |
+
args.activation_dropout = getattr(args, "activation_dropout", 0.0)
|
| 106 |
+
args.relu_dropout = getattr(args, "relu_dropout", 0.0)
|
| 107 |
+
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
| 108 |
+
args.decoder_output_dim = getattr(
|
| 109 |
+
args, "decoder_output_dim", args.decoder_embed_dim
|
| 110 |
+
)
|
| 111 |
+
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
| 112 |
+
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 2048)
|
| 113 |
+
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
| 114 |
+
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
| 115 |
+
# Model training is not stable without this
|
| 116 |
+
args.decoder_normalize_before = True
|
| 117 |
+
args.no_decoder_final_norm = getattr(args, "no_decoder_final_norm", False)
|
| 118 |
+
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
| 119 |
+
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
| 120 |
+
args.adaptive_softmax_factor = getattr(args, "adaptive_softmax_factor", 4)
|
| 121 |
+
args.no_token_positional_embeddings = getattr(
|
| 122 |
+
args, "no_token_positional_embeddings", False
|
| 123 |
+
)
|
| 124 |
+
args.share_decoder_input_output_embed = getattr(
|
| 125 |
+
args, "share_decoder_input_output_embed", False
|
| 126 |
+
)
|
| 127 |
+
args.character_embeddings = getattr(args, "character_embeddings", False)
|
| 128 |
+
args.character_filters = getattr(
|
| 129 |
+
args,
|
| 130 |
+
"character_filters",
|
| 131 |
+
"[(1, 64), (2, 128), (3, 192), (4, 256), (5, 256), (6, 256), (7, 256)]",
|
| 132 |
+
)
|
| 133 |
+
args.character_embedding_dim = getattr(args, "character_embedding_dim", 4)
|
| 134 |
+
args.char_embedder_highway_layers = getattr(args, "char_embedder_highway_layers", 2)
|
| 135 |
+
args.adaptive_input = getattr(args, "adaptive_input", False)
|
| 136 |
+
args.adaptive_input_factor = getattr(args, "adaptive_input_factor", 4)
|
| 137 |
+
args.adaptive_input_cutoff = getattr(args, "adaptive_input_cutoff", None)
|
| 138 |
+
args.tie_adaptive_weights = getattr(args, "tie_adaptive_weights", False)
|
| 139 |
+
args.tie_adaptive_proj = getattr(args, "tie_adaptive_proj", False)
|
| 140 |
+
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
| 141 |
+
args.decoder_layerdrop = getattr(args, "decoder_layerdrop", 0.0)
|
| 142 |
+
args.decoder_layers_to_keep = getattr(args, "decoder_layers_to_keep", None)
|
| 143 |
+
args.layernorm_embedding = getattr(args, "layernorm_embedding", False)
|
| 144 |
+
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
| 145 |
+
args.quant_noise_pq = getattr(args, "quant_noise_pq", 0.0)
|
| 146 |
+
args.quant_noise_pq_block_size = getattr(args, "quant_noise_pq_block_size", 8)
|
| 147 |
+
args.quant_noise_scalar = getattr(args, "quant_noise_scalar", 0.0)
|
| 148 |
+
args.add_bos_token = getattr(args, "add_bos_token", False)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
@register_model_architecture("model_parallel_transformer_lm", "transformer_lm_megatron")
|
| 152 |
+
def transformer_lm_megatron(args):
|
| 153 |
+
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 3072)
|
| 154 |
+
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 3072 * 4)
|
| 155 |
+
args.decoder_layers = getattr(args, "decoder_layers", 72)
|
| 156 |
+
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 32)
|
| 157 |
+
args.dropout = getattr(args, "dropout", 0.1)
|
| 158 |
+
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
| 159 |
+
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
| 160 |
+
base_lm_architecture(args)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
@register_model_architecture(
|
| 164 |
+
"model_parallel_transformer_lm", "transformer_lm_megatron_11b"
|
| 165 |
+
)
|
| 166 |
+
def transformer_lm_megatron_11b(args):
|
| 167 |
+
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 3072)
|
| 168 |
+
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 3072 * 6)
|
| 169 |
+
args.decoder_layers = getattr(args, "decoder_layers", 72)
|
| 170 |
+
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 32)
|
| 171 |
+
args.dropout = getattr(args, "dropout", 0.1)
|
| 172 |
+
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
| 173 |
+
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
| 174 |
+
base_lm_architecture(args)
|
avh-align_core/fairseq/model_parallel/modules/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""isort:skip_file"""
|
| 6 |
+
|
| 7 |
+
from .multihead_attention import ModelParallelMultiheadAttention
|
| 8 |
+
from .transformer_layer import (
|
| 9 |
+
ModelParallelTransformerEncoderLayer,
|
| 10 |
+
ModelParallelTransformerDecoderLayer,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
"ModelParallelMultiheadAttention",
|
| 15 |
+
"ModelParallelTransformerEncoderLayer",
|
| 16 |
+
"ModelParallelTransformerDecoderLayer",
|
| 17 |
+
]
|
avh-align_core/fairseq/model_parallel/modules/multihead_attention.py
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from typing import Dict, Optional, Tuple
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from fairseq import utils
|
| 11 |
+
from fairseq.incremental_decoding_utils import with_incremental_state
|
| 12 |
+
from fairseq.modules.fairseq_dropout import FairseqDropout
|
| 13 |
+
from torch import Tensor, nn
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
from fairseq.model_parallel.megatron.mpu import (
|
| 18 |
+
get_cuda_rng_tracker,
|
| 19 |
+
get_model_parallel_world_size,
|
| 20 |
+
ColumnParallelLinear,
|
| 21 |
+
RowParallelLinear,
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
has_megatron_submodule = True
|
| 25 |
+
except (ImportError, ModuleNotFoundError):
|
| 26 |
+
has_megatron_submodule = False
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@with_incremental_state
|
| 30 |
+
class ModelParallelMultiheadAttention(nn.Module):
|
| 31 |
+
"""Model parallel Multi-headed attention.
|
| 32 |
+
This performs the Multi-headed attention over multiple gpus.
|
| 33 |
+
|
| 34 |
+
See "Megatron-LM: https://arxiv.org/pdf/1909.08053.pdf" for more details.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(
|
| 38 |
+
self,
|
| 39 |
+
embed_dim,
|
| 40 |
+
num_heads,
|
| 41 |
+
kdim=None,
|
| 42 |
+
vdim=None,
|
| 43 |
+
dropout=0.0,
|
| 44 |
+
bias=True,
|
| 45 |
+
self_attention=False,
|
| 46 |
+
encoder_decoder_attention=False,
|
| 47 |
+
):
|
| 48 |
+
super().__init__()
|
| 49 |
+
if not has_megatron_submodule:
|
| 50 |
+
raise ImportError(
|
| 51 |
+
"\n\nPlease install the megatron submodule:"
|
| 52 |
+
"\n\n git submodule update --init "
|
| 53 |
+
"fairseq/model_parallel/megatron"
|
| 54 |
+
)
|
| 55 |
+
self.embed_dim = embed_dim
|
| 56 |
+
self.kdim = kdim if kdim is not None else embed_dim
|
| 57 |
+
self.vdim = vdim if vdim is not None else embed_dim
|
| 58 |
+
self.qkv_same_dim = self.kdim == embed_dim and self.vdim == embed_dim
|
| 59 |
+
|
| 60 |
+
self.model_parallel_size = get_model_parallel_world_size()
|
| 61 |
+
|
| 62 |
+
self.num_heads_partition = num_heads // self.model_parallel_size
|
| 63 |
+
assert (
|
| 64 |
+
self.num_heads_partition * self.model_parallel_size == num_heads
|
| 65 |
+
), "Number of heads must be divisible by model parallel size"
|
| 66 |
+
|
| 67 |
+
self.dropout_module = FairseqDropout(
|
| 68 |
+
dropout, module_name=self.__class__.__name__
|
| 69 |
+
)
|
| 70 |
+
self.head_dim = embed_dim // num_heads
|
| 71 |
+
assert (
|
| 72 |
+
self.head_dim * num_heads == self.embed_dim
|
| 73 |
+
), "embed_dim must be divisible by num_heads"
|
| 74 |
+
self.scaling = self.head_dim ** -0.5
|
| 75 |
+
|
| 76 |
+
self.self_attention = self_attention
|
| 77 |
+
self.encoder_decoder_attention = encoder_decoder_attention
|
| 78 |
+
|
| 79 |
+
assert (
|
| 80 |
+
not self.self_attention or self.qkv_same_dim
|
| 81 |
+
), "Self-attention requires query, key and value to be of the same size"
|
| 82 |
+
|
| 83 |
+
self.k_proj = ColumnParallelLinear(
|
| 84 |
+
self.kdim, embed_dim, bias=bias, gather_output=False
|
| 85 |
+
)
|
| 86 |
+
self.v_proj = ColumnParallelLinear(
|
| 87 |
+
self.vdim, embed_dim, bias=bias, gather_output=False
|
| 88 |
+
)
|
| 89 |
+
self.q_proj = ColumnParallelLinear(
|
| 90 |
+
embed_dim, embed_dim, bias=bias, gather_output=False
|
| 91 |
+
)
|
| 92 |
+
self.out_proj = RowParallelLinear(
|
| 93 |
+
embed_dim, embed_dim, bias=bias, input_is_parallel=True
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
def forward(
|
| 97 |
+
self,
|
| 98 |
+
query,
|
| 99 |
+
key: Optional[Tensor],
|
| 100 |
+
value: Optional[Tensor],
|
| 101 |
+
key_padding_mask: Optional[Tensor] = None,
|
| 102 |
+
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
| 103 |
+
static_kv: bool = False,
|
| 104 |
+
attn_mask: Optional[Tensor] = None,
|
| 105 |
+
**unused_kwargs,
|
| 106 |
+
) -> Tuple[Tensor, Optional[Tensor]]:
|
| 107 |
+
"""Input shape: Time x Batch x Channel
|
| 108 |
+
|
| 109 |
+
Args:
|
| 110 |
+
key_padding_mask (ByteTensor, optional): mask to exclude
|
| 111 |
+
keys that are pads, of shape `(batch, src_len)`, where
|
| 112 |
+
padding elements are indicated by 1s.
|
| 113 |
+
attn_mask (ByteTensor, optional): typically used to
|
| 114 |
+
implement causal attention, where the mask prevents the
|
| 115 |
+
attention from looking forward in time (default: None).
|
| 116 |
+
"""
|
| 117 |
+
tgt_len, bsz, embed_dim = query.size()
|
| 118 |
+
assert embed_dim == self.embed_dim
|
| 119 |
+
assert list(query.size()) == [tgt_len, bsz, embed_dim]
|
| 120 |
+
|
| 121 |
+
is_tpu = query.device.type == "xla"
|
| 122 |
+
|
| 123 |
+
if incremental_state is not None:
|
| 124 |
+
saved_state = self._get_input_buffer(incremental_state)
|
| 125 |
+
if saved_state is not None and "prev_key" in saved_state:
|
| 126 |
+
# previous time steps are cached - no need to recompute
|
| 127 |
+
# key and value if they are static
|
| 128 |
+
if static_kv:
|
| 129 |
+
assert self.encoder_decoder_attention and not self.self_attention
|
| 130 |
+
key = value = None
|
| 131 |
+
else:
|
| 132 |
+
saved_state = None
|
| 133 |
+
|
| 134 |
+
if self.self_attention:
|
| 135 |
+
q = self.q_proj(query)
|
| 136 |
+
k = self.k_proj(query)
|
| 137 |
+
v = self.v_proj(query)
|
| 138 |
+
elif self.encoder_decoder_attention:
|
| 139 |
+
# encoder-decoder attention
|
| 140 |
+
q = self.q_proj(query)
|
| 141 |
+
if key is None:
|
| 142 |
+
assert value is None
|
| 143 |
+
k = v = None
|
| 144 |
+
else:
|
| 145 |
+
k = self.k_proj(key)
|
| 146 |
+
v = self.v_proj(key)
|
| 147 |
+
|
| 148 |
+
else:
|
| 149 |
+
assert key is not None and value is not None
|
| 150 |
+
q = self.q_proj(query)
|
| 151 |
+
k = self.k_proj(key)
|
| 152 |
+
v = self.v_proj(value)
|
| 153 |
+
q *= self.scaling
|
| 154 |
+
|
| 155 |
+
q = (
|
| 156 |
+
q.contiguous()
|
| 157 |
+
.view(tgt_len, bsz * self.num_heads_partition, self.head_dim)
|
| 158 |
+
.transpose(0, 1)
|
| 159 |
+
)
|
| 160 |
+
if k is not None:
|
| 161 |
+
k = (
|
| 162 |
+
k.contiguous()
|
| 163 |
+
.view(-1, bsz * self.num_heads_partition, self.head_dim)
|
| 164 |
+
.transpose(0, 1)
|
| 165 |
+
)
|
| 166 |
+
if v is not None:
|
| 167 |
+
v = (
|
| 168 |
+
v.contiguous()
|
| 169 |
+
.view(-1, bsz * self.num_heads_partition, self.head_dim)
|
| 170 |
+
.transpose(0, 1)
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
if saved_state is not None:
|
| 174 |
+
# saved states are stored with shape (bsz, num_heads_partition, seq_len, head_dim)
|
| 175 |
+
if "prev_key" in saved_state:
|
| 176 |
+
_prev_key = saved_state["prev_key"]
|
| 177 |
+
assert _prev_key is not None
|
| 178 |
+
prev_key = _prev_key.view(
|
| 179 |
+
bsz * self.num_heads_partition, -1, self.head_dim
|
| 180 |
+
)
|
| 181 |
+
if static_kv:
|
| 182 |
+
k = prev_key
|
| 183 |
+
else:
|
| 184 |
+
assert k is not None
|
| 185 |
+
k = torch.cat([prev_key, k], dim=1)
|
| 186 |
+
if "prev_value" in saved_state:
|
| 187 |
+
_prev_value = saved_state["prev_value"]
|
| 188 |
+
assert _prev_value is not None
|
| 189 |
+
prev_value = _prev_value.view(
|
| 190 |
+
bsz * self.num_heads_partition, -1, self.head_dim
|
| 191 |
+
)
|
| 192 |
+
if static_kv:
|
| 193 |
+
v = prev_value
|
| 194 |
+
else:
|
| 195 |
+
assert v is not None
|
| 196 |
+
v = torch.cat([prev_value, v], dim=1)
|
| 197 |
+
prev_key_padding_mask: Optional[Tensor] = None
|
| 198 |
+
if "prev_key_padding_mask" in saved_state:
|
| 199 |
+
prev_key_padding_mask = saved_state["prev_key_padding_mask"]
|
| 200 |
+
assert k is not None and v is not None
|
| 201 |
+
key_padding_mask = (
|
| 202 |
+
ModelParallelMultiheadAttention._append_prev_key_padding_mask(
|
| 203 |
+
key_padding_mask=key_padding_mask,
|
| 204 |
+
prev_key_padding_mask=prev_key_padding_mask,
|
| 205 |
+
batch_size=bsz,
|
| 206 |
+
src_len=k.size(1),
|
| 207 |
+
static_kv=static_kv,
|
| 208 |
+
)
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
saved_state["prev_key"] = k.view(
|
| 212 |
+
bsz, self.num_heads_partition, -1, self.head_dim
|
| 213 |
+
)
|
| 214 |
+
saved_state["prev_value"] = v.view(
|
| 215 |
+
bsz, self.num_heads_partition, -1, self.head_dim
|
| 216 |
+
)
|
| 217 |
+
saved_state["prev_key_padding_mask"] = key_padding_mask
|
| 218 |
+
# In this branch incremental_state is never None
|
| 219 |
+
assert incremental_state is not None
|
| 220 |
+
incremental_state = self._set_input_buffer(incremental_state, saved_state)
|
| 221 |
+
assert k is not None
|
| 222 |
+
src_len = k.size(1)
|
| 223 |
+
|
| 224 |
+
# This is part of a workaround to get around fork/join parallelism
|
| 225 |
+
# not supporting Optional types.
|
| 226 |
+
if key_padding_mask is not None and key_padding_mask.dim() == 0:
|
| 227 |
+
key_padding_mask = None
|
| 228 |
+
|
| 229 |
+
if key_padding_mask is not None:
|
| 230 |
+
assert key_padding_mask.size(0) == bsz
|
| 231 |
+
assert key_padding_mask.size(1) == src_len
|
| 232 |
+
|
| 233 |
+
attn_weights = torch.bmm(q, k.transpose(1, 2))
|
| 234 |
+
|
| 235 |
+
assert list(attn_weights.size()) == [
|
| 236 |
+
bsz * self.num_heads_partition,
|
| 237 |
+
tgt_len,
|
| 238 |
+
src_len,
|
| 239 |
+
]
|
| 240 |
+
|
| 241 |
+
if attn_mask is not None:
|
| 242 |
+
attn_mask = attn_mask.unsqueeze(0)
|
| 243 |
+
attn_weights += attn_mask
|
| 244 |
+
|
| 245 |
+
if key_padding_mask is not None:
|
| 246 |
+
# don't attend to padding symbols
|
| 247 |
+
attn_weights = attn_weights.view(
|
| 248 |
+
bsz, self.num_heads_partition, tgt_len, src_len
|
| 249 |
+
)
|
| 250 |
+
if not is_tpu:
|
| 251 |
+
attn_weights = attn_weights.masked_fill(
|
| 252 |
+
key_padding_mask.unsqueeze(1).unsqueeze(2).to(torch.bool),
|
| 253 |
+
float("-inf"),
|
| 254 |
+
)
|
| 255 |
+
else:
|
| 256 |
+
attn_weights = attn_weights.transpose(0, 2)
|
| 257 |
+
attn_weights = attn_weights.masked_fill(key_padding_mask, float("-inf"))
|
| 258 |
+
attn_weights = attn_weights.transpose(0, 2)
|
| 259 |
+
attn_weights = attn_weights.view(
|
| 260 |
+
bsz * self.num_heads_partition, tgt_len, src_len
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
attn_weights_float = utils.softmax(attn_weights, dim=-1)
|
| 264 |
+
attn_weights = attn_weights_float.type_as(attn_weights)
|
| 265 |
+
|
| 266 |
+
with get_cuda_rng_tracker().fork():
|
| 267 |
+
attn_probs = self.dropout_module(attn_weights)
|
| 268 |
+
|
| 269 |
+
assert v is not None
|
| 270 |
+
attn = torch.bmm(attn_probs, v)
|
| 271 |
+
assert list(attn.size()) == [
|
| 272 |
+
bsz * self.num_heads_partition,
|
| 273 |
+
tgt_len,
|
| 274 |
+
self.head_dim,
|
| 275 |
+
]
|
| 276 |
+
embed_dim_partition = embed_dim // self.model_parallel_size
|
| 277 |
+
attn = attn.transpose(0, 1).contiguous().view(tgt_len, bsz, embed_dim_partition)
|
| 278 |
+
attn = self.out_proj(attn)
|
| 279 |
+
# return attn_weights None to keep the return type same as single gpu multihead attention
|
| 280 |
+
# This will be deprecated.
|
| 281 |
+
attn_weights: Optional[Tensor] = None
|
| 282 |
+
|
| 283 |
+
return attn, attn_weights
|
| 284 |
+
|
| 285 |
+
@staticmethod
|
| 286 |
+
def _append_prev_key_padding_mask(
|
| 287 |
+
key_padding_mask: Optional[Tensor],
|
| 288 |
+
prev_key_padding_mask: Optional[Tensor],
|
| 289 |
+
batch_size: int,
|
| 290 |
+
src_len: int,
|
| 291 |
+
static_kv: bool,
|
| 292 |
+
) -> Optional[Tensor]:
|
| 293 |
+
# saved key padding masks have shape (bsz, seq_len)
|
| 294 |
+
if prev_key_padding_mask is not None and static_kv:
|
| 295 |
+
new_key_padding_mask = prev_key_padding_mask
|
| 296 |
+
elif prev_key_padding_mask is not None and key_padding_mask is not None:
|
| 297 |
+
new_key_padding_mask = torch.cat(
|
| 298 |
+
[prev_key_padding_mask.float(), key_padding_mask.float()], dim=1
|
| 299 |
+
)
|
| 300 |
+
# During incremental decoding, as the padding token enters and
|
| 301 |
+
# leaves the frame, there will be a time when prev or current
|
| 302 |
+
# is None
|
| 303 |
+
elif prev_key_padding_mask is not None:
|
| 304 |
+
|
| 305 |
+
filler = torch.zeros(batch_size, src_len - prev_key_padding_mask.size(1))
|
| 306 |
+
if prev_key_padding_mask.is_cuda:
|
| 307 |
+
filler = filler.cuda()
|
| 308 |
+
new_key_padding_mask = torch.cat(
|
| 309 |
+
[prev_key_padding_mask.float(), filler.float()], dim=1
|
| 310 |
+
)
|
| 311 |
+
elif key_padding_mask is not None:
|
| 312 |
+
filler = torch.zeros(batch_size, src_len - key_padding_mask.size(1))
|
| 313 |
+
if key_padding_mask.is_cuda:
|
| 314 |
+
filler = filler.cuda()
|
| 315 |
+
new_key_padding_mask = torch.cat(
|
| 316 |
+
[filler.float(), key_padding_mask.float()], dim=1
|
| 317 |
+
)
|
| 318 |
+
else:
|
| 319 |
+
new_key_padding_mask = prev_key_padding_mask
|
| 320 |
+
return new_key_padding_mask
|
| 321 |
+
|
| 322 |
+
def reorder_incremental_state(
|
| 323 |
+
self, incremental_state: Dict[str, Dict[str, Optional[Tensor]]], new_order
|
| 324 |
+
):
|
| 325 |
+
"""Reorder buffered internal state (for incremental generation)."""
|
| 326 |
+
input_buffer = self._get_input_buffer(incremental_state)
|
| 327 |
+
if input_buffer is not None:
|
| 328 |
+
for k in input_buffer.keys():
|
| 329 |
+
if input_buffer[k] is not None:
|
| 330 |
+
input_buffer[k] = input_buffer[k].index_select(0, new_order)
|
| 331 |
+
incremental_state = self._set_input_buffer(incremental_state, input_buffer)
|
| 332 |
+
return incremental_state
|
| 333 |
+
|
| 334 |
+
def _get_input_buffer(
|
| 335 |
+
self, incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]]
|
| 336 |
+
) -> Dict[str, Optional[Tensor]]:
|
| 337 |
+
result = self.get_incremental_state(incremental_state, "attn_state")
|
| 338 |
+
if result is not None:
|
| 339 |
+
return result
|
| 340 |
+
else:
|
| 341 |
+
empty_result: Dict[str, Optional[Tensor]] = {}
|
| 342 |
+
return empty_result
|
| 343 |
+
|
| 344 |
+
def _set_input_buffer(
|
| 345 |
+
self,
|
| 346 |
+
incremental_state: Dict[str, Dict[str, Optional[Tensor]]],
|
| 347 |
+
buffer: Dict[str, Optional[Tensor]],
|
| 348 |
+
):
|
| 349 |
+
return self.set_incremental_state(incremental_state, "attn_state", buffer)
|
avh-align_core/fairseq/model_parallel/modules/transformer_layer.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from fairseq.model_parallel.modules import ModelParallelMultiheadAttention
|
| 7 |
+
from fairseq.modules import TransformerDecoderLayer, TransformerEncoderLayer
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
from fairseq.model_parallel.megatron.mpu import (
|
| 12 |
+
ColumnParallelLinear,
|
| 13 |
+
RowParallelLinear,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
has_megatron_submodule = True
|
| 17 |
+
except (ImportError, ModuleNotFoundError):
|
| 18 |
+
has_megatron_submodule = False
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ModelParallelTransformerEncoderLayer(TransformerEncoderLayer):
|
| 22 |
+
"""Encoder layer block over multiple gpus.
|
| 23 |
+
|
| 24 |
+
See "Megatron-LM: https://arxiv.org/pdf/1909.08053.pdf" for more details.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
def build_fc1(self, input_dim, output_dim, q_noise, qn_block_size):
|
| 28 |
+
if q_noise > 0:
|
| 29 |
+
raise NotImplementedError
|
| 30 |
+
return ColumnParallelLinear(input_dim, output_dim, gather_output=False)
|
| 31 |
+
|
| 32 |
+
def build_fc2(self, input_dim, output_dim, q_noise, qn_block_size):
|
| 33 |
+
if q_noise > 0:
|
| 34 |
+
raise NotImplementedError
|
| 35 |
+
return RowParallelLinear(input_dim, output_dim, input_is_parallel=True)
|
| 36 |
+
|
| 37 |
+
def build_self_attention(self, embed_dim, args, **unused_kwargs):
|
| 38 |
+
return ModelParallelMultiheadAttention(
|
| 39 |
+
embed_dim,
|
| 40 |
+
args.encoder_attention_heads,
|
| 41 |
+
dropout=args.attention_dropout,
|
| 42 |
+
self_attention=True,
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class ModelParallelTransformerDecoderLayer(TransformerDecoderLayer):
|
| 47 |
+
"""Decoder layer block.
|
| 48 |
+
|
| 49 |
+
See "Megatron-LM: https://arxiv.org/pdf/1909.08053.pdf" for more details.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
def build_fc1(self, input_dim, output_dim, q_noise, qn_block_size):
|
| 53 |
+
if q_noise > 0:
|
| 54 |
+
raise NotImplementedError
|
| 55 |
+
return ColumnParallelLinear(input_dim, output_dim, gather_output=False)
|
| 56 |
+
|
| 57 |
+
def build_fc2(self, input_dim, output_dim, q_noise, qn_block_size):
|
| 58 |
+
if q_noise > 0:
|
| 59 |
+
raise NotImplementedError
|
| 60 |
+
return RowParallelLinear(input_dim, output_dim, input_is_parallel=True)
|
| 61 |
+
|
| 62 |
+
def build_self_attention(self, embed_dim, args, **unused_kwargs):
|
| 63 |
+
return ModelParallelMultiheadAttention(
|
| 64 |
+
embed_dim=embed_dim,
|
| 65 |
+
num_heads=args.decoder_attention_heads,
|
| 66 |
+
dropout=args.attention_dropout,
|
| 67 |
+
self_attention=not getattr(args, "cross_self_attention", False),
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
def build_encoder_attention(self, embed_dim, args, **unused_kwargs):
|
| 71 |
+
return ModelParallelMultiheadAttention(
|
| 72 |
+
embed_dim=embed_dim,
|
| 73 |
+
num_heads=args.decoder_attention_heads,
|
| 74 |
+
kdim=getattr(args, "encoder_embed_dim", None),
|
| 75 |
+
vdim=getattr(args, "encoder_embed_dim", None),
|
| 76 |
+
dropout=args.attention_dropout,
|
| 77 |
+
encoder_decoder_attention=True,
|
| 78 |
+
)
|
avh-align_core/fairseq/models/__init__.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""isort:skip_file"""
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import importlib
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
from fairseq.dataclass import FairseqDataclass
|
| 12 |
+
from fairseq.dataclass.utils import merge_with_parent, populate_dataclass
|
| 13 |
+
from hydra.core.config_store import ConfigStore
|
| 14 |
+
|
| 15 |
+
from .composite_encoder import CompositeEncoder
|
| 16 |
+
from .distributed_fairseq_model import DistributedFairseqModel
|
| 17 |
+
from .fairseq_decoder import FairseqDecoder
|
| 18 |
+
from .fairseq_encoder import FairseqEncoder
|
| 19 |
+
from .fairseq_incremental_decoder import FairseqIncrementalDecoder
|
| 20 |
+
from .fairseq_model import (
|
| 21 |
+
BaseFairseqModel,
|
| 22 |
+
FairseqEncoderDecoderModel,
|
| 23 |
+
FairseqEncoderModel,
|
| 24 |
+
FairseqLanguageModel,
|
| 25 |
+
FairseqModel,
|
| 26 |
+
FairseqMultiModel,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
MODEL_REGISTRY = {}
|
| 31 |
+
MODEL_DATACLASS_REGISTRY = {}
|
| 32 |
+
ARCH_MODEL_REGISTRY = {}
|
| 33 |
+
ARCH_MODEL_NAME_REGISTRY = {}
|
| 34 |
+
ARCH_MODEL_INV_REGISTRY = {}
|
| 35 |
+
ARCH_CONFIG_REGISTRY = {}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
__all__ = [
|
| 39 |
+
"BaseFairseqModel",
|
| 40 |
+
"CompositeEncoder",
|
| 41 |
+
"DistributedFairseqModel",
|
| 42 |
+
"FairseqDecoder",
|
| 43 |
+
"FairseqEncoder",
|
| 44 |
+
"FairseqEncoderDecoderModel",
|
| 45 |
+
"FairseqEncoderModel",
|
| 46 |
+
"FairseqIncrementalDecoder",
|
| 47 |
+
"FairseqLanguageModel",
|
| 48 |
+
"FairseqModel",
|
| 49 |
+
"FairseqMultiModel",
|
| 50 |
+
]
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def build_model(cfg: FairseqDataclass, task):
|
| 54 |
+
|
| 55 |
+
model = None
|
| 56 |
+
model_type = getattr(cfg, "_name", None) or getattr(cfg, "arch", None)
|
| 57 |
+
|
| 58 |
+
if not model_type and len(cfg) == 1:
|
| 59 |
+
# this is hit if config object is nested in directory that is named after model type
|
| 60 |
+
|
| 61 |
+
model_type = next(iter(cfg))
|
| 62 |
+
if model_type in MODEL_DATACLASS_REGISTRY:
|
| 63 |
+
cfg = cfg[model_type]
|
| 64 |
+
else:
|
| 65 |
+
raise Exception(
|
| 66 |
+
"Could not infer model type from directory. Please add _name field to indicate model type. "
|
| 67 |
+
"Available models: "
|
| 68 |
+
+ str(MODEL_DATACLASS_REGISTRY.keys())
|
| 69 |
+
+ " Requested model type: "
|
| 70 |
+
+ model_type
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
if model_type in ARCH_MODEL_REGISTRY:
|
| 74 |
+
# case 1: legacy models
|
| 75 |
+
model = ARCH_MODEL_REGISTRY[model_type]
|
| 76 |
+
elif model_type in MODEL_DATACLASS_REGISTRY:
|
| 77 |
+
# case 2: config-driven models
|
| 78 |
+
model = MODEL_REGISTRY[model_type]
|
| 79 |
+
|
| 80 |
+
if model_type in MODEL_DATACLASS_REGISTRY:
|
| 81 |
+
# set defaults from dataclass. note that arch name and model name can be the same
|
| 82 |
+
dc = MODEL_DATACLASS_REGISTRY[model_type]
|
| 83 |
+
if isinstance(cfg, argparse.Namespace):
|
| 84 |
+
cfg = populate_dataclass(dc(), cfg)
|
| 85 |
+
else:
|
| 86 |
+
cfg = merge_with_parent(dc(), cfg)
|
| 87 |
+
|
| 88 |
+
assert model is not None, (
|
| 89 |
+
f"Could not infer model type from {cfg}. "
|
| 90 |
+
f"Available models: "
|
| 91 |
+
+ str(MODEL_DATACLASS_REGISTRY.keys())
|
| 92 |
+
+ " Requested model type: "
|
| 93 |
+
+ model_type
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
return model.build_model(cfg, task)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def register_model(name, dataclass=None):
|
| 100 |
+
"""
|
| 101 |
+
New model types can be added to fairseq with the :func:`register_model`
|
| 102 |
+
function decorator.
|
| 103 |
+
|
| 104 |
+
For example::
|
| 105 |
+
|
| 106 |
+
@register_model('lstm')
|
| 107 |
+
class LSTM(FairseqEncoderDecoderModel):
|
| 108 |
+
(...)
|
| 109 |
+
|
| 110 |
+
.. note:: All models must implement the :class:`BaseFairseqModel` interface.
|
| 111 |
+
Typically you will extend :class:`FairseqEncoderDecoderModel` for
|
| 112 |
+
sequence-to-sequence tasks or :class:`FairseqLanguageModel` for
|
| 113 |
+
language modeling tasks.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
name (str): the name of the model
|
| 117 |
+
"""
|
| 118 |
+
|
| 119 |
+
def register_model_cls(cls):
|
| 120 |
+
if name in MODEL_REGISTRY:
|
| 121 |
+
raise ValueError("Cannot register duplicate model ({})".format(name))
|
| 122 |
+
if not issubclass(cls, BaseFairseqModel):
|
| 123 |
+
raise ValueError(
|
| 124 |
+
"Model ({}: {}) must extend BaseFairseqModel".format(name, cls.__name__)
|
| 125 |
+
)
|
| 126 |
+
MODEL_REGISTRY[name] = cls
|
| 127 |
+
if dataclass is not None and not issubclass(dataclass, FairseqDataclass):
|
| 128 |
+
raise ValueError(
|
| 129 |
+
"Dataclass {} must extend FairseqDataclass".format(dataclass)
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
cls.__dataclass = dataclass
|
| 133 |
+
if dataclass is not None:
|
| 134 |
+
MODEL_DATACLASS_REGISTRY[name] = dataclass
|
| 135 |
+
|
| 136 |
+
cs = ConfigStore.instance()
|
| 137 |
+
node = dataclass()
|
| 138 |
+
node._name = name
|
| 139 |
+
cs.store(name=name, group="model", node=node, provider="fairseq")
|
| 140 |
+
|
| 141 |
+
@register_model_architecture(name, name)
|
| 142 |
+
def noop(_):
|
| 143 |
+
pass
|
| 144 |
+
|
| 145 |
+
return cls
|
| 146 |
+
|
| 147 |
+
return register_model_cls
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def register_model_architecture(model_name, arch_name):
|
| 151 |
+
"""
|
| 152 |
+
New model architectures can be added to fairseq with the
|
| 153 |
+
:func:`register_model_architecture` function decorator. After registration,
|
| 154 |
+
model architectures can be selected with the ``--arch`` command-line
|
| 155 |
+
argument.
|
| 156 |
+
|
| 157 |
+
For example::
|
| 158 |
+
|
| 159 |
+
@register_model_architecture('lstm', 'lstm_luong_wmt_en_de')
|
| 160 |
+
def lstm_luong_wmt_en_de(cfg):
|
| 161 |
+
args.encoder_embed_dim = getattr(cfg.model, 'encoder_embed_dim', 1000)
|
| 162 |
+
(...)
|
| 163 |
+
|
| 164 |
+
The decorated function should take a single argument *cfg*, which is a
|
| 165 |
+
:class:`omegaconf.DictConfig`. The decorated function should modify these
|
| 166 |
+
arguments in-place to match the desired architecture.
|
| 167 |
+
|
| 168 |
+
Args:
|
| 169 |
+
model_name (str): the name of the Model (Model must already be
|
| 170 |
+
registered)
|
| 171 |
+
arch_name (str): the name of the model architecture (``--arch``)
|
| 172 |
+
"""
|
| 173 |
+
|
| 174 |
+
def register_model_arch_fn(fn):
|
| 175 |
+
if model_name not in MODEL_REGISTRY:
|
| 176 |
+
raise ValueError(
|
| 177 |
+
"Cannot register model architecture for unknown model type ({})".format(
|
| 178 |
+
model_name
|
| 179 |
+
)
|
| 180 |
+
)
|
| 181 |
+
if arch_name in ARCH_MODEL_REGISTRY:
|
| 182 |
+
raise ValueError(
|
| 183 |
+
"Cannot register duplicate model architecture ({})".format(arch_name)
|
| 184 |
+
)
|
| 185 |
+
if not callable(fn):
|
| 186 |
+
raise ValueError(
|
| 187 |
+
"Model architecture must be callable ({})".format(arch_name)
|
| 188 |
+
)
|
| 189 |
+
ARCH_MODEL_REGISTRY[arch_name] = MODEL_REGISTRY[model_name]
|
| 190 |
+
ARCH_MODEL_NAME_REGISTRY[arch_name] = model_name
|
| 191 |
+
ARCH_MODEL_INV_REGISTRY.setdefault(model_name, []).append(arch_name)
|
| 192 |
+
ARCH_CONFIG_REGISTRY[arch_name] = fn
|
| 193 |
+
return fn
|
| 194 |
+
|
| 195 |
+
return register_model_arch_fn
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def import_models(models_dir, namespace):
|
| 199 |
+
for file in os.listdir(models_dir):
|
| 200 |
+
path = os.path.join(models_dir, file)
|
| 201 |
+
if (
|
| 202 |
+
not file.startswith("_")
|
| 203 |
+
and not file.startswith(".")
|
| 204 |
+
and (file.endswith(".py") or os.path.isdir(path))
|
| 205 |
+
):
|
| 206 |
+
model_name = file[: file.find(".py")] if file.endswith(".py") else file
|
| 207 |
+
importlib.import_module(namespace + "." + model_name)
|
| 208 |
+
|
| 209 |
+
# extra `model_parser` for sphinx
|
| 210 |
+
if model_name in MODEL_REGISTRY:
|
| 211 |
+
parser = argparse.ArgumentParser(add_help=False)
|
| 212 |
+
group_archs = parser.add_argument_group("Named architectures")
|
| 213 |
+
group_archs.add_argument(
|
| 214 |
+
"--arch", choices=ARCH_MODEL_INV_REGISTRY[model_name]
|
| 215 |
+
)
|
| 216 |
+
group_args = parser.add_argument_group(
|
| 217 |
+
"Additional command-line arguments"
|
| 218 |
+
)
|
| 219 |
+
MODEL_REGISTRY[model_name].add_args(group_args)
|
| 220 |
+
globals()[model_name + "_parser"] = parser
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
# automatically import any Python files in the models/ directory
|
| 224 |
+
models_dir = os.path.dirname(__file__)
|
| 225 |
+
import_models(models_dir, "fairseq.models")
|
avh-align_core/fairseq/models/bart/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .hub_interface import * # noqa
|
| 7 |
+
from .model import * # noqa
|
avh-align_core/fairseq/models/bart/hub_interface.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import copy
|
| 7 |
+
import logging
|
| 8 |
+
from typing import Dict, List
|
| 9 |
+
|
| 10 |
+
import numpy as np
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
from fairseq import utils
|
| 15 |
+
from fairseq.data import encoders
|
| 16 |
+
from fairseq.hub_utils import GeneratorHubInterface
|
| 17 |
+
from omegaconf import open_dict
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class BARTHubInterface(GeneratorHubInterface):
|
| 24 |
+
"""A simple PyTorch Hub interface to BART.
|
| 25 |
+
|
| 26 |
+
Usage: https://github.com/pytorch/fairseq/tree/master/examples/bart
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
def __init__(self, cfg, task, model):
|
| 30 |
+
super().__init__(cfg, task, [model])
|
| 31 |
+
self.model = self.models[0]
|
| 32 |
+
|
| 33 |
+
def encode(
|
| 34 |
+
self, sentence: str, *addl_sentences, no_separator=True
|
| 35 |
+
) -> torch.LongTensor:
|
| 36 |
+
"""
|
| 37 |
+
BPE-encode a sentence (or multiple sentences).
|
| 38 |
+
|
| 39 |
+
Every sequence begins with a beginning-of-sentence (`<s>`) symbol.
|
| 40 |
+
Every sentence ends with an end-of-sentence (`</s>`).
|
| 41 |
+
|
| 42 |
+
Example (single sentence): `<s> a b c </s>`
|
| 43 |
+
Example (sentence pair): `<s> d e f </s> 1 2 3 </s>`
|
| 44 |
+
|
| 45 |
+
The BPE encoding follows GPT-2. One subtle detail is that the GPT-2 BPE
|
| 46 |
+
requires leading spaces. For example::
|
| 47 |
+
|
| 48 |
+
>>> bart.encode('Hello world').tolist()
|
| 49 |
+
[0, 31414, 232, 2]
|
| 50 |
+
>>> bart.encode(' world').tolist()
|
| 51 |
+
[0, 232, 2]
|
| 52 |
+
>>> bart.encode('world').tolist()
|
| 53 |
+
[0, 8331, 2]
|
| 54 |
+
"""
|
| 55 |
+
tokens = self.bpe.encode(sentence)
|
| 56 |
+
if len(tokens.split(" ")) > min(self.max_positions) - 2:
|
| 57 |
+
tokens = " ".join(tokens.split(" ")[: min(self.max_positions) - 2])
|
| 58 |
+
bpe_sentence = "<s> " + tokens + " </s>"
|
| 59 |
+
for s in addl_sentences:
|
| 60 |
+
bpe_sentence += " </s>" if not no_separator else ""
|
| 61 |
+
bpe_sentence += " " + self.bpe.encode(s) + " </s>"
|
| 62 |
+
tokens = self.task.source_dictionary.encode_line(bpe_sentence, append_eos=False)
|
| 63 |
+
return tokens.long()
|
| 64 |
+
|
| 65 |
+
def decode(self, tokens: torch.LongTensor):
|
| 66 |
+
assert tokens.dim() == 1
|
| 67 |
+
tokens = tokens.cpu().numpy()
|
| 68 |
+
if tokens[0] == self.task.source_dictionary.bos():
|
| 69 |
+
tokens = tokens[1:] # remove <s>
|
| 70 |
+
eos_mask = tokens == self.task.source_dictionary.eos()
|
| 71 |
+
doc_mask = eos_mask[1:] & eos_mask[:-1]
|
| 72 |
+
sentences = np.split(tokens, doc_mask.nonzero()[0] + 1)
|
| 73 |
+
sentences = [
|
| 74 |
+
self.bpe.decode(self.task.source_dictionary.string(s)) for s in sentences
|
| 75 |
+
]
|
| 76 |
+
if len(sentences) == 1:
|
| 77 |
+
return sentences[0]
|
| 78 |
+
return sentences
|
| 79 |
+
|
| 80 |
+
def _build_sample(self, src_tokens: List[torch.LongTensor]):
|
| 81 |
+
# assert torch.is_tensor(src_tokens)
|
| 82 |
+
dataset = self.task.build_dataset_for_inference(
|
| 83 |
+
src_tokens,
|
| 84 |
+
[x.numel() for x in src_tokens],
|
| 85 |
+
)
|
| 86 |
+
sample = dataset.collater(dataset)
|
| 87 |
+
sample = utils.apply_to_sample(lambda tensor: tensor.to(self.device), sample)
|
| 88 |
+
return sample
|
| 89 |
+
|
| 90 |
+
def generate(
|
| 91 |
+
self,
|
| 92 |
+
tokenized_sentences: List[torch.LongTensor],
|
| 93 |
+
*args,
|
| 94 |
+
inference_step_args=None,
|
| 95 |
+
skip_invalid_size_inputs=False,
|
| 96 |
+
**kwargs
|
| 97 |
+
) -> List[List[Dict[str, torch.Tensor]]]:
|
| 98 |
+
inference_step_args = inference_step_args or {}
|
| 99 |
+
if "prefix_tokens" in inference_step_args:
|
| 100 |
+
raise NotImplementedError("prefix generation not implemented for BART")
|
| 101 |
+
res = []
|
| 102 |
+
for batch in self._build_batches(tokenized_sentences, skip_invalid_size_inputs):
|
| 103 |
+
src_tokens = batch['net_input']['src_tokens']
|
| 104 |
+
inference_step_args["prefix_tokens"] =src_tokens.new_full(
|
| 105 |
+
(src_tokens.size(0), 1), fill_value=self.task.source_dictionary.bos()
|
| 106 |
+
).to(device=self.device)
|
| 107 |
+
results = super().generate(
|
| 108 |
+
src_tokens,
|
| 109 |
+
*args,
|
| 110 |
+
inference_step_args=inference_step_args,
|
| 111 |
+
skip_invalid_size_inputs=skip_invalid_size_inputs,
|
| 112 |
+
**kwargs
|
| 113 |
+
)
|
| 114 |
+
for id, hypos in zip(batch['id'].tolist(), results):
|
| 115 |
+
res.append((id, hypos))
|
| 116 |
+
res = [hypos for _, hypos in sorted(res, key=lambda x: x[0])]
|
| 117 |
+
return res
|
| 118 |
+
|
| 119 |
+
def extract_features(
|
| 120 |
+
self, tokens: torch.LongTensor, return_all_hiddens: bool = False
|
| 121 |
+
) -> torch.Tensor:
|
| 122 |
+
if tokens.dim() == 1:
|
| 123 |
+
tokens = tokens.unsqueeze(0)
|
| 124 |
+
if tokens.size(-1) > min(self.model.max_positions()):
|
| 125 |
+
raise ValueError(
|
| 126 |
+
"tokens exceeds maximum length: {} > {}".format(
|
| 127 |
+
tokens.size(-1), self.model.max_positions()
|
| 128 |
+
)
|
| 129 |
+
)
|
| 130 |
+
tokens.to(device=self.device),
|
| 131 |
+
prev_output_tokens = tokens.clone()
|
| 132 |
+
|
| 133 |
+
prev_output_tokens[:, 0] = tokens.gather(
|
| 134 |
+
1,
|
| 135 |
+
(tokens.ne(self.task.source_dictionary.pad()).sum(dim=1) - 1).unsqueeze(-1),
|
| 136 |
+
).squeeze()
|
| 137 |
+
|
| 138 |
+
prev_output_tokens[:, 1:] = tokens[:, :-1]
|
| 139 |
+
features, extra = self.model(
|
| 140 |
+
src_tokens=tokens,
|
| 141 |
+
src_lengths=None,
|
| 142 |
+
prev_output_tokens=prev_output_tokens,
|
| 143 |
+
features_only=True,
|
| 144 |
+
return_all_hiddens=return_all_hiddens,
|
| 145 |
+
)
|
| 146 |
+
if return_all_hiddens:
|
| 147 |
+
# convert from T x B x C -> B x T x C
|
| 148 |
+
inner_states = extra["inner_states"]
|
| 149 |
+
return [inner_state.transpose(0, 1) for inner_state in inner_states]
|
| 150 |
+
else:
|
| 151 |
+
return features # just the last layer's features
|
| 152 |
+
|
| 153 |
+
def register_classification_head(
|
| 154 |
+
self, name: str, num_classes: int = None, embedding_size: int = None, **kwargs
|
| 155 |
+
):
|
| 156 |
+
self.model.register_classification_head(
|
| 157 |
+
name, num_classes=num_classes, embedding_size=embedding_size, **kwargs
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
def predict(self, head: str, tokens: torch.LongTensor, return_logits: bool = False):
|
| 161 |
+
if tokens.dim() == 1:
|
| 162 |
+
tokens = tokens.unsqueeze(0)
|
| 163 |
+
features = self.extract_features(tokens.to(device=self.device))
|
| 164 |
+
sentence_representation = features[
|
| 165 |
+
tokens.eq(self.task.source_dictionary.eos()), :
|
| 166 |
+
].view(features.size(0), -1, features.size(-1))[:, -1, :]
|
| 167 |
+
|
| 168 |
+
logits = self.model.classification_heads[head](sentence_representation)
|
| 169 |
+
if return_logits:
|
| 170 |
+
return logits
|
| 171 |
+
return F.log_softmax(logits, dim=-1)
|
| 172 |
+
|
| 173 |
+
def fill_mask(
|
| 174 |
+
self,
|
| 175 |
+
masked_inputs: List[str],
|
| 176 |
+
topk: int = 5,
|
| 177 |
+
match_source_len: bool = True,
|
| 178 |
+
**generate_kwargs
|
| 179 |
+
):
|
| 180 |
+
masked_token = '<mask>'
|
| 181 |
+
batch_tokens = []
|
| 182 |
+
for masked_input in masked_inputs:
|
| 183 |
+
assert masked_token in masked_input, \
|
| 184 |
+
"please add one {} token for the input".format(masked_token)
|
| 185 |
+
|
| 186 |
+
text_spans = masked_input.split(masked_token)
|
| 187 |
+
text_spans_bpe = (' {0} '.format(masked_token)).join(
|
| 188 |
+
[self.bpe.encode(text_span.rstrip()) for text_span in text_spans]
|
| 189 |
+
).strip()
|
| 190 |
+
tokens = self.task.source_dictionary.encode_line(
|
| 191 |
+
'<s> ' + text_spans_bpe + ' </s>',
|
| 192 |
+
append_eos=False,
|
| 193 |
+
add_if_not_exist=False,
|
| 194 |
+
).long()
|
| 195 |
+
batch_tokens.append(tokens)
|
| 196 |
+
|
| 197 |
+
# ensure beam size is at least as big as topk
|
| 198 |
+
generate_kwargs['beam'] = max(
|
| 199 |
+
topk,
|
| 200 |
+
generate_kwargs.get('beam', -1),
|
| 201 |
+
)
|
| 202 |
+
generate_kwargs['match_source_len'] = match_source_len
|
| 203 |
+
batch_hypos = self.generate(batch_tokens, **generate_kwargs)
|
| 204 |
+
|
| 205 |
+
return [
|
| 206 |
+
[(self.decode(hypo['tokens']), hypo['score']) for hypo in hypos[:topk]]
|
| 207 |
+
for hypos in batch_hypos
|
| 208 |
+
]
|
avh-align_core/fairseq/models/bart/model.py
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
"""
|
| 6 |
+
BART: Denoising Sequence-to-Sequence Pre-training for
|
| 7 |
+
Natural Language Generation, Translation, and Comprehension
|
| 8 |
+
"""
|
| 9 |
+
from typing import Optional
|
| 10 |
+
|
| 11 |
+
import logging
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
import torch.nn as nn
|
| 15 |
+
from fairseq import utils
|
| 16 |
+
from fairseq.models import register_model, register_model_architecture
|
| 17 |
+
from fairseq.models.transformer import TransformerModel
|
| 18 |
+
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
| 19 |
+
|
| 20 |
+
from .hub_interface import BARTHubInterface
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@register_model("bart")
|
| 27 |
+
class BARTModel(TransformerModel):
|
| 28 |
+
__jit_unused_properties__ = ["supported_targets"]
|
| 29 |
+
|
| 30 |
+
@classmethod
|
| 31 |
+
def hub_models(cls):
|
| 32 |
+
return {
|
| 33 |
+
"bart.base": "http://dl.fbaipublicfiles.com/fairseq/models/bart.base.tar.gz",
|
| 34 |
+
"bart.large": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.tar.gz",
|
| 35 |
+
"bart.large.mnli": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.mnli.tar.gz",
|
| 36 |
+
"bart.large.cnn": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.cnn.tar.gz",
|
| 37 |
+
"bart.large.xsum": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.xsum.tar.gz",
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
def __init__(self, args, encoder, decoder):
|
| 41 |
+
super().__init__(args, encoder, decoder)
|
| 42 |
+
|
| 43 |
+
# We follow BERT's random weight initialization
|
| 44 |
+
self.apply(init_bert_params)
|
| 45 |
+
|
| 46 |
+
self.classification_heads = nn.ModuleDict()
|
| 47 |
+
if hasattr(self.encoder, "dictionary"):
|
| 48 |
+
self.eos: int = self.encoder.dictionary.eos()
|
| 49 |
+
|
| 50 |
+
@staticmethod
|
| 51 |
+
def add_args(parser):
|
| 52 |
+
super(BARTModel, BARTModel).add_args(parser)
|
| 53 |
+
parser.add_argument(
|
| 54 |
+
"--pooler-dropout",
|
| 55 |
+
type=float,
|
| 56 |
+
metavar="D",
|
| 57 |
+
help="dropout probability in the masked_lm pooler layers",
|
| 58 |
+
)
|
| 59 |
+
parser.add_argument(
|
| 60 |
+
"--pooler-activation-fn",
|
| 61 |
+
choices=utils.get_available_activation_fns(),
|
| 62 |
+
help="activation function to use for pooler layer",
|
| 63 |
+
)
|
| 64 |
+
parser.add_argument(
|
| 65 |
+
"--spectral-norm-classification-head",
|
| 66 |
+
action="store_true",
|
| 67 |
+
help="Apply spectral normalization on the classification head",
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
@property
|
| 71 |
+
def supported_targets(self):
|
| 72 |
+
return {"self"}
|
| 73 |
+
|
| 74 |
+
def forward(
|
| 75 |
+
self,
|
| 76 |
+
src_tokens,
|
| 77 |
+
src_lengths,
|
| 78 |
+
prev_output_tokens,
|
| 79 |
+
features_only: bool = False,
|
| 80 |
+
classification_head_name: Optional[str] = None,
|
| 81 |
+
token_embeddings: Optional[torch.Tensor] = None,
|
| 82 |
+
return_all_hiddens: bool = True,
|
| 83 |
+
alignment_layer: Optional[int] = None,
|
| 84 |
+
alignment_heads: Optional[int] = None,
|
| 85 |
+
):
|
| 86 |
+
if classification_head_name is not None:
|
| 87 |
+
features_only = True
|
| 88 |
+
|
| 89 |
+
encoder_out = self.encoder(
|
| 90 |
+
src_tokens,
|
| 91 |
+
src_lengths=src_lengths,
|
| 92 |
+
token_embeddings=token_embeddings,
|
| 93 |
+
return_all_hiddens=return_all_hiddens
|
| 94 |
+
)
|
| 95 |
+
x, extra = self.decoder(
|
| 96 |
+
prev_output_tokens,
|
| 97 |
+
encoder_out=encoder_out,
|
| 98 |
+
features_only=features_only,
|
| 99 |
+
alignment_layer=alignment_layer,
|
| 100 |
+
alignment_heads=alignment_heads,
|
| 101 |
+
src_lengths=src_lengths,
|
| 102 |
+
return_all_hiddens=return_all_hiddens,
|
| 103 |
+
)
|
| 104 |
+
eos: int = self.eos
|
| 105 |
+
if classification_head_name is not None:
|
| 106 |
+
sentence_representation = x[
|
| 107 |
+
src_tokens.eq(eos), :
|
| 108 |
+
].view(x.size(0), -1, x.size(-1))[:, -1, :]
|
| 109 |
+
for k, head in self.classification_heads.items():
|
| 110 |
+
# for torch script only supports iteration
|
| 111 |
+
if k == classification_head_name:
|
| 112 |
+
x = head(sentence_representation)
|
| 113 |
+
break
|
| 114 |
+
return x, extra
|
| 115 |
+
|
| 116 |
+
@classmethod
|
| 117 |
+
def from_pretrained(
|
| 118 |
+
cls,
|
| 119 |
+
model_name_or_path,
|
| 120 |
+
checkpoint_file="model.pt",
|
| 121 |
+
data_name_or_path=".",
|
| 122 |
+
bpe="gpt2",
|
| 123 |
+
sample_break_mode="eos",
|
| 124 |
+
**kwargs,
|
| 125 |
+
):
|
| 126 |
+
from fairseq import hub_utils
|
| 127 |
+
|
| 128 |
+
x = hub_utils.from_pretrained(
|
| 129 |
+
model_name_or_path,
|
| 130 |
+
checkpoint_file,
|
| 131 |
+
data_name_or_path,
|
| 132 |
+
archive_map=cls.hub_models(),
|
| 133 |
+
bpe=bpe,
|
| 134 |
+
load_checkpoint_heads=True,
|
| 135 |
+
sample_break_mode=sample_break_mode,
|
| 136 |
+
**kwargs,
|
| 137 |
+
)
|
| 138 |
+
return BARTHubInterface(x["args"], x["task"], x["models"][0])
|
| 139 |
+
|
| 140 |
+
def register_classification_head(
|
| 141 |
+
self, name, num_classes=None, inner_dim=None, **kwargs
|
| 142 |
+
):
|
| 143 |
+
"""Register a classification head."""
|
| 144 |
+
logger.info("Registering classification head: {0}".format(name))
|
| 145 |
+
if name in self.classification_heads:
|
| 146 |
+
prev_num_classes = self.classification_heads[name].out_proj.out_features
|
| 147 |
+
prev_inner_dim = self.classification_heads[name].dense.out_features
|
| 148 |
+
if num_classes != prev_num_classes or inner_dim != prev_inner_dim:
|
| 149 |
+
logger.warning(
|
| 150 |
+
're-registering head "{}" with num_classes {} (prev: {}) '
|
| 151 |
+
"and inner_dim {} (prev: {})".format(
|
| 152 |
+
name, num_classes, prev_num_classes, inner_dim, prev_inner_dim
|
| 153 |
+
)
|
| 154 |
+
)
|
| 155 |
+
self.classification_heads[name] = BARTClassificationHead(
|
| 156 |
+
input_dim=self.args.encoder_embed_dim,
|
| 157 |
+
inner_dim=inner_dim or self.args.encoder_embed_dim,
|
| 158 |
+
num_classes=num_classes,
|
| 159 |
+
activation_fn=self.args.pooler_activation_fn,
|
| 160 |
+
pooler_dropout=self.args.pooler_dropout,
|
| 161 |
+
do_spectral_norm=getattr(
|
| 162 |
+
self.args, "spectral_norm_classification_head", False
|
| 163 |
+
),
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
def upgrade_state_dict_named(self, state_dict, name):
|
| 167 |
+
super().upgrade_state_dict_named(state_dict, name)
|
| 168 |
+
|
| 169 |
+
prefix = name + "." if name != "" else ""
|
| 170 |
+
current_head_names = (
|
| 171 |
+
[]
|
| 172 |
+
if not hasattr(self, "classification_heads")
|
| 173 |
+
else self.classification_heads.keys()
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
# Handle new classification heads present in the state dict.
|
| 177 |
+
keys_to_delete = []
|
| 178 |
+
for k in state_dict.keys():
|
| 179 |
+
if not k.startswith(prefix + "classification_heads."):
|
| 180 |
+
continue
|
| 181 |
+
|
| 182 |
+
head_name = k[len(prefix + "classification_heads.") :].split(".")[0]
|
| 183 |
+
num_classes = state_dict[
|
| 184 |
+
prefix + "classification_heads." + head_name + ".out_proj.weight"
|
| 185 |
+
].size(0)
|
| 186 |
+
inner_dim = state_dict[
|
| 187 |
+
prefix + "classification_heads." + head_name + ".dense.weight"
|
| 188 |
+
].size(0)
|
| 189 |
+
|
| 190 |
+
if getattr(self.args, "load_checkpoint_heads", False):
|
| 191 |
+
if head_name not in current_head_names:
|
| 192 |
+
self.register_classification_head(head_name, num_classes, inner_dim)
|
| 193 |
+
else:
|
| 194 |
+
if head_name not in current_head_names:
|
| 195 |
+
logger.warning(
|
| 196 |
+
"deleting classification head ({}) from checkpoint "
|
| 197 |
+
"not present in current model: {}".format(head_name, k)
|
| 198 |
+
)
|
| 199 |
+
keys_to_delete.append(k)
|
| 200 |
+
elif (
|
| 201 |
+
num_classes
|
| 202 |
+
!= self.classification_heads[head_name].out_proj.out_features
|
| 203 |
+
or inner_dim
|
| 204 |
+
!= self.classification_heads[head_name].dense.out_features
|
| 205 |
+
):
|
| 206 |
+
logger.warning(
|
| 207 |
+
"deleting classification head ({}) from checkpoint "
|
| 208 |
+
"with different dimensions than current model: {}".format(
|
| 209 |
+
head_name, k
|
| 210 |
+
)
|
| 211 |
+
)
|
| 212 |
+
keys_to_delete.append(k)
|
| 213 |
+
for k in keys_to_delete:
|
| 214 |
+
del state_dict[k]
|
| 215 |
+
|
| 216 |
+
def truncate_emb(key):
|
| 217 |
+
if key in state_dict:
|
| 218 |
+
state_dict[key] = state_dict[key][:-1, :]
|
| 219 |
+
|
| 220 |
+
# When finetuning on translation task, remove last row of
|
| 221 |
+
# embedding matrix that corresponds to mask_idx token.
|
| 222 |
+
loaded_dict_size = state_dict["encoder.embed_tokens.weight"].size(0)
|
| 223 |
+
if (
|
| 224 |
+
loaded_dict_size == len(self.encoder.dictionary) + 1
|
| 225 |
+
and "<mask>" not in self.encoder.dictionary
|
| 226 |
+
):
|
| 227 |
+
truncate_emb("encoder.embed_tokens.weight")
|
| 228 |
+
truncate_emb("decoder.embed_tokens.weight")
|
| 229 |
+
truncate_emb("encoder.output_projection.weight")
|
| 230 |
+
truncate_emb("decoder.output_projection.weight")
|
| 231 |
+
|
| 232 |
+
# When continued pretraining on new set of languages for mbart,
|
| 233 |
+
# add extra lang embeddings at the end of embed_tokens.
|
| 234 |
+
# Note: newly added languages are assumed to have been added at the end.
|
| 235 |
+
if self.args.task == "multilingual_denoising" and loaded_dict_size < len(
|
| 236 |
+
self.encoder.dictionary
|
| 237 |
+
):
|
| 238 |
+
logger.info(
|
| 239 |
+
"Adding extra language embeddings not found in pretrained model for "
|
| 240 |
+
"continued pretraining of MBART on new set of languages."
|
| 241 |
+
)
|
| 242 |
+
loaded_mask_token_embedding = state_dict["encoder.embed_tokens.weight"][
|
| 243 |
+
-1, :
|
| 244 |
+
]
|
| 245 |
+
|
| 246 |
+
num_langids_to_add = len(self.encoder.dictionary) - loaded_dict_size
|
| 247 |
+
embed_dim = state_dict["encoder.embed_tokens.weight"].size(1)
|
| 248 |
+
|
| 249 |
+
new_lang_embed_to_add = torch.zeros(num_langids_to_add, embed_dim)
|
| 250 |
+
nn.init.normal_(new_lang_embed_to_add, mean=0, std=embed_dim ** -0.5)
|
| 251 |
+
new_lang_embed_to_add = new_lang_embed_to_add.to(
|
| 252 |
+
dtype=state_dict["encoder.embed_tokens.weight"].dtype,
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
state_dict["encoder.embed_tokens.weight"] = torch.cat(
|
| 256 |
+
[
|
| 257 |
+
state_dict["encoder.embed_tokens.weight"][
|
| 258 |
+
: loaded_dict_size - 1, :
|
| 259 |
+
],
|
| 260 |
+
new_lang_embed_to_add,
|
| 261 |
+
loaded_mask_token_embedding.unsqueeze(0),
|
| 262 |
+
]
|
| 263 |
+
)
|
| 264 |
+
state_dict["decoder.embed_tokens.weight"] = torch.cat(
|
| 265 |
+
[
|
| 266 |
+
state_dict["decoder.embed_tokens.weight"][
|
| 267 |
+
: loaded_dict_size - 1, :
|
| 268 |
+
],
|
| 269 |
+
new_lang_embed_to_add,
|
| 270 |
+
loaded_mask_token_embedding.unsqueeze(0),
|
| 271 |
+
]
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
# Copy any newly-added classification heads into the state dict
|
| 275 |
+
# with their current weights.
|
| 276 |
+
if hasattr(self, "classification_heads"):
|
| 277 |
+
cur_state = self.classification_heads.state_dict()
|
| 278 |
+
for k, v in cur_state.items():
|
| 279 |
+
if prefix + "classification_heads." + k not in state_dict:
|
| 280 |
+
logger.info("Overwriting " + prefix + "classification_heads." + k)
|
| 281 |
+
state_dict[prefix + "classification_heads." + k] = v
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
class BARTClassificationHead(nn.Module):
|
| 285 |
+
"""Head for sentence-level classification tasks."""
|
| 286 |
+
|
| 287 |
+
def __init__(
|
| 288 |
+
self,
|
| 289 |
+
input_dim,
|
| 290 |
+
inner_dim,
|
| 291 |
+
num_classes,
|
| 292 |
+
activation_fn,
|
| 293 |
+
pooler_dropout,
|
| 294 |
+
do_spectral_norm=False,
|
| 295 |
+
):
|
| 296 |
+
super().__init__()
|
| 297 |
+
self.dense = nn.Linear(input_dim, inner_dim)
|
| 298 |
+
self.activation_fn = utils.get_activation_fn(activation_fn)
|
| 299 |
+
self.dropout = nn.Dropout(p=pooler_dropout)
|
| 300 |
+
self.out_proj = nn.Linear(inner_dim, num_classes)
|
| 301 |
+
|
| 302 |
+
if do_spectral_norm:
|
| 303 |
+
self.out_proj = torch.nn.utils.spectral_norm(self.out_proj)
|
| 304 |
+
|
| 305 |
+
def forward(self, features, **kwargs):
|
| 306 |
+
x = features
|
| 307 |
+
x = self.dropout(x)
|
| 308 |
+
x = self.dense(x)
|
| 309 |
+
x = self.activation_fn(x)
|
| 310 |
+
x = self.dropout(x)
|
| 311 |
+
x = self.out_proj(x)
|
| 312 |
+
return x
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
@register_model_architecture("bart", "bart_large")
|
| 316 |
+
def bart_large_architecture(args):
|
| 317 |
+
args.encoder_embed_path = getattr(args, "encoder_embed_path", None)
|
| 318 |
+
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
| 319 |
+
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4 * 1024)
|
| 320 |
+
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
| 321 |
+
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
| 322 |
+
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
| 323 |
+
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", True)
|
| 324 |
+
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
| 325 |
+
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", args.encoder_embed_dim)
|
| 326 |
+
args.decoder_ffn_embed_dim = getattr(
|
| 327 |
+
args, "decoder_ffn_embed_dim", args.encoder_ffn_embed_dim
|
| 328 |
+
)
|
| 329 |
+
args.decoder_layers = getattr(args, "decoder_layers", 12)
|
| 330 |
+
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
| 331 |
+
args.decoder_normalize_before = getattr(args, "decoder_normalize_before", False)
|
| 332 |
+
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", True)
|
| 333 |
+
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
| 334 |
+
args.relu_dropout = getattr(args, "relu_dropout", 0.0)
|
| 335 |
+
args.dropout = getattr(args, "dropout", 0.1)
|
| 336 |
+
args.max_target_positions = getattr(args, "max_target_positions", 1024)
|
| 337 |
+
args.max_source_positions = getattr(args, "max_source_positions", 1024)
|
| 338 |
+
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
| 339 |
+
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
| 340 |
+
args.share_decoder_input_output_embed = getattr(
|
| 341 |
+
args, "share_decoder_input_output_embed", True
|
| 342 |
+
)
|
| 343 |
+
args.share_all_embeddings = getattr(args, "share_all_embeddings", True)
|
| 344 |
+
|
| 345 |
+
args.decoder_output_dim = getattr(
|
| 346 |
+
args, "decoder_output_dim", args.decoder_embed_dim
|
| 347 |
+
)
|
| 348 |
+
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
| 349 |
+
|
| 350 |
+
args.no_scale_embedding = getattr(args, "no_scale_embedding", True)
|
| 351 |
+
args.layernorm_embedding = getattr(args, "layernorm_embedding", True)
|
| 352 |
+
|
| 353 |
+
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
| 354 |
+
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
| 355 |
+
args.pooler_dropout = getattr(args, "pooler_dropout", 0.0)
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
@register_model_architecture("bart", "bart_base")
|
| 359 |
+
def bart_base_architecture(args):
|
| 360 |
+
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
| 361 |
+
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4 * 768)
|
| 362 |
+
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
| 363 |
+
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 12)
|
| 364 |
+
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
| 365 |
+
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 12)
|
| 366 |
+
bart_large_architecture(args)
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
@register_model_architecture("bart", "mbart_large")
|
| 370 |
+
def mbart_large_architecture(args):
|
| 371 |
+
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
| 372 |
+
bart_large_architecture(args)
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
@register_model_architecture("bart", "mbart_base")
|
| 376 |
+
def mbart_base_architecture(args):
|
| 377 |
+
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
| 378 |
+
bart_base_architecture(args)
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
@register_model_architecture("bart", "mbart_base_wmt20")
|
| 382 |
+
def mbart_base_wmt20_architecture(args):
|
| 383 |
+
args.layernorm_embedding = getattr(args, "layernorm_embedding", False)
|
| 384 |
+
mbart_base_architecture(args)
|
avh-align_core/fairseq/models/composite_encoder.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from .fairseq_encoder import FairseqEncoder
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class CompositeEncoder(FairseqEncoder):
|
| 10 |
+
"""
|
| 11 |
+
A wrapper around a dictionary of :class:`FairseqEncoder` objects.
|
| 12 |
+
|
| 13 |
+
We run forward on each encoder and return a dictionary of outputs. The first
|
| 14 |
+
encoder's dictionary is used for initialization.
|
| 15 |
+
|
| 16 |
+
Args:
|
| 17 |
+
encoders (dict): a dictionary of :class:`FairseqEncoder` objects.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
def __init__(self, encoders):
|
| 21 |
+
super().__init__(next(iter(encoders.values())).dictionary)
|
| 22 |
+
self.encoders = encoders
|
| 23 |
+
for key in self.encoders:
|
| 24 |
+
self.add_module(key, self.encoders[key])
|
| 25 |
+
|
| 26 |
+
def forward(self, src_tokens, src_lengths):
|
| 27 |
+
"""
|
| 28 |
+
Args:
|
| 29 |
+
src_tokens (LongTensor): tokens in the source language of shape
|
| 30 |
+
`(batch, src_len)`
|
| 31 |
+
src_lengths (LongTensor): lengths of each source sentence of shape
|
| 32 |
+
`(batch)`
|
| 33 |
+
|
| 34 |
+
Returns:
|
| 35 |
+
dict:
|
| 36 |
+
the outputs from each Encoder
|
| 37 |
+
"""
|
| 38 |
+
encoder_out = {}
|
| 39 |
+
for key in self.encoders:
|
| 40 |
+
encoder_out[key] = self.encoders[key](src_tokens, src_lengths)
|
| 41 |
+
return encoder_out
|
| 42 |
+
|
| 43 |
+
def reorder_encoder_out(self, encoder_out, new_order):
|
| 44 |
+
"""Reorder encoder output according to new_order."""
|
| 45 |
+
for key in self.encoders:
|
| 46 |
+
encoder_out[key] = self.encoders[key].reorder_encoder_out(
|
| 47 |
+
encoder_out[key], new_order
|
| 48 |
+
)
|
| 49 |
+
return encoder_out
|
| 50 |
+
|
| 51 |
+
def max_positions(self):
|
| 52 |
+
return min(self.encoders[key].max_positions() for key in self.encoders)
|
| 53 |
+
|
| 54 |
+
def upgrade_state_dict(self, state_dict):
|
| 55 |
+
for key in self.encoders:
|
| 56 |
+
self.encoders[key].upgrade_state_dict(state_dict)
|
| 57 |
+
return state_dict
|
avh-align_core/fairseq/models/distributed_fairseq_model.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import logging
|
| 7 |
+
import os
|
| 8 |
+
import signal
|
| 9 |
+
import threading
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from torch.nn.parallel import DistributedDataParallel
|
| 14 |
+
|
| 15 |
+
from fairseq.distributed import (
|
| 16 |
+
DistributedTimeoutWrapper,
|
| 17 |
+
LegacyDistributedDataParallel,
|
| 18 |
+
ModuleProxyWrapper,
|
| 19 |
+
TPUDistributedDataParallel,
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
_GOSSIP_DISABLED = False
|
| 27 |
+
try:
|
| 28 |
+
import gossip
|
| 29 |
+
except ImportError:
|
| 30 |
+
_GOSSIP_DISABLED = True
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def DistributedFairseqModel(args, model, process_group, device):
|
| 34 |
+
"""
|
| 35 |
+
Wrap a *model* to support distributed data parallel training.
|
| 36 |
+
|
| 37 |
+
This is similar to the built-in DistributedDataParallel, but allows
|
| 38 |
+
additional configuration of the DistributedDataParallel class to
|
| 39 |
+
use, and also provides easier access to the wrapped model by
|
| 40 |
+
forwarding requests for missing attributes to the wrapped model.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
args (argparse.Namespace): fairseq args
|
| 44 |
+
model (BaseFairseqModel): model to wrap
|
| 45 |
+
process_group: the c10d process group to be used for distributed data
|
| 46 |
+
parallel all-reduction.
|
| 47 |
+
device: device to move model to
|
| 48 |
+
"""
|
| 49 |
+
assert isinstance(model, nn.Module)
|
| 50 |
+
if args.tpu:
|
| 51 |
+
wrapped_model = TPUDistributedDataParallel(
|
| 52 |
+
module=model.to(device),
|
| 53 |
+
process_group=process_group,
|
| 54 |
+
)
|
| 55 |
+
# forward missing getattr and state_dict/load_state_dict to orig model
|
| 56 |
+
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
| 57 |
+
elif args.ddp_backend in {"c10d", "pytorch_ddp"}:
|
| 58 |
+
wrapped_model = DistributedDataParallel(
|
| 59 |
+
module=model.to(device),
|
| 60 |
+
device_ids=[args.device_id],
|
| 61 |
+
output_device=args.device_id,
|
| 62 |
+
broadcast_buffers=args.broadcast_buffers,
|
| 63 |
+
bucket_cap_mb=args.bucket_cap_mb,
|
| 64 |
+
process_group=process_group,
|
| 65 |
+
find_unused_parameters=args.find_unused_parameters,
|
| 66 |
+
)
|
| 67 |
+
if args.ddp_comm_hook == "fp16":
|
| 68 |
+
logger.info("enable fp16 communication hook in DDP")
|
| 69 |
+
try:
|
| 70 |
+
from torch.distributed.algorithms.ddp_comm_hooks import (
|
| 71 |
+
register_ddp_comm_hook,
|
| 72 |
+
DDPCommHookType,
|
| 73 |
+
)
|
| 74 |
+
except:
|
| 75 |
+
logger.error(
|
| 76 |
+
"Could not import from torch.distributed.algorithms.ddp_comm_hooks; you may need to update your pytorch version"
|
| 77 |
+
)
|
| 78 |
+
raise
|
| 79 |
+
|
| 80 |
+
register_ddp_comm_hook(DDPCommHookType.FP16_COMPRESS, wrapped_model)
|
| 81 |
+
# forward missing getattr and state_dict/load_state_dict to orig model
|
| 82 |
+
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
| 83 |
+
elif args.ddp_backend in {"no_c10d", "legacy_ddp"}:
|
| 84 |
+
wrapped_model = LegacyDistributedDataParallel(
|
| 85 |
+
module=model.to(device),
|
| 86 |
+
buffer_size=2 ** 28,
|
| 87 |
+
process_group=process_group,
|
| 88 |
+
)
|
| 89 |
+
# forward missing getattr and state_dict/load_state_dict to orig model
|
| 90 |
+
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
| 91 |
+
elif args.ddp_backend == "slow_mo":
|
| 92 |
+
if _GOSSIP_DISABLED:
|
| 93 |
+
raise ImportError(
|
| 94 |
+
"Cannot find gossip library. Please install from: "
|
| 95 |
+
"github.com/facebookresearch/stochastic_gradient_push"
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
# The values of slowmo_momentum below were obtained by tuning on the
|
| 99 |
+
# En-De 16 dataset by training the transformer_wmt_en_de_large model
|
| 100 |
+
if args.slowmo_momentum is None:
|
| 101 |
+
if args.distributed_world_size <= 16:
|
| 102 |
+
args.slowmo_momentum = 0.0
|
| 103 |
+
elif args.distributed_world_size <= 32:
|
| 104 |
+
args.slowmo_momentum = 0.2
|
| 105 |
+
elif args.distributed_world_size <= 64:
|
| 106 |
+
args.slowmo_momentum = 0.5
|
| 107 |
+
else:
|
| 108 |
+
args.slowmo_momentum = 0.6
|
| 109 |
+
|
| 110 |
+
wrapped_model = gossip.GossipDataParallel(
|
| 111 |
+
module=model.to(device),
|
| 112 |
+
device_ids=[args.device_id],
|
| 113 |
+
output_device=args.device_id,
|
| 114 |
+
broadcast_buffers=args.broadcast_buffers,
|
| 115 |
+
nprocs_per_node=args.nprocs_per_node,
|
| 116 |
+
slowmo_momentum=args.slowmo_momentum,
|
| 117 |
+
localsgd=(args.slowmo_algorithm == "LocalSGD"),
|
| 118 |
+
localsgd_frequency=args.localsgd_frequency,
|
| 119 |
+
)
|
| 120 |
+
# forward missing getattr and state_dict/load_state_dict to orig model
|
| 121 |
+
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
| 122 |
+
elif args.ddp_backend == "fully_sharded":
|
| 123 |
+
try:
|
| 124 |
+
from fairscale.nn.data_parallel import FullyShardedDataParallel as FSDP
|
| 125 |
+
except ImportError:
|
| 126 |
+
raise ImportError(
|
| 127 |
+
"Cannot find FullyShardedDataParallel. "
|
| 128 |
+
"Please install fairscale with: pip install fairscale"
|
| 129 |
+
)
|
| 130 |
+
assert isinstance(model, FSDP), "expected model to already be wrapped in FSDP"
|
| 131 |
+
wrapped_model = model
|
| 132 |
+
if args.memory_efficient_fp16:
|
| 133 |
+
wrapped_model = wrapped_model.half()
|
| 134 |
+
if not args.cpu_offload:
|
| 135 |
+
wrapped_model = wrapped_model.to(device=device)
|
| 136 |
+
else:
|
| 137 |
+
raise ValueError("Unknown --ddp-backend: " + args.ddp_backend)
|
| 138 |
+
|
| 139 |
+
# kill hung distributed jobs after a timeout
|
| 140 |
+
if getattr(args, "heartbeat_timeout", -1) > 0:
|
| 141 |
+
wrapped_model = DistributedTimeoutWrapper(
|
| 142 |
+
wrapped_model, timeout=getattr(args, "heartbeat_timeout", -1)
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
return wrapped_model
|
avh-align_core/fairseq/models/fairseq_decoder.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from typing import Dict, List, Optional, Tuple
|
| 7 |
+
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from fairseq import utils
|
| 10 |
+
from torch import Tensor
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class FairseqDecoder(nn.Module):
|
| 14 |
+
"""Base class for decoders."""
|
| 15 |
+
|
| 16 |
+
def __init__(self, dictionary):
|
| 17 |
+
super().__init__()
|
| 18 |
+
self.dictionary = dictionary
|
| 19 |
+
self.onnx_trace = False
|
| 20 |
+
self.adaptive_softmax = None
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def forward(self, prev_output_tokens, encoder_out=None, **kwargs):
|
| 24 |
+
"""
|
| 25 |
+
Args:
|
| 26 |
+
prev_output_tokens (LongTensor): shifted output tokens of shape
|
| 27 |
+
`(batch, tgt_len)`, for teacher forcing
|
| 28 |
+
encoder_out (dict, optional): output from the encoder, used for
|
| 29 |
+
encoder-side attention
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
tuple:
|
| 33 |
+
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
| 34 |
+
- a dictionary with any model-specific outputs
|
| 35 |
+
"""
|
| 36 |
+
x, extra = self.extract_features(
|
| 37 |
+
prev_output_tokens, encoder_out=encoder_out, **kwargs
|
| 38 |
+
)
|
| 39 |
+
x = self.output_layer(x)
|
| 40 |
+
return x, extra
|
| 41 |
+
|
| 42 |
+
def extract_features(self, prev_output_tokens, encoder_out=None, **kwargs):
|
| 43 |
+
"""
|
| 44 |
+
Returns:
|
| 45 |
+
tuple:
|
| 46 |
+
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
| 47 |
+
- a dictionary with any model-specific outputs
|
| 48 |
+
"""
|
| 49 |
+
raise NotImplementedError
|
| 50 |
+
|
| 51 |
+
def output_layer(self, features, **kwargs):
|
| 52 |
+
"""
|
| 53 |
+
Project features to the default output size, e.g., vocabulary size.
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
features (Tensor): features returned by *extract_features*.
|
| 57 |
+
"""
|
| 58 |
+
raise NotImplementedError
|
| 59 |
+
|
| 60 |
+
def get_normalized_probs(
|
| 61 |
+
self,
|
| 62 |
+
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
| 63 |
+
log_probs: bool,
|
| 64 |
+
sample: Optional[Dict[str, Tensor]] = None,
|
| 65 |
+
):
|
| 66 |
+
"""Get normalized probabilities (or log probs) from a net's output."""
|
| 67 |
+
return self.get_normalized_probs_scriptable(net_output, log_probs, sample)
|
| 68 |
+
|
| 69 |
+
# TorchScript doesn't support super() method so that the scriptable Subclass
|
| 70 |
+
# can't access the base class model in Torchscript.
|
| 71 |
+
# Current workaround is to add a helper function with different name and
|
| 72 |
+
# call the helper function from scriptable Subclass.
|
| 73 |
+
def get_normalized_probs_scriptable(
|
| 74 |
+
self,
|
| 75 |
+
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
| 76 |
+
log_probs: bool,
|
| 77 |
+
sample: Optional[Dict[str, Tensor]] = None,
|
| 78 |
+
):
|
| 79 |
+
"""Get normalized probabilities (or log probs) from a net's output."""
|
| 80 |
+
|
| 81 |
+
if hasattr(self, "adaptive_softmax") and self.adaptive_softmax is not None:
|
| 82 |
+
if sample is not None:
|
| 83 |
+
assert "target" in sample
|
| 84 |
+
target = sample["target"]
|
| 85 |
+
else:
|
| 86 |
+
target = None
|
| 87 |
+
out = self.adaptive_softmax.get_log_prob(net_output[0], target=target)
|
| 88 |
+
return out.exp_() if not log_probs else out
|
| 89 |
+
|
| 90 |
+
logits = net_output[0]
|
| 91 |
+
if log_probs:
|
| 92 |
+
return utils.log_softmax(logits, dim=-1, onnx_trace=self.onnx_trace)
|
| 93 |
+
else:
|
| 94 |
+
return utils.softmax(logits, dim=-1, onnx_trace=self.onnx_trace)
|
| 95 |
+
|
| 96 |
+
def max_positions(self):
|
| 97 |
+
"""Maximum input length supported by the decoder."""
|
| 98 |
+
return 1e6 # an arbitrary large number
|
| 99 |
+
|
| 100 |
+
def upgrade_state_dict_named(self, state_dict, name):
|
| 101 |
+
"""Upgrade old state dicts to work with newer code."""
|
| 102 |
+
return state_dict
|
| 103 |
+
|
| 104 |
+
def prepare_for_onnx_export_(self):
|
| 105 |
+
self.onnx_trace = True
|
avh-align_core/fairseq/models/fairseq_encoder.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from typing import Dict, List, NamedTuple, Optional
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
from torch import Tensor
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
EncoderOut = NamedTuple(
|
| 14 |
+
"EncoderOut",
|
| 15 |
+
[
|
| 16 |
+
("encoder_out", Tensor), # T x B x C
|
| 17 |
+
("encoder_padding_mask", Optional[Tensor]), # B x T
|
| 18 |
+
("encoder_embedding", Optional[Tensor]), # B x T x C
|
| 19 |
+
("encoder_states", Optional[List[Tensor]]), # List[T x B x C]
|
| 20 |
+
("src_tokens", Optional[Tensor]), # B x T
|
| 21 |
+
("src_lengths", Optional[Tensor]), # B x 1
|
| 22 |
+
],
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class FairseqEncoder(nn.Module):
|
| 27 |
+
"""Base class for encoders."""
|
| 28 |
+
|
| 29 |
+
def __init__(self, dictionary):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.dictionary = dictionary
|
| 32 |
+
|
| 33 |
+
def forward(self, src_tokens, src_lengths=None, **kwargs):
|
| 34 |
+
"""
|
| 35 |
+
Args:
|
| 36 |
+
src_tokens (LongTensor): tokens in the source language of shape
|
| 37 |
+
`(batch, src_len)`
|
| 38 |
+
src_lengths (LongTensor): lengths of each source sentence of shape
|
| 39 |
+
`(batch)`
|
| 40 |
+
"""
|
| 41 |
+
raise NotImplementedError
|
| 42 |
+
|
| 43 |
+
def forward_torchscript(self, net_input: Dict[str, Tensor]):
|
| 44 |
+
"""A TorchScript-compatible version of forward.
|
| 45 |
+
|
| 46 |
+
Encoders which use additional arguments may want to override
|
| 47 |
+
this method for TorchScript compatibility.
|
| 48 |
+
"""
|
| 49 |
+
if torch.jit.is_scripting():
|
| 50 |
+
return self.forward(
|
| 51 |
+
src_tokens=net_input["src_tokens"],
|
| 52 |
+
src_lengths=net_input["src_lengths"],
|
| 53 |
+
)
|
| 54 |
+
else:
|
| 55 |
+
return self.forward_non_torchscript(net_input)
|
| 56 |
+
|
| 57 |
+
@torch.jit.unused
|
| 58 |
+
def forward_non_torchscript(self, net_input: Dict[str, Tensor]):
|
| 59 |
+
encoder_input = {
|
| 60 |
+
k: v for k, v in net_input.items() if k != "prev_output_tokens"
|
| 61 |
+
}
|
| 62 |
+
return self.forward(**encoder_input)
|
| 63 |
+
|
| 64 |
+
def reorder_encoder_out(self, encoder_out, new_order):
|
| 65 |
+
"""
|
| 66 |
+
Reorder encoder output according to `new_order`.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
encoder_out: output from the ``forward()`` method
|
| 70 |
+
new_order (LongTensor): desired order
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
`encoder_out` rearranged according to `new_order`
|
| 74 |
+
"""
|
| 75 |
+
raise NotImplementedError
|
| 76 |
+
|
| 77 |
+
def max_positions(self):
|
| 78 |
+
"""Maximum input length supported by the encoder."""
|
| 79 |
+
return 1e6 # an arbitrary large number
|
| 80 |
+
|
| 81 |
+
def upgrade_state_dict_named(self, state_dict, name):
|
| 82 |
+
"""Upgrade old state dicts to work with newer code."""
|
| 83 |
+
return state_dict
|
| 84 |
+
|
| 85 |
+
def set_num_updates(self, num_updates):
|
| 86 |
+
"""State from trainer to pass along to model at every update."""
|
| 87 |
+
|
| 88 |
+
def _apply(m):
|
| 89 |
+
if hasattr(m, "set_num_updates") and m != self:
|
| 90 |
+
m.set_num_updates(num_updates)
|
| 91 |
+
|
| 92 |
+
self.apply(_apply)
|