chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""isort:skip_file"""
|
||||
|
||||
import argparse
|
||||
import importlib
|
||||
import os
|
||||
|
||||
from fairseq.dataclass import FairseqDataclass
|
||||
from fairseq.dataclass.utils import merge_with_parent, populate_dataclass
|
||||
from hydra.core.config_store import ConfigStore
|
||||
|
||||
from .composite_encoder import CompositeEncoder
|
||||
from .distributed_fairseq_model import DistributedFairseqModel
|
||||
from .fairseq_decoder import FairseqDecoder
|
||||
from .fairseq_encoder import FairseqEncoder
|
||||
from .fairseq_incremental_decoder import FairseqIncrementalDecoder
|
||||
from .fairseq_model import (
|
||||
BaseFairseqModel,
|
||||
FairseqEncoderDecoderModel,
|
||||
FairseqEncoderModel,
|
||||
FairseqLanguageModel,
|
||||
FairseqModel,
|
||||
FairseqMultiModel,
|
||||
)
|
||||
|
||||
|
||||
MODEL_REGISTRY = {}
|
||||
MODEL_DATACLASS_REGISTRY = {}
|
||||
ARCH_MODEL_REGISTRY = {}
|
||||
ARCH_MODEL_NAME_REGISTRY = {}
|
||||
ARCH_MODEL_INV_REGISTRY = {}
|
||||
ARCH_CONFIG_REGISTRY = {}
|
||||
|
||||
|
||||
__all__ = [
|
||||
"BaseFairseqModel",
|
||||
"CompositeEncoder",
|
||||
"DistributedFairseqModel",
|
||||
"FairseqDecoder",
|
||||
"FairseqEncoder",
|
||||
"FairseqEncoderDecoderModel",
|
||||
"FairseqEncoderModel",
|
||||
"FairseqIncrementalDecoder",
|
||||
"FairseqLanguageModel",
|
||||
"FairseqModel",
|
||||
"FairseqMultiModel",
|
||||
]
|
||||
|
||||
|
||||
def build_model(cfg: FairseqDataclass, task):
|
||||
|
||||
model = None
|
||||
model_type = getattr(cfg, "_name", None) or getattr(cfg, "arch", None)
|
||||
|
||||
if not model_type and len(cfg) == 1:
|
||||
# this is hit if config object is nested in directory that is named after model type
|
||||
|
||||
model_type = next(iter(cfg))
|
||||
if model_type in MODEL_DATACLASS_REGISTRY:
|
||||
cfg = cfg[model_type]
|
||||
else:
|
||||
raise Exception(
|
||||
"Could not infer model type from directory. Please add _name field to indicate model type. "
|
||||
"Available models: "
|
||||
+ str(MODEL_DATACLASS_REGISTRY.keys())
|
||||
+ " Requested model type: "
|
||||
+ model_type
|
||||
)
|
||||
|
||||
if model_type in ARCH_MODEL_REGISTRY:
|
||||
# case 1: legacy models
|
||||
model = ARCH_MODEL_REGISTRY[model_type]
|
||||
elif model_type in MODEL_DATACLASS_REGISTRY:
|
||||
# case 2: config-driven models
|
||||
model = MODEL_REGISTRY[model_type]
|
||||
|
||||
if model_type in MODEL_DATACLASS_REGISTRY:
|
||||
# set defaults from dataclass. note that arch name and model name can be the same
|
||||
dc = MODEL_DATACLASS_REGISTRY[model_type]
|
||||
if isinstance(cfg, argparse.Namespace):
|
||||
cfg = populate_dataclass(dc(), cfg)
|
||||
else:
|
||||
cfg = merge_with_parent(dc(), cfg)
|
||||
|
||||
assert model is not None, (
|
||||
f"Could not infer model type from {cfg}. "
|
||||
f"Available models: "
|
||||
+ str(MODEL_DATACLASS_REGISTRY.keys())
|
||||
+ " Requested model type: "
|
||||
+ model_type
|
||||
)
|
||||
|
||||
return model.build_model(cfg, task)
|
||||
|
||||
|
||||
def register_model(name, dataclass=None):
|
||||
"""
|
||||
New model types can be added to fairseq with the :func:`register_model`
|
||||
function decorator.
|
||||
|
||||
For example::
|
||||
|
||||
@register_model('lstm')
|
||||
class LSTM(FairseqEncoderDecoderModel):
|
||||
(...)
|
||||
|
||||
.. note:: All models must implement the :class:`BaseFairseqModel` interface.
|
||||
Typically you will extend :class:`FairseqEncoderDecoderModel` for
|
||||
sequence-to-sequence tasks or :class:`FairseqLanguageModel` for
|
||||
language modeling tasks.
|
||||
|
||||
Args:
|
||||
name (str): the name of the model
|
||||
"""
|
||||
|
||||
def register_model_cls(cls):
|
||||
if name in MODEL_REGISTRY:
|
||||
raise ValueError("Cannot register duplicate model ({})".format(name))
|
||||
if not issubclass(cls, BaseFairseqModel):
|
||||
raise ValueError(
|
||||
"Model ({}: {}) must extend BaseFairseqModel".format(name, cls.__name__)
|
||||
)
|
||||
MODEL_REGISTRY[name] = cls
|
||||
if dataclass is not None and not issubclass(dataclass, FairseqDataclass):
|
||||
raise ValueError(
|
||||
"Dataclass {} must extend FairseqDataclass".format(dataclass)
|
||||
)
|
||||
|
||||
cls.__dataclass = dataclass
|
||||
if dataclass is not None:
|
||||
MODEL_DATACLASS_REGISTRY[name] = dataclass
|
||||
|
||||
cs = ConfigStore.instance()
|
||||
node = dataclass()
|
||||
node._name = name
|
||||
cs.store(name=name, group="model", node=node, provider="fairseq")
|
||||
|
||||
@register_model_architecture(name, name)
|
||||
def noop(_):
|
||||
pass
|
||||
|
||||
return cls
|
||||
|
||||
return register_model_cls
|
||||
|
||||
|
||||
def register_model_architecture(model_name, arch_name):
|
||||
"""
|
||||
New model architectures can be added to fairseq with the
|
||||
:func:`register_model_architecture` function decorator. After registration,
|
||||
model architectures can be selected with the ``--arch`` command-line
|
||||
argument.
|
||||
|
||||
For example::
|
||||
|
||||
@register_model_architecture('lstm', 'lstm_luong_wmt_en_de')
|
||||
def lstm_luong_wmt_en_de(cfg):
|
||||
args.encoder_embed_dim = getattr(cfg.model, 'encoder_embed_dim', 1000)
|
||||
(...)
|
||||
|
||||
The decorated function should take a single argument *cfg*, which is a
|
||||
:class:`omegaconf.DictConfig`. The decorated function should modify these
|
||||
arguments in-place to match the desired architecture.
|
||||
|
||||
Args:
|
||||
model_name (str): the name of the Model (Model must already be
|
||||
registered)
|
||||
arch_name (str): the name of the model architecture (``--arch``)
|
||||
"""
|
||||
|
||||
def register_model_arch_fn(fn):
|
||||
if model_name not in MODEL_REGISTRY:
|
||||
raise ValueError(
|
||||
"Cannot register model architecture for unknown model type ({})".format(
|
||||
model_name
|
||||
)
|
||||
)
|
||||
if arch_name in ARCH_MODEL_REGISTRY:
|
||||
raise ValueError(
|
||||
"Cannot register duplicate model architecture ({})".format(arch_name)
|
||||
)
|
||||
if not callable(fn):
|
||||
raise ValueError(
|
||||
"Model architecture must be callable ({})".format(arch_name)
|
||||
)
|
||||
ARCH_MODEL_REGISTRY[arch_name] = MODEL_REGISTRY[model_name]
|
||||
ARCH_MODEL_NAME_REGISTRY[arch_name] = model_name
|
||||
ARCH_MODEL_INV_REGISTRY.setdefault(model_name, []).append(arch_name)
|
||||
ARCH_CONFIG_REGISTRY[arch_name] = fn
|
||||
return fn
|
||||
|
||||
return register_model_arch_fn
|
||||
|
||||
|
||||
# automatically import any Python files in the models/ directory
|
||||
models_dir = os.path.dirname(__file__)
|
||||
for file in os.listdir(models_dir):
|
||||
path = os.path.join(models_dir, file)
|
||||
if (
|
||||
not file.startswith("_")
|
||||
and not file.startswith(".")
|
||||
and (file.endswith(".py") or os.path.isdir(path))
|
||||
):
|
||||
model_name = file[: file.find(".py")] if file.endswith(".py") else file
|
||||
module = importlib.import_module("fairseq.models." + model_name)
|
||||
|
||||
# extra `model_parser` for sphinx
|
||||
if model_name in MODEL_REGISTRY:
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
group_archs = parser.add_argument_group("Named architectures")
|
||||
group_archs.add_argument(
|
||||
"--arch", choices=ARCH_MODEL_INV_REGISTRY[model_name]
|
||||
)
|
||||
group_args = parser.add_argument_group("Additional command-line arguments")
|
||||
MODEL_REGISTRY[model_name].add_args(group_args)
|
||||
globals()[model_name + "_parser"] = parser
|
||||
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from .hub_interface import * # noqa
|
||||
from .model import * # noqa
|
||||
@@ -0,0 +1,206 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import copy
|
||||
import logging
|
||||
from typing import Dict, List
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.data import encoders
|
||||
from fairseq.hub_utils import GeneratorHubInterface
|
||||
from omegaconf import open_dict
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BARTHubInterface(GeneratorHubInterface):
|
||||
"""A simple PyTorch Hub interface to BART.
|
||||
|
||||
Usage: https://github.com/pytorch/fairseq/tree/master/examples/bart
|
||||
"""
|
||||
|
||||
def __init__(self, cfg, task, model):
|
||||
super().__init__(cfg, task, [model])
|
||||
self.model = self.models[0]
|
||||
|
||||
def encode(
|
||||
self, sentence: str, *addl_sentences, no_separator=True
|
||||
) -> torch.LongTensor:
|
||||
"""
|
||||
BPE-encode a sentence (or multiple sentences).
|
||||
|
||||
Every sequence begins with a beginning-of-sentence (`<s>`) symbol.
|
||||
Every sentence ends with an end-of-sentence (`</s>`).
|
||||
|
||||
Example (single sentence): `<s> a b c </s>`
|
||||
Example (sentence pair): `<s> d e f </s> 1 2 3 </s>`
|
||||
|
||||
The BPE encoding follows GPT-2. One subtle detail is that the GPT-2 BPE
|
||||
requires leading spaces. For example::
|
||||
|
||||
>>> bart.encode('Hello world').tolist()
|
||||
[0, 31414, 232, 2]
|
||||
>>> bart.encode(' world').tolist()
|
||||
[0, 232, 2]
|
||||
>>> bart.encode('world').tolist()
|
||||
[0, 8331, 2]
|
||||
"""
|
||||
tokens = self.bpe.encode(sentence)
|
||||
if len(tokens.split(" ")) > min(self.max_positions) - 2:
|
||||
tokens = " ".join(tokens.split(" ")[: min(self.max_positions) - 2])
|
||||
bpe_sentence = "<s> " + tokens + " </s>"
|
||||
for s in addl_sentences:
|
||||
bpe_sentence += " </s>" if not no_separator else ""
|
||||
bpe_sentence += " " + self.bpe.encode(s) + " </s>"
|
||||
tokens = self.task.source_dictionary.encode_line(bpe_sentence, append_eos=False)
|
||||
return tokens.long()
|
||||
|
||||
def decode(self, tokens: torch.LongTensor):
|
||||
assert tokens.dim() == 1
|
||||
tokens = tokens.cpu().numpy()
|
||||
if tokens[0] == self.task.source_dictionary.bos():
|
||||
tokens = tokens[1:] # remove <s>
|
||||
eos_mask = tokens == self.task.source_dictionary.eos()
|
||||
doc_mask = eos_mask[1:] & eos_mask[:-1]
|
||||
sentences = np.split(tokens, doc_mask.nonzero()[0] + 1)
|
||||
sentences = [
|
||||
self.bpe.decode(self.task.source_dictionary.string(s)) for s in sentences
|
||||
]
|
||||
if len(sentences) == 1:
|
||||
return sentences[0]
|
||||
return sentences
|
||||
|
||||
def _build_sample(self, src_tokens: List[torch.LongTensor]):
|
||||
# assert torch.is_tensor(src_tokens)
|
||||
dataset = self.task.build_dataset_for_inference(
|
||||
src_tokens,
|
||||
[x.numel() for x in src_tokens],
|
||||
)
|
||||
sample = dataset.collater(dataset)
|
||||
sample = utils.apply_to_sample(lambda tensor: tensor.to(self.device), sample)
|
||||
return sample
|
||||
|
||||
def generate(
|
||||
self,
|
||||
tokenized_sentences: List[torch.LongTensor],
|
||||
*args,
|
||||
inference_step_args=None,
|
||||
skip_invalid_size_inputs=False,
|
||||
**kwargs
|
||||
) -> List[List[Dict[str, torch.Tensor]]]:
|
||||
inference_step_args = inference_step_args or {}
|
||||
if "prefix_tokens" in inference_step_args:
|
||||
raise NotImplementedError("prefix generation not implemented for BART")
|
||||
res = []
|
||||
for batch in self._build_batches(tokenized_sentences, skip_invalid_size_inputs):
|
||||
src_tokens = batch['net_input']['src_tokens']
|
||||
inference_step_args["prefix_tokens"] =src_tokens.new_full(
|
||||
(src_tokens.size(0), 1), fill_value=self.task.source_dictionary.bos()
|
||||
).to(device=self.device)
|
||||
results = super().generate(
|
||||
src_tokens,
|
||||
*args,
|
||||
inference_step_args=inference_step_args,
|
||||
skip_invalid_size_inputs=skip_invalid_size_inputs,
|
||||
**kwargs
|
||||
)
|
||||
res.extend(results)
|
||||
return res
|
||||
|
||||
def extract_features(
|
||||
self, tokens: torch.LongTensor, return_all_hiddens: bool = False
|
||||
) -> torch.Tensor:
|
||||
if tokens.dim() == 1:
|
||||
tokens = tokens.unsqueeze(0)
|
||||
if tokens.size(-1) > min(self.model.max_positions()):
|
||||
raise ValueError(
|
||||
"tokens exceeds maximum length: {} > {}".format(
|
||||
tokens.size(-1), self.model.max_positions()
|
||||
)
|
||||
)
|
||||
tokens.to(device=self.device),
|
||||
prev_output_tokens = tokens.clone()
|
||||
|
||||
prev_output_tokens[:, 0] = tokens.gather(
|
||||
1,
|
||||
(tokens.ne(self.task.source_dictionary.pad()).sum(dim=1) - 1).unsqueeze(-1),
|
||||
).squeeze()
|
||||
|
||||
prev_output_tokens[:, 1:] = tokens[:, :-1]
|
||||
features, extra = self.model(
|
||||
src_tokens=tokens,
|
||||
src_lengths=None,
|
||||
prev_output_tokens=prev_output_tokens,
|
||||
features_only=True,
|
||||
return_all_hiddens=return_all_hiddens,
|
||||
)
|
||||
if return_all_hiddens:
|
||||
# convert from T x B x C -> B x T x C
|
||||
inner_states = extra["inner_states"]
|
||||
return [inner_state.transpose(0, 1) for inner_state in inner_states]
|
||||
else:
|
||||
return features # just the last layer's features
|
||||
|
||||
def register_classification_head(
|
||||
self, name: str, num_classes: int = None, embedding_size: int = None, **kwargs
|
||||
):
|
||||
self.model.register_classification_head(
|
||||
name, num_classes=num_classes, embedding_size=embedding_size, **kwargs
|
||||
)
|
||||
|
||||
def predict(self, head: str, tokens: torch.LongTensor, return_logits: bool = False):
|
||||
if tokens.dim() == 1:
|
||||
tokens = tokens.unsqueeze(0)
|
||||
features = self.extract_features(tokens.to(device=self.device))
|
||||
sentence_representation = features[
|
||||
tokens.eq(self.task.source_dictionary.eos()), :
|
||||
].view(features.size(0), -1, features.size(-1))[:, -1, :]
|
||||
|
||||
logits = self.model.classification_heads[head](sentence_representation)
|
||||
if return_logits:
|
||||
return logits
|
||||
return F.log_softmax(logits, dim=-1)
|
||||
|
||||
def fill_mask(
|
||||
self,
|
||||
masked_inputs: List[str],
|
||||
topk: int = 5,
|
||||
match_source_len: bool = True,
|
||||
**generate_kwargs
|
||||
):
|
||||
masked_token = '<mask>'
|
||||
batch_tokens = []
|
||||
for masked_input in masked_inputs:
|
||||
assert masked_token in masked_input, \
|
||||
"please add one {} token for the input".format(masked_token)
|
||||
|
||||
text_spans = masked_input.split(masked_token)
|
||||
text_spans_bpe = (' {0} '.format(masked_token)).join(
|
||||
[self.bpe.encode(text_span.rstrip()) for text_span in text_spans]
|
||||
).strip()
|
||||
tokens = self.task.source_dictionary.encode_line(
|
||||
'<s> ' + text_spans_bpe + ' </s>',
|
||||
append_eos=False,
|
||||
add_if_not_exist=False,
|
||||
).long()
|
||||
batch_tokens.append(tokens)
|
||||
|
||||
# ensure beam size is at least as big as topk
|
||||
generate_kwargs['beam'] = max(
|
||||
topk,
|
||||
generate_kwargs.get('beam', -1),
|
||||
)
|
||||
generate_kwargs['match_source_len'] = match_source_len
|
||||
batch_hypos = self.generate(batch_tokens, **generate_kwargs)
|
||||
|
||||
return [
|
||||
[(self.decode(hypo['tokens']), hypo['score']) for hypo in hypos[:topk]]
|
||||
for hypos in batch_hypos
|
||||
]
|
||||
@@ -0,0 +1,384 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
BART: Denoising Sequence-to-Sequence Pre-training for
|
||||
Natural Language Generation, Translation, and Comprehension
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
import logging
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from fairseq import utils
|
||||
from fairseq.models import register_model, register_model_architecture
|
||||
from fairseq.models.transformer import TransformerModel
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
|
||||
from .hub_interface import BARTHubInterface
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_model("bart")
|
||||
class BARTModel(TransformerModel):
|
||||
__jit_unused_properties__ = ["supported_targets"]
|
||||
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
"bart.base": "http://dl.fbaipublicfiles.com/fairseq/models/bart.base.tar.gz",
|
||||
"bart.large": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.tar.gz",
|
||||
"bart.large.mnli": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.mnli.tar.gz",
|
||||
"bart.large.cnn": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.cnn.tar.gz",
|
||||
"bart.large.xsum": "http://dl.fbaipublicfiles.com/fairseq/models/bart.large.xsum.tar.gz",
|
||||
}
|
||||
|
||||
def __init__(self, args, encoder, decoder):
|
||||
super().__init__(args, encoder, decoder)
|
||||
|
||||
# We follow BERT's random weight initialization
|
||||
self.apply(init_bert_params)
|
||||
|
||||
self.classification_heads = nn.ModuleDict()
|
||||
if hasattr(self.encoder, "dictionary"):
|
||||
self.eos: int = self.encoder.dictionary.eos()
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
super(BARTModel, BARTModel).add_args(parser)
|
||||
parser.add_argument(
|
||||
"--pooler-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability in the masked_lm pooler layers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pooler-activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use for pooler layer",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--spectral-norm-classification-head",
|
||||
action="store_true",
|
||||
help="Apply spectral normalization on the classification head",
|
||||
)
|
||||
|
||||
@property
|
||||
def supported_targets(self):
|
||||
return {"self"}
|
||||
|
||||
def forward(
|
||||
self,
|
||||
src_tokens,
|
||||
src_lengths,
|
||||
prev_output_tokens,
|
||||
features_only: bool = False,
|
||||
classification_head_name: Optional[str] = None,
|
||||
token_embeddings: Optional[torch.Tensor] = None,
|
||||
return_all_hiddens: bool = True,
|
||||
alignment_layer: Optional[int] = None,
|
||||
alignment_heads: Optional[int] = None,
|
||||
):
|
||||
if classification_head_name is not None:
|
||||
features_only = True
|
||||
|
||||
encoder_out = self.encoder(
|
||||
src_tokens,
|
||||
src_lengths=src_lengths,
|
||||
token_embeddings=token_embeddings,
|
||||
return_all_hiddens=return_all_hiddens
|
||||
)
|
||||
x, extra = self.decoder(
|
||||
prev_output_tokens,
|
||||
encoder_out=encoder_out,
|
||||
features_only=features_only,
|
||||
alignment_layer=alignment_layer,
|
||||
alignment_heads=alignment_heads,
|
||||
src_lengths=src_lengths,
|
||||
return_all_hiddens=return_all_hiddens,
|
||||
)
|
||||
eos: int = self.eos
|
||||
if classification_head_name is not None:
|
||||
sentence_representation = x[
|
||||
src_tokens.eq(eos), :
|
||||
].view(x.size(0), -1, x.size(-1))[:, -1, :]
|
||||
for k, head in self.classification_heads.items():
|
||||
# for torch script only supports iteration
|
||||
if k == classification_head_name:
|
||||
x = head(sentence_representation)
|
||||
break
|
||||
return x, extra
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(
|
||||
cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file="model.pt",
|
||||
data_name_or_path=".",
|
||||
bpe="gpt2",
|
||||
sample_break_mode="eos",
|
||||
**kwargs,
|
||||
):
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
bpe=bpe,
|
||||
load_checkpoint_heads=True,
|
||||
sample_break_mode=sample_break_mode,
|
||||
**kwargs,
|
||||
)
|
||||
return BARTHubInterface(x["args"], x["task"], x["models"][0])
|
||||
|
||||
def register_classification_head(
|
||||
self, name, num_classes=None, inner_dim=None, **kwargs
|
||||
):
|
||||
"""Register a classification head."""
|
||||
logger.info("Registering classification head: {0}".format(name))
|
||||
if name in self.classification_heads:
|
||||
prev_num_classes = self.classification_heads[name].out_proj.out_features
|
||||
prev_inner_dim = self.classification_heads[name].dense.out_features
|
||||
if num_classes != prev_num_classes or inner_dim != prev_inner_dim:
|
||||
logger.warning(
|
||||
're-registering head "{}" with num_classes {} (prev: {}) '
|
||||
"and inner_dim {} (prev: {})".format(
|
||||
name, num_classes, prev_num_classes, inner_dim, prev_inner_dim
|
||||
)
|
||||
)
|
||||
self.classification_heads[name] = BARTClassificationHead(
|
||||
input_dim=self.args.encoder_embed_dim,
|
||||
inner_dim=inner_dim or self.args.encoder_embed_dim,
|
||||
num_classes=num_classes,
|
||||
activation_fn=self.args.pooler_activation_fn,
|
||||
pooler_dropout=self.args.pooler_dropout,
|
||||
do_spectral_norm=getattr(
|
||||
self.args, "spectral_norm_classification_head", False
|
||||
),
|
||||
)
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
|
||||
prefix = name + "." if name != "" else ""
|
||||
current_head_names = (
|
||||
[]
|
||||
if not hasattr(self, "classification_heads")
|
||||
else self.classification_heads.keys()
|
||||
)
|
||||
|
||||
# Handle new classification heads present in the state dict.
|
||||
keys_to_delete = []
|
||||
for k in state_dict.keys():
|
||||
if not k.startswith(prefix + "classification_heads."):
|
||||
continue
|
||||
|
||||
head_name = k[len(prefix + "classification_heads.") :].split(".")[0]
|
||||
num_classes = state_dict[
|
||||
prefix + "classification_heads." + head_name + ".out_proj.weight"
|
||||
].size(0)
|
||||
inner_dim = state_dict[
|
||||
prefix + "classification_heads." + head_name + ".dense.weight"
|
||||
].size(0)
|
||||
|
||||
if getattr(self.args, "load_checkpoint_heads", False):
|
||||
if head_name not in current_head_names:
|
||||
self.register_classification_head(head_name, num_classes, inner_dim)
|
||||
else:
|
||||
if head_name not in current_head_names:
|
||||
logger.warning(
|
||||
"deleting classification head ({}) from checkpoint "
|
||||
"not present in current model: {}".format(head_name, k)
|
||||
)
|
||||
keys_to_delete.append(k)
|
||||
elif (
|
||||
num_classes
|
||||
!= self.classification_heads[head_name].out_proj.out_features
|
||||
or inner_dim
|
||||
!= self.classification_heads[head_name].dense.out_features
|
||||
):
|
||||
logger.warning(
|
||||
"deleting classification head ({}) from checkpoint "
|
||||
"with different dimensions than current model: {}".format(
|
||||
head_name, k
|
||||
)
|
||||
)
|
||||
keys_to_delete.append(k)
|
||||
for k in keys_to_delete:
|
||||
del state_dict[k]
|
||||
|
||||
def truncate_emb(key):
|
||||
if key in state_dict:
|
||||
state_dict[key] = state_dict[key][:-1, :]
|
||||
|
||||
# When finetuning on translation task, remove last row of
|
||||
# embedding matrix that corresponds to mask_idx token.
|
||||
loaded_dict_size = state_dict["encoder.embed_tokens.weight"].size(0)
|
||||
if (
|
||||
loaded_dict_size == len(self.encoder.dictionary) + 1
|
||||
and "<mask>" not in self.encoder.dictionary
|
||||
):
|
||||
truncate_emb("encoder.embed_tokens.weight")
|
||||
truncate_emb("decoder.embed_tokens.weight")
|
||||
truncate_emb("encoder.output_projection.weight")
|
||||
truncate_emb("decoder.output_projection.weight")
|
||||
|
||||
# When continued pretraining on new set of languages for mbart,
|
||||
# add extra lang embeddings at the end of embed_tokens.
|
||||
# Note: newly added languages are assumed to have been added at the end.
|
||||
if self.args.task == "multilingual_denoising" and loaded_dict_size < len(
|
||||
self.encoder.dictionary
|
||||
):
|
||||
logger.info(
|
||||
"Adding extra language embeddings not found in pretrained model for "
|
||||
"continued pretraining of MBART on new set of languages."
|
||||
)
|
||||
loaded_mask_token_embedding = state_dict["encoder.embed_tokens.weight"][
|
||||
-1, :
|
||||
]
|
||||
|
||||
num_langids_to_add = len(self.encoder.dictionary) - loaded_dict_size
|
||||
embed_dim = state_dict["encoder.embed_tokens.weight"].size(1)
|
||||
|
||||
new_lang_embed_to_add = torch.zeros(num_langids_to_add, embed_dim)
|
||||
nn.init.normal_(new_lang_embed_to_add, mean=0, std=embed_dim ** -0.5)
|
||||
new_lang_embed_to_add = new_lang_embed_to_add.to(
|
||||
dtype=state_dict["encoder.embed_tokens.weight"].dtype,
|
||||
)
|
||||
|
||||
state_dict["encoder.embed_tokens.weight"] = torch.cat(
|
||||
[
|
||||
state_dict["encoder.embed_tokens.weight"][
|
||||
: loaded_dict_size - 1, :
|
||||
],
|
||||
new_lang_embed_to_add,
|
||||
loaded_mask_token_embedding.unsqueeze(0),
|
||||
]
|
||||
)
|
||||
state_dict["decoder.embed_tokens.weight"] = torch.cat(
|
||||
[
|
||||
state_dict["decoder.embed_tokens.weight"][
|
||||
: loaded_dict_size - 1, :
|
||||
],
|
||||
new_lang_embed_to_add,
|
||||
loaded_mask_token_embedding.unsqueeze(0),
|
||||
]
|
||||
)
|
||||
|
||||
# Copy any newly-added classification heads into the state dict
|
||||
# with their current weights.
|
||||
if hasattr(self, "classification_heads"):
|
||||
cur_state = self.classification_heads.state_dict()
|
||||
for k, v in cur_state.items():
|
||||
if prefix + "classification_heads." + k not in state_dict:
|
||||
logger.info("Overwriting " + prefix + "classification_heads." + k)
|
||||
state_dict[prefix + "classification_heads." + k] = v
|
||||
|
||||
|
||||
class BARTClassificationHead(nn.Module):
|
||||
"""Head for sentence-level classification tasks."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
input_dim,
|
||||
inner_dim,
|
||||
num_classes,
|
||||
activation_fn,
|
||||
pooler_dropout,
|
||||
do_spectral_norm=False,
|
||||
):
|
||||
super().__init__()
|
||||
self.dense = nn.Linear(input_dim, inner_dim)
|
||||
self.activation_fn = utils.get_activation_fn(activation_fn)
|
||||
self.dropout = nn.Dropout(p=pooler_dropout)
|
||||
self.out_proj = nn.Linear(inner_dim, num_classes)
|
||||
|
||||
if do_spectral_norm:
|
||||
self.out_proj = torch.nn.utils.spectral_norm(self.out_proj)
|
||||
|
||||
def forward(self, features, **kwargs):
|
||||
x = features
|
||||
x = self.dropout(x)
|
||||
x = self.dense(x)
|
||||
x = self.activation_fn(x)
|
||||
x = self.dropout(x)
|
||||
x = self.out_proj(x)
|
||||
return x
|
||||
|
||||
|
||||
@register_model_architecture("bart", "bart_large")
|
||||
def bart_large_architecture(args):
|
||||
args.encoder_embed_path = getattr(args, "encoder_embed_path", None)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4 * 1024)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", True)
|
||||
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", args.encoder_embed_dim)
|
||||
args.decoder_ffn_embed_dim = getattr(
|
||||
args, "decoder_ffn_embed_dim", args.encoder_ffn_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 12)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
||||
args.decoder_normalize_before = getattr(args, "decoder_normalize_before", False)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", True)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
||||
args.relu_dropout = getattr(args, "relu_dropout", 0.0)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.max_target_positions = getattr(args, "max_target_positions", 1024)
|
||||
args.max_source_positions = getattr(args, "max_source_positions", 1024)
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", True
|
||||
)
|
||||
args.share_all_embeddings = getattr(args, "share_all_embeddings", True)
|
||||
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", True)
|
||||
args.layernorm_embedding = getattr(args, "layernorm_embedding", True)
|
||||
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
||||
args.pooler_dropout = getattr(args, "pooler_dropout", 0.0)
|
||||
|
||||
|
||||
@register_model_architecture("bart", "bart_base")
|
||||
def bart_base_architecture(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4 * 768)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 12)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 12)
|
||||
bart_large_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("bart", "mbart_large")
|
||||
def mbart_large_architecture(args):
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
||||
bart_large_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("bart", "mbart_base")
|
||||
def mbart_base_architecture(args):
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
||||
bart_base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("bart", "mbart_base_wmt20")
|
||||
def mbart_base_wmt20_architecture(args):
|
||||
args.layernorm_embedding = getattr(args, "layernorm_embedding", False)
|
||||
mbart_base_architecture(args)
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from .fairseq_encoder import FairseqEncoder
|
||||
|
||||
|
||||
class CompositeEncoder(FairseqEncoder):
|
||||
"""
|
||||
A wrapper around a dictionary of :class:`FairseqEncoder` objects.
|
||||
|
||||
We run forward on each encoder and return a dictionary of outputs. The first
|
||||
encoder's dictionary is used for initialization.
|
||||
|
||||
Args:
|
||||
encoders (dict): a dictionary of :class:`FairseqEncoder` objects.
|
||||
"""
|
||||
|
||||
def __init__(self, encoders):
|
||||
super().__init__(next(iter(encoders.values())).dictionary)
|
||||
self.encoders = encoders
|
||||
for key in self.encoders:
|
||||
self.add_module(key, self.encoders[key])
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
"""
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens in the source language of shape
|
||||
`(batch, src_len)`
|
||||
src_lengths (LongTensor): lengths of each source sentence of shape
|
||||
`(batch)`
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
the outputs from each Encoder
|
||||
"""
|
||||
encoder_out = {}
|
||||
for key in self.encoders:
|
||||
encoder_out[key] = self.encoders[key](src_tokens, src_lengths)
|
||||
return encoder_out
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
"""Reorder encoder output according to new_order."""
|
||||
for key in self.encoders:
|
||||
encoder_out[key] = self.encoders[key].reorder_encoder_out(
|
||||
encoder_out[key], new_order
|
||||
)
|
||||
return encoder_out
|
||||
|
||||
def max_positions(self):
|
||||
return min(self.encoders[key].max_positions() for key in self.encoders)
|
||||
|
||||
def upgrade_state_dict(self, state_dict):
|
||||
for key in self.encoders:
|
||||
self.encoders[key].upgrade_state_dict(state_dict)
|
||||
return state_dict
|
||||
@@ -0,0 +1,116 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import threading
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from torch.nn.parallel import DistributedDataParallel
|
||||
|
||||
from fairseq.distributed import (
|
||||
DistributedTimeoutWrapper,
|
||||
LegacyDistributedDataParallel,
|
||||
ModuleProxyWrapper,
|
||||
TPUDistributedDataParallel,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_GOSSIP_DISABLED = False
|
||||
try:
|
||||
import gossip
|
||||
except ImportError:
|
||||
_GOSSIP_DISABLED = True
|
||||
|
||||
|
||||
def DistributedFairseqModel(args, model, process_group, device):
|
||||
"""
|
||||
Wrap a *model* to support distributed data parallel training.
|
||||
|
||||
This is similar to the built-in DistributedDataParallel, but allows
|
||||
additional configuration of the DistributedDataParallel class to
|
||||
use, and also provides easier access to the wrapped model by
|
||||
forwarding requests for missing attributes to the wrapped model.
|
||||
|
||||
Args:
|
||||
args (argparse.Namespace): fairseq args
|
||||
model (BaseFairseqModel): model to wrap
|
||||
process_group: the c10d process group to be used for distributed data
|
||||
parallel all-reduction.
|
||||
device: device to move model to
|
||||
"""
|
||||
assert isinstance(model, nn.Module)
|
||||
if args.tpu:
|
||||
wrapped_model = TPUDistributedDataParallel(
|
||||
module=model.to(device),
|
||||
process_group=process_group,
|
||||
)
|
||||
# forward missing getattr and state_dict/load_state_dict to orig model
|
||||
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
||||
elif args.ddp_backend in {"c10d", "pytorch_ddp"}:
|
||||
wrapped_model = DistributedDataParallel(
|
||||
module=model.to(device),
|
||||
device_ids=[args.device_id],
|
||||
output_device=args.device_id,
|
||||
broadcast_buffers=args.broadcast_buffers,
|
||||
bucket_cap_mb=args.bucket_cap_mb,
|
||||
process_group=process_group,
|
||||
find_unused_parameters=args.find_unused_parameters,
|
||||
)
|
||||
# forward missing getattr and state_dict/load_state_dict to orig model
|
||||
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
||||
elif args.ddp_backend in {"no_c10d", "legacy_ddp"}:
|
||||
wrapped_model = LegacyDistributedDataParallel(
|
||||
module=model.to(device),
|
||||
buffer_size=2 ** 28,
|
||||
process_group=process_group,
|
||||
)
|
||||
# forward missing getattr and state_dict/load_state_dict to orig model
|
||||
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
||||
elif args.ddp_backend == "slow_mo":
|
||||
if _GOSSIP_DISABLED:
|
||||
raise ImportError(
|
||||
"Cannot find gossip library. Please install from: "
|
||||
"github.com/facebookresearch/stochastic_gradient_push"
|
||||
)
|
||||
|
||||
# The values of slowmo_momentum below were obtained by tuning on the
|
||||
# En-De 16 dataset by training the transformer_wmt_en_de_large model
|
||||
if args.slowmo_momentum is None:
|
||||
if args.distributed_world_size <= 16:
|
||||
args.slowmo_momentum = 0.0
|
||||
elif args.distributed_world_size <= 32:
|
||||
args.slowmo_momentum = 0.2
|
||||
elif args.distributed_world_size <= 64:
|
||||
args.slowmo_momentum = 0.5
|
||||
else:
|
||||
args.slowmo_momentum = 0.6
|
||||
|
||||
wrapped_model = gossip.GossipDataParallel(
|
||||
module=model.to(device),
|
||||
device_ids=[args.device_id],
|
||||
output_device=args.device_id,
|
||||
broadcast_buffers=args.broadcast_buffers,
|
||||
nprocs_per_node=args.nprocs_per_node,
|
||||
slowmo_momentum=args.slowmo_momentum,
|
||||
localsgd=(args.slowmo_algorithm == "LocalSGD"),
|
||||
localsgd_frequency=args.localsgd_frequency,
|
||||
)
|
||||
# forward missing getattr and state_dict/load_state_dict to orig model
|
||||
wrapped_model = ModuleProxyWrapper(wrapped_model)
|
||||
else:
|
||||
raise ValueError("Unknown --ddp-backend: " + args.ddp_backend)
|
||||
|
||||
# kill hung distributed jobs after a timeout
|
||||
wrapped_model = DistributedTimeoutWrapper(
|
||||
wrapped_model, timeout=getattr(args, "heartbeat_timeout", -1)
|
||||
)
|
||||
|
||||
return wrapped_model
|
||||
@@ -0,0 +1,105 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import torch.nn as nn
|
||||
from fairseq import utils
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
class FairseqDecoder(nn.Module):
|
||||
"""Base class for decoders."""
|
||||
|
||||
def __init__(self, dictionary):
|
||||
super().__init__()
|
||||
self.dictionary = dictionary
|
||||
self.onnx_trace = False
|
||||
self.adaptive_softmax = None
|
||||
|
||||
|
||||
def forward(self, prev_output_tokens, encoder_out=None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
prev_output_tokens (LongTensor): shifted output tokens of shape
|
||||
`(batch, tgt_len)`, for teacher forcing
|
||||
encoder_out (dict, optional): output from the encoder, used for
|
||||
encoder-side attention
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
x, extra = self.extract_features(
|
||||
prev_output_tokens, encoder_out=encoder_out, **kwargs
|
||||
)
|
||||
x = self.output_layer(x)
|
||||
return x, extra
|
||||
|
||||
def extract_features(self, prev_output_tokens, encoder_out=None, **kwargs):
|
||||
"""
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def output_layer(self, features, **kwargs):
|
||||
"""
|
||||
Project features to the default output size, e.g., vocabulary size.
|
||||
|
||||
Args:
|
||||
features (Tensor): features returned by *extract_features*.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_normalized_probs(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
return self.get_normalized_probs_scriptable(net_output, log_probs, sample)
|
||||
|
||||
# TorchScript doesn't support super() method so that the scriptable Subclass
|
||||
# can't access the base class model in Torchscript.
|
||||
# Current workaround is to add a helper function with different name and
|
||||
# call the helper function from scriptable Subclass.
|
||||
def get_normalized_probs_scriptable(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
|
||||
if hasattr(self, "adaptive_softmax") and self.adaptive_softmax is not None:
|
||||
if sample is not None:
|
||||
assert "target" in sample
|
||||
target = sample["target"]
|
||||
else:
|
||||
target = None
|
||||
out = self.adaptive_softmax.get_log_prob(net_output[0], target=target)
|
||||
return out.exp_() if not log_probs else out
|
||||
|
||||
logits = net_output[0]
|
||||
if log_probs:
|
||||
return utils.log_softmax(logits, dim=-1, onnx_trace=self.onnx_trace)
|
||||
else:
|
||||
return utils.softmax(logits, dim=-1, onnx_trace=self.onnx_trace)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the decoder."""
|
||||
return 1e6 # an arbitrary large number
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
"""Upgrade old state dicts to work with newer code."""
|
||||
return state_dict
|
||||
|
||||
def prepare_for_onnx_export_(self):
|
||||
self.onnx_trace = True
|
||||
@@ -0,0 +1,92 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
EncoderOut = NamedTuple(
|
||||
"EncoderOut",
|
||||
[
|
||||
("encoder_out", Tensor), # T x B x C
|
||||
("encoder_padding_mask", Optional[Tensor]), # B x T
|
||||
("encoder_embedding", Optional[Tensor]), # B x T x C
|
||||
("encoder_states", Optional[List[Tensor]]), # List[T x B x C]
|
||||
("src_tokens", Optional[Tensor]), # B x T
|
||||
("src_lengths", Optional[Tensor]), # B x 1
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class FairseqEncoder(nn.Module):
|
||||
"""Base class for encoders."""
|
||||
|
||||
def __init__(self, dictionary):
|
||||
super().__init__()
|
||||
self.dictionary = dictionary
|
||||
|
||||
def forward(self, src_tokens, src_lengths=None, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens in the source language of shape
|
||||
`(batch, src_len)`
|
||||
src_lengths (LongTensor): lengths of each source sentence of shape
|
||||
`(batch)`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def forward_torchscript(self, net_input: Dict[str, Tensor]):
|
||||
"""A TorchScript-compatible version of forward.
|
||||
|
||||
Encoders which use additional arguments may want to override
|
||||
this method for TorchScript compatibility.
|
||||
"""
|
||||
if torch.jit.is_scripting():
|
||||
return self.forward(
|
||||
src_tokens=net_input["src_tokens"],
|
||||
src_lengths=net_input["src_lengths"],
|
||||
)
|
||||
else:
|
||||
return self.forward_non_torchscript(net_input)
|
||||
|
||||
@torch.jit.unused
|
||||
def forward_non_torchscript(self, net_input: Dict[str, Tensor]):
|
||||
encoder_input = {
|
||||
k: v for k, v in net_input.items() if k != "prev_output_tokens"
|
||||
}
|
||||
return self.forward(**encoder_input)
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
"""
|
||||
Reorder encoder output according to `new_order`.
|
||||
|
||||
Args:
|
||||
encoder_out: output from the ``forward()`` method
|
||||
new_order (LongTensor): desired order
|
||||
|
||||
Returns:
|
||||
`encoder_out` rearranged according to `new_order`
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return 1e6 # an arbitrary large number
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
"""Upgrade old state dicts to work with newer code."""
|
||||
return state_dict
|
||||
|
||||
def set_num_updates(self, num_updates):
|
||||
"""State from trainer to pass along to model at every update."""
|
||||
|
||||
def _apply(m):
|
||||
if hasattr(m, "set_num_updates") and m != self:
|
||||
m.set_num_updates(num_updates)
|
||||
|
||||
self.apply(_apply)
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import logging
|
||||
from typing import Dict, Optional
|
||||
|
||||
from fairseq.incremental_decoding_utils import with_incremental_state
|
||||
from fairseq.models import FairseqDecoder
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@with_incremental_state
|
||||
class FairseqIncrementalDecoder(FairseqDecoder):
|
||||
"""Base class for incremental decoders.
|
||||
|
||||
Incremental decoding is a special mode at inference time where the Model
|
||||
only receives a single timestep of input corresponding to the previous
|
||||
output token (for teacher forcing) and must produce the next output
|
||||
*incrementally*. Thus the model must cache any long-term state that is
|
||||
needed about the sequence, e.g., hidden states, convolutional states, etc.
|
||||
|
||||
Compared to the standard :class:`FairseqDecoder` interface, the incremental
|
||||
decoder interface allows :func:`forward` functions to take an extra keyword
|
||||
argument (*incremental_state*) that can be used to cache state across
|
||||
time-steps.
|
||||
|
||||
The :class:`FairseqIncrementalDecoder` interface also defines the
|
||||
:func:`reorder_incremental_state` method, which is used during beam search
|
||||
to select and reorder the incremental state based on the selection of beams.
|
||||
|
||||
To learn more about how incremental decoding works, refer to `this blog
|
||||
<http://www.telesens.co/2019/04/21/understanding-incremental-decoding-in-fairseq/>`_.
|
||||
"""
|
||||
|
||||
def __init__(self, dictionary):
|
||||
super().__init__(dictionary)
|
||||
|
||||
def forward(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **kwargs
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
prev_output_tokens (LongTensor): shifted output tokens of shape
|
||||
`(batch, tgt_len)`, for teacher forcing
|
||||
encoder_out (dict, optional): output from the encoder, used for
|
||||
encoder-side attention
|
||||
incremental_state (dict, optional): dictionary used for storing
|
||||
state during :ref:`Incremental decoding`
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def extract_features(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **kwargs
|
||||
):
|
||||
"""
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def reorder_incremental_state(
|
||||
self,
|
||||
incremental_state: Dict[str, Dict[str, Optional[Tensor]]],
|
||||
new_order: Tensor,
|
||||
):
|
||||
"""Reorder incremental state.
|
||||
|
||||
This will be called when the order of the input has changed from the
|
||||
previous time step. A typical use case is beam search, where the input
|
||||
order changes between time steps based on the selection of beams.
|
||||
"""
|
||||
pass
|
||||
|
||||
def reorder_incremental_state_scripting(
|
||||
self,
|
||||
incremental_state: Dict[str, Dict[str, Optional[Tensor]]],
|
||||
new_order: Tensor,
|
||||
):
|
||||
"""Main entry point for reordering the incremental state.
|
||||
|
||||
Due to limitations in TorchScript, we call this function in
|
||||
:class:`fairseq.sequence_generator.SequenceGenerator` instead of
|
||||
calling :func:`reorder_incremental_state` directly.
|
||||
"""
|
||||
for module in self.modules():
|
||||
if hasattr(module, "reorder_incremental_state"):
|
||||
result = module.reorder_incremental_state(incremental_state, new_order)
|
||||
if result is not None:
|
||||
incremental_state = result
|
||||
|
||||
def set_beam_size(self, beam_size):
|
||||
"""Sets the beam size in the decoder and all children."""
|
||||
if getattr(self, "_beam_size", -1) != beam_size:
|
||||
seen = set()
|
||||
|
||||
def apply_set_beam_size(module):
|
||||
if (
|
||||
module != self
|
||||
and hasattr(module, "set_beam_size")
|
||||
and module not in seen
|
||||
):
|
||||
seen.add(module)
|
||||
module.set_beam_size(beam_size)
|
||||
|
||||
self.apply(apply_set_beam_size)
|
||||
self._beam_size = beam_size
|
||||
@@ -0,0 +1,559 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
Base classes for various fairseq models.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from argparse import Namespace
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.data import Dictionary
|
||||
from fairseq.dataclass.utils import (
|
||||
convert_namespace_to_omegaconf,
|
||||
gen_parser_from_dataclass,
|
||||
)
|
||||
from fairseq.models import FairseqDecoder, FairseqEncoder
|
||||
from omegaconf import DictConfig
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseFairseqModel(nn.Module):
|
||||
"""Base class for fairseq models."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._is_generation_fast = False
|
||||
|
||||
@classmethod
|
||||
def add_args(cls, parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
dc = getattr(cls, "__dataclass", None)
|
||||
if dc is not None:
|
||||
# do not set defaults so that settings defaults from various architectures still works
|
||||
gen_parser_from_dataclass(parser, dc(), delete_default=True)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
raise NotImplementedError("Model must implement the build_model method")
|
||||
|
||||
def get_targets(self, sample, net_output):
|
||||
"""Get targets from either the sample or the net's output."""
|
||||
return sample["target"]
|
||||
|
||||
def get_normalized_probs(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
return self.get_normalized_probs_scriptable(net_output, log_probs, sample)
|
||||
|
||||
# TorchScript doesn't support super() method so that the scriptable Subclass
|
||||
# can't access the base class model in Torchscript.
|
||||
# Current workaround is to add a helper function with different name and
|
||||
# call the helper function from scriptable Subclass.
|
||||
def get_normalized_probs_scriptable(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
"""Scriptable helper function for get_normalized_probs in ~BaseFairseqModel"""
|
||||
if hasattr(self, "decoder"):
|
||||
return self.decoder.get_normalized_probs(net_output, log_probs, sample)
|
||||
elif torch.is_tensor(net_output):
|
||||
# syntactic sugar for simple models which don't have a decoder
|
||||
# (e.g., the classification tutorial)
|
||||
logits = net_output.float()
|
||||
if log_probs:
|
||||
return F.log_softmax(logits, dim=-1)
|
||||
else:
|
||||
return F.softmax(logits, dim=-1)
|
||||
raise NotImplementedError
|
||||
|
||||
def extract_features(self, *args, **kwargs):
|
||||
"""Similar to *forward* but only return features."""
|
||||
return self(*args, **kwargs)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return None
|
||||
|
||||
def load_state_dict(
|
||||
self,
|
||||
state_dict,
|
||||
strict=True,
|
||||
model_cfg: Optional[DictConfig] = None,
|
||||
args: Optional[Namespace] = None,
|
||||
):
|
||||
"""Copies parameters and buffers from *state_dict* into this module and
|
||||
its descendants.
|
||||
|
||||
Overrides the method in :class:`nn.Module`. Compared with that method
|
||||
this additionally "upgrades" *state_dicts* from old checkpoints.
|
||||
"""
|
||||
|
||||
if model_cfg is None and args is not None:
|
||||
logger.warn("using 'args' is deprecated, please update your code to use dataclass config")
|
||||
model_cfg = convert_namespace_to_omegaconf(args).model
|
||||
|
||||
self.upgrade_state_dict(state_dict)
|
||||
from fairseq.checkpoint_utils import prune_state_dict
|
||||
new_state_dict = prune_state_dict(state_dict, model_cfg)
|
||||
return super().load_state_dict(new_state_dict, strict)
|
||||
|
||||
def upgrade_state_dict(self, state_dict):
|
||||
"""Upgrade old state dicts to work with newer code."""
|
||||
self.upgrade_state_dict_named(state_dict, "")
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
"""Upgrade old state dicts to work with newer code.
|
||||
|
||||
Args:
|
||||
state_dict (dict): state dictionary to upgrade, in place
|
||||
name (str): the state dict key corresponding to the current module
|
||||
"""
|
||||
assert state_dict is not None
|
||||
|
||||
def do_upgrade(m, prefix):
|
||||
if len(prefix) > 0:
|
||||
prefix += "."
|
||||
|
||||
for n, c in m.named_children():
|
||||
name = prefix + n
|
||||
if hasattr(c, "upgrade_state_dict_named"):
|
||||
c.upgrade_state_dict_named(state_dict, name)
|
||||
elif hasattr(c, "upgrade_state_dict"):
|
||||
c.upgrade_state_dict(state_dict)
|
||||
do_upgrade(c, name)
|
||||
|
||||
do_upgrade(self, name)
|
||||
|
||||
def set_num_updates(self, num_updates):
|
||||
"""State from trainer to pass along to model at every update."""
|
||||
|
||||
def _apply(m):
|
||||
if hasattr(m, "set_num_updates") and m != self:
|
||||
m.set_num_updates(num_updates)
|
||||
|
||||
self.apply(_apply)
|
||||
|
||||
def prepare_for_inference_(self, cfg: DictConfig):
|
||||
"""Prepare model for inference."""
|
||||
kwargs = {}
|
||||
kwargs["beamable_mm_beam_size"] = (
|
||||
None
|
||||
if getattr(cfg.generation, "no_beamable_mm", False)
|
||||
else getattr(cfg.generation, "beam", 5)
|
||||
)
|
||||
kwargs["need_attn"] = getattr(cfg.generation, "print_alignment", False)
|
||||
if getattr(cfg.generation, "retain_dropout", False):
|
||||
kwargs["retain_dropout"] = cfg.generation.retain_dropout
|
||||
kwargs["retain_dropout_modules"] = cfg.generation.retain_dropout_modules
|
||||
self.make_generation_fast_(**kwargs)
|
||||
|
||||
def make_generation_fast_(self, **kwargs):
|
||||
"""
|
||||
Legacy entry point to optimize model for faster generation.
|
||||
Prefer prepare_for_inference_.
|
||||
"""
|
||||
if self._is_generation_fast:
|
||||
return # only apply once
|
||||
self._is_generation_fast = True
|
||||
|
||||
# remove weight norm from all modules in the network
|
||||
def apply_remove_weight_norm(module):
|
||||
try:
|
||||
nn.utils.remove_weight_norm(module)
|
||||
except (AttributeError, ValueError): # this module didn't have weight norm
|
||||
return
|
||||
|
||||
self.apply(apply_remove_weight_norm)
|
||||
|
||||
def apply_make_generation_fast_(module, prefix):
|
||||
if len(prefix) > 0:
|
||||
prefix += "."
|
||||
|
||||
base_func = BaseFairseqModel.make_generation_fast_
|
||||
for n, m in module.named_modules():
|
||||
if (
|
||||
m != self
|
||||
and hasattr(m, "make_generation_fast_")
|
||||
# don't call this implementation again, e.g., if
|
||||
# children modules also inherit from BaseFairseqModel
|
||||
and m.make_generation_fast_.__func__ is not base_func
|
||||
):
|
||||
name = prefix + n
|
||||
m.make_generation_fast_(name=name, **kwargs)
|
||||
|
||||
apply_make_generation_fast_(self, "")
|
||||
|
||||
def train(mode=True):
|
||||
if mode:
|
||||
raise RuntimeError("cannot train after make_generation_fast")
|
||||
|
||||
# this model should no longer be used for training
|
||||
self.eval()
|
||||
self.train = train
|
||||
|
||||
def prepare_for_onnx_export_(self, **kwargs):
|
||||
"""Make model exportable via ONNX trace."""
|
||||
seen = set()
|
||||
|
||||
def apply_prepare_for_onnx_export_(module):
|
||||
if (
|
||||
module != self
|
||||
and hasattr(module, "prepare_for_onnx_export_")
|
||||
and module not in seen
|
||||
):
|
||||
seen.add(module)
|
||||
module.prepare_for_onnx_export_(**kwargs)
|
||||
|
||||
self.apply(apply_prepare_for_onnx_export_)
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(
|
||||
cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file="model.pt",
|
||||
data_name_or_path=".",
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Load a :class:`~fairseq.models.FairseqModel` from a pre-trained model
|
||||
file. Downloads and caches the pre-trained model file if needed.
|
||||
|
||||
The base implementation returns a
|
||||
:class:`~fairseq.hub_utils.GeneratorHubInterface`, which can be used to
|
||||
generate translations or sample from language models. The underlying
|
||||
:class:`~fairseq.models.FairseqModel` can be accessed via the
|
||||
*generator.models* attribute.
|
||||
|
||||
Other models may override this to implement custom hub interfaces.
|
||||
|
||||
Args:
|
||||
model_name_or_path (str): either the name of a pre-trained model to
|
||||
load or a path/URL to a pre-trained model state dict
|
||||
checkpoint_file (str, optional): colon-separated list of checkpoint
|
||||
files in the model archive to ensemble (default: 'model.pt')
|
||||
data_name_or_path (str, optional): point args.data to the archive
|
||||
at the given path/URL. Can start with '.' or './' to reuse the
|
||||
model archive path.
|
||||
"""
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
**kwargs,
|
||||
)
|
||||
logger.info(x["args"])
|
||||
return hub_utils.GeneratorHubInterface(x["args"], x["task"], x["models"])
|
||||
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {}
|
||||
|
||||
|
||||
class FairseqEncoderDecoderModel(BaseFairseqModel):
|
||||
"""Base class for encoder-decoder models.
|
||||
|
||||
Args:
|
||||
encoder (FairseqEncoder): the encoder
|
||||
decoder (FairseqDecoder): the decoder
|
||||
"""
|
||||
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__()
|
||||
|
||||
self.encoder = encoder
|
||||
self.decoder = decoder
|
||||
assert isinstance(self.encoder, FairseqEncoder)
|
||||
assert isinstance(self.decoder, FairseqDecoder)
|
||||
|
||||
def forward(self, src_tokens, src_lengths, prev_output_tokens, **kwargs):
|
||||
"""
|
||||
Run the forward pass for an encoder-decoder model.
|
||||
|
||||
First feed a batch of source tokens through the encoder. Then, feed the
|
||||
encoder output and previous decoder outputs (i.e., teacher forcing) to
|
||||
the decoder to produce the next outputs::
|
||||
|
||||
encoder_out = self.encoder(src_tokens, src_lengths)
|
||||
return self.decoder(prev_output_tokens, encoder_out)
|
||||
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens in the source language of shape
|
||||
`(batch, src_len)`
|
||||
src_lengths (LongTensor): source sentence lengths of shape `(batch)`
|
||||
prev_output_tokens (LongTensor): previous decoder outputs of shape
|
||||
`(batch, tgt_len)`, for teacher forcing
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
encoder_out = self.encoder(src_tokens, src_lengths=src_lengths, **kwargs)
|
||||
decoder_out = self.decoder(
|
||||
prev_output_tokens, encoder_out=encoder_out, **kwargs
|
||||
)
|
||||
return decoder_out
|
||||
|
||||
def forward_decoder(self, prev_output_tokens, **kwargs):
|
||||
return self.decoder(prev_output_tokens, **kwargs)
|
||||
|
||||
def extract_features(self, src_tokens, src_lengths, prev_output_tokens, **kwargs):
|
||||
"""
|
||||
Similar to *forward* but only return features.
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
encoder_out = self.encoder(src_tokens, src_lengths=src_lengths, **kwargs)
|
||||
features = self.decoder.extract_features(
|
||||
prev_output_tokens, encoder_out=encoder_out, **kwargs
|
||||
)
|
||||
return features
|
||||
|
||||
def output_layer(self, features, **kwargs):
|
||||
"""Project features to the default output size (typically vocabulary size)."""
|
||||
return self.decoder.output_layer(features, **kwargs)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return (self.encoder.max_positions(), self.decoder.max_positions())
|
||||
|
||||
def max_decoder_positions(self):
|
||||
"""Maximum length supported by the decoder."""
|
||||
return self.decoder.max_positions()
|
||||
|
||||
|
||||
class FairseqModel(FairseqEncoderDecoderModel):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
utils.deprecation_warning(
|
||||
"FairseqModel is deprecated, please use FairseqEncoderDecoderModel "
|
||||
"or BaseFairseqModel instead",
|
||||
stacklevel=4,
|
||||
)
|
||||
|
||||
|
||||
class FairseqMultiModel(BaseFairseqModel):
|
||||
"""Base class for combining multiple encoder-decoder models."""
|
||||
|
||||
def __init__(self, encoders, decoders):
|
||||
super().__init__()
|
||||
assert encoders.keys() == decoders.keys()
|
||||
self.keys = list(encoders.keys())
|
||||
for key in self.keys:
|
||||
assert isinstance(encoders[key], FairseqEncoder)
|
||||
assert isinstance(decoders[key], FairseqDecoder)
|
||||
|
||||
self.models = nn.ModuleDict(
|
||||
{
|
||||
key: FairseqEncoderDecoderModel(encoders[key], decoders[key])
|
||||
for key in self.keys
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def build_shared_embeddings(
|
||||
dicts: Dict[str, Dictionary],
|
||||
langs: List[str],
|
||||
embed_dim: int,
|
||||
build_embedding: callable,
|
||||
pretrained_embed_path: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Helper function to build shared embeddings for a set of languages after
|
||||
checking that all dicts corresponding to those languages are equivalent.
|
||||
|
||||
Args:
|
||||
dicts: Dict of lang_id to its corresponding Dictionary
|
||||
langs: languages that we want to share embeddings for
|
||||
embed_dim: embedding dimension
|
||||
build_embedding: callable function to actually build the embedding
|
||||
pretrained_embed_path: Optional path to load pretrained embeddings
|
||||
"""
|
||||
shared_dict = dicts[langs[0]]
|
||||
if any(dicts[lang] != shared_dict for lang in langs):
|
||||
raise ValueError(
|
||||
"--share-*-embeddings requires a joined dictionary: "
|
||||
"--share-encoder-embeddings requires a joined source "
|
||||
"dictionary, --share-decoder-embeddings requires a joined "
|
||||
"target dictionary, and --share-all-embeddings requires a "
|
||||
"joint source + target dictionary."
|
||||
)
|
||||
return build_embedding(shared_dict, embed_dim, pretrained_embed_path)
|
||||
|
||||
def forward(self, src_tokens, src_lengths, prev_output_tokens, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return {
|
||||
key: (
|
||||
self.models[key].encoder.max_positions(),
|
||||
self.models[key].decoder.max_positions(),
|
||||
)
|
||||
for key in self.keys
|
||||
}
|
||||
|
||||
def max_decoder_positions(self):
|
||||
"""Maximum length supported by the decoder."""
|
||||
return min(model.decoder.max_positions() for model in self.models.values())
|
||||
|
||||
@property
|
||||
def encoder(self):
|
||||
return self.models[self.keys[0]].encoder
|
||||
|
||||
@property
|
||||
def decoder(self):
|
||||
return self.models[self.keys[0]].decoder
|
||||
|
||||
def forward_decoder(self, prev_output_tokens, **kwargs):
|
||||
return self.decoder(prev_output_tokens, **kwargs)
|
||||
|
||||
def load_state_dict(
|
||||
self,
|
||||
state_dict,
|
||||
strict=True,
|
||||
model_cfg=None,
|
||||
args: Optional[Namespace] = None,
|
||||
):
|
||||
"""Copies parameters and buffers from *state_dict* into this module and
|
||||
its descendants.
|
||||
|
||||
Overrides the method in :class:`nn.Module`. Compared with that method
|
||||
this additionally "upgrades" *state_dicts* from old checkpoints.
|
||||
"""
|
||||
|
||||
if model_cfg is None and args is not None:
|
||||
logger.warn("using 'args' is deprecated, please update your code to use dataclass config")
|
||||
model_cfg = convert_namespace_to_omegaconf(args).model
|
||||
|
||||
self.upgrade_state_dict(state_dict)
|
||||
from fairseq.checkpoint_utils import prune_state_dict
|
||||
new_state_dict = prune_state_dict(state_dict, model_cfg)
|
||||
return super().load_state_dict(new_state_dict, strict)
|
||||
|
||||
|
||||
class FairseqLanguageModel(BaseFairseqModel):
|
||||
"""Base class for decoder-only models.
|
||||
|
||||
Args:
|
||||
decoder (FairseqDecoder): the decoder
|
||||
"""
|
||||
|
||||
def __init__(self, decoder):
|
||||
super().__init__()
|
||||
self.decoder = decoder
|
||||
assert isinstance(self.decoder, FairseqDecoder)
|
||||
|
||||
def forward(self, src_tokens, **kwargs):
|
||||
"""
|
||||
Run the forward pass for a decoder-only model.
|
||||
|
||||
Feeds a batch of tokens through the decoder to predict the next tokens.
|
||||
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens on which to condition the decoder,
|
||||
of shape `(batch, tgt_len)`
|
||||
src_lengths (LongTensor): source sentence lengths of shape `(batch)`
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's output of shape `(batch, seq_len, vocab)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
return self.decoder(src_tokens, **kwargs)
|
||||
|
||||
def forward_decoder(self, prev_output_tokens, **kwargs):
|
||||
return self.decoder(prev_output_tokens, **kwargs)
|
||||
|
||||
def extract_features(self, src_tokens, **kwargs):
|
||||
"""
|
||||
Similar to *forward* but only return features.
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, seq_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
return self.decoder.extract_features(src_tokens, **kwargs)
|
||||
|
||||
def output_layer(self, features, **kwargs):
|
||||
"""Project features to the default output size (typically vocabulary size)."""
|
||||
return self.decoder.output_layer(features, **kwargs)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return self.decoder.max_positions()
|
||||
|
||||
def max_decoder_positions(self):
|
||||
"""Maximum length supported by the decoder."""
|
||||
return self.decoder.max_positions()
|
||||
|
||||
@property
|
||||
def supported_targets(self):
|
||||
return {"future"}
|
||||
|
||||
|
||||
class FairseqEncoderModel(BaseFairseqModel):
|
||||
"""Base class for encoder-only models.
|
||||
|
||||
Args:
|
||||
encoder (FairseqEncoder): the encoder
|
||||
"""
|
||||
|
||||
def __init__(self, encoder):
|
||||
super().__init__()
|
||||
self.encoder = encoder
|
||||
assert isinstance(self.encoder, FairseqEncoder)
|
||||
|
||||
def forward(self, src_tokens, src_lengths, **kwargs):
|
||||
"""
|
||||
Run the forward pass for a encoder-only model.
|
||||
|
||||
Feeds a batch of tokens through the encoder to generate features.
|
||||
|
||||
Args:
|
||||
src_tokens (LongTensor): input tokens of shape `(batch, src_len)`
|
||||
src_lengths (LongTensor): source sentence lengths of shape `(batch)`
|
||||
|
||||
Returns:
|
||||
the encoder's output, typically of shape `(batch, src_len, features)`
|
||||
"""
|
||||
return self.encoder(src_tokens, src_lengths, **kwargs)
|
||||
|
||||
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
encoder_out = net_output["encoder_out"]
|
||||
if torch.is_tensor(encoder_out):
|
||||
logits = encoder_out.float()
|
||||
if log_probs:
|
||||
return F.log_softmax(logits, dim=-1)
|
||||
else:
|
||||
return F.softmax(logits, dim=-1)
|
||||
raise NotImplementedError
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return self.encoder.max_positions()
|
||||
@@ -0,0 +1,756 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import math
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
FairseqIncrementalDecoder,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.modules import (
|
||||
AdaptiveSoftmax,
|
||||
BeamableMM,
|
||||
FairseqDropout,
|
||||
GradMultiply,
|
||||
LearnedPositionalEmbedding,
|
||||
LinearizedConvolution,
|
||||
)
|
||||
|
||||
|
||||
@register_model("fconv")
|
||||
class FConvModel(FairseqEncoderDecoderModel):
|
||||
"""
|
||||
A fully convolutional model, i.e. a convolutional encoder and a
|
||||
convolutional decoder, as described in `"Convolutional Sequence to Sequence
|
||||
Learning" (Gehring et al., 2017) <https://arxiv.org/abs/1705.03122>`_.
|
||||
|
||||
Args:
|
||||
encoder (FConvEncoder): the encoder
|
||||
decoder (FConvDecoder): the decoder
|
||||
|
||||
The Convolutional model provides the following named architectures and
|
||||
command-line arguments:
|
||||
|
||||
.. argparse::
|
||||
:ref: fairseq.models.fconv_parser
|
||||
:prog:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
def moses_subword(path):
|
||||
return {
|
||||
"path": path,
|
||||
"tokenizer": "moses",
|
||||
"bpe": "subword_nmt",
|
||||
}
|
||||
|
||||
return {
|
||||
"conv.wmt14.en-fr": moses_subword(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/wmt14.v2.en-fr.fconv-py.tar.bz2"
|
||||
),
|
||||
"conv.wmt14.en-de": moses_subword(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/wmt14.en-de.fconv-py.tar.bz2"
|
||||
),
|
||||
"conv.wmt17.en-de": moses_subword(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/wmt17.v2.en-de.fconv-py.tar.bz2"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
self.encoder.num_attention_layers = sum(
|
||||
layer is not None for layer in decoder.attention
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# fmt: off
|
||||
parser.add_argument('--dropout', type=float, metavar='D',
|
||||
help='dropout probability')
|
||||
parser.add_argument('--encoder-embed-dim', type=int, metavar='N',
|
||||
help='encoder embedding dimension')
|
||||
parser.add_argument('--encoder-embed-path', type=str, metavar='STR',
|
||||
help='path to pre-trained encoder embedding')
|
||||
parser.add_argument('--encoder-layers', type=str, metavar='EXPR',
|
||||
help='encoder layers [(dim, kernel_size), ...]')
|
||||
parser.add_argument('--decoder-embed-dim', type=int, metavar='N',
|
||||
help='decoder embedding dimension')
|
||||
parser.add_argument('--decoder-embed-path', type=str, metavar='STR',
|
||||
help='path to pre-trained decoder embedding')
|
||||
parser.add_argument('--decoder-layers', type=str, metavar='EXPR',
|
||||
help='decoder layers [(dim, kernel_size), ...]')
|
||||
parser.add_argument('--decoder-out-embed-dim', type=int, metavar='N',
|
||||
help='decoder output embedding dimension')
|
||||
parser.add_argument('--decoder-attention', type=str, metavar='EXPR',
|
||||
help='decoder attention [True, ...]')
|
||||
parser.add_argument('--share-input-output-embed', action='store_true',
|
||||
help='share input and output embeddings (requires'
|
||||
' --decoder-out-embed-dim and --decoder-embed-dim'
|
||||
' to be equal)')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
# make sure that all args are properly defaulted (in case there are any new ones)
|
||||
base_architecture(args)
|
||||
|
||||
encoder_embed_dict = None
|
||||
if args.encoder_embed_path:
|
||||
encoder_embed_dict = utils.parse_embedding(args.encoder_embed_path)
|
||||
utils.print_embed_overlap(encoder_embed_dict, task.source_dictionary)
|
||||
|
||||
decoder_embed_dict = None
|
||||
if args.decoder_embed_path:
|
||||
decoder_embed_dict = utils.parse_embedding(args.decoder_embed_path)
|
||||
utils.print_embed_overlap(decoder_embed_dict, task.target_dictionary)
|
||||
|
||||
encoder = FConvEncoder(
|
||||
dictionary=task.source_dictionary,
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
embed_dict=encoder_embed_dict,
|
||||
convolutions=eval(args.encoder_layers),
|
||||
dropout=args.dropout,
|
||||
max_positions=args.max_source_positions,
|
||||
)
|
||||
decoder = FConvDecoder(
|
||||
dictionary=task.target_dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
embed_dict=decoder_embed_dict,
|
||||
convolutions=eval(args.decoder_layers),
|
||||
out_embed_dim=args.decoder_out_embed_dim,
|
||||
attention=eval(args.decoder_attention),
|
||||
dropout=args.dropout,
|
||||
max_positions=args.max_target_positions,
|
||||
share_embed=args.share_input_output_embed,
|
||||
)
|
||||
return FConvModel(encoder, decoder)
|
||||
|
||||
|
||||
class FConvEncoder(FairseqEncoder):
|
||||
"""
|
||||
Convolutional encoder consisting of `len(convolutions)` layers.
|
||||
|
||||
Args:
|
||||
dictionary (~fairseq.data.Dictionary): encoding dictionary
|
||||
embed_dim (int, optional): embedding dimension
|
||||
embed_dict (str, optional): filename from which to load pre-trained
|
||||
embeddings
|
||||
max_positions (int, optional): maximum supported input sequence length
|
||||
convolutions (list, optional): the convolutional layer structure. Each
|
||||
list item `i` corresponds to convolutional layer `i`. Layers are
|
||||
given as ``(out_channels, kernel_width, [residual])``. Residual
|
||||
connections are added between layers when ``residual=1`` (which is
|
||||
the default behavior).
|
||||
dropout (float, optional): dropout to be applied before each conv layer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
embed_dict=None,
|
||||
max_positions=1024,
|
||||
convolutions=((512, 3),) * 20,
|
||||
dropout=0.1,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.dropout_module = FairseqDropout(
|
||||
dropout, module_name=self.__class__.__name__
|
||||
)
|
||||
self.num_attention_layers = None
|
||||
|
||||
num_embeddings = len(dictionary)
|
||||
self.padding_idx = dictionary.pad()
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, self.padding_idx)
|
||||
if embed_dict:
|
||||
self.embed_tokens = utils.load_embedding(
|
||||
embed_dict, self.dictionary, self.embed_tokens
|
||||
)
|
||||
|
||||
self.embed_positions = PositionalEmbedding(
|
||||
max_positions,
|
||||
embed_dim,
|
||||
self.padding_idx,
|
||||
)
|
||||
|
||||
convolutions = extend_conv_spec(convolutions)
|
||||
in_channels = convolutions[0][0]
|
||||
self.fc1 = Linear(embed_dim, in_channels, dropout=dropout)
|
||||
self.projections = nn.ModuleList()
|
||||
self.convolutions = nn.ModuleList()
|
||||
self.residuals = []
|
||||
|
||||
layer_in_channels = [in_channels]
|
||||
for _, (out_channels, kernel_size, residual) in enumerate(convolutions):
|
||||
if residual == 0:
|
||||
residual_dim = out_channels
|
||||
else:
|
||||
residual_dim = layer_in_channels[-residual]
|
||||
self.projections.append(
|
||||
Linear(residual_dim, out_channels)
|
||||
if residual_dim != out_channels
|
||||
else None
|
||||
)
|
||||
if kernel_size % 2 == 1:
|
||||
padding = kernel_size // 2
|
||||
else:
|
||||
padding = 0
|
||||
self.convolutions.append(
|
||||
ConvTBC(
|
||||
in_channels,
|
||||
out_channels * 2,
|
||||
kernel_size,
|
||||
dropout=dropout,
|
||||
padding=padding,
|
||||
)
|
||||
)
|
||||
self.residuals.append(residual)
|
||||
in_channels = out_channels
|
||||
layer_in_channels.append(out_channels)
|
||||
self.fc2 = Linear(in_channels, embed_dim)
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
"""
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens in the source language of shape
|
||||
`(batch, src_len)`
|
||||
src_lengths (LongTensor): lengths of each source sentence of shape
|
||||
`(batch)`
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
- **encoder_out** (tuple): a tuple with two elements, where the
|
||||
first element is the last encoder layer's output and the
|
||||
second element is the same quantity summed with the input
|
||||
embedding (used for attention). The shape of both tensors is
|
||||
`(batch, src_len, embed_dim)`.
|
||||
- **encoder_padding_mask** (ByteTensor): the positions of
|
||||
padding elements of shape `(batch, src_len)`
|
||||
"""
|
||||
# embed tokens and positions
|
||||
x = self.embed_tokens(src_tokens) + self.embed_positions(src_tokens)
|
||||
x = self.dropout_module(x)
|
||||
input_embedding = x
|
||||
|
||||
# project to size of convolution
|
||||
x = self.fc1(x)
|
||||
|
||||
# used to mask padding in input
|
||||
encoder_padding_mask = src_tokens.eq(self.padding_idx).t() # -> T x B
|
||||
if not encoder_padding_mask.any():
|
||||
encoder_padding_mask = None
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
residuals = [x]
|
||||
# temporal convolutions
|
||||
for proj, conv, res_layer in zip(
|
||||
self.projections, self.convolutions, self.residuals
|
||||
):
|
||||
if res_layer > 0:
|
||||
residual = residuals[-res_layer]
|
||||
residual = residual if proj is None else proj(residual)
|
||||
else:
|
||||
residual = None
|
||||
|
||||
if encoder_padding_mask is not None:
|
||||
x = x.masked_fill(encoder_padding_mask.unsqueeze(-1), 0)
|
||||
|
||||
x = self.dropout_module(x)
|
||||
if conv.kernel_size[0] % 2 == 1:
|
||||
# padding is implicit in the conv
|
||||
x = conv(x)
|
||||
else:
|
||||
padding_l = (conv.kernel_size[0] - 1) // 2
|
||||
padding_r = conv.kernel_size[0] // 2
|
||||
x = F.pad(x, (0, 0, 0, 0, padding_l, padding_r))
|
||||
x = conv(x)
|
||||
x = F.glu(x, dim=2)
|
||||
|
||||
if residual is not None:
|
||||
x = (x + residual) * math.sqrt(0.5)
|
||||
residuals.append(x)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(1, 0)
|
||||
|
||||
# project back to size of embedding
|
||||
x = self.fc2(x)
|
||||
|
||||
if encoder_padding_mask is not None:
|
||||
encoder_padding_mask = encoder_padding_mask.t() # -> B x T
|
||||
x = x.masked_fill(encoder_padding_mask.unsqueeze(-1), 0)
|
||||
|
||||
# scale gradients (this only affects backward, not forward)
|
||||
x = GradMultiply.apply(x, 1.0 / (2.0 * self.num_attention_layers))
|
||||
|
||||
# add output to input embedding for attention
|
||||
y = (x + input_embedding) * math.sqrt(0.5)
|
||||
|
||||
return {
|
||||
"encoder_out": (x, y),
|
||||
"encoder_padding_mask": encoder_padding_mask, # B x T
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
if encoder_out["encoder_out"] is not None:
|
||||
encoder_out["encoder_out"] = (
|
||||
encoder_out["encoder_out"][0].index_select(0, new_order),
|
||||
encoder_out["encoder_out"][1].index_select(0, new_order),
|
||||
)
|
||||
if encoder_out["encoder_padding_mask"] is not None:
|
||||
encoder_out["encoder_padding_mask"] = encoder_out[
|
||||
"encoder_padding_mask"
|
||||
].index_select(0, new_order)
|
||||
return encoder_out
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return self.embed_positions.max_positions
|
||||
|
||||
|
||||
class AttentionLayer(nn.Module):
|
||||
def __init__(self, conv_channels, embed_dim, bmm=None):
|
||||
super().__init__()
|
||||
# projects from output of convolution to embedding dimension
|
||||
self.in_projection = Linear(conv_channels, embed_dim)
|
||||
# projects from embedding dimension to convolution size
|
||||
self.out_projection = Linear(embed_dim, conv_channels)
|
||||
|
||||
self.bmm = bmm if bmm is not None else torch.bmm
|
||||
|
||||
def forward(self, x, target_embedding, encoder_out, encoder_padding_mask):
|
||||
residual = x
|
||||
|
||||
# attention
|
||||
x = (self.in_projection(x) + target_embedding) * math.sqrt(0.5)
|
||||
x = self.bmm(x, encoder_out[0])
|
||||
|
||||
# don't attend over padding
|
||||
if encoder_padding_mask is not None:
|
||||
x = (
|
||||
x.float()
|
||||
.masked_fill(encoder_padding_mask.unsqueeze(1), float("-inf"))
|
||||
.type_as(x)
|
||||
) # FP16 support: cast to float and back
|
||||
|
||||
# softmax over last dim
|
||||
sz = x.size()
|
||||
x = F.softmax(x.view(sz[0] * sz[1], sz[2]), dim=1)
|
||||
x = x.view(sz)
|
||||
attn_scores = x
|
||||
|
||||
x = self.bmm(x, encoder_out[1])
|
||||
|
||||
# scale attention output (respecting potentially different lengths)
|
||||
s = encoder_out[1].size(1)
|
||||
if encoder_padding_mask is None:
|
||||
x = x * (s * math.sqrt(1.0 / s))
|
||||
else:
|
||||
s = s - encoder_padding_mask.type_as(x).sum(
|
||||
dim=1, keepdim=True
|
||||
) # exclude padding
|
||||
s = s.unsqueeze(-1)
|
||||
x = x * (s * s.rsqrt())
|
||||
|
||||
# project back
|
||||
x = (self.out_projection(x) + residual) * math.sqrt(0.5)
|
||||
return x, attn_scores
|
||||
|
||||
def make_generation_fast_(self, beamable_mm_beam_size=None, **kwargs):
|
||||
"""Replace torch.bmm with BeamableMM."""
|
||||
if beamable_mm_beam_size is not None:
|
||||
del self.bmm
|
||||
self.add_module("bmm", BeamableMM(beamable_mm_beam_size))
|
||||
|
||||
|
||||
class FConvDecoder(FairseqIncrementalDecoder):
|
||||
"""Convolutional decoder"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
embed_dict=None,
|
||||
out_embed_dim=256,
|
||||
max_positions=1024,
|
||||
convolutions=((512, 3),) * 20,
|
||||
attention=True,
|
||||
dropout=0.1,
|
||||
share_embed=False,
|
||||
positional_embeddings=True,
|
||||
adaptive_softmax_cutoff=None,
|
||||
adaptive_softmax_dropout=0.0,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.register_buffer("version", torch.Tensor([2]))
|
||||
self.dropout_module = FairseqDropout(
|
||||
dropout, module_name=self.__class__.__name__
|
||||
)
|
||||
self.need_attn = True
|
||||
|
||||
convolutions = extend_conv_spec(convolutions)
|
||||
in_channels = convolutions[0][0]
|
||||
if isinstance(attention, bool):
|
||||
# expand True into [True, True, ...] and do the same with False
|
||||
attention = [attention] * len(convolutions)
|
||||
if not isinstance(attention, list) or len(attention) != len(convolutions):
|
||||
raise ValueError(
|
||||
"Attention is expected to be a list of booleans of "
|
||||
"length equal to the number of layers."
|
||||
)
|
||||
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
if embed_dict:
|
||||
self.embed_tokens = utils.load_embedding(
|
||||
embed_dict, self.dictionary, self.embed_tokens
|
||||
)
|
||||
|
||||
self.embed_positions = (
|
||||
PositionalEmbedding(
|
||||
max_positions,
|
||||
embed_dim,
|
||||
padding_idx,
|
||||
)
|
||||
if positional_embeddings
|
||||
else None
|
||||
)
|
||||
|
||||
self.fc1 = Linear(embed_dim, in_channels, dropout=dropout)
|
||||
self.projections = nn.ModuleList()
|
||||
self.convolutions = nn.ModuleList()
|
||||
self.attention = nn.ModuleList()
|
||||
self.residuals = []
|
||||
|
||||
layer_in_channels = [in_channels]
|
||||
for i, (out_channels, kernel_size, residual) in enumerate(convolutions):
|
||||
if residual == 0:
|
||||
residual_dim = out_channels
|
||||
else:
|
||||
residual_dim = layer_in_channels[-residual]
|
||||
self.projections.append(
|
||||
Linear(residual_dim, out_channels)
|
||||
if residual_dim != out_channels
|
||||
else None
|
||||
)
|
||||
self.convolutions.append(
|
||||
LinearizedConv1d(
|
||||
in_channels,
|
||||
out_channels * 2,
|
||||
kernel_size,
|
||||
padding=(kernel_size - 1),
|
||||
dropout=dropout,
|
||||
)
|
||||
)
|
||||
self.attention.append(
|
||||
AttentionLayer(out_channels, embed_dim) if attention[i] else None
|
||||
)
|
||||
self.residuals.append(residual)
|
||||
in_channels = out_channels
|
||||
layer_in_channels.append(out_channels)
|
||||
|
||||
self.adaptive_softmax = None
|
||||
self.fc2 = self.fc3 = None
|
||||
|
||||
if adaptive_softmax_cutoff is not None:
|
||||
assert not share_embed
|
||||
self.adaptive_softmax = AdaptiveSoftmax(
|
||||
num_embeddings,
|
||||
in_channels,
|
||||
adaptive_softmax_cutoff,
|
||||
dropout=adaptive_softmax_dropout,
|
||||
)
|
||||
else:
|
||||
self.fc2 = Linear(in_channels, out_embed_dim)
|
||||
if share_embed:
|
||||
assert out_embed_dim == embed_dim, (
|
||||
"Shared embed weights implies same dimensions "
|
||||
" out_embed_dim={} vs embed_dim={}".format(out_embed_dim, embed_dim)
|
||||
)
|
||||
self.fc3 = nn.Linear(out_embed_dim, num_embeddings)
|
||||
self.fc3.weight = self.embed_tokens.weight
|
||||
else:
|
||||
self.fc3 = Linear(out_embed_dim, num_embeddings, dropout=dropout)
|
||||
|
||||
def forward(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **unused
|
||||
):
|
||||
if encoder_out is not None:
|
||||
encoder_padding_mask = encoder_out["encoder_padding_mask"]
|
||||
encoder_out = encoder_out["encoder_out"]
|
||||
|
||||
# split and transpose encoder outputs
|
||||
encoder_a, encoder_b = self._split_encoder_out(
|
||||
encoder_out, incremental_state
|
||||
)
|
||||
|
||||
if self.embed_positions is not None:
|
||||
pos_embed = self.embed_positions(prev_output_tokens, incremental_state)
|
||||
else:
|
||||
pos_embed = 0
|
||||
|
||||
if incremental_state is not None:
|
||||
prev_output_tokens = prev_output_tokens[:, -1:]
|
||||
x = self._embed_tokens(prev_output_tokens, incremental_state)
|
||||
|
||||
# embed tokens and combine with positional embeddings
|
||||
x += pos_embed
|
||||
x = self.dropout_module(x)
|
||||
target_embedding = x
|
||||
|
||||
# project to size of convolution
|
||||
x = self.fc1(x)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = self._transpose_if_training(x, incremental_state)
|
||||
|
||||
# temporal convolutions
|
||||
avg_attn_scores = None
|
||||
num_attn_layers = len(self.attention)
|
||||
residuals = [x]
|
||||
for proj, conv, attention, res_layer in zip(
|
||||
self.projections, self.convolutions, self.attention, self.residuals
|
||||
):
|
||||
if res_layer > 0:
|
||||
residual = residuals[-res_layer]
|
||||
residual = residual if proj is None else proj(residual)
|
||||
else:
|
||||
residual = None
|
||||
|
||||
x = self.dropout_module(x)
|
||||
x = conv(x, incremental_state)
|
||||
x = F.glu(x, dim=2)
|
||||
|
||||
# attention
|
||||
if attention is not None:
|
||||
x = self._transpose_if_training(x, incremental_state)
|
||||
|
||||
x, attn_scores = attention(
|
||||
x, target_embedding, (encoder_a, encoder_b), encoder_padding_mask
|
||||
)
|
||||
|
||||
if not self.training and self.need_attn:
|
||||
attn_scores = attn_scores / num_attn_layers
|
||||
if avg_attn_scores is None:
|
||||
avg_attn_scores = attn_scores
|
||||
else:
|
||||
avg_attn_scores.add_(attn_scores)
|
||||
|
||||
x = self._transpose_if_training(x, incremental_state)
|
||||
|
||||
# residual
|
||||
if residual is not None:
|
||||
x = (x + residual) * math.sqrt(0.5)
|
||||
residuals.append(x)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = self._transpose_if_training(x, incremental_state)
|
||||
|
||||
# project back to size of vocabulary if not using adaptive softmax
|
||||
if self.fc2 is not None and self.fc3 is not None:
|
||||
x = self.fc2(x)
|
||||
x = self.dropout_module(x)
|
||||
x = self.fc3(x)
|
||||
|
||||
return x, avg_attn_scores
|
||||
|
||||
def reorder_incremental_state(self, incremental_state, new_order):
|
||||
super().reorder_incremental_state(incremental_state, new_order)
|
||||
encoder_out = utils.get_incremental_state(
|
||||
self, incremental_state, "encoder_out"
|
||||
)
|
||||
if encoder_out is not None:
|
||||
encoder_out = tuple(eo.index_select(0, new_order) for eo in encoder_out)
|
||||
utils.set_incremental_state(
|
||||
self, incremental_state, "encoder_out", encoder_out
|
||||
)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the decoder."""
|
||||
return (
|
||||
self.embed_positions.max_positions
|
||||
if self.embed_positions is not None
|
||||
else float("inf")
|
||||
)
|
||||
|
||||
def upgrade_state_dict(self, state_dict):
|
||||
if utils.item(state_dict.get("decoder.version", torch.Tensor([1]))[0]) < 2:
|
||||
# old models use incorrect weight norm dimension
|
||||
for i, conv in enumerate(self.convolutions):
|
||||
# reconfigure weight norm
|
||||
nn.utils.remove_weight_norm(conv)
|
||||
self.convolutions[i] = nn.utils.weight_norm(conv, dim=0)
|
||||
state_dict["decoder.version"] = torch.Tensor([1])
|
||||
return state_dict
|
||||
|
||||
def make_generation_fast_(self, need_attn=False, **kwargs):
|
||||
self.need_attn = need_attn
|
||||
|
||||
def _embed_tokens(self, tokens, incremental_state):
|
||||
if incremental_state is not None:
|
||||
# keep only the last token for incremental forward pass
|
||||
tokens = tokens[:, -1:]
|
||||
return self.embed_tokens(tokens)
|
||||
|
||||
def _split_encoder_out(self, encoder_out, incremental_state):
|
||||
"""Split and transpose encoder outputs.
|
||||
|
||||
This is cached when doing incremental inference.
|
||||
"""
|
||||
cached_result = utils.get_incremental_state(
|
||||
self, incremental_state, "encoder_out"
|
||||
)
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
|
||||
# transpose only once to speed up attention layers
|
||||
encoder_a, encoder_b = encoder_out
|
||||
encoder_a = encoder_a.transpose(1, 2).contiguous()
|
||||
result = (encoder_a, encoder_b)
|
||||
|
||||
if incremental_state is not None:
|
||||
utils.set_incremental_state(self, incremental_state, "encoder_out", result)
|
||||
return result
|
||||
|
||||
def _transpose_if_training(self, x, incremental_state):
|
||||
if incremental_state is None:
|
||||
x = x.transpose(0, 1)
|
||||
return x
|
||||
|
||||
|
||||
def extend_conv_spec(convolutions):
|
||||
"""
|
||||
Extends convolutional spec that is a list of tuples of 2 or 3 parameters
|
||||
(kernel size, dim size and optionally how many layers behind to look for residual)
|
||||
to default the residual propagation param if it is not specified
|
||||
"""
|
||||
extended = []
|
||||
for spec in convolutions:
|
||||
if len(spec) == 3:
|
||||
extended.append(spec)
|
||||
elif len(spec) == 2:
|
||||
extended.append(spec + (1,))
|
||||
else:
|
||||
raise Exception(
|
||||
"invalid number of parameters in convolution spec "
|
||||
+ str(spec)
|
||||
+ ". expected 2 or 3"
|
||||
)
|
||||
return tuple(extended)
|
||||
|
||||
|
||||
def Embedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
|
||||
nn.init.normal_(m.weight, 0, 0.1)
|
||||
nn.init.constant_(m.weight[padding_idx], 0)
|
||||
return m
|
||||
|
||||
|
||||
def PositionalEmbedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = LearnedPositionalEmbedding(num_embeddings, embedding_dim, padding_idx)
|
||||
nn.init.normal_(m.weight, 0, 0.1)
|
||||
nn.init.constant_(m.weight[padding_idx], 0)
|
||||
return m
|
||||
|
||||
|
||||
def Linear(in_features, out_features, dropout=0.0):
|
||||
"""Weight-normalized Linear layer (input: N x T x C)"""
|
||||
m = nn.Linear(in_features, out_features)
|
||||
nn.init.normal_(m.weight, mean=0, std=math.sqrt((1 - dropout) / in_features))
|
||||
nn.init.constant_(m.bias, 0)
|
||||
return nn.utils.weight_norm(m)
|
||||
|
||||
|
||||
def LinearizedConv1d(in_channels, out_channels, kernel_size, dropout=0.0, **kwargs):
|
||||
"""Weight-normalized Conv1d layer optimized for decoding"""
|
||||
m = LinearizedConvolution(in_channels, out_channels, kernel_size, **kwargs)
|
||||
std = math.sqrt((4 * (1.0 - dropout)) / (m.kernel_size[0] * in_channels))
|
||||
nn.init.normal_(m.weight, mean=0, std=std)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
return nn.utils.weight_norm(m, dim=2)
|
||||
|
||||
|
||||
def ConvTBC(in_channels, out_channels, kernel_size, dropout=0.0, **kwargs):
|
||||
"""Weight-normalized Conv1d layer"""
|
||||
from fairseq.modules import ConvTBC
|
||||
|
||||
m = ConvTBC(in_channels, out_channels, kernel_size, **kwargs)
|
||||
std = math.sqrt((4 * (1.0 - dropout)) / (m.kernel_size[0] * in_channels))
|
||||
nn.init.normal_(m.weight, mean=0, std=std)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
return nn.utils.weight_norm(m, dim=2)
|
||||
|
||||
|
||||
@register_model_architecture("fconv", "fconv")
|
||||
def base_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_embed_path = getattr(args, "encoder_embed_path", None)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", "[(512, 3)] * 20")
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", "[(512, 3)] * 20")
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 256)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "True")
|
||||
args.share_input_output_embed = getattr(args, "share_input_output_embed", False)
|
||||
|
||||
|
||||
@register_model_architecture("fconv", "fconv_iwslt_de_en")
|
||||
def fconv_iwslt_de_en(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 256)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", "[(256, 3)] * 4")
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 256)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", "[(256, 3)] * 3")
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 256)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("fconv", "fconv_wmt_en_ro")
|
||||
def fconv_wmt_en_ro(args):
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 512)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("fconv", "fconv_wmt_en_de")
|
||||
def fconv_wmt_en_de(args):
|
||||
convs = "[(512, 3)] * 9" # first 9 layers have 512 units
|
||||
convs += " + [(1024, 3)] * 4" # next 4 layers have 1024 units
|
||||
convs += " + [(2048, 1)] * 2" # final 2 layers use 1x1 convolutions
|
||||
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", convs)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 768)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", convs)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 512)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("fconv", "fconv_wmt_en_fr")
|
||||
def fconv_wmt_en_fr(args):
|
||||
convs = "[(512, 3)] * 6" # first 6 layers have 512 units
|
||||
convs += " + [(768, 3)] * 4" # next 4 layers have 768 units
|
||||
convs += " + [(1024, 3)] * 3" # next 3 layers have 1024 units
|
||||
convs += " + [(2048, 1)] * 1" # next 1 layer uses 1x1 convolutions
|
||||
convs += " + [(4096, 1)] * 1" # final 1 layer uses 1x1 convolutions
|
||||
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", convs)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 768)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", convs)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 512)
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,135 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqLanguageModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.fconv import FConvDecoder
|
||||
|
||||
|
||||
@register_model("fconv_lm")
|
||||
class FConvLanguageModel(FairseqLanguageModel):
|
||||
def __init__(self, decoder):
|
||||
super().__init__(decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
parser.add_argument(
|
||||
"--dropout", type=float, metavar="D", help="dropout probability"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-layers",
|
||||
type=str,
|
||||
metavar="EXPR",
|
||||
help="decoder layers [(dim, kernel_size), ...]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-out-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder output embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-softmax-cutoff",
|
||||
metavar="EXPR",
|
||||
help="comma separated list of adaptive softmax cutoff points. "
|
||||
"Must be used with adaptive_loss criterion",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-softmax-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="sets adaptive softmax dropout for the tail projections",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-attention",
|
||||
type=str,
|
||||
metavar="EXPR",
|
||||
help="decoder attention [True, ...]",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
# make sure all arguments are present in older models
|
||||
base_lm_architecture(args)
|
||||
|
||||
if hasattr(args, "max_target_positions") and not hasattr(
|
||||
args, "tokens_per_sample"
|
||||
):
|
||||
args.tokens_per_sample = args.max_target_positions
|
||||
|
||||
decoder = FConvDecoder(
|
||||
dictionary=task.target_dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
convolutions=eval(args.decoder_layers),
|
||||
out_embed_dim=args.decoder_embed_dim,
|
||||
attention=eval(args.decoder_attention),
|
||||
dropout=args.dropout,
|
||||
max_positions=args.tokens_per_sample,
|
||||
share_embed=False,
|
||||
positional_embeddings=False,
|
||||
adaptive_softmax_cutoff=(
|
||||
utils.eval_str_list(args.adaptive_softmax_cutoff, type=int)
|
||||
if args.criterion == "adaptive_loss"
|
||||
else None
|
||||
),
|
||||
adaptive_softmax_dropout=args.adaptive_softmax_dropout,
|
||||
)
|
||||
return FConvLanguageModel(decoder)
|
||||
|
||||
|
||||
@register_model_architecture("fconv_lm", "fconv_lm")
|
||||
def base_lm_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 128)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", "[(1268, 4)] * 13")
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "False")
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
|
||||
|
||||
@register_model_architecture("fconv_lm", "fconv_lm_dauphin_wikitext103")
|
||||
def fconv_lm_dauphin_wikitext103(args):
|
||||
layers = "[(850, 6)] * 3"
|
||||
layers += " + [(850, 1)] * 1"
|
||||
layers += " + [(850, 5)] * 4"
|
||||
layers += " + [(850, 1)] * 1"
|
||||
layers += " + [(850, 4)] * 3"
|
||||
layers += " + [(1024, 4)] * 1"
|
||||
layers += " + [(2048, 4)] * 1"
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 280)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", layers)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "False")
|
||||
args.adaptive_softmax_cutoff = getattr(
|
||||
args, "adaptive_softmax_cutoff", "10000,20000,200000"
|
||||
)
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("fconv_lm", "fconv_lm_dauphin_gbw")
|
||||
def fconv_lm_dauphin_gbw(args):
|
||||
layers = "[(512, 5)]"
|
||||
layers += " + [(128, 1, 0), (128, 5, 0), (512, 1, 3)] * 3"
|
||||
layers += " + [(512, 1, 0), (512, 5, 0), (1024, 1, 3)] * 3"
|
||||
layers += " + [(1024, 1, 0), (1024, 5, 0), (2048, 1, 3)] * 6"
|
||||
layers += " + [(1024, 1, 0), (1024, 5, 0), (4096, 1, 3)]"
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 128)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", layers)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "False")
|
||||
args.adaptive_softmax_cutoff = getattr(
|
||||
args, "adaptive_softmax_cutoff", "10000,50000,200000"
|
||||
)
|
||||
base_lm_architecture(args)
|
||||
@@ -0,0 +1,674 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import checkpoint_utils
|
||||
from fairseq.incremental_decoding_utils import with_incremental_state
|
||||
from fairseq.models import (
|
||||
CompositeEncoder,
|
||||
FairseqDecoder,
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.modules import (
|
||||
DownsampledMultiHeadAttention,
|
||||
FairseqDropout,
|
||||
GradMultiply,
|
||||
LayerNorm,
|
||||
LearnedPositionalEmbedding,
|
||||
LinearizedConvolution,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_model("fconv_self_att")
|
||||
class FConvModelSelfAtt(FairseqEncoderDecoderModel):
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
"conv.stories.pretrained": {
|
||||
"path": "https://dl.fbaipublicfiles.com/fairseq/models/stories_checkpoint.tar.gz",
|
||||
"checkpoint_file": "pretrained_checkpoint.pt",
|
||||
"tokenizer": "nltk",
|
||||
},
|
||||
"conv.stories": {
|
||||
"path": "https://dl.fbaipublicfiles.com/fairseq/models/stories_checkpoint.tar.gz",
|
||||
"checkpoint_file": "fusion_checkpoint.pt",
|
||||
"tokenizer": "nltk",
|
||||
"pretrained": "True",
|
||||
"pretrained_checkpoint": "./pretrained_checkpoint.pt",
|
||||
},
|
||||
# Test set containing dictionaries
|
||||
"data.stories": "https://dl.fbaipublicfiles.com/fairseq/data/stories_test.tar.bz2",
|
||||
}
|
||||
|
||||
def __init__(self, encoder, decoder, pretrained_encoder=None):
|
||||
super().__init__(encoder, decoder)
|
||||
self.encoder.num_attention_layers = sum(
|
||||
layer is not None for layer in decoder.attention
|
||||
)
|
||||
self.pretrained_encoder = pretrained_encoder
|
||||
if self.pretrained_encoder is None:
|
||||
encoders = {"encoder": encoder}
|
||||
else:
|
||||
encoders = {"encoder": encoder, "pretrained": self.pretrained_encoder}
|
||||
# for fusion model, CompositeEncoder contains both pretrained and training encoders
|
||||
# these are forwarded and then combined in the decoder
|
||||
self.encoder = CompositeEncoder(encoders)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# fmt: off
|
||||
parser.add_argument('--dropout', type=float, metavar='D',
|
||||
help='dropout probability')
|
||||
parser.add_argument('--encoder-embed-dim', type=int, metavar='N',
|
||||
help='encoder embedding dimension')
|
||||
parser.add_argument('--encoder-layers', type=str, metavar='EXPR',
|
||||
help='encoder layers [(dim, kernel_size), ...]')
|
||||
parser.add_argument('--decoder-embed-dim', type=int, metavar='N',
|
||||
help='decoder embedding dimension')
|
||||
parser.add_argument('--decoder-layers', type=str, metavar='EXPR',
|
||||
help='decoder layers [(dim, kernel_size), ...]')
|
||||
parser.add_argument('--decoder-out-embed-dim', type=int, metavar='N',
|
||||
help='decoder output embedding dimension')
|
||||
parser.add_argument('--decoder-attention', type=str, metavar='EXPR',
|
||||
help='decoder attention [True, ...]')
|
||||
parser.add_argument('--self-attention', type=str, metavar='EXPR',
|
||||
help='decoder self-attention layers, ex: [True] + [False]*5')
|
||||
parser.add_argument('--multihead-attention-nheads', type=int,
|
||||
help='Number of heads to use in attention')
|
||||
parser.add_argument('--multihead-self-attention-nheads', type=int,
|
||||
help='Number of heads to use in self-attention')
|
||||
parser.add_argument('--encoder-attention', type=str, metavar='EXPR',
|
||||
help='encoder attention [True, ...]')
|
||||
parser.add_argument('--encoder-attention-nheads', type=int,
|
||||
help='Number of heads to use in encoder attention')
|
||||
parser.add_argument('--project-input', type=str, metavar='EXPR',
|
||||
help='Use projections in self-attention [True, ...]')
|
||||
parser.add_argument('--gated-attention', type=str, metavar='EXPR',
|
||||
help='Use GLU layers in self-attention projections [True, ...]')
|
||||
parser.add_argument('--downsample', type=str, metavar='EXPR',
|
||||
help='Use downsampling in self-attention [True, ...]')
|
||||
parser.add_argument('--pretrained-checkpoint', metavar='DIR',
|
||||
help='path to load checkpoint from pretrained model')
|
||||
parser.add_argument('--pretrained', type=str, metavar='EXPR',
|
||||
help='use pretrained model when training [True, ...]')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
trained_encoder, trained_decoder = None, None
|
||||
pretrained = eval(args.pretrained)
|
||||
if pretrained:
|
||||
logger.info("loading pretrained model")
|
||||
if not os.path.exists(args.pretrained_checkpoint):
|
||||
new_pretrained_checkpoint = os.path.join(
|
||||
args.data, args.pretrained_checkpoint
|
||||
)
|
||||
if os.path.exists(new_pretrained_checkpoint):
|
||||
args.pretrained_checkpoint = new_pretrained_checkpoint
|
||||
trained_model = checkpoint_utils.load_model_ensemble(
|
||||
filenames=[args.pretrained_checkpoint],
|
||||
task=task,
|
||||
)[0][0]
|
||||
trained_decoder = list(trained_model.children())[1]
|
||||
trained_encoder = list(trained_model.children())[0]
|
||||
|
||||
# freeze pretrained model
|
||||
for param in trained_decoder.parameters():
|
||||
param.requires_grad = False
|
||||
for param in trained_encoder.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
encoder = FConvEncoder(
|
||||
task.source_dictionary,
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
convolutions=eval(args.encoder_layers),
|
||||
dropout=args.dropout,
|
||||
max_positions=args.max_source_positions,
|
||||
attention=eval(args.encoder_attention),
|
||||
attention_nheads=args.encoder_attention_nheads,
|
||||
)
|
||||
|
||||
decoder = FConvDecoder(
|
||||
task.target_dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
convolutions=eval(args.decoder_layers),
|
||||
out_embed_dim=args.decoder_out_embed_dim,
|
||||
attention=eval(args.decoder_attention),
|
||||
dropout=args.dropout,
|
||||
max_positions=args.max_target_positions,
|
||||
selfattention=eval(args.self_attention),
|
||||
attention_nheads=args.multihead_attention_nheads,
|
||||
selfattention_nheads=args.multihead_self_attention_nheads,
|
||||
project_input=eval(args.project_input),
|
||||
gated_attention=eval(args.gated_attention),
|
||||
downsample=eval(args.downsample),
|
||||
pretrained=pretrained,
|
||||
trained_decoder=trained_decoder,
|
||||
)
|
||||
model = FConvModelSelfAtt(encoder, decoder, trained_encoder)
|
||||
|
||||
return model
|
||||
|
||||
@property
|
||||
def pretrained(self):
|
||||
return self.pretrained_encoder is not None
|
||||
|
||||
|
||||
class FConvEncoder(FairseqEncoder):
|
||||
"""Convolutional encoder"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
max_positions=1024,
|
||||
convolutions=((512, 3),) * 20,
|
||||
dropout=0.1,
|
||||
attention=False,
|
||||
attention_nheads=1,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.dropout_module = FairseqDropout(
|
||||
dropout, module_name=self.__class__.__name__
|
||||
)
|
||||
self.num_attention_layers = None
|
||||
|
||||
num_embeddings = len(dictionary)
|
||||
self.padding_idx = dictionary.pad()
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, self.padding_idx)
|
||||
self.embed_positions = PositionalEmbedding(
|
||||
max_positions,
|
||||
embed_dim,
|
||||
self.padding_idx,
|
||||
)
|
||||
|
||||
def expand_bool_array(val):
|
||||
if isinstance(val, bool):
|
||||
# expand True into [True, True, ...] and do the same with False
|
||||
return [val] * len(convolutions)
|
||||
return val
|
||||
|
||||
attention = expand_bool_array(attention)
|
||||
|
||||
in_channels = convolutions[0][0]
|
||||
self.fc1 = Linear(embed_dim, in_channels, dropout=dropout)
|
||||
self.projections = nn.ModuleList()
|
||||
self.convolutions = nn.ModuleList()
|
||||
self.attention = nn.ModuleList()
|
||||
self.attproj = nn.ModuleList()
|
||||
for i, (out_channels, kernel_size) in enumerate(convolutions):
|
||||
self.projections.append(
|
||||
Linear(in_channels, out_channels)
|
||||
if in_channels != out_channels
|
||||
else None
|
||||
)
|
||||
self.convolutions.append(
|
||||
ConvTBC(in_channels, out_channels * 2, kernel_size, dropout=dropout)
|
||||
)
|
||||
|
||||
self.attention.append(
|
||||
SelfAttention(out_channels, embed_dim, attention_nheads)
|
||||
if attention[i]
|
||||
else None
|
||||
)
|
||||
in_channels = out_channels
|
||||
|
||||
self.fc2 = Linear(in_channels, embed_dim)
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
# embed tokens and positions
|
||||
x = self.embed_tokens(src_tokens) + self.embed_positions(src_tokens)
|
||||
x = self.dropout_module(x)
|
||||
input_embedding = x.transpose(0, 1)
|
||||
|
||||
# project to size of convolution
|
||||
x = self.fc1(x)
|
||||
|
||||
encoder_padding_mask = src_tokens.eq(self.padding_idx).t() # -> T x B
|
||||
if not encoder_padding_mask.any():
|
||||
encoder_padding_mask = None
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# temporal convolutions
|
||||
for proj, conv, attention in zip(
|
||||
self.projections, self.convolutions, self.attention
|
||||
):
|
||||
residual = x if proj is None else proj(x)
|
||||
|
||||
if encoder_padding_mask is not None:
|
||||
x = x.masked_fill(encoder_padding_mask.unsqueeze(-1), 0)
|
||||
|
||||
x = self.dropout_module(x)
|
||||
padding_l = (conv.kernel_size[0] - 1) // 2
|
||||
padding_r = conv.kernel_size[0] // 2
|
||||
x = F.pad(x, (0, 0, 0, 0, padding_l, padding_r))
|
||||
x = conv(x)
|
||||
x = F.glu(x, dim=2)
|
||||
if attention is not None:
|
||||
x = attention(x)
|
||||
x = (x + residual) * math.sqrt(0.5)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(1, 0)
|
||||
|
||||
# project back to size of embedding
|
||||
x = self.fc2(x)
|
||||
|
||||
if encoder_padding_mask is not None:
|
||||
encoder_padding_mask = encoder_padding_mask.t() # -> B x T
|
||||
x = x.masked_fill(encoder_padding_mask.unsqueeze(-1), 0)
|
||||
|
||||
# scale gradients (this only affects backward, not forward)
|
||||
x = GradMultiply.apply(x, 1.0 / (2.0 * self.num_attention_layers))
|
||||
|
||||
# add output to input embedding for attention
|
||||
y = (x + input_embedding.transpose(0, 1)) * math.sqrt(0.5)
|
||||
|
||||
return {
|
||||
"encoder_out": (x, y),
|
||||
"encoder_padding_mask": encoder_padding_mask, # B x T
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
encoder_out["encoder_out"] = tuple(
|
||||
eo.index_select(0, new_order) for eo in encoder_out["encoder_out"]
|
||||
)
|
||||
|
||||
if encoder_out["encoder_padding_mask"] is not None:
|
||||
encoder_out["encoder_padding_mask"] = encoder_out[
|
||||
"encoder_padding_mask"
|
||||
].index_select(0, new_order)
|
||||
|
||||
if "pretrained" in encoder_out:
|
||||
encoder_out["pretrained"]["encoder_out"] = tuple(
|
||||
eo.index_select(0, new_order)
|
||||
for eo in encoder_out["pretrained"]["encoder_out"]
|
||||
)
|
||||
|
||||
return encoder_out
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return self.embed_positions.max_positions
|
||||
|
||||
|
||||
@with_incremental_state
|
||||
class FConvDecoder(FairseqDecoder):
|
||||
"""Convolutional decoder"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
out_embed_dim=256,
|
||||
max_positions=1024,
|
||||
convolutions=((512, 3),) * 8,
|
||||
attention=True,
|
||||
dropout=0.1,
|
||||
selfattention=False,
|
||||
attention_nheads=1,
|
||||
selfattention_nheads=1,
|
||||
project_input=False,
|
||||
gated_attention=False,
|
||||
downsample=False,
|
||||
pretrained=False,
|
||||
trained_decoder=None,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.register_buffer("version", torch.Tensor([2]))
|
||||
self.pretrained = pretrained
|
||||
self.pretrained_decoder = trained_decoder
|
||||
self.dropout_module = FairseqDropout(
|
||||
dropout, module_name=self.__class__.__name__
|
||||
)
|
||||
self.need_attn = True
|
||||
in_channels = convolutions[0][0]
|
||||
|
||||
def expand_bool_array(val):
|
||||
if isinstance(val, bool):
|
||||
# expand True into [True, True, ...] and do the same with False
|
||||
return [val] * len(convolutions)
|
||||
return val
|
||||
|
||||
attention = expand_bool_array(attention)
|
||||
selfattention = expand_bool_array(selfattention)
|
||||
|
||||
if not isinstance(attention, list) or len(attention) != len(convolutions):
|
||||
raise ValueError(
|
||||
"Attention is expected to be a list of booleans of "
|
||||
"length equal to the number of layers."
|
||||
)
|
||||
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
|
||||
self.embed_positions = PositionalEmbedding(
|
||||
max_positions,
|
||||
embed_dim,
|
||||
padding_idx,
|
||||
)
|
||||
|
||||
self.fc1 = Linear(embed_dim, in_channels, dropout=dropout)
|
||||
self.projections = nn.ModuleList()
|
||||
self.convolutions = nn.ModuleList()
|
||||
self.attention = nn.ModuleList()
|
||||
self.selfattention = nn.ModuleList()
|
||||
self.attproj = nn.ModuleList()
|
||||
for i, (out_channels, kernel_size) in enumerate(convolutions):
|
||||
self.projections.append(
|
||||
Linear(in_channels, out_channels)
|
||||
if in_channels != out_channels
|
||||
else None
|
||||
)
|
||||
self.convolutions.append(
|
||||
LinearizedConv1d(
|
||||
in_channels,
|
||||
out_channels * 2,
|
||||
kernel_size,
|
||||
padding=(kernel_size - 1),
|
||||
dropout=dropout,
|
||||
)
|
||||
)
|
||||
|
||||
self.attention.append(
|
||||
DownsampledMultiHeadAttention(
|
||||
out_channels,
|
||||
embed_dim,
|
||||
attention_nheads,
|
||||
project_input=project_input,
|
||||
gated=False,
|
||||
downsample=False,
|
||||
)
|
||||
if attention[i]
|
||||
else None
|
||||
)
|
||||
|
||||
self.attproj.append(
|
||||
Linear(out_channels, embed_dim, dropout=dropout)
|
||||
if attention[i]
|
||||
else None
|
||||
)
|
||||
self.selfattention.append(
|
||||
SelfAttention(
|
||||
out_channels,
|
||||
embed_dim,
|
||||
selfattention_nheads,
|
||||
project_input=project_input,
|
||||
gated=gated_attention,
|
||||
downsample=downsample,
|
||||
)
|
||||
if selfattention[i]
|
||||
else None
|
||||
)
|
||||
in_channels = out_channels
|
||||
|
||||
self.fc2 = Linear(in_channels, out_embed_dim)
|
||||
self.fc3 = Linear(out_embed_dim, num_embeddings, dropout=dropout)
|
||||
|
||||
# model fusion
|
||||
if self.pretrained:
|
||||
# independent gates are learned from the concatenated input
|
||||
self.gate1 = nn.Sequential(
|
||||
Linear(out_embed_dim * 2, out_embed_dim), nn.Sigmoid()
|
||||
)
|
||||
self.gate2 = nn.Sequential(
|
||||
Linear(out_embed_dim * 2, out_embed_dim), nn.Sigmoid()
|
||||
)
|
||||
# pretrained and trained models are joined
|
||||
self.joining = nn.Sequential(
|
||||
Linear(out_embed_dim * 2, out_embed_dim * 2),
|
||||
LayerNorm(out_embed_dim * 2),
|
||||
nn.GLU(),
|
||||
Linear(out_embed_dim, out_embed_dim * 2),
|
||||
LayerNorm(out_embed_dim * 2),
|
||||
nn.GLU(),
|
||||
Linear(out_embed_dim, out_embed_dim),
|
||||
LayerNorm(out_embed_dim),
|
||||
)
|
||||
# pretrained model contains an output layer that is nhid -> vocab size
|
||||
# but the models are combined in their hidden state
|
||||
# the hook stores the output of the pretrained model forward
|
||||
self.pretrained_outputs = {}
|
||||
|
||||
def save_output():
|
||||
def hook(a, b, output):
|
||||
self.pretrained_outputs["out"] = output
|
||||
|
||||
return hook
|
||||
|
||||
self.pretrained_decoder.fc2.register_forward_hook(save_output())
|
||||
|
||||
def forward(self, prev_output_tokens, encoder_out):
|
||||
trained_encoder_out = encoder_out["pretrained"] if self.pretrained else None
|
||||
encoder_out = encoder_out["encoder"]["encoder_out"]
|
||||
|
||||
encoder_a, encoder_b = self._split_encoder_out(encoder_out)
|
||||
|
||||
# embed positions
|
||||
positions = self.embed_positions(prev_output_tokens)
|
||||
|
||||
# embed tokens and positions
|
||||
x = self.embed_tokens(prev_output_tokens) + positions
|
||||
x = self.dropout_module(x)
|
||||
target_embedding = x.transpose(0, 1)
|
||||
|
||||
# project to size of convolution
|
||||
x = self.fc1(x)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# temporal convolutions
|
||||
avg_attn_scores = None
|
||||
for proj, conv, attention, selfattention, attproj in zip(
|
||||
self.projections,
|
||||
self.convolutions,
|
||||
self.attention,
|
||||
self.selfattention,
|
||||
self.attproj,
|
||||
):
|
||||
residual = x if proj is None else proj(x)
|
||||
|
||||
x = self.dropout_module(x)
|
||||
x = conv(x)
|
||||
x = F.glu(x, dim=2)
|
||||
|
||||
# attention
|
||||
if attention is not None:
|
||||
r = x
|
||||
x, attn_scores = attention(
|
||||
attproj(x) + target_embedding, encoder_a, encoder_b
|
||||
)
|
||||
x = x + r
|
||||
if not self.training and self.need_attn:
|
||||
if avg_attn_scores is None:
|
||||
avg_attn_scores = attn_scores
|
||||
else:
|
||||
avg_attn_scores.add_(attn_scores)
|
||||
|
||||
if selfattention is not None:
|
||||
x = selfattention(x)
|
||||
|
||||
x = (x + residual) * math.sqrt(0.5)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# project back to size of vocabulary
|
||||
x = self.fc2(x)
|
||||
x = self.dropout_module(x)
|
||||
if not self.pretrained:
|
||||
x = self.fc3(x)
|
||||
|
||||
# fusion gating
|
||||
if self.pretrained:
|
||||
trained_x, _ = self.pretrained_decoder.forward(
|
||||
prev_output_tokens, trained_encoder_out
|
||||
)
|
||||
y = torch.cat([x, self.pretrained_outputs["out"]], dim=-1)
|
||||
gate1 = self.gate1(y)
|
||||
gate2 = self.gate2(y)
|
||||
gated_x1 = gate1 * x
|
||||
gated_x2 = gate2 * self.pretrained_outputs["out"]
|
||||
fusion = torch.cat([gated_x1, gated_x2], dim=-1)
|
||||
fusion = self.joining(fusion)
|
||||
fusion_output = self.fc3(fusion)
|
||||
return fusion_output, avg_attn_scores
|
||||
else:
|
||||
return x, avg_attn_scores
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the decoder."""
|
||||
return self.embed_positions.max_positions
|
||||
|
||||
def make_generation_fast_(self, need_attn=False, **kwargs):
|
||||
self.need_attn = need_attn
|
||||
|
||||
def _split_encoder_out(self, encoder_out):
|
||||
"""Split and transpose encoder outputs."""
|
||||
# transpose only once to speed up attention layers
|
||||
encoder_a, encoder_b = encoder_out
|
||||
encoder_a = encoder_a.transpose(0, 1).contiguous()
|
||||
encoder_b = encoder_b.transpose(0, 1).contiguous()
|
||||
result = (encoder_a, encoder_b)
|
||||
return result
|
||||
|
||||
|
||||
class SelfAttention(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
out_channels,
|
||||
embed_dim,
|
||||
num_heads,
|
||||
project_input=False,
|
||||
gated=False,
|
||||
downsample=False,
|
||||
):
|
||||
super().__init__()
|
||||
self.attention = DownsampledMultiHeadAttention(
|
||||
out_channels,
|
||||
embed_dim,
|
||||
num_heads,
|
||||
dropout=0,
|
||||
bias=True,
|
||||
project_input=project_input,
|
||||
gated=gated,
|
||||
downsample=downsample,
|
||||
)
|
||||
self.in_proj_q = Linear(out_channels, embed_dim)
|
||||
self.in_proj_k = Linear(out_channels, embed_dim)
|
||||
self.in_proj_v = Linear(out_channels, embed_dim)
|
||||
self.ln = LayerNorm(out_channels)
|
||||
|
||||
def forward(self, x):
|
||||
residual = x
|
||||
query = self.in_proj_q(x)
|
||||
key = self.in_proj_k(x)
|
||||
value = self.in_proj_v(x)
|
||||
x, _ = self.attention(
|
||||
query, key, value, mask_future_timesteps=True, use_scalar_bias=True
|
||||
)
|
||||
return self.ln(x + residual)
|
||||
|
||||
|
||||
def Embedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
|
||||
m.weight.data.normal_(0, 0.1)
|
||||
return m
|
||||
|
||||
|
||||
def PositionalEmbedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = LearnedPositionalEmbedding(num_embeddings, embedding_dim, padding_idx)
|
||||
m.weight.data.normal_(0, 0.1)
|
||||
return m
|
||||
|
||||
|
||||
def Linear(in_features, out_features, dropout=0.0):
|
||||
"""Weight-normalized Linear layer (input: N x T x C)"""
|
||||
m = nn.Linear(in_features, out_features)
|
||||
m.weight.data.normal_(mean=0, std=math.sqrt((1 - dropout) / in_features))
|
||||
m.bias.data.zero_()
|
||||
return m
|
||||
|
||||
|
||||
def LinearizedConv1d(in_channels, out_channels, kernel_size, dropout=0.0, **kwargs):
|
||||
"""Weight-normalized Conv1d layer optimized for decoding"""
|
||||
m = LinearizedConvolution(in_channels, out_channels, kernel_size, **kwargs)
|
||||
std = math.sqrt((4 * (1.0 - dropout)) / (m.kernel_size[0] * in_channels))
|
||||
m.weight.data.normal_(mean=0, std=std)
|
||||
m.bias.data.zero_()
|
||||
return m
|
||||
|
||||
|
||||
def ConvTBC(in_channels, out_channels, kernel_size, dropout=0.0, **kwargs):
|
||||
"""Weight-normalized Conv1d layer"""
|
||||
from fairseq.modules import ConvTBC
|
||||
|
||||
m = ConvTBC(in_channels, out_channels, kernel_size, **kwargs)
|
||||
std = math.sqrt((4 * (1.0 - dropout)) / (m.kernel_size[0] * in_channels))
|
||||
m.weight.data.normal_(mean=0, std=std)
|
||||
m.bias.data.zero_()
|
||||
return m
|
||||
|
||||
|
||||
@register_model_architecture("fconv_self_att", "fconv_self_att")
|
||||
def base_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", "[(512, 3)] * 3")
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", "[(512, 3)] * 8")
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 256)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "True")
|
||||
args.self_attention = getattr(args, "self_attention", "False")
|
||||
args.encoder_attention = getattr(args, "encoder_attention", "False")
|
||||
args.multihead_attention_nheads = getattr(args, "multihead_attention_nheads", 1)
|
||||
args.multihead_self_attention_nheads = getattr(
|
||||
args, "multihead_self_attention_nheads", 1
|
||||
)
|
||||
args.encoder_attention_nheads = getattr(args, "encoder_attention_nheads", 1)
|
||||
args.project_input = getattr(args, "project_input", "False")
|
||||
args.gated_attention = getattr(args, "gated_attention", "False")
|
||||
args.downsample = getattr(args, "downsample", "False")
|
||||
args.pretrained_checkpoint = getattr(args, "pretrained_checkpoint", "")
|
||||
args.pretrained = getattr(args, "pretrained", "False")
|
||||
|
||||
|
||||
@register_model_architecture("fconv_self_att", "fconv_self_att_wp")
|
||||
def fconv_self_att_wp(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 256)
|
||||
args.encoder_layers = getattr(
|
||||
args, "encoder_layers", "[(128, 3)] * 2 + [(512,3)] * 1"
|
||||
)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 256)
|
||||
args.decoder_layers = getattr(
|
||||
args, "decoder_layers", "[(512, 4)] * 4 + [(768, 4)] * 2 + [(1024, 4)] * 1"
|
||||
)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 256)
|
||||
args.self_attention = getattr(args, "self_attention", "True")
|
||||
args.multihead_self_attention_nheads = getattr(
|
||||
args, "multihead_self_attention_nheads", 4
|
||||
)
|
||||
args.project_input = getattr(args, "project_input", "True")
|
||||
args.gated_attention = getattr(args, "gated_attention", "True")
|
||||
args.downsample = getattr(args, "downsample", "True")
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import importlib
|
||||
import os
|
||||
|
||||
|
||||
# automatically import any Python files in the models/huggingface/ directory
|
||||
models_dir = os.path.dirname(__file__)
|
||||
for file in os.listdir(models_dir):
|
||||
path = os.path.join(models_dir, file)
|
||||
if (
|
||||
not file.startswith("_")
|
||||
and not file.startswith(".")
|
||||
and (file.endswith(".py") or os.path.isdir(path))
|
||||
):
|
||||
model_name = file[: file.find(".py")] if file.endswith(".py") else file
|
||||
module = importlib.import_module("fairseq.models.huggingface." + model_name)
|
||||
@@ -0,0 +1,168 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import torch
|
||||
from fairseq.models import (
|
||||
FairseqIncrementalDecoder,
|
||||
FairseqLanguageModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DEFAULT_MAX_TARGET_POSITIONS = 1024
|
||||
|
||||
|
||||
@register_model("hf_gpt2")
|
||||
class HuggingFaceGPT2LanguageModel(FairseqLanguageModel):
|
||||
def __init__(self, decoder):
|
||||
super().__init__(decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# fmt: off
|
||||
parser.add_argument('--embed-dim', type=int, metavar='N',
|
||||
help='embedding dimension')
|
||||
parser.add_argument('--num-attention-heads', type=int, metavar='N',
|
||||
help='num attention heads')
|
||||
parser.add_argument('--num-layers', type=int, metavar='N',
|
||||
help='num layers')
|
||||
parser.add_argument('--dropout', type=float, metavar='D',
|
||||
help='dropout probability for all fully connected layers '
|
||||
'in the embeddings, encoder, and pooler')
|
||||
parser.add_argument('--attention-dropout', type=float, metavar='D',
|
||||
help='dropout probability for attention weights')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
default_architecture(args)
|
||||
return cls(HuggingFaceGPT2Decoder(args, task))
|
||||
|
||||
|
||||
class HuggingFaceGPT2Decoder(FairseqIncrementalDecoder):
|
||||
def __init__(self, args, task):
|
||||
try:
|
||||
from transformers import GPT2Config, GPT2LMHeadModel
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"\n\nPlease install huggingface/transformers with:"
|
||||
"\n\n pip install transformers"
|
||||
)
|
||||
|
||||
super().__init__(task.target_dictionary)
|
||||
|
||||
config = GPT2Config(
|
||||
vocab_size=len(task.target_dictionary),
|
||||
n_positions=args.max_target_positions + 1,
|
||||
n_ctx=args.max_target_positions,
|
||||
n_embd=args.embed_dim,
|
||||
n_layer=args.num_layers,
|
||||
n_head=args.num_attention_heads,
|
||||
resid_pdrop=args.dropout,
|
||||
embd_pdrop=args.dropout,
|
||||
attn_pdrop=args.attention_dropout,
|
||||
layer_norm_epsilon=1e-6,
|
||||
)
|
||||
self.model = GPT2LMHeadModel(config)
|
||||
|
||||
# set zero embedding for padding symbol
|
||||
self.pad_idx = task.target_dictionary.pad()
|
||||
self.model.transformer.wte.weight.data[self.pad_idx].zero_()
|
||||
self.model.transformer.wpe.weight.data[0].zero_()
|
||||
|
||||
def forward(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
src_lengths=None,
|
||||
incremental_state: Optional[Dict[str, List[torch.Tensor]]] = None,
|
||||
encoder_out=None,
|
||||
):
|
||||
features = self.extract_features(prev_output_tokens, incremental_state)
|
||||
lm_logits = self.model.lm_head(features)
|
||||
return (lm_logits,)
|
||||
|
||||
def extract_features(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
incremental_state: Optional[Dict[str, List[torch.Tensor]]] = None,
|
||||
):
|
||||
if incremental_state:
|
||||
past = self.get_incremental_state("past")
|
||||
else:
|
||||
past = None
|
||||
|
||||
# don't attend to padding symbols
|
||||
attention_mask = prev_output_tokens.ne(self.pad_idx).int()
|
||||
|
||||
# set position ids to exclude padding symbols
|
||||
position_ids = attention_mask * (
|
||||
torch.arange(1, 1 + prev_output_tokens.size(1))
|
||||
.to(prev_output_tokens)
|
||||
.repeat(prev_output_tokens.size(0), 1)
|
||||
)
|
||||
|
||||
outputs = self.model.transformer(
|
||||
input_ids=prev_output_tokens,
|
||||
past=past,
|
||||
attention_mask=attention_mask,
|
||||
position_ids=position_ids,
|
||||
)
|
||||
last_hidden_states = outputs[0]
|
||||
|
||||
if incremental_state:
|
||||
self.set_incremental_state(incremental_state, "past", outputs[1])
|
||||
|
||||
return last_hidden_states
|
||||
|
||||
def max_positions(self):
|
||||
return self.model.config.n_positions - 1
|
||||
|
||||
|
||||
@register_model_architecture("hf_gpt2", "hf_gpt2")
|
||||
def default_architecture(args):
|
||||
if getattr(args, "max_target_positions", None) is None:
|
||||
args.max_target_positions = getattr(
|
||||
args, "tokens_per_sample", DEFAULT_MAX_TARGET_POSITIONS
|
||||
)
|
||||
args.embed_dim = getattr(args, "embed_dim", 768)
|
||||
args.num_attention_heads = getattr(args, "num_attention_heads", 12)
|
||||
args.num_layers = getattr(args, "num_layers", 12)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
|
||||
|
||||
@register_model_architecture("hf_gpt2", "hf_gpt2_medium")
|
||||
def hf_gpt2_medium(args):
|
||||
args.embed_dim = getattr(args, "embed_dim", 1024)
|
||||
args.num_attention_heads = getattr(args, "num_attention_heads", 16)
|
||||
args.num_layers = getattr(args, "num_layers", 24)
|
||||
default_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("hf_gpt2", "hf_gpt2_large")
|
||||
def hf_gpt2_large(args):
|
||||
args.embed_dim = getattr(args, "embed_dim", 1280)
|
||||
args.num_attention_heads = getattr(args, "num_attention_heads", 20)
|
||||
args.num_layers = getattr(args, "num_layers", 36)
|
||||
default_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("hf_gpt2", "hf_gpt2_xl")
|
||||
def hf_gpt2_xl(args):
|
||||
args.embed_dim = getattr(args, "embed_dim", 1600)
|
||||
args.num_attention_heads = getattr(args, "num_attention_heads", 25)
|
||||
args.num_layers = getattr(args, "num_layers", 48)
|
||||
default_architecture(args)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,306 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqLanguageModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.lightconv import Embedding, LightConvDecoder
|
||||
from fairseq.modules import AdaptiveInput, CharacterTokenEmbedder
|
||||
|
||||
|
||||
@register_model("lightconv_lm")
|
||||
class LightConvLanguageModel(FairseqLanguageModel):
|
||||
def __init__(self, decoder):
|
||||
super().__init__(decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
parser.add_argument(
|
||||
"--dropout",
|
||||
default=0.1,
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dropout",
|
||||
default=0.0,
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for attention weights",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--relu-dropout",
|
||||
default=0.0,
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability after ReLU in FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability of the inputs",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-output-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder output dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-input-dim", type=int, metavar="N", help="decoder input dimension"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-layers", type=int, metavar="N", help="num decoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num decoder attention heads or LightConv/DynamicConv heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-normalize-before",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="apply layernorm before each decoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-softmax-cutoff",
|
||||
metavar="EXPR",
|
||||
help="comma separated list of adaptive softmax cutoff points. "
|
||||
"Must be used with adaptive_loss criterion",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-softmax-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="sets adaptive softmax dropout for the tail projections",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-softmax-factor",
|
||||
type=float,
|
||||
metavar="N",
|
||||
help="adaptive input factor",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-token-positional-embeddings",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="if set, disables positional embeddings (outside self attention)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-decoder-input-output-embed",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="share decoder input and output embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--character-embeddings",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="if set, uses character embedding convolutions to produce token embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--character-filters",
|
||||
type=str,
|
||||
metavar="LIST",
|
||||
default="[(1, 64), (2, 128), (3, 192), (4, 256), (5, 256), (6, 256), (7, 256)]",
|
||||
help="size of character embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--character-embedding-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
default=4,
|
||||
help="size of character embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--char-embedder-highway-layers",
|
||||
type=int,
|
||||
metavar="N",
|
||||
default=2,
|
||||
help="number of highway layers for character token embeddder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-input",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="if set, uses adaptive input",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-input-factor",
|
||||
type=float,
|
||||
metavar="N",
|
||||
help="adaptive input factor",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--adaptive-input-cutoff",
|
||||
metavar="EXPR",
|
||||
help="comma separated list of adaptive input cutoff points.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tie-adaptive-weights",
|
||||
action="store_true",
|
||||
help="if set, ties the weights of adaptive softmax and adaptive input",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tie-adaptive-proj",
|
||||
action="store_true",
|
||||
help="if set, ties the projection weights of adaptive softmax and adaptive input",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-learned-pos",
|
||||
action="store_true",
|
||||
help="use learned positional embeddings in the decoder",
|
||||
)
|
||||
|
||||
"""LightConv and DynamicConv arguments"""
|
||||
parser.add_argument(
|
||||
"--decoder-kernel-size-list",
|
||||
type=lambda x: utils.eval_str_list(x, int),
|
||||
help='list of kernel size (default: "[3,7,15,31,31,31]")',
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-glu", type=utils.eval_bool, help="glu after in proj"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-conv-type",
|
||||
default="dynamic",
|
||||
type=str,
|
||||
choices=["dynamic", "lightweight"],
|
||||
help="type of convolution",
|
||||
)
|
||||
parser.add_argument("--weight-softmax", default=True, type=utils.eval_bool)
|
||||
parser.add_argument(
|
||||
"--weight-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for conv weights",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_lm_architecture(args)
|
||||
|
||||
if getattr(args, "max_source_positions", None) is None:
|
||||
args.max_source_positions = args.tokens_per_sample
|
||||
if getattr(args, "max_target_positions", None) is None:
|
||||
args.max_target_positions = args.tokens_per_sample
|
||||
|
||||
if args.character_embeddings:
|
||||
embed_tokens = CharacterTokenEmbedder(
|
||||
task.dictionary,
|
||||
eval(args.character_filters),
|
||||
args.character_embedding_dim,
|
||||
args.decoder_embed_dim,
|
||||
args.char_embedder_highway_layers,
|
||||
)
|
||||
elif args.adaptive_input:
|
||||
embed_tokens = AdaptiveInput(
|
||||
len(task.dictionary),
|
||||
task.dictionary.pad(),
|
||||
args.decoder_input_dim,
|
||||
args.adaptive_input_factor,
|
||||
args.decoder_embed_dim,
|
||||
utils.eval_str_list(args.adaptive_input_cutoff, type=int),
|
||||
)
|
||||
else:
|
||||
embed_tokens = Embedding(
|
||||
len(task.dictionary), args.decoder_input_dim, task.dictionary.pad()
|
||||
)
|
||||
|
||||
if args.tie_adaptive_weights:
|
||||
assert args.adaptive_input
|
||||
assert args.adaptive_input_factor == args.adaptive_softmax_factor
|
||||
assert (
|
||||
args.adaptive_softmax_cutoff == args.adaptive_input_cutoff
|
||||
), "{} != {}".format(
|
||||
args.adaptive_softmax_cutoff, args.adaptive_input_cutoff
|
||||
)
|
||||
assert args.decoder_input_dim == args.decoder_output_dim
|
||||
|
||||
decoder = LightConvDecoder(
|
||||
args,
|
||||
task.output_dictionary,
|
||||
embed_tokens,
|
||||
no_encoder_attn=True,
|
||||
final_norm=False,
|
||||
)
|
||||
return LightConvLanguageModel(decoder)
|
||||
|
||||
|
||||
@register_model_architecture("lightconv_lm", "lightconv_lm")
|
||||
def base_lm_architecture(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 2048)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.adaptive_softmax_factor = getattr(args, "adaptive_softmax_factor", 4)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
||||
|
||||
args.character_embeddings = getattr(args, "character_embeddings", False)
|
||||
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
args.decoder_conv_dim = getattr(args, "decoder_conv_dim", args.decoder_embed_dim)
|
||||
|
||||
# The model training is not stable without this
|
||||
args.decoder_normalize_before = True
|
||||
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
args.adaptive_input_factor = getattr(args, "adaptive_input_factor", 4)
|
||||
args.adaptive_input_cutoff = getattr(args, "adaptive_input_cutoff", None)
|
||||
|
||||
args.tie_adaptive_weights = getattr(args, "tie_adaptive_weights", False)
|
||||
args.tie_adaptive_proj = getattr(args, "tie_adaptive_proj", False)
|
||||
|
||||
args.decoder_kernel_size_list = getattr(
|
||||
args, "decoder_kernel_size_list", [3, 7, 15, 31, 31, 31]
|
||||
)
|
||||
if len(args.decoder_kernel_size_list) == 1:
|
||||
args.decoder_kernel_size_list = (
|
||||
args.decoder_kernel_size_list * args.decoder_layers
|
||||
)
|
||||
assert (
|
||||
len(args.decoder_kernel_size_list) == args.decoder_layers
|
||||
), "decoder_kernel_size_list doesn't match decoder_layers"
|
||||
args.decoder_glu = getattr(args, "decoder_glu", True)
|
||||
args.input_dropout = getattr(args, "input_dropout", 0.1)
|
||||
args.weight_dropout = getattr(args, "weight_dropout", args.attention_dropout)
|
||||
|
||||
|
||||
@register_model_architecture("lightconv_lm", "lightconv_lm_gbw")
|
||||
def lightconv_lm_gbw(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 4096)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
||||
base_lm_architecture(args)
|
||||
@@ -0,0 +1,753 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
FairseqIncrementalDecoder,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.modules import AdaptiveSoftmax, FairseqDropout
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
DEFAULT_MAX_SOURCE_POSITIONS = 1e5
|
||||
DEFAULT_MAX_TARGET_POSITIONS = 1e5
|
||||
|
||||
|
||||
@register_model("lstm")
|
||||
class LSTMModel(FairseqEncoderDecoderModel):
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# fmt: off
|
||||
parser.add_argument('--dropout', type=float, metavar='D',
|
||||
help='dropout probability')
|
||||
parser.add_argument('--encoder-embed-dim', type=int, metavar='N',
|
||||
help='encoder embedding dimension')
|
||||
parser.add_argument('--encoder-embed-path', type=str, metavar='STR',
|
||||
help='path to pre-trained encoder embedding')
|
||||
parser.add_argument('--encoder-freeze-embed', action='store_true',
|
||||
help='freeze encoder embeddings')
|
||||
parser.add_argument('--encoder-hidden-size', type=int, metavar='N',
|
||||
help='encoder hidden size')
|
||||
parser.add_argument('--encoder-layers', type=int, metavar='N',
|
||||
help='number of encoder layers')
|
||||
parser.add_argument('--encoder-bidirectional', action='store_true',
|
||||
help='make all layers of encoder bidirectional')
|
||||
parser.add_argument('--decoder-embed-dim', type=int, metavar='N',
|
||||
help='decoder embedding dimension')
|
||||
parser.add_argument('--decoder-embed-path', type=str, metavar='STR',
|
||||
help='path to pre-trained decoder embedding')
|
||||
parser.add_argument('--decoder-freeze-embed', action='store_true',
|
||||
help='freeze decoder embeddings')
|
||||
parser.add_argument('--decoder-hidden-size', type=int, metavar='N',
|
||||
help='decoder hidden size')
|
||||
parser.add_argument('--decoder-layers', type=int, metavar='N',
|
||||
help='number of decoder layers')
|
||||
parser.add_argument('--decoder-out-embed-dim', type=int, metavar='N',
|
||||
help='decoder output embedding dimension')
|
||||
parser.add_argument('--decoder-attention', type=str, metavar='BOOL',
|
||||
help='decoder attention')
|
||||
parser.add_argument('--adaptive-softmax-cutoff', metavar='EXPR',
|
||||
help='comma separated list of adaptive softmax cutoff points. '
|
||||
'Must be used with adaptive_loss criterion')
|
||||
parser.add_argument('--share-decoder-input-output-embed', default=False,
|
||||
action='store_true',
|
||||
help='share decoder input and output embeddings')
|
||||
parser.add_argument('--share-all-embeddings', default=False, action='store_true',
|
||||
help='share encoder, decoder and output embeddings'
|
||||
' (requires shared dictionary and embed dim)')
|
||||
|
||||
# Granular dropout settings (if not specified these default to --dropout)
|
||||
parser.add_argument('--encoder-dropout-in', type=float, metavar='D',
|
||||
help='dropout probability for encoder input embedding')
|
||||
parser.add_argument('--encoder-dropout-out', type=float, metavar='D',
|
||||
help='dropout probability for encoder output')
|
||||
parser.add_argument('--decoder-dropout-in', type=float, metavar='D',
|
||||
help='dropout probability for decoder input embedding')
|
||||
parser.add_argument('--decoder-dropout-out', type=float, metavar='D',
|
||||
help='dropout probability for decoder output')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
# make sure that all args are properly defaulted (in case there are any new ones)
|
||||
base_architecture(args)
|
||||
|
||||
if args.encoder_layers != args.decoder_layers:
|
||||
raise ValueError("--encoder-layers must match --decoder-layers")
|
||||
|
||||
max_source_positions = getattr(
|
||||
args, "max_source_positions", DEFAULT_MAX_SOURCE_POSITIONS
|
||||
)
|
||||
max_target_positions = getattr(
|
||||
args, "max_target_positions", DEFAULT_MAX_TARGET_POSITIONS
|
||||
)
|
||||
|
||||
def load_pretrained_embedding_from_file(embed_path, dictionary, embed_dim):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
embed_tokens = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
embed_dict = utils.parse_embedding(embed_path)
|
||||
utils.print_embed_overlap(embed_dict, dictionary)
|
||||
return utils.load_embedding(embed_dict, dictionary, embed_tokens)
|
||||
|
||||
if args.encoder_embed_path:
|
||||
pretrained_encoder_embed = load_pretrained_embedding_from_file(
|
||||
args.encoder_embed_path, task.source_dictionary, args.encoder_embed_dim
|
||||
)
|
||||
else:
|
||||
num_embeddings = len(task.source_dictionary)
|
||||
pretrained_encoder_embed = Embedding(
|
||||
num_embeddings, args.encoder_embed_dim, task.source_dictionary.pad()
|
||||
)
|
||||
|
||||
if args.share_all_embeddings:
|
||||
# double check all parameters combinations are valid
|
||||
if task.source_dictionary != task.target_dictionary:
|
||||
raise ValueError("--share-all-embeddings requires a joint dictionary")
|
||||
if args.decoder_embed_path and (
|
||||
args.decoder_embed_path != args.encoder_embed_path
|
||||
):
|
||||
raise ValueError(
|
||||
"--share-all-embed not compatible with --decoder-embed-path"
|
||||
)
|
||||
if args.encoder_embed_dim != args.decoder_embed_dim:
|
||||
raise ValueError(
|
||||
"--share-all-embeddings requires --encoder-embed-dim to "
|
||||
"match --decoder-embed-dim"
|
||||
)
|
||||
pretrained_decoder_embed = pretrained_encoder_embed
|
||||
args.share_decoder_input_output_embed = True
|
||||
else:
|
||||
# separate decoder input embeddings
|
||||
pretrained_decoder_embed = None
|
||||
if args.decoder_embed_path:
|
||||
pretrained_decoder_embed = load_pretrained_embedding_from_file(
|
||||
args.decoder_embed_path,
|
||||
task.target_dictionary,
|
||||
args.decoder_embed_dim,
|
||||
)
|
||||
# one last double check of parameter combinations
|
||||
if args.share_decoder_input_output_embed and (
|
||||
args.decoder_embed_dim != args.decoder_out_embed_dim
|
||||
):
|
||||
raise ValueError(
|
||||
"--share-decoder-input-output-embeddings requires "
|
||||
"--decoder-embed-dim to match --decoder-out-embed-dim"
|
||||
)
|
||||
|
||||
if args.encoder_freeze_embed:
|
||||
pretrained_encoder_embed.weight.requires_grad = False
|
||||
if args.decoder_freeze_embed:
|
||||
pretrained_decoder_embed.weight.requires_grad = False
|
||||
|
||||
encoder = LSTMEncoder(
|
||||
dictionary=task.source_dictionary,
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
hidden_size=args.encoder_hidden_size,
|
||||
num_layers=args.encoder_layers,
|
||||
dropout_in=args.encoder_dropout_in,
|
||||
dropout_out=args.encoder_dropout_out,
|
||||
bidirectional=args.encoder_bidirectional,
|
||||
pretrained_embed=pretrained_encoder_embed,
|
||||
max_source_positions=max_source_positions,
|
||||
)
|
||||
decoder = LSTMDecoder(
|
||||
dictionary=task.target_dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
hidden_size=args.decoder_hidden_size,
|
||||
out_embed_dim=args.decoder_out_embed_dim,
|
||||
num_layers=args.decoder_layers,
|
||||
dropout_in=args.decoder_dropout_in,
|
||||
dropout_out=args.decoder_dropout_out,
|
||||
attention=utils.eval_bool(args.decoder_attention),
|
||||
encoder_output_units=encoder.output_units,
|
||||
pretrained_embed=pretrained_decoder_embed,
|
||||
share_input_output_embed=args.share_decoder_input_output_embed,
|
||||
adaptive_softmax_cutoff=(
|
||||
utils.eval_str_list(args.adaptive_softmax_cutoff, type=int)
|
||||
if args.criterion == "adaptive_loss"
|
||||
else None
|
||||
),
|
||||
max_target_positions=max_target_positions,
|
||||
residuals=False,
|
||||
)
|
||||
return cls(encoder, decoder)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
src_tokens,
|
||||
src_lengths,
|
||||
prev_output_tokens,
|
||||
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
||||
):
|
||||
encoder_out = self.encoder(src_tokens, src_lengths=src_lengths)
|
||||
decoder_out = self.decoder(
|
||||
prev_output_tokens,
|
||||
encoder_out=encoder_out,
|
||||
incremental_state=incremental_state,
|
||||
)
|
||||
return decoder_out
|
||||
|
||||
|
||||
class LSTMEncoder(FairseqEncoder):
|
||||
"""LSTM encoder."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
hidden_size=512,
|
||||
num_layers=1,
|
||||
dropout_in=0.1,
|
||||
dropout_out=0.1,
|
||||
bidirectional=False,
|
||||
left_pad=True,
|
||||
pretrained_embed=None,
|
||||
padding_idx=None,
|
||||
max_source_positions=DEFAULT_MAX_SOURCE_POSITIONS,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.num_layers = num_layers
|
||||
self.dropout_in_module = FairseqDropout(
|
||||
dropout_in, module_name=self.__class__.__name__
|
||||
)
|
||||
self.dropout_out_module = FairseqDropout(
|
||||
dropout_out, module_name=self.__class__.__name__
|
||||
)
|
||||
self.bidirectional = bidirectional
|
||||
self.hidden_size = hidden_size
|
||||
self.max_source_positions = max_source_positions
|
||||
|
||||
num_embeddings = len(dictionary)
|
||||
self.padding_idx = padding_idx if padding_idx is not None else dictionary.pad()
|
||||
if pretrained_embed is None:
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, self.padding_idx)
|
||||
else:
|
||||
self.embed_tokens = pretrained_embed
|
||||
|
||||
self.lstm = LSTM(
|
||||
input_size=embed_dim,
|
||||
hidden_size=hidden_size,
|
||||
num_layers=num_layers,
|
||||
dropout=self.dropout_out_module.p if num_layers > 1 else 0.0,
|
||||
bidirectional=bidirectional,
|
||||
)
|
||||
self.left_pad = left_pad
|
||||
|
||||
self.output_units = hidden_size
|
||||
if bidirectional:
|
||||
self.output_units *= 2
|
||||
|
||||
def forward(
|
||||
self,
|
||||
src_tokens: Tensor,
|
||||
src_lengths: Tensor,
|
||||
enforce_sorted: bool = True,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
src_tokens (LongTensor): tokens in the source language of
|
||||
shape `(batch, src_len)`
|
||||
src_lengths (LongTensor): lengths of each source sentence of
|
||||
shape `(batch)`
|
||||
enforce_sorted (bool, optional): if True, `src_tokens` is
|
||||
expected to contain sequences sorted by length in a
|
||||
decreasing order. If False, this condition is not
|
||||
required. Default: True.
|
||||
"""
|
||||
if self.left_pad:
|
||||
# nn.utils.rnn.pack_padded_sequence requires right-padding;
|
||||
# convert left-padding to right-padding
|
||||
src_tokens = utils.convert_padding_direction(
|
||||
src_tokens,
|
||||
torch.zeros_like(src_tokens).fill_(self.padding_idx),
|
||||
left_to_right=True,
|
||||
)
|
||||
|
||||
bsz, seqlen = src_tokens.size()
|
||||
|
||||
# embed tokens
|
||||
x = self.embed_tokens(src_tokens)
|
||||
x = self.dropout_in_module(x)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# pack embedded source tokens into a PackedSequence
|
||||
packed_x = nn.utils.rnn.pack_padded_sequence(
|
||||
x, src_lengths.cpu(), enforce_sorted=enforce_sorted
|
||||
)
|
||||
|
||||
# apply LSTM
|
||||
if self.bidirectional:
|
||||
state_size = 2 * self.num_layers, bsz, self.hidden_size
|
||||
else:
|
||||
state_size = self.num_layers, bsz, self.hidden_size
|
||||
h0 = x.new_zeros(*state_size)
|
||||
c0 = x.new_zeros(*state_size)
|
||||
packed_outs, (final_hiddens, final_cells) = self.lstm(packed_x, (h0, c0))
|
||||
|
||||
# unpack outputs and apply dropout
|
||||
x, _ = nn.utils.rnn.pad_packed_sequence(
|
||||
packed_outs, padding_value=self.padding_idx * 1.0
|
||||
)
|
||||
x = self.dropout_out_module(x)
|
||||
assert list(x.size()) == [seqlen, bsz, self.output_units]
|
||||
|
||||
if self.bidirectional:
|
||||
final_hiddens = self.combine_bidir(final_hiddens, bsz)
|
||||
final_cells = self.combine_bidir(final_cells, bsz)
|
||||
|
||||
encoder_padding_mask = src_tokens.eq(self.padding_idx).t()
|
||||
|
||||
return tuple(
|
||||
(
|
||||
x, # seq_len x batch x hidden
|
||||
final_hiddens, # num_layers x batch x num_directions*hidden
|
||||
final_cells, # num_layers x batch x num_directions*hidden
|
||||
encoder_padding_mask, # seq_len x batch
|
||||
)
|
||||
)
|
||||
|
||||
def combine_bidir(self, outs, bsz: int):
|
||||
out = outs.view(self.num_layers, 2, bsz, -1).transpose(1, 2).contiguous()
|
||||
return out.view(self.num_layers, bsz, -1)
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
return tuple(
|
||||
(
|
||||
encoder_out[0].index_select(1, new_order),
|
||||
encoder_out[1].index_select(1, new_order),
|
||||
encoder_out[2].index_select(1, new_order),
|
||||
encoder_out[3].index_select(1, new_order),
|
||||
)
|
||||
)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return self.max_source_positions
|
||||
|
||||
|
||||
class AttentionLayer(nn.Module):
|
||||
def __init__(self, input_embed_dim, source_embed_dim, output_embed_dim, bias=False):
|
||||
super().__init__()
|
||||
|
||||
self.input_proj = Linear(input_embed_dim, source_embed_dim, bias=bias)
|
||||
self.output_proj = Linear(
|
||||
input_embed_dim + source_embed_dim, output_embed_dim, bias=bias
|
||||
)
|
||||
|
||||
def forward(self, input, source_hids, encoder_padding_mask):
|
||||
# input: bsz x input_embed_dim
|
||||
# source_hids: srclen x bsz x source_embed_dim
|
||||
|
||||
# x: bsz x source_embed_dim
|
||||
x = self.input_proj(input)
|
||||
|
||||
# compute attention
|
||||
attn_scores = (source_hids * x.unsqueeze(0)).sum(dim=2)
|
||||
|
||||
# don't attend over padding
|
||||
if encoder_padding_mask is not None:
|
||||
attn_scores = (
|
||||
attn_scores.float()
|
||||
.masked_fill_(encoder_padding_mask, float("-inf"))
|
||||
.type_as(attn_scores)
|
||||
) # FP16 support: cast to float and back
|
||||
|
||||
attn_scores = F.softmax(attn_scores, dim=0) # srclen x bsz
|
||||
|
||||
# sum weighted sources
|
||||
x = (attn_scores.unsqueeze(2) * source_hids).sum(dim=0)
|
||||
|
||||
x = torch.tanh(self.output_proj(torch.cat((x, input), dim=1)))
|
||||
return x, attn_scores
|
||||
|
||||
|
||||
class LSTMDecoder(FairseqIncrementalDecoder):
|
||||
"""LSTM decoder."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim=512,
|
||||
hidden_size=512,
|
||||
out_embed_dim=512,
|
||||
num_layers=1,
|
||||
dropout_in=0.1,
|
||||
dropout_out=0.1,
|
||||
attention=True,
|
||||
encoder_output_units=512,
|
||||
pretrained_embed=None,
|
||||
share_input_output_embed=False,
|
||||
adaptive_softmax_cutoff=None,
|
||||
max_target_positions=DEFAULT_MAX_TARGET_POSITIONS,
|
||||
residuals=False,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
self.dropout_in_module = FairseqDropout(
|
||||
dropout_in, module_name=self.__class__.__name__
|
||||
)
|
||||
self.dropout_out_module = FairseqDropout(
|
||||
dropout_out, module_name=self.__class__.__name__
|
||||
)
|
||||
self.hidden_size = hidden_size
|
||||
self.share_input_output_embed = share_input_output_embed
|
||||
self.need_attn = True
|
||||
self.max_target_positions = max_target_positions
|
||||
self.residuals = residuals
|
||||
self.num_layers = num_layers
|
||||
|
||||
self.adaptive_softmax = None
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
if pretrained_embed is None:
|
||||
self.embed_tokens = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
else:
|
||||
self.embed_tokens = pretrained_embed
|
||||
|
||||
self.encoder_output_units = encoder_output_units
|
||||
if encoder_output_units != hidden_size and encoder_output_units != 0:
|
||||
self.encoder_hidden_proj = Linear(encoder_output_units, hidden_size)
|
||||
self.encoder_cell_proj = Linear(encoder_output_units, hidden_size)
|
||||
else:
|
||||
self.encoder_hidden_proj = self.encoder_cell_proj = None
|
||||
|
||||
# disable input feeding if there is no encoder
|
||||
# input feeding is described in arxiv.org/abs/1508.04025
|
||||
input_feed_size = 0 if encoder_output_units == 0 else hidden_size
|
||||
self.layers = nn.ModuleList(
|
||||
[
|
||||
LSTMCell(
|
||||
input_size=input_feed_size + embed_dim
|
||||
if layer == 0
|
||||
else hidden_size,
|
||||
hidden_size=hidden_size,
|
||||
)
|
||||
for layer in range(num_layers)
|
||||
]
|
||||
)
|
||||
|
||||
if attention:
|
||||
# TODO make bias configurable
|
||||
self.attention = AttentionLayer(
|
||||
hidden_size, encoder_output_units, hidden_size, bias=False
|
||||
)
|
||||
else:
|
||||
self.attention = None
|
||||
|
||||
if hidden_size != out_embed_dim:
|
||||
self.additional_fc = Linear(hidden_size, out_embed_dim)
|
||||
|
||||
if adaptive_softmax_cutoff is not None:
|
||||
# setting adaptive_softmax dropout to dropout_out for now but can be redefined
|
||||
self.adaptive_softmax = AdaptiveSoftmax(
|
||||
num_embeddings,
|
||||
hidden_size,
|
||||
adaptive_softmax_cutoff,
|
||||
dropout=dropout_out,
|
||||
)
|
||||
elif not self.share_input_output_embed:
|
||||
self.fc_out = Linear(out_embed_dim, num_embeddings, dropout=dropout_out)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out: Optional[Tuple[Tensor, Tensor, Tensor, Tensor]] = None,
|
||||
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
||||
src_lengths: Optional[Tensor] = None,
|
||||
):
|
||||
x, attn_scores = self.extract_features(
|
||||
prev_output_tokens, encoder_out, incremental_state
|
||||
)
|
||||
return self.output_layer(x), attn_scores
|
||||
|
||||
def extract_features(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out: Optional[Tuple[Tensor, Tensor, Tensor, Tensor]] = None,
|
||||
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
||||
):
|
||||
"""
|
||||
Similar to *forward* but only return features.
|
||||
"""
|
||||
# get outputs from encoder
|
||||
if encoder_out is not None:
|
||||
encoder_outs = encoder_out[0]
|
||||
encoder_hiddens = encoder_out[1]
|
||||
encoder_cells = encoder_out[2]
|
||||
encoder_padding_mask = encoder_out[3]
|
||||
else:
|
||||
encoder_outs = torch.empty(0)
|
||||
encoder_hiddens = torch.empty(0)
|
||||
encoder_cells = torch.empty(0)
|
||||
encoder_padding_mask = torch.empty(0)
|
||||
srclen = encoder_outs.size(0)
|
||||
|
||||
if incremental_state is not None and len(incremental_state) > 0:
|
||||
prev_output_tokens = prev_output_tokens[:, -1:]
|
||||
|
||||
bsz, seqlen = prev_output_tokens.size()
|
||||
|
||||
# embed tokens
|
||||
x = self.embed_tokens(prev_output_tokens)
|
||||
x = self.dropout_in_module(x)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# initialize previous states (or get from cache during incremental generation)
|
||||
if incremental_state is not None and len(incremental_state) > 0:
|
||||
prev_hiddens, prev_cells, input_feed = self.get_cached_state(
|
||||
incremental_state
|
||||
)
|
||||
elif encoder_out is not None:
|
||||
# setup recurrent cells
|
||||
prev_hiddens = [encoder_hiddens[i] for i in range(self.num_layers)]
|
||||
prev_cells = [encoder_cells[i] for i in range(self.num_layers)]
|
||||
if self.encoder_hidden_proj is not None:
|
||||
prev_hiddens = [self.encoder_hidden_proj(y) for y in prev_hiddens]
|
||||
prev_cells = [self.encoder_cell_proj(y) for y in prev_cells]
|
||||
input_feed = x.new_zeros(bsz, self.hidden_size)
|
||||
else:
|
||||
# setup zero cells, since there is no encoder
|
||||
zero_state = x.new_zeros(bsz, self.hidden_size)
|
||||
prev_hiddens = [zero_state for i in range(self.num_layers)]
|
||||
prev_cells = [zero_state for i in range(self.num_layers)]
|
||||
input_feed = None
|
||||
|
||||
assert (
|
||||
srclen > 0 or self.attention is None
|
||||
), "attention is not supported if there are no encoder outputs"
|
||||
attn_scores: Optional[Tensor] = (
|
||||
x.new_zeros(srclen, seqlen, bsz) if self.attention is not None else None
|
||||
)
|
||||
outs = []
|
||||
for j in range(seqlen):
|
||||
# input feeding: concatenate context vector from previous time step
|
||||
if input_feed is not None:
|
||||
input = torch.cat((x[j, :, :], input_feed), dim=1)
|
||||
else:
|
||||
input = x[j]
|
||||
|
||||
for i, rnn in enumerate(self.layers):
|
||||
# recurrent cell
|
||||
hidden, cell = rnn(input, (prev_hiddens[i], prev_cells[i]))
|
||||
|
||||
# hidden state becomes the input to the next layer
|
||||
input = self.dropout_out_module(hidden)
|
||||
if self.residuals:
|
||||
input = input + prev_hiddens[i]
|
||||
|
||||
# save state for next time step
|
||||
prev_hiddens[i] = hidden
|
||||
prev_cells[i] = cell
|
||||
|
||||
# apply attention using the last layer's hidden state
|
||||
if self.attention is not None:
|
||||
assert attn_scores is not None
|
||||
out, attn_scores[:, j, :] = self.attention(
|
||||
hidden, encoder_outs, encoder_padding_mask
|
||||
)
|
||||
else:
|
||||
out = hidden
|
||||
out = self.dropout_out_module(out)
|
||||
|
||||
# input feeding
|
||||
if input_feed is not None:
|
||||
input_feed = out
|
||||
|
||||
# save final output
|
||||
outs.append(out)
|
||||
|
||||
# Stack all the necessary tensors together and store
|
||||
prev_hiddens_tensor = torch.stack(prev_hiddens)
|
||||
prev_cells_tensor = torch.stack(prev_cells)
|
||||
cache_state = torch.jit.annotate(
|
||||
Dict[str, Optional[Tensor]],
|
||||
{
|
||||
"prev_hiddens": prev_hiddens_tensor,
|
||||
"prev_cells": prev_cells_tensor,
|
||||
"input_feed": input_feed,
|
||||
},
|
||||
)
|
||||
self.set_incremental_state(incremental_state, "cached_state", cache_state)
|
||||
|
||||
# collect outputs across time steps
|
||||
x = torch.cat(outs, dim=0).view(seqlen, bsz, self.hidden_size)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(1, 0)
|
||||
|
||||
if hasattr(self, "additional_fc") and self.adaptive_softmax is None:
|
||||
x = self.additional_fc(x)
|
||||
x = self.dropout_out_module(x)
|
||||
# srclen x tgtlen x bsz -> bsz x tgtlen x srclen
|
||||
if not self.training and self.need_attn and self.attention is not None:
|
||||
assert attn_scores is not None
|
||||
attn_scores = attn_scores.transpose(0, 2)
|
||||
else:
|
||||
attn_scores = None
|
||||
return x, attn_scores
|
||||
|
||||
def output_layer(self, x):
|
||||
"""Project features to the vocabulary size."""
|
||||
if self.adaptive_softmax is None:
|
||||
if self.share_input_output_embed:
|
||||
x = F.linear(x, self.embed_tokens.weight)
|
||||
else:
|
||||
x = self.fc_out(x)
|
||||
return x
|
||||
|
||||
def get_cached_state(
|
||||
self,
|
||||
incremental_state: Dict[str, Dict[str, Optional[Tensor]]],
|
||||
) -> Tuple[List[Tensor], List[Tensor], Optional[Tensor]]:
|
||||
cached_state = self.get_incremental_state(incremental_state, "cached_state")
|
||||
assert cached_state is not None
|
||||
prev_hiddens_ = cached_state["prev_hiddens"]
|
||||
assert prev_hiddens_ is not None
|
||||
prev_cells_ = cached_state["prev_cells"]
|
||||
assert prev_cells_ is not None
|
||||
prev_hiddens = [prev_hiddens_[i] for i in range(self.num_layers)]
|
||||
prev_cells = [prev_cells_[j] for j in range(self.num_layers)]
|
||||
input_feed = cached_state[
|
||||
"input_feed"
|
||||
] # can be None for decoder-only language models
|
||||
return prev_hiddens, prev_cells, input_feed
|
||||
|
||||
def reorder_incremental_state(
|
||||
self,
|
||||
incremental_state: Dict[str, Dict[str, Optional[Tensor]]],
|
||||
new_order: Tensor,
|
||||
):
|
||||
if incremental_state is None or len(incremental_state) == 0:
|
||||
return
|
||||
prev_hiddens, prev_cells, input_feed = self.get_cached_state(incremental_state)
|
||||
prev_hiddens = [p.index_select(0, new_order) for p in prev_hiddens]
|
||||
prev_cells = [p.index_select(0, new_order) for p in prev_cells]
|
||||
if input_feed is not None:
|
||||
input_feed = input_feed.index_select(0, new_order)
|
||||
cached_state_new = torch.jit.annotate(
|
||||
Dict[str, Optional[Tensor]],
|
||||
{
|
||||
"prev_hiddens": torch.stack(prev_hiddens),
|
||||
"prev_cells": torch.stack(prev_cells),
|
||||
"input_feed": input_feed,
|
||||
},
|
||||
)
|
||||
self.set_incremental_state(incremental_state, "cached_state", cached_state_new),
|
||||
return
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the decoder."""
|
||||
return self.max_target_positions
|
||||
|
||||
def make_generation_fast_(self, need_attn=False, **kwargs):
|
||||
self.need_attn = need_attn
|
||||
|
||||
|
||||
def Embedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
|
||||
nn.init.uniform_(m.weight, -0.1, 0.1)
|
||||
nn.init.constant_(m.weight[padding_idx], 0)
|
||||
return m
|
||||
|
||||
|
||||
def LSTM(input_size, hidden_size, **kwargs):
|
||||
m = nn.LSTM(input_size, hidden_size, **kwargs)
|
||||
for name, param in m.named_parameters():
|
||||
if "weight" in name or "bias" in name:
|
||||
param.data.uniform_(-0.1, 0.1)
|
||||
return m
|
||||
|
||||
|
||||
def LSTMCell(input_size, hidden_size, **kwargs):
|
||||
m = nn.LSTMCell(input_size, hidden_size, **kwargs)
|
||||
for name, param in m.named_parameters():
|
||||
if "weight" in name or "bias" in name:
|
||||
param.data.uniform_(-0.1, 0.1)
|
||||
return m
|
||||
|
||||
|
||||
def Linear(in_features, out_features, bias=True, dropout=0.0):
|
||||
"""Linear layer (input: N x T x C)"""
|
||||
m = nn.Linear(in_features, out_features, bias=bias)
|
||||
m.weight.data.uniform_(-0.1, 0.1)
|
||||
if bias:
|
||||
m.bias.data.uniform_(-0.1, 0.1)
|
||||
return m
|
||||
|
||||
|
||||
@register_model_architecture("lstm", "lstm")
|
||||
def base_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_embed_path = getattr(args, "encoder_embed_path", None)
|
||||
args.encoder_freeze_embed = getattr(args, "encoder_freeze_embed", False)
|
||||
args.encoder_hidden_size = getattr(
|
||||
args, "encoder_hidden_size", args.encoder_embed_dim
|
||||
)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 1)
|
||||
args.encoder_bidirectional = getattr(args, "encoder_bidirectional", False)
|
||||
args.encoder_dropout_in = getattr(args, "encoder_dropout_in", args.dropout)
|
||||
args.encoder_dropout_out = getattr(args, "encoder_dropout_out", args.dropout)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
||||
args.decoder_freeze_embed = getattr(args, "decoder_freeze_embed", False)
|
||||
args.decoder_hidden_size = getattr(
|
||||
args, "decoder_hidden_size", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 1)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 512)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "1")
|
||||
args.decoder_dropout_in = getattr(args, "decoder_dropout_in", args.dropout)
|
||||
args.decoder_dropout_out = getattr(args, "decoder_dropout_out", args.dropout)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.share_all_embeddings = getattr(args, "share_all_embeddings", False)
|
||||
args.adaptive_softmax_cutoff = getattr(
|
||||
args, "adaptive_softmax_cutoff", "10000,50000,200000"
|
||||
)
|
||||
|
||||
|
||||
@register_model_architecture("lstm", "lstm_wiseman_iwslt_de_en")
|
||||
def lstm_wiseman_iwslt_de_en(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 256)
|
||||
args.encoder_dropout_in = getattr(args, "encoder_dropout_in", 0)
|
||||
args.encoder_dropout_out = getattr(args, "encoder_dropout_out", 0)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 256)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 256)
|
||||
args.decoder_dropout_in = getattr(args, "decoder_dropout_in", 0)
|
||||
args.decoder_dropout_out = getattr(args, "decoder_dropout_out", args.dropout)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("lstm", "lstm_luong_wmt_en_de")
|
||||
def lstm_luong_wmt_en_de(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1000)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 4)
|
||||
args.encoder_dropout_out = getattr(args, "encoder_dropout_out", 0)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 1000)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 4)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 1000)
|
||||
args.decoder_dropout_out = getattr(args, "decoder_dropout_out", 0)
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,142 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqLanguageModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.lstm import Embedding, LSTMDecoder
|
||||
|
||||
|
||||
DEFAULT_MAX_TARGET_POSITIONS = 1e5
|
||||
|
||||
|
||||
@register_model("lstm_lm")
|
||||
class LSTMLanguageModel(FairseqLanguageModel):
|
||||
def __init__(self, decoder):
|
||||
super().__init__(decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# fmt: off
|
||||
parser.add_argument('--dropout', type=float, metavar='D',
|
||||
help='dropout probability')
|
||||
parser.add_argument('--decoder-embed-dim', type=int, metavar='N',
|
||||
help='decoder embedding dimension')
|
||||
parser.add_argument('--decoder-embed-path', type=str, metavar='STR',
|
||||
help='path to pre-trained decoder embedding')
|
||||
parser.add_argument('--decoder-hidden-size', type=int, metavar='N',
|
||||
help='decoder hidden size')
|
||||
parser.add_argument('--decoder-layers', type=int, metavar='N',
|
||||
help='number of decoder layers')
|
||||
parser.add_argument('--decoder-out-embed-dim', type=int, metavar='N',
|
||||
help='decoder output embedding dimension')
|
||||
parser.add_argument('--decoder-attention', type=str, metavar='BOOL',
|
||||
help='decoder attention')
|
||||
parser.add_argument('--adaptive-softmax-cutoff', metavar='EXPR',
|
||||
help='comma separated list of adaptive softmax cutoff points. '
|
||||
'Must be used with adaptive_loss criterion')
|
||||
parser.add_argument('--residuals', default=False,
|
||||
action='store_true',
|
||||
help='applying residuals between LSTM layers')
|
||||
|
||||
# Granular dropout settings (if not specified these default to --dropout)
|
||||
parser.add_argument('--decoder-dropout-in', type=float, metavar='D',
|
||||
help='dropout probability for decoder input embedding')
|
||||
parser.add_argument('--decoder-dropout-out', type=float, metavar='D',
|
||||
help='dropout probability for decoder output')
|
||||
parser.add_argument('--share-decoder-input-output-embed', default=False,
|
||||
action='store_true',
|
||||
help='share decoder input and output embeddings')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_architecture(args)
|
||||
|
||||
if getattr(args, "max_target_positions", None) is not None:
|
||||
max_target_positions = args.max_target_positions
|
||||
else:
|
||||
max_target_positions = getattr(
|
||||
args, "tokens_per_sample", DEFAULT_MAX_TARGET_POSITIONS
|
||||
)
|
||||
|
||||
def load_pretrained_embedding_from_file(embed_path, dictionary, embed_dim):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
embed_tokens = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
embed_dict = utils.parse_embedding(embed_path)
|
||||
utils.print_embed_overlap(embed_dict, dictionary)
|
||||
return utils.load_embedding(embed_dict, dictionary, embed_tokens)
|
||||
|
||||
pretrained_decoder_embed = None
|
||||
if args.decoder_embed_path:
|
||||
pretrained_decoder_embed = load_pretrained_embedding_from_file(
|
||||
args.decoder_embed_path, task.target_dictionary, args.decoder_embed_dim
|
||||
)
|
||||
|
||||
if args.share_decoder_input_output_embed:
|
||||
# double check all parameters combinations are valid
|
||||
if task.source_dictionary != task.target_dictionary:
|
||||
raise ValueError(
|
||||
"--share-decoder-input-output-embeddings requires a joint dictionary"
|
||||
)
|
||||
|
||||
if args.decoder_embed_dim != args.decoder_out_embed_dim:
|
||||
raise ValueError(
|
||||
"--share-decoder-input-output-embeddings requires "
|
||||
"--decoder-embed-dim to match --decoder-out-embed-dim"
|
||||
)
|
||||
|
||||
decoder = LSTMDecoder(
|
||||
dictionary=task.dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
hidden_size=args.decoder_hidden_size,
|
||||
out_embed_dim=args.decoder_out_embed_dim,
|
||||
num_layers=args.decoder_layers,
|
||||
dropout_in=args.decoder_dropout_in,
|
||||
dropout_out=args.decoder_dropout_out,
|
||||
attention=False, # decoder-only language model doesn't support attention
|
||||
encoder_output_units=0,
|
||||
pretrained_embed=pretrained_decoder_embed,
|
||||
share_input_output_embed=args.share_decoder_input_output_embed,
|
||||
adaptive_softmax_cutoff=(
|
||||
utils.eval_str_list(args.adaptive_softmax_cutoff, type=int)
|
||||
if args.criterion == "adaptive_loss"
|
||||
else None
|
||||
),
|
||||
max_target_positions=max_target_positions,
|
||||
residuals=args.residuals,
|
||||
)
|
||||
|
||||
return cls(decoder)
|
||||
|
||||
|
||||
@register_model_architecture("lstm_lm", "lstm_lm")
|
||||
def base_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
||||
args.decoder_hidden_size = getattr(
|
||||
args, "decoder_hidden_size", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 1)
|
||||
args.decoder_out_embed_dim = getattr(args, "decoder_out_embed_dim", 512)
|
||||
args.decoder_attention = getattr(args, "decoder_attention", "0")
|
||||
args.decoder_dropout_in = getattr(args, "decoder_dropout_in", args.dropout)
|
||||
args.decoder_dropout_out = getattr(args, "decoder_dropout_out", args.dropout)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.adaptive_softmax_cutoff = getattr(
|
||||
args, "adaptive_softmax_cutoff", "10000,50000,200000"
|
||||
)
|
||||
args.residuals = getattr(args, "residuals", False)
|
||||
@@ -0,0 +1,403 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import logging
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.modules import (
|
||||
LayerNorm,
|
||||
SinusoidalPositionalEmbedding,
|
||||
TransformerSentenceEncoder,
|
||||
)
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_model("masked_lm")
|
||||
class MaskedLMModel(FairseqEncoderModel):
|
||||
"""
|
||||
Class for training a Masked Language Model. It also supports an
|
||||
additional sentence level prediction if the sent-loss argument is set.
|
||||
"""
|
||||
|
||||
def __init__(self, args, encoder):
|
||||
super().__init__(encoder)
|
||||
self.args = args
|
||||
|
||||
# if specified then apply bert initialization on the model. We need
|
||||
# to explictly call this to make sure that the output embeddings
|
||||
# and projection layers are also correctly initialized
|
||||
if getattr(args, "apply_bert_init", False):
|
||||
self.apply(init_bert_params)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# Arguments related to dropout
|
||||
parser.add_argument(
|
||||
"--dropout", type=float, metavar="D", help="dropout probability"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for" " attention weights",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--act-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability after" " activation in FFN",
|
||||
)
|
||||
|
||||
# Arguments related to hidden states and self-attention
|
||||
parser.add_argument(
|
||||
"--encoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-layers", type=int, metavar="N", help="num encoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num encoder attention heads",
|
||||
)
|
||||
|
||||
# Arguments related to input and output embeddings
|
||||
parser.add_argument(
|
||||
"--encoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-encoder-input-output-embed",
|
||||
action="store_true",
|
||||
help="share encoder input" " and output embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-learned-pos",
|
||||
action="store_true",
|
||||
help="use learned positional embeddings in the encoder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-token-positional-embeddings",
|
||||
action="store_true",
|
||||
help="if set, disables positional embeddings" " (outside self attention)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num-segment", type=int, metavar="N", help="num segment in the input"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-positions", type=int, help="number of positional embeddings to learn"
|
||||
)
|
||||
|
||||
# Arguments related to sentence level prediction
|
||||
parser.add_argument(
|
||||
"--sentence-class-num",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="number of classes for sentence task",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sent-loss",
|
||||
action="store_true",
|
||||
help="if set," " calculate sentence level predictions",
|
||||
)
|
||||
|
||||
# Arguments related to parameter initialization
|
||||
parser.add_argument(
|
||||
"--apply-bert-init",
|
||||
action="store_true",
|
||||
help="use custom param initialization for BERT",
|
||||
)
|
||||
|
||||
# misc params
|
||||
parser.add_argument(
|
||||
"--activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pooler-activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="Which activation function to use for pooler layer.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each encoder block",
|
||||
)
|
||||
|
||||
def forward(self, src_tokens, segment_labels=None, **kwargs):
|
||||
return self.encoder(src_tokens, segment_labels=segment_labels, **kwargs)
|
||||
|
||||
def max_positions(self):
|
||||
return self.encoder.max_positions
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
# make sure all arguments are present in older models
|
||||
base_architecture(args)
|
||||
|
||||
if not hasattr(args, "max_positions"):
|
||||
args.max_positions = args.tokens_per_sample
|
||||
|
||||
logger.info(args)
|
||||
|
||||
encoder = MaskedLMEncoder(args, task.dictionary)
|
||||
return cls(args, encoder)
|
||||
|
||||
|
||||
class MaskedLMEncoder(FairseqEncoder):
|
||||
"""
|
||||
Encoder for Masked Language Modelling.
|
||||
"""
|
||||
|
||||
def __init__(self, args, dictionary):
|
||||
super().__init__(dictionary)
|
||||
|
||||
self.padding_idx = dictionary.pad()
|
||||
self.vocab_size = dictionary.__len__()
|
||||
self.max_positions = args.max_positions
|
||||
|
||||
self.sentence_encoder = TransformerSentenceEncoder(
|
||||
padding_idx=self.padding_idx,
|
||||
vocab_size=self.vocab_size,
|
||||
num_encoder_layers=args.encoder_layers,
|
||||
embedding_dim=args.encoder_embed_dim,
|
||||
ffn_embedding_dim=args.encoder_ffn_embed_dim,
|
||||
num_attention_heads=args.encoder_attention_heads,
|
||||
dropout=args.dropout,
|
||||
attention_dropout=args.attention_dropout,
|
||||
activation_dropout=args.act_dropout,
|
||||
max_seq_len=self.max_positions,
|
||||
num_segments=args.num_segment,
|
||||
use_position_embeddings=not args.no_token_positional_embeddings,
|
||||
encoder_normalize_before=args.encoder_normalize_before,
|
||||
apply_bert_init=args.apply_bert_init,
|
||||
activation_fn=args.activation_fn,
|
||||
learned_pos_embedding=args.encoder_learned_pos,
|
||||
)
|
||||
|
||||
self.share_input_output_embed = args.share_encoder_input_output_embed
|
||||
self.embed_out = None
|
||||
self.sentence_projection_layer = None
|
||||
self.sentence_out_dim = args.sentence_class_num
|
||||
self.lm_output_learned_bias = None
|
||||
|
||||
# Remove head is set to true during fine-tuning
|
||||
self.load_softmax = not getattr(args, "remove_head", False)
|
||||
|
||||
self.masked_lm_pooler = nn.Linear(
|
||||
args.encoder_embed_dim, args.encoder_embed_dim
|
||||
)
|
||||
self.pooler_activation = utils.get_activation_fn(args.pooler_activation_fn)
|
||||
|
||||
self.lm_head_transform_weight = nn.Linear(
|
||||
args.encoder_embed_dim, args.encoder_embed_dim
|
||||
)
|
||||
self.activation_fn = utils.get_activation_fn(args.activation_fn)
|
||||
self.layer_norm = LayerNorm(args.encoder_embed_dim)
|
||||
|
||||
self.lm_output_learned_bias = None
|
||||
if self.load_softmax:
|
||||
self.lm_output_learned_bias = nn.Parameter(torch.zeros(self.vocab_size))
|
||||
|
||||
if not self.share_input_output_embed:
|
||||
self.embed_out = nn.Linear(
|
||||
args.encoder_embed_dim, self.vocab_size, bias=False
|
||||
)
|
||||
|
||||
if args.sent_loss:
|
||||
self.sentence_projection_layer = nn.Linear(
|
||||
args.encoder_embed_dim, self.sentence_out_dim, bias=False
|
||||
)
|
||||
|
||||
def forward(self, src_tokens, segment_labels=None, masked_tokens=None, **unused):
|
||||
"""
|
||||
Forward pass for Masked LM encoder. This first computes the token
|
||||
embedding using the token embedding matrix, position embeddings (if
|
||||
specified) and segment embeddings (if specified).
|
||||
|
||||
Here we assume that the sentence representation corresponds to the
|
||||
output of the classification_token (see bert_task or cross_lingual_lm
|
||||
task for more details).
|
||||
Args:
|
||||
- src_tokens: B x T matrix representing sentences
|
||||
- segment_labels: B x T matrix representing segment label for tokens
|
||||
Returns:
|
||||
- a tuple of the following:
|
||||
- logits for predictions in format B x T x C to be used in
|
||||
softmax afterwards
|
||||
- a dictionary of additional data, where 'pooled_output' contains
|
||||
the representation for classification_token and 'inner_states'
|
||||
is a list of internal model states used to compute the
|
||||
predictions (similar in ELMO). 'sentence_logits'
|
||||
is the prediction logit for NSP task and is only computed if
|
||||
this is specified in the input arguments.
|
||||
"""
|
||||
|
||||
inner_states, sentence_rep = self.sentence_encoder(
|
||||
src_tokens,
|
||||
segment_labels=segment_labels,
|
||||
)
|
||||
|
||||
x = inner_states[-1].transpose(0, 1)
|
||||
# project masked tokens only
|
||||
if masked_tokens is not None:
|
||||
x = x[masked_tokens, :]
|
||||
x = self.layer_norm(self.activation_fn(self.lm_head_transform_weight(x)))
|
||||
|
||||
pooled_output = self.pooler_activation(self.masked_lm_pooler(sentence_rep))
|
||||
|
||||
# project back to size of vocabulary
|
||||
if self.share_input_output_embed and hasattr(
|
||||
self.sentence_encoder.embed_tokens, "weight"
|
||||
):
|
||||
x = F.linear(x, self.sentence_encoder.embed_tokens.weight)
|
||||
elif self.embed_out is not None:
|
||||
x = self.embed_out(x)
|
||||
if self.lm_output_learned_bias is not None:
|
||||
x = x + self.lm_output_learned_bias
|
||||
sentence_logits = None
|
||||
if self.sentence_projection_layer:
|
||||
sentence_logits = self.sentence_projection_layer(pooled_output)
|
||||
|
||||
return x, {
|
||||
"inner_states": inner_states,
|
||||
"pooled_output": pooled_output,
|
||||
"sentence_logits": sentence_logits,
|
||||
}
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the encoder."""
|
||||
return self.max_positions
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
if isinstance(
|
||||
self.sentence_encoder.embed_positions, SinusoidalPositionalEmbedding
|
||||
):
|
||||
state_dict[
|
||||
name + ".sentence_encoder.embed_positions._float_tensor"
|
||||
] = torch.FloatTensor(1)
|
||||
if not self.load_softmax:
|
||||
for k in list(state_dict.keys()):
|
||||
if (
|
||||
"embed_out.weight" in k
|
||||
or "sentence_projection_layer.weight" in k
|
||||
or "lm_output_learned_bias" in k
|
||||
):
|
||||
del state_dict[k]
|
||||
return state_dict
|
||||
|
||||
|
||||
@register_model_architecture("masked_lm", "masked_lm")
|
||||
def base_architecture(args):
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.act_dropout = getattr(args, "act_dropout", 0.0)
|
||||
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4096)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.share_encoder_input_output_embed = getattr(
|
||||
args, "share_encoder_input_output_embed", False
|
||||
)
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", False)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.num_segment = getattr(args, "num_segment", 2)
|
||||
|
||||
args.sentence_class_num = getattr(args, "sentence_class_num", 2)
|
||||
args.sent_loss = getattr(args, "sent_loss", False)
|
||||
|
||||
args.apply_bert_init = getattr(args, "apply_bert_init", False)
|
||||
|
||||
args.activation_fn = getattr(args, "activation_fn", "relu")
|
||||
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
|
||||
|
||||
@register_model_architecture("masked_lm", "bert_base")
|
||||
def bert_base_architecture(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
||||
args.share_encoder_input_output_embed = getattr(
|
||||
args, "share_encoder_input_output_embed", True
|
||||
)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", True)
|
||||
args.num_segment = getattr(args, "num_segment", 2)
|
||||
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
||||
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 12)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 3072)
|
||||
|
||||
args.sentence_class_num = getattr(args, "sentence_class_num", 2)
|
||||
args.sent_loss = getattr(args, "sent_loss", True)
|
||||
|
||||
args.apply_bert_init = getattr(args, "apply_bert_init", True)
|
||||
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", True)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("masked_lm", "bert_large")
|
||||
def bert_large_architecture(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 24)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4096)
|
||||
bert_base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("masked_lm", "xlm_base")
|
||||
def xlm_architecture(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.share_encoder_input_output_embed = getattr(
|
||||
args, "share_encoder_input_output_embed", True
|
||||
)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", True)
|
||||
args.num_segment = getattr(args, "num_segment", 1)
|
||||
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4096)
|
||||
|
||||
args.sent_loss = getattr(args, "sent_loss", False)
|
||||
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
||||
args.apply_bert_init = getattr(args, "apply_bert_init", True)
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,92 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
import torch
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
@torch.jit.script
|
||||
def script_skip_tensor_list(x: List[Tensor], mask):
|
||||
res = [xi[mask] if xi.size(0) == mask.size(0) else xi[:, mask] for xi in x]
|
||||
outputs = []
|
||||
for i, t in enumerate(res):
|
||||
if t.numel() != 0:
|
||||
outputs.append(t)
|
||||
else:
|
||||
outputs.append(x[i])
|
||||
return outputs
|
||||
|
||||
|
||||
@torch.jit.script
|
||||
def script_skip_tensor(x: Tensor, mask):
|
||||
# None case
|
||||
if x.size(0) == 0:
|
||||
return x
|
||||
res = x[mask] if x.size(0) == mask.size(0) else x[:, mask]
|
||||
if res.numel() == 0:
|
||||
return x
|
||||
else:
|
||||
return res
|
||||
|
||||
|
||||
@torch.jit.script
|
||||
def expand_2d_or_3d_tensor(x, trg_dim: int, padding_idx: int):
|
||||
"""
|
||||
Expand 2D/3D tensor on dim=1
|
||||
"""
|
||||
if x is None:
|
||||
return None
|
||||
|
||||
assert x.dim() == 2 or x.dim() == 3
|
||||
assert trg_dim >= x.size(1), (trg_dim, x.size())
|
||||
if trg_dim == x.size(1):
|
||||
return x
|
||||
|
||||
dims = [x.size(0), trg_dim - x.size(1)]
|
||||
if x.dim() == 3:
|
||||
dims.append(x.size(2))
|
||||
x = torch.cat([x, torch.zeros(dims).to(x).fill_(padding_idx)], 1)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
@torch.jit.script
|
||||
def coalesce(x: Optional[Tensor], y: Tensor) -> Tensor:
|
||||
return x if x is not None else y
|
||||
|
||||
|
||||
@torch.jit.script
|
||||
def fill_tensors(
|
||||
x: Optional[Tensor], mask, y: Optional[Tensor], padding_idx: int
|
||||
) -> Optional[Tensor]:
|
||||
"""
|
||||
Filling tensor x with y at masked positions (dim=0).
|
||||
"""
|
||||
if x is None or x.size()[0] == 0 or y is None:
|
||||
return x
|
||||
assert x.dim() == y.dim() and mask.size(0) == x.size(0)
|
||||
assert x.dim() == 2 or (x.dim() == 3 and x.size(2) == y.size(2))
|
||||
|
||||
n_selected = mask.sum()
|
||||
if n_selected == 0:
|
||||
return x
|
||||
assert n_selected == y.size(0)
|
||||
if n_selected == x.size(0):
|
||||
return y
|
||||
|
||||
if x.size(1) < y.size(1):
|
||||
x = expand_2d_or_3d_tensor(x, y.size(1), padding_idx)
|
||||
x[mask] = y
|
||||
elif x.size(1) > y.size(1):
|
||||
x[mask] = torch.tensor(padding_idx).type_as(x)
|
||||
if x.dim() == 2:
|
||||
x[mask, : y.size(1)] = y
|
||||
else:
|
||||
x[mask, : y.size(1), :] = y
|
||||
else:
|
||||
x[mask] = y
|
||||
return x
|
||||
@@ -0,0 +1,228 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqMultiModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.transformer import (
|
||||
Embedding,
|
||||
TransformerDecoder,
|
||||
TransformerEncoder,
|
||||
TransformerModel,
|
||||
base_architecture,
|
||||
)
|
||||
|
||||
|
||||
@register_model("multilingual_transformer")
|
||||
class MultilingualTransformerModel(FairseqMultiModel):
|
||||
"""Train Transformer models for multiple language pairs simultaneously.
|
||||
|
||||
Requires `--task multilingual_translation`.
|
||||
|
||||
We inherit all arguments from TransformerModel and assume that all language
|
||||
pairs use a single Transformer architecture. In addition, we provide several
|
||||
options that are specific to the multilingual setting.
|
||||
|
||||
Args:
|
||||
--share-encoder-embeddings: share encoder embeddings across all source languages
|
||||
--share-decoder-embeddings: share decoder embeddings across all target languages
|
||||
--share-encoders: share all encoder params (incl. embeddings) across all source languages
|
||||
--share-decoders: share all decoder params (incl. embeddings) across all target languages
|
||||
"""
|
||||
|
||||
def __init__(self, encoders, decoders):
|
||||
super().__init__(encoders, decoders)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
TransformerModel.add_args(parser)
|
||||
parser.add_argument(
|
||||
"--share-encoder-embeddings",
|
||||
action="store_true",
|
||||
help="share encoder embeddings across languages",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-decoder-embeddings",
|
||||
action="store_true",
|
||||
help="share decoder embeddings across languages",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-encoders",
|
||||
action="store_true",
|
||||
help="share encoders across languages",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-decoders",
|
||||
action="store_true",
|
||||
help="share decoders across languages",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
from fairseq.tasks.multilingual_translation import MultilingualTranslationTask
|
||||
|
||||
assert isinstance(task, MultilingualTranslationTask)
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_multilingual_architecture(args)
|
||||
|
||||
if not hasattr(args, "max_source_positions"):
|
||||
args.max_source_positions = 1024
|
||||
if not hasattr(args, "max_target_positions"):
|
||||
args.max_target_positions = 1024
|
||||
|
||||
src_langs = [lang_pair.split("-")[0] for lang_pair in task.model_lang_pairs]
|
||||
tgt_langs = [lang_pair.split("-")[1] for lang_pair in task.model_lang_pairs]
|
||||
|
||||
if args.share_encoders:
|
||||
args.share_encoder_embeddings = True
|
||||
if args.share_decoders:
|
||||
args.share_decoder_embeddings = True
|
||||
|
||||
def build_embedding(dictionary, embed_dim, path=None):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
emb = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
# if provided, load from preloaded dictionaries
|
||||
if path:
|
||||
embed_dict = utils.parse_embedding(path)
|
||||
utils.load_embedding(embed_dict, dictionary, emb)
|
||||
return emb
|
||||
|
||||
# build shared embeddings (if applicable)
|
||||
shared_encoder_embed_tokens, shared_decoder_embed_tokens = None, None
|
||||
if args.share_all_embeddings:
|
||||
if args.encoder_embed_dim != args.decoder_embed_dim:
|
||||
raise ValueError(
|
||||
"--share-all-embeddings requires --encoder-embed-dim to match --decoder-embed-dim"
|
||||
)
|
||||
if args.decoder_embed_path and (
|
||||
args.decoder_embed_path != args.encoder_embed_path
|
||||
):
|
||||
raise ValueError(
|
||||
"--share-all-embeddings not compatible with --decoder-embed-path"
|
||||
)
|
||||
shared_encoder_embed_tokens = FairseqMultiModel.build_shared_embeddings(
|
||||
dicts=task.dicts,
|
||||
langs=task.langs,
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
build_embedding=build_embedding,
|
||||
pretrained_embed_path=args.encoder_embed_path,
|
||||
)
|
||||
shared_decoder_embed_tokens = shared_encoder_embed_tokens
|
||||
args.share_decoder_input_output_embed = True
|
||||
else:
|
||||
if args.share_encoder_embeddings:
|
||||
shared_encoder_embed_tokens = FairseqMultiModel.build_shared_embeddings(
|
||||
dicts=task.dicts,
|
||||
langs=src_langs,
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
build_embedding=build_embedding,
|
||||
pretrained_embed_path=args.encoder_embed_path,
|
||||
)
|
||||
if args.share_decoder_embeddings:
|
||||
shared_decoder_embed_tokens = FairseqMultiModel.build_shared_embeddings(
|
||||
dicts=task.dicts,
|
||||
langs=tgt_langs,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
build_embedding=build_embedding,
|
||||
pretrained_embed_path=args.decoder_embed_path,
|
||||
)
|
||||
|
||||
# encoders/decoders for each language
|
||||
lang_encoders, lang_decoders = {}, {}
|
||||
|
||||
def get_encoder(lang):
|
||||
if lang not in lang_encoders:
|
||||
if shared_encoder_embed_tokens is not None:
|
||||
encoder_embed_tokens = shared_encoder_embed_tokens
|
||||
else:
|
||||
encoder_embed_tokens = build_embedding(
|
||||
task.dicts[lang],
|
||||
args.encoder_embed_dim,
|
||||
args.encoder_embed_path,
|
||||
)
|
||||
lang_encoders[lang] = cls._get_module_class(
|
||||
True, args, task.dicts[lang], encoder_embed_tokens, src_langs
|
||||
)
|
||||
return lang_encoders[lang]
|
||||
|
||||
def get_decoder(lang):
|
||||
if lang not in lang_decoders:
|
||||
if shared_decoder_embed_tokens is not None:
|
||||
decoder_embed_tokens = shared_decoder_embed_tokens
|
||||
else:
|
||||
decoder_embed_tokens = build_embedding(
|
||||
task.dicts[lang],
|
||||
args.decoder_embed_dim,
|
||||
args.decoder_embed_path,
|
||||
)
|
||||
lang_decoders[lang] = cls._get_module_class(
|
||||
False, args, task.dicts[lang], decoder_embed_tokens, tgt_langs
|
||||
)
|
||||
return lang_decoders[lang]
|
||||
|
||||
# shared encoders/decoders (if applicable)
|
||||
shared_encoder, shared_decoder = None, None
|
||||
if args.share_encoders:
|
||||
shared_encoder = get_encoder(src_langs[0])
|
||||
if args.share_decoders:
|
||||
shared_decoder = get_decoder(tgt_langs[0])
|
||||
|
||||
encoders, decoders = OrderedDict(), OrderedDict()
|
||||
for lang_pair, src, tgt in zip(task.model_lang_pairs, src_langs, tgt_langs):
|
||||
encoders[lang_pair] = (
|
||||
shared_encoder if shared_encoder is not None else get_encoder(src)
|
||||
)
|
||||
decoders[lang_pair] = (
|
||||
shared_decoder if shared_decoder is not None else get_decoder(tgt)
|
||||
)
|
||||
|
||||
return MultilingualTransformerModel(encoders, decoders)
|
||||
|
||||
@classmethod
|
||||
def _get_module_class(cls, is_encoder, args, lang_dict, embed_tokens, langs):
|
||||
module_class = TransformerEncoder if is_encoder else TransformerDecoder
|
||||
return module_class(args, lang_dict, embed_tokens)
|
||||
|
||||
def load_state_dict(self, state_dict, strict=True, model_cfg=None):
|
||||
state_dict_subset = state_dict.copy()
|
||||
for k, _ in state_dict.items():
|
||||
assert k.startswith("models.")
|
||||
lang_pair = k.split(".")[1]
|
||||
if lang_pair not in self.models:
|
||||
del state_dict_subset[k]
|
||||
super().load_state_dict(state_dict_subset, strict=strict, model_cfg=model_cfg)
|
||||
|
||||
|
||||
@register_model_architecture("multilingual_transformer", "multilingual_transformer")
|
||||
def base_multilingual_architecture(args):
|
||||
base_architecture(args)
|
||||
args.share_encoder_embeddings = getattr(args, "share_encoder_embeddings", False)
|
||||
args.share_decoder_embeddings = getattr(args, "share_decoder_embeddings", False)
|
||||
args.share_encoders = getattr(args, "share_encoders", False)
|
||||
args.share_decoders = getattr(args, "share_decoders", False)
|
||||
|
||||
|
||||
@register_model_architecture(
|
||||
"multilingual_transformer", "multilingual_transformer_iwslt_de_en"
|
||||
)
|
||||
def multilingual_transformer_iwslt_de_en(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 1024)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 4)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 1024)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 4)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
base_multilingual_architecture(args)
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""isort:skip_file"""
|
||||
|
||||
from .fairseq_nat_model import *
|
||||
from .nonautoregressive_transformer import *
|
||||
@@ -0,0 +1,170 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import math
|
||||
|
||||
import torch
|
||||
from fairseq.models.transformer import (
|
||||
TransformerDecoder,
|
||||
TransformerEncoder,
|
||||
TransformerModel,
|
||||
)
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
|
||||
|
||||
def ensemble_encoder(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.ensemble_models is None or len(self.ensemble_models) == 1:
|
||||
return func(self, *args, **kwargs)
|
||||
encoder_outs = [func(model, *args, **kwargs, return_all_hiddens=True) for model in self.ensemble_models]
|
||||
_encoder_out = encoder_outs[0].copy()
|
||||
|
||||
def stack(key):
|
||||
outs = [e[key][0] for e in encoder_outs]
|
||||
return [torch.stack(outs, -1) if outs[0] is not None else None]
|
||||
|
||||
_encoder_out["encoder_out"] = stack("encoder_out")
|
||||
_encoder_out["encoder_embedding"] = stack("encoder_embedding")
|
||||
|
||||
num_layers = len(_encoder_out["encoder_states"])
|
||||
if num_layers > 0:
|
||||
_encoder_out["encoder_states"] = [
|
||||
torch.stack([e["encoder_states"][i] for e in encoder_outs], -1)
|
||||
for i in range(num_layers)
|
||||
]
|
||||
return _encoder_out
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def ensemble_decoder(func):
|
||||
def wrapper(self, normalize=False, encoder_out=None, *args, **kwargs):
|
||||
if self.ensemble_models is None or len(self.ensemble_models) == 1:
|
||||
return func(
|
||||
self, normalize=normalize, encoder_out=encoder_out, *args, **kwargs
|
||||
)
|
||||
|
||||
def _replace(encoder_out, new_val):
|
||||
new_encoder_out = encoder_out.copy()
|
||||
new_encoder_out["encoder_out"] = [new_val]
|
||||
return new_encoder_out
|
||||
|
||||
action_outs = [
|
||||
func(
|
||||
model,
|
||||
normalize=normalize,
|
||||
encoder_out=_replace(
|
||||
encoder_out,
|
||||
encoder_out["encoder_out"][0][:, :, :, i]
|
||||
),
|
||||
*args,
|
||||
**kwargs
|
||||
)
|
||||
for i, model in enumerate(self.ensemble_models)
|
||||
]
|
||||
|
||||
if not isinstance(action_outs[0], tuple): # return multiple values
|
||||
action_outs = [[a] for a in action_outs]
|
||||
else:
|
||||
action_outs = [list(a) for a in action_outs]
|
||||
|
||||
ensembled_outs = []
|
||||
for i in range(len(action_outs[0])):
|
||||
if i == 0 and normalize:
|
||||
ensembled_outs += [
|
||||
torch.logsumexp(
|
||||
torch.stack([a[i] for a in action_outs], -1), dim=-1
|
||||
)
|
||||
- math.log(len(self.ensemble_models))
|
||||
]
|
||||
elif action_outs[0][i] is not None:
|
||||
ensembled_outs += [torch.stack([a[i] for a in action_outs], -1)]
|
||||
else:
|
||||
ensembled_outs += [None]
|
||||
|
||||
if len(ensembled_outs) == 1:
|
||||
return ensembled_outs[0]
|
||||
return tuple(ensembled_outs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class FairseqNATModel(TransformerModel):
|
||||
"""
|
||||
Abstract class for all nonautoregressive-based models
|
||||
"""
|
||||
|
||||
def __init__(self, args, encoder, decoder):
|
||||
super().__init__(args, encoder, decoder)
|
||||
self.tgt_dict = decoder.dictionary
|
||||
self.bos = decoder.dictionary.bos()
|
||||
self.eos = decoder.dictionary.eos()
|
||||
self.pad = decoder.dictionary.pad()
|
||||
self.unk = decoder.dictionary.unk()
|
||||
|
||||
self.ensemble_models = None
|
||||
|
||||
@property
|
||||
def allow_length_beam(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def allow_ensemble(self):
|
||||
return True
|
||||
|
||||
def enable_ensemble(self, models):
|
||||
self.encoder.ensemble_models = [m.encoder for m in models]
|
||||
self.decoder.ensemble_models = [m.decoder for m in models]
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
TransformerModel.add_args(parser)
|
||||
parser.add_argument(
|
||||
"--apply-bert-init",
|
||||
action="store_true",
|
||||
help="use custom param initialization for BERT",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, tgt_dict, embed_tokens):
|
||||
decoder = FairseqNATDecoder(args, tgt_dict, embed_tokens)
|
||||
if getattr(args, "apply_bert_init", False):
|
||||
decoder.apply(init_bert_params)
|
||||
return decoder
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args, src_dict, embed_tokens):
|
||||
encoder = FairseqNATEncoder(args, src_dict, embed_tokens)
|
||||
if getattr(args, "apply_bert_init", False):
|
||||
encoder.apply(init_bert_params)
|
||||
return encoder
|
||||
|
||||
def forward_encoder(self, encoder_inputs):
|
||||
return self.encoder(*encoder_inputs)
|
||||
|
||||
def forward_decoder(self, *args, **kwargs):
|
||||
return NotImplementedError
|
||||
|
||||
def initialize_output_tokens(self, *args, **kwargs):
|
||||
return NotImplementedError
|
||||
|
||||
def forward(self, *args, **kwargs):
|
||||
return NotImplementedError
|
||||
|
||||
|
||||
class FairseqNATEncoder(TransformerEncoder):
|
||||
def __init__(self, args, dictionary, embed_tokens):
|
||||
super().__init__(args, dictionary, embed_tokens)
|
||||
self.ensemble_models = None
|
||||
|
||||
@ensemble_encoder
|
||||
def forward(self, *args, **kwargs):
|
||||
return super().forward(*args, **kwargs)
|
||||
|
||||
|
||||
class FairseqNATDecoder(TransformerDecoder):
|
||||
def __init__(self, args, dictionary, embed_tokens, no_encoder_attn=False):
|
||||
super().__init__(args, dictionary, embed_tokens, no_encoder_attn)
|
||||
self.ensemble_models = None
|
||||
@@ -0,0 +1,253 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import math
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from fairseq.models.nat import (
|
||||
_apply_del_words,
|
||||
_apply_ins_masks,
|
||||
_apply_ins_words,
|
||||
_fill,
|
||||
_skip,
|
||||
_skip_encoder_out,
|
||||
)
|
||||
|
||||
|
||||
class _EnsembleModelEncoder(object):
|
||||
def __init__(self, models):
|
||||
self.models = models
|
||||
|
||||
def reorder_encoder_out(self, encoder_outs, new_order):
|
||||
encoder_outs = [
|
||||
model.encoder.reorder_encoder_out(encoder_out, new_order)
|
||||
for model, encoder_out in zip(self.models, encoder_outs)
|
||||
]
|
||||
return encoder_outs
|
||||
|
||||
|
||||
class BasicEnsembleModel(torch.nn.Module):
|
||||
"""A wrapper around an ensemble of models."""
|
||||
|
||||
def __init__(self, models):
|
||||
super().__init__()
|
||||
self.models = torch.nn.ModuleList(models)
|
||||
self.bos = self.models[0].decoder.dictionary.bos()
|
||||
self.eos = self.models[0].decoder.dictionary.eos()
|
||||
self.pad = self.models[0].decoder.dictionary.pad()
|
||||
self.unk = self.models[0].decoder.dictionary.unk()
|
||||
self.encoder = _EnsembleModelEncoder(self.models)
|
||||
|
||||
def has_encoder(self):
|
||||
return hasattr(self.models[0], "encoder")
|
||||
|
||||
def max_decoder_positions(self):
|
||||
return min(m.max_decoder_positions() for m in self.models)
|
||||
|
||||
@torch.no_grad()
|
||||
def forward_encoder(self, encoder_input):
|
||||
if not self.has_encoder():
|
||||
return None
|
||||
return [model.forward_encoder(encoder_input) for model in self.models]
|
||||
|
||||
@torch.no_grad()
|
||||
def forward_decoder(self, *inputs):
|
||||
raise NotImplementedError
|
||||
|
||||
def initialize_output_tokens(self, *inputs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class EnsembleLevT(BasicEnsembleModel):
|
||||
"""A wrapper around an ensemble of models."""
|
||||
|
||||
def __init__(self, models):
|
||||
super().__init__(models)
|
||||
|
||||
@torch.no_grad()
|
||||
def forward_decoder(
|
||||
self, decoder_out, encoder_outs, eos_penalty=0.0, max_ratio=None, **kwargs
|
||||
):
|
||||
# LevT ensembling
|
||||
# A pipeline of three steps: deletion, placeholder, and word insertion.
|
||||
# We need to average scores in each step in a pipeline way because of dependence.
|
||||
# deletion
|
||||
output_tokens = decoder_out.output_tokens
|
||||
output_scores = decoder_out.output_scores
|
||||
attn = decoder_out.attn
|
||||
|
||||
bsz = output_tokens.size(0)
|
||||
if max_ratio is None:
|
||||
max_lens = output_tokens.new().fill_(255)
|
||||
else:
|
||||
if not encoder_outs[0]["encoder_padding_mask"]:
|
||||
src_lens = (
|
||||
encoder_outs[0]["encoder_out"][0].new(bsz)
|
||||
.fill_(encoder_outs[0]["encoder_out"][0].size(1))
|
||||
)
|
||||
else:
|
||||
src_lens = (~encoder_outs[0]["encoder_padding_mask"][0]).sum(1)
|
||||
max_lens = (src_lens * max_ratio).clamp(min=10).long()
|
||||
|
||||
# delete words
|
||||
# do not delete tokens if it is <s> </s>
|
||||
can_del_word = output_tokens.ne(self.pad).sum(1) > 2
|
||||
if can_del_word.sum() != 0: # we cannot delete, skip
|
||||
output_tokens, output_scores, attn = self.forward_word_del(
|
||||
encoder_outs,
|
||||
output_tokens,
|
||||
output_scores,
|
||||
attn,
|
||||
can_del_word,
|
||||
)
|
||||
|
||||
# insert placeholders
|
||||
can_ins_mask = output_tokens.ne(self.pad).sum(1) < max_lens
|
||||
if can_ins_mask.sum() != 0:
|
||||
output_tokens, output_scores = self.forward_mask_ins(
|
||||
encoder_outs,
|
||||
output_tokens,
|
||||
output_scores,
|
||||
can_ins_mask,
|
||||
eos_penalty,
|
||||
max_lens,
|
||||
)
|
||||
|
||||
# insert words
|
||||
can_ins_word = output_tokens.eq(self.unk).sum(1) > 0
|
||||
if can_ins_word.sum() != 0:
|
||||
output_tokens, output_scores, attn = self.forward_word_ins(
|
||||
encoder_outs,
|
||||
output_tokens,
|
||||
output_scores,
|
||||
attn,
|
||||
can_ins_word,
|
||||
)
|
||||
|
||||
# delete some unnecessary paddings
|
||||
cut_off = output_tokens.ne(self.pad).sum(1).max()
|
||||
output_tokens = output_tokens[:, :cut_off]
|
||||
output_scores = output_scores[:, :cut_off]
|
||||
attn = None if attn is None else attn[:, :cut_off, :]
|
||||
return decoder_out._replace(
|
||||
output_tokens=output_tokens,
|
||||
output_scores=output_scores,
|
||||
attn=attn,
|
||||
history=None,
|
||||
)
|
||||
|
||||
def forward_word_del(
|
||||
self, encoder_outs, output_tokens, output_scores, attn, can_del_word
|
||||
):
|
||||
word_del_score_avg = []
|
||||
word_del_attn_avg = []
|
||||
for model, encoder_out in zip(self.models, encoder_outs):
|
||||
word_del_out, word_del_attn = model.decoder.forward_word_del(
|
||||
_skip(output_tokens, can_del_word),
|
||||
_skip_encoder_out(model.encoder, encoder_out, can_del_word),
|
||||
)
|
||||
word_del_score = F.log_softmax(word_del_out, 2)
|
||||
word_del_score_avg.append(word_del_score)
|
||||
word_del_attn_avg.append(word_del_attn)
|
||||
word_del_score_avg = torch.logsumexp(
|
||||
torch.stack(word_del_score_avg, dim=0), dim=0
|
||||
) - math.log(len(self.models))
|
||||
word_del_pred = word_del_score_avg.max(-1)[1].bool()
|
||||
if word_del_attn_avg[0] is not None:
|
||||
word_del_attn_avg = torch.stack(word_del_attn_avg, dim=0) / len(self.models)
|
||||
else:
|
||||
word_del_attn_avg = None
|
||||
|
||||
_tokens, _scores, _attn = _apply_del_words(
|
||||
output_tokens[can_del_word],
|
||||
output_scores[can_del_word],
|
||||
word_del_attn_avg,
|
||||
word_del_pred,
|
||||
self.pad,
|
||||
self.bos,
|
||||
self.eos,
|
||||
)
|
||||
output_tokens = _fill(output_tokens, can_del_word, _tokens, self.pad)
|
||||
output_scores = _fill(output_scores, can_del_word, _scores, 0)
|
||||
attn = _fill(attn, can_del_word, _attn, 0.0)
|
||||
return output_tokens, output_scores, attn
|
||||
|
||||
def forward_mask_ins(
|
||||
self,
|
||||
encoder_outs,
|
||||
output_tokens,
|
||||
output_scores,
|
||||
can_ins_mask,
|
||||
eos_penalty,
|
||||
max_lens,
|
||||
):
|
||||
mask_ins_score_avg = []
|
||||
for model, encoder_out in zip(self.models, encoder_outs):
|
||||
mask_ins_out, _ = model.decoder.forward_mask_ins(
|
||||
_skip(output_tokens, can_ins_mask),
|
||||
_skip_encoder_out(model.encoder, encoder_out, can_ins_mask),
|
||||
)
|
||||
mask_ins_score = F.log_softmax(mask_ins_out, 2)
|
||||
if eos_penalty > 0.0:
|
||||
mask_ins_score[:, :, 0] -= eos_penalty
|
||||
mask_ins_score_avg.append(mask_ins_score)
|
||||
mask_ins_score_avg = torch.logsumexp(
|
||||
torch.stack(mask_ins_score_avg, dim=0), dim=0
|
||||
) - math.log(len(self.models))
|
||||
mask_ins_pred = mask_ins_score_avg.max(-1)[1]
|
||||
mask_ins_pred = torch.min(
|
||||
mask_ins_pred, max_lens[can_ins_mask, None].expand_as(mask_ins_pred)
|
||||
)
|
||||
_tokens, _scores = _apply_ins_masks(
|
||||
output_tokens[can_ins_mask],
|
||||
output_scores[can_ins_mask],
|
||||
mask_ins_pred,
|
||||
self.pad,
|
||||
self.unk,
|
||||
self.eos,
|
||||
)
|
||||
output_tokens = _fill(output_tokens, can_ins_mask, _tokens, self.pad)
|
||||
output_scores = _fill(output_scores, can_ins_mask, _scores, 0)
|
||||
return output_tokens, output_scores
|
||||
|
||||
def forward_word_ins(
|
||||
self, encoder_outs, output_tokens, output_scores, attn, can_ins_word
|
||||
):
|
||||
word_ins_score_avg = []
|
||||
word_ins_attn_avg = []
|
||||
for model, encoder_out in zip(self.models, encoder_outs):
|
||||
word_ins_out, word_ins_attn = model.decoder.forward_word_ins(
|
||||
_skip(output_tokens, can_ins_word),
|
||||
_skip_encoder_out(model.encoder, encoder_out, can_ins_word),
|
||||
)
|
||||
word_ins_score = F.log_softmax(word_ins_out, 2)
|
||||
word_ins_score_avg.append(word_ins_score)
|
||||
word_ins_attn_avg.append(word_ins_attn)
|
||||
word_ins_score_avg = torch.logsumexp(
|
||||
torch.stack(word_ins_score_avg, dim=0), dim=0
|
||||
) - math.log(len(self.models))
|
||||
if word_ins_attn_avg[0] is not None:
|
||||
word_ins_attn_avg = torch.stack(word_ins_attn_avg, dim=0) / len(self.models)
|
||||
else:
|
||||
word_ins_attn_avg = None
|
||||
word_ins_score_max, word_ins_pred = word_ins_score_avg.max(-1)
|
||||
|
||||
_tokens, _scores = _apply_ins_words(
|
||||
output_tokens[can_ins_word],
|
||||
output_scores[can_ins_word],
|
||||
word_ins_pred,
|
||||
word_ins_score_max,
|
||||
self.unk,
|
||||
)
|
||||
|
||||
output_tokens = _fill(output_tokens, can_ins_word, _tokens, self.pad)
|
||||
output_scores = _fill(output_scores, can_ins_word, _scores, 0)
|
||||
attn = _fill(attn, can_ins_word, word_ins_attn, 0.0)
|
||||
return output_tokens, output_scores, attn
|
||||
|
||||
def initialize_output_tokens(self, encoder_outs, src_tokens):
|
||||
# LevT doesn't do length prediction.
|
||||
return self.models[0].initialize_output_tokens(encoder_outs[0], src_tokens)
|
||||
@@ -0,0 +1,450 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.iterative_refinement_generator import DecoderOut
|
||||
from fairseq.models import register_model, register_model_architecture
|
||||
from fairseq.models.nat import FairseqNATDecoder, FairseqNATEncoder, FairseqNATModel, ensemble_decoder, ensemble_encoder
|
||||
from fairseq.models.transformer import Embedding
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from torch import Tensor
|
||||
|
||||
def _mean_pooling(enc_feats, src_masks):
|
||||
# enc_feats: T x B x C
|
||||
# src_masks: B x T or None
|
||||
if src_masks is None:
|
||||
enc_feats = enc_feats.mean(0)
|
||||
else:
|
||||
src_masks = (~src_masks).transpose(0, 1).type_as(enc_feats)
|
||||
enc_feats = (
|
||||
(enc_feats / src_masks.sum(0)[None, :, None]) * src_masks[:, :, None]
|
||||
).sum(0)
|
||||
return enc_feats
|
||||
|
||||
|
||||
def _argmax(x, dim):
|
||||
return (x == x.max(dim, keepdim=True)[0]).type_as(x)
|
||||
|
||||
|
||||
def _uniform_assignment(src_lens, trg_lens):
|
||||
max_trg_len = trg_lens.max()
|
||||
steps = (src_lens.float() - 1) / (trg_lens.float() - 1) # step-size
|
||||
# max_trg_len
|
||||
index_t = utils.new_arange(trg_lens, max_trg_len).float()
|
||||
index_t = steps[:, None] * index_t[None, :] # batch_size X max_trg_len
|
||||
index_t = torch.round(index_t).long().detach()
|
||||
return index_t
|
||||
|
||||
|
||||
@register_model("nonautoregressive_transformer")
|
||||
class NATransformerModel(FairseqNATModel):
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
FairseqNATModel.add_args(parser)
|
||||
|
||||
parser.add_argument(
|
||||
"--src-embedding-copy",
|
||||
action="store_true",
|
||||
help="copy encoder word embeddings as the initial input of the decoder",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args, tgt_dict, embed_tokens):
|
||||
encoder = NATransformerEncoder(args, tgt_dict, embed_tokens)
|
||||
if getattr(args, "apply_bert_init", False):
|
||||
encoder.apply(init_bert_params)
|
||||
return encoder
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, tgt_dict, embed_tokens):
|
||||
decoder = NATransformerDecoder(args, tgt_dict, embed_tokens)
|
||||
if getattr(args, "apply_bert_init", False):
|
||||
decoder.apply(init_bert_params)
|
||||
return decoder
|
||||
|
||||
def forward(
|
||||
self, src_tokens, src_lengths, prev_output_tokens, tgt_tokens, **kwargs
|
||||
):
|
||||
# encoding
|
||||
encoder_out = self.encoder(src_tokens, src_lengths=src_lengths, **kwargs)
|
||||
|
||||
# decoding
|
||||
word_ins_out = self.decoder(
|
||||
normalize=False,
|
||||
prev_output_tokens=prev_output_tokens,
|
||||
encoder_out=encoder_out,
|
||||
)
|
||||
|
||||
return {
|
||||
"word_ins": {
|
||||
"out": word_ins_out,
|
||||
"tgt": tgt_tokens,
|
||||
"mask": tgt_tokens.ne(self.pad),
|
||||
"ls": self.args.label_smoothing,
|
||||
"nll_loss": True,
|
||||
}
|
||||
}
|
||||
|
||||
def forward_decoder(self, decoder_out, encoder_out, decoding_format=None, **kwargs):
|
||||
step = decoder_out.step
|
||||
output_tokens = decoder_out.output_tokens
|
||||
output_scores = decoder_out.output_scores
|
||||
history = decoder_out.history
|
||||
|
||||
# execute the decoder
|
||||
output_masks = output_tokens.ne(self.pad)
|
||||
_scores, _tokens = self.decoder(
|
||||
normalize=True,
|
||||
prev_output_tokens=output_tokens,
|
||||
encoder_out=encoder_out,
|
||||
step=step,
|
||||
).max(-1)
|
||||
|
||||
output_tokens.masked_scatter_(output_masks, _tokens[output_masks])
|
||||
output_scores.masked_scatter_(output_masks, _scores[output_masks])
|
||||
if history is not None:
|
||||
history.append(output_tokens.clone())
|
||||
|
||||
return decoder_out._replace(
|
||||
output_tokens=output_tokens,
|
||||
output_scores=output_scores,
|
||||
attn=None,
|
||||
history=history,
|
||||
)
|
||||
|
||||
|
||||
class NATransformerDecoder(FairseqNATDecoder):
|
||||
def __init__(self, args, dictionary, embed_tokens, no_encoder_attn=False):
|
||||
super().__init__(
|
||||
args, dictionary, embed_tokens, no_encoder_attn=no_encoder_attn
|
||||
)
|
||||
self.dictionary = dictionary
|
||||
self.bos = dictionary.bos()
|
||||
self.unk = dictionary.unk()
|
||||
self.eos = dictionary.eos()
|
||||
|
||||
self.encoder_embed_dim = args.encoder_embed_dim
|
||||
self.src_embedding_copy = getattr(args, "src_embedding_copy", False)
|
||||
if self.src_embedding_copy:
|
||||
self.copy_attn = torch.nn.Linear(self.embed_dim, self.embed_dim, bias=False)
|
||||
|
||||
@ensemble_decoder
|
||||
def forward(self, normalize, encoder_out, prev_output_tokens, step=0, **unused):
|
||||
features, _ = self.extract_features(
|
||||
prev_output_tokens,
|
||||
encoder_out=encoder_out,
|
||||
embedding_copy=(step == 0) & self.src_embedding_copy,
|
||||
)
|
||||
decoder_out = self.output_layer(features)
|
||||
return F.log_softmax(decoder_out, -1) if normalize else decoder_out
|
||||
|
||||
def extract_features(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out=None,
|
||||
early_exit=None,
|
||||
embedding_copy=False,
|
||||
**unused
|
||||
):
|
||||
"""
|
||||
Similar to *forward* but only return features.
|
||||
|
||||
Inputs:
|
||||
prev_output_tokens: Tensor(B, T)
|
||||
encoder_out: a dictionary of hidden states and masks
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
the LevenshteinTransformer decoder has full-attention to all generated tokens
|
||||
"""
|
||||
positions = (
|
||||
self.embed_positions(prev_output_tokens)
|
||||
if self.embed_positions is not None
|
||||
else None
|
||||
)
|
||||
# embedding
|
||||
if embedding_copy:
|
||||
src_embd = encoder_out["encoder_embedding"][0]
|
||||
if len(encoder_out["encoder_padding_mask"]) > 0:
|
||||
src_mask = encoder_out["encoder_padding_mask"][0]
|
||||
else:
|
||||
src_mask = None
|
||||
|
||||
bsz, seq_len = prev_output_tokens.size()
|
||||
attn_score = torch.bmm(self.copy_attn(positions),
|
||||
(src_embd + encoder_out['encoder_pos'][0]).transpose(1, 2))
|
||||
if src_mask is not None:
|
||||
attn_score = attn_score.masked_fill(src_mask.unsqueeze(1).expand(-1, seq_len, -1), float('-inf'))
|
||||
attn_weight = F.softmax(attn_score, dim=-1)
|
||||
x = torch.bmm(attn_weight, src_embd)
|
||||
mask_target_x, decoder_padding_mask = self.forward_embedding(prev_output_tokens)
|
||||
output_mask = prev_output_tokens.eq(self.unk)
|
||||
cat_x = torch.cat([mask_target_x.unsqueeze(2), x.unsqueeze(2)], dim=2).view(-1, x.size(2))
|
||||
# torch.arange(bsz * seq_len).cuda()
|
||||
x = cat_x.index_select(dim=0, index=torch.arange(bsz * seq_len).cuda() * 2 +
|
||||
output_mask.view(-1).long()).reshape(bsz, seq_len, x.size(2))
|
||||
else:
|
||||
|
||||
x, decoder_padding_mask = self.forward_embedding(prev_output_tokens)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
positions = positions.transpose(0, 1)
|
||||
attn = None
|
||||
inner_states = [x]
|
||||
|
||||
# decoder layers
|
||||
for i, layer in enumerate(self.layers):
|
||||
|
||||
# early exit from the decoder.
|
||||
if (early_exit is not None) and (i >= early_exit):
|
||||
break
|
||||
|
||||
if positions is not None:
|
||||
x += positions
|
||||
x = self.dropout_module(x)
|
||||
|
||||
x, attn, _ = layer(
|
||||
x,
|
||||
encoder_out["encoder_out"][0]
|
||||
if (encoder_out is not None and len(encoder_out["encoder_out"]) > 0)
|
||||
else None,
|
||||
encoder_out["encoder_padding_mask"][0]
|
||||
if (
|
||||
encoder_out is not None
|
||||
and len(encoder_out["encoder_padding_mask"]) > 0
|
||||
)
|
||||
else None,
|
||||
self_attn_mask=None,
|
||||
self_attn_padding_mask=decoder_padding_mask,
|
||||
)
|
||||
inner_states.append(x)
|
||||
|
||||
if self.layer_norm:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
if self.project_out_dim is not None:
|
||||
x = self.project_out_dim(x)
|
||||
|
||||
return x, {"attn": attn, "inner_states": inner_states}
|
||||
|
||||
def forward_embedding(self, prev_output_tokens, states=None):
|
||||
# embed tokens
|
||||
if states is None:
|
||||
x = self.embed_tokens(prev_output_tokens)
|
||||
if self.project_in_dim is not None:
|
||||
x = self.project_in_dim(x)
|
||||
else:
|
||||
x = states
|
||||
|
||||
decoder_padding_mask = prev_output_tokens.eq(self.padding_idx)
|
||||
return x, decoder_padding_mask
|
||||
|
||||
def forward_copying_source(self, src_embeds, src_masks, tgt_masks):
|
||||
length_sources = src_masks.sum(1)
|
||||
length_targets = tgt_masks.sum(1)
|
||||
mapped_inputs = _uniform_assignment(length_sources, length_targets).masked_fill(
|
||||
~tgt_masks, 0
|
||||
)
|
||||
copied_embedding = torch.gather(
|
||||
src_embeds,
|
||||
1,
|
||||
mapped_inputs.unsqueeze(-1).expand(
|
||||
*mapped_inputs.size(), src_embeds.size(-1)
|
||||
),
|
||||
)
|
||||
return copied_embedding
|
||||
|
||||
|
||||
class NATransformerEncoder(FairseqNATEncoder):
|
||||
def __init__(self, args, dictionary, embed_tokens):
|
||||
super().__init__(args, dictionary, embed_tokens)
|
||||
|
||||
@ensemble_encoder
|
||||
def forward(
|
||||
self,
|
||||
src_tokens,
|
||||
src_lengths: Optional[torch.Tensor] = None,
|
||||
return_all_hiddens: bool = False,
|
||||
token_embeddings: Optional[torch.Tensor] = None,
|
||||
):
|
||||
encoder_padding_mask = src_tokens.eq(self.padding_idx)
|
||||
has_pads = (src_tokens.device.type == "xla" or encoder_padding_mask.any())
|
||||
|
||||
x, encoder_embedding = self.forward_embedding(src_tokens, token_embeddings)
|
||||
|
||||
encoder_pos = self.embed_positions(src_tokens)
|
||||
# account for padding while computing the representation
|
||||
if encoder_padding_mask is not None:
|
||||
x = x * (1 - encoder_padding_mask.unsqueeze(-1).type_as(x))
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
encoder_states = []
|
||||
|
||||
if return_all_hiddens:
|
||||
encoder_states.append(x)
|
||||
# encoder layers
|
||||
for layer in self.layers:
|
||||
x = layer(
|
||||
x, encoder_padding_mask=encoder_padding_mask if has_pads else None
|
||||
)
|
||||
if return_all_hiddens:
|
||||
assert encoder_states is not None
|
||||
encoder_states.append(x)
|
||||
|
||||
if self.layer_norm is not None:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
# The Pytorch Mobile lite interpreter does not supports returning NamedTuple in
|
||||
# `forward` so we use a dictionary instead.
|
||||
# TorchScript does not support mixed values so the values are all lists.
|
||||
# The empty list is equivalent to None.
|
||||
return {
|
||||
"encoder_out": [x], # T x B x C
|
||||
"encoder_padding_mask": [encoder_padding_mask], # B x T
|
||||
"encoder_embedding": [encoder_embedding], # B x T x C
|
||||
"encoder_pos": [encoder_pos],
|
||||
"encoder_states": encoder_states, # List[T x B x C]
|
||||
"src_tokens": [],
|
||||
"src_lengths": [],
|
||||
}
|
||||
|
||||
def forward_embedding(
|
||||
self, src_tokens, token_embedding: Optional[torch.Tensor] = None
|
||||
):
|
||||
# embed tokens and positions
|
||||
if token_embedding is None:
|
||||
token_embedding = self.embed_tokens(src_tokens)
|
||||
x = embed = token_embedding
|
||||
if self.embed_positions is not None:
|
||||
x = embed + self.embed_positions(src_tokens)
|
||||
if self.layernorm_embedding is not None:
|
||||
x = self.layernorm_embedding(x)
|
||||
x = self.dropout_module(x)
|
||||
if self.quant_noise is not None:
|
||||
x = self.quant_noise(x)
|
||||
return x, embed
|
||||
|
||||
@torch.jit.export
|
||||
def reorder_encoder_out(self, encoder_out: Dict[str, List[Tensor]], new_order):
|
||||
"""
|
||||
Reorder encoder output according to *new_order*.
|
||||
|
||||
Args:
|
||||
encoder_out: output from the ``forward()`` method
|
||||
new_order (LongTensor): desired order
|
||||
|
||||
Returns:
|
||||
*encoder_out* rearranged according to *new_order*
|
||||
"""
|
||||
if len(encoder_out["encoder_out"]) == 0:
|
||||
new_encoder_out = []
|
||||
else:
|
||||
new_encoder_out = [encoder_out["encoder_out"][0].index_select(1, new_order)]
|
||||
if len(encoder_out["encoder_padding_mask"]) == 0:
|
||||
new_encoder_padding_mask = []
|
||||
else:
|
||||
new_encoder_padding_mask = [
|
||||
encoder_out["encoder_padding_mask"][0].index_select(0, new_order)
|
||||
]
|
||||
if len(encoder_out["encoder_embedding"]) == 0:
|
||||
new_encoder_embedding = []
|
||||
else:
|
||||
new_encoder_embedding = [
|
||||
encoder_out["encoder_embedding"][0].index_select(0, new_order)
|
||||
]
|
||||
if len(encoder_out["encoder_pos"]) == 0:
|
||||
new_encoder_pos = []
|
||||
else:
|
||||
new_encoder_pos = [
|
||||
encoder_out["encoder_pos"][0].index_select(0, new_order)
|
||||
]
|
||||
|
||||
if len(encoder_out["src_tokens"]) == 0:
|
||||
src_tokens = []
|
||||
else:
|
||||
src_tokens = [(encoder_out["src_tokens"][0]).index_select(0, new_order)]
|
||||
|
||||
if len(encoder_out["src_lengths"]) == 0:
|
||||
src_lengths = []
|
||||
else:
|
||||
src_lengths = [(encoder_out["src_lengths"][0]).index_select(0, new_order)]
|
||||
|
||||
encoder_states = encoder_out["encoder_states"]
|
||||
if len(encoder_states) > 0:
|
||||
for idx, state in enumerate(encoder_states):
|
||||
encoder_states[idx] = state.index_select(1, new_order)
|
||||
|
||||
return {
|
||||
"encoder_out": new_encoder_out, # T x B x C
|
||||
"encoder_padding_mask": new_encoder_padding_mask, # B x T
|
||||
"encoder_embedding": new_encoder_embedding, # B x T x C
|
||||
"encoder_pos": new_encoder_pos,
|
||||
"encoder_states": encoder_states, # List[T x B x C]
|
||||
"src_tokens": src_tokens, # B x T
|
||||
"src_lengths": src_lengths, # B x 1
|
||||
}
|
||||
|
||||
@register_model_architecture(
|
||||
"nonautoregressive_transformer", "nonautoregressive_transformer"
|
||||
)
|
||||
def base_architecture(args):
|
||||
args.encoder_embed_path = getattr(args, "encoder_embed_path", None)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 2048)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", False)
|
||||
args.decoder_embed_path = getattr(args, "decoder_embed_path", None)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", args.encoder_embed_dim)
|
||||
args.decoder_ffn_embed_dim = getattr(
|
||||
args, "decoder_ffn_embed_dim", args.encoder_ffn_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.decoder_normalize_before = getattr(args, "decoder_normalize_before", False)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
||||
args.activation_dropout = getattr(args, "activation_dropout", 0.0)
|
||||
args.activation_fn = getattr(args, "activation_fn", "relu")
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.share_all_embeddings = getattr(args, "share_all_embeddings", False)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
args.apply_bert_init = getattr(args, "apply_bert_init", False)
|
||||
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
|
||||
# --- special arguments ---
|
||||
args.src_embedding_copy = getattr(args, "src_embedding_copy", False)
|
||||
|
||||
|
||||
@register_model_architecture(
|
||||
"nonautoregressive_transformer", "nonautoregressive_transformer_wmt_en_de"
|
||||
)
|
||||
def nonautoregressive_transformer_wmt_en_de(args):
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from .hub_interface import * # noqa
|
||||
from .model import * # noqa
|
||||
from .model_camembert import * # noqa
|
||||
from .model_gottbert import * # noqa
|
||||
from .model_xlmr import * # noqa
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from collections import Counter
|
||||
from typing import List
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
def align_bpe_to_words(roberta, bpe_tokens: torch.LongTensor, other_tokens: List[str]):
|
||||
"""
|
||||
Helper to align GPT-2 BPE to other tokenization formats (e.g., spaCy).
|
||||
|
||||
Args:
|
||||
roberta (RobertaHubInterface): RoBERTa instance
|
||||
bpe_tokens (torch.LongTensor): GPT-2 BPE tokens of shape `(T_bpe)`
|
||||
other_tokens (List[str]): other tokens of shape `(T_words)`
|
||||
|
||||
Returns:
|
||||
List[str]: mapping from *other_tokens* to corresponding *bpe_tokens*.
|
||||
"""
|
||||
assert bpe_tokens.dim() == 1
|
||||
assert bpe_tokens[0] == 0
|
||||
|
||||
def clean(text):
|
||||
return text.strip()
|
||||
|
||||
# remove whitespaces to simplify alignment
|
||||
bpe_tokens = [roberta.task.source_dictionary.string([x]) for x in bpe_tokens]
|
||||
bpe_tokens = [
|
||||
clean(roberta.bpe.decode(x) if x not in {"<s>", ""} else x) for x in bpe_tokens
|
||||
]
|
||||
other_tokens = [clean(str(o)) for o in other_tokens]
|
||||
|
||||
# strip leading <s>
|
||||
bpe_tokens = bpe_tokens[1:]
|
||||
assert "".join(bpe_tokens) == "".join(other_tokens)
|
||||
|
||||
# create alignment from every word to a list of BPE tokens
|
||||
alignment = []
|
||||
bpe_toks = filter(lambda item: item[1] != "", enumerate(bpe_tokens, start=1))
|
||||
j, bpe_tok = next(bpe_toks)
|
||||
for other_tok in other_tokens:
|
||||
bpe_indices = []
|
||||
while True:
|
||||
if other_tok.startswith(bpe_tok):
|
||||
bpe_indices.append(j)
|
||||
other_tok = other_tok[len(bpe_tok) :]
|
||||
try:
|
||||
j, bpe_tok = next(bpe_toks)
|
||||
except StopIteration:
|
||||
j, bpe_tok = None, None
|
||||
elif bpe_tok.startswith(other_tok):
|
||||
# other_tok spans multiple BPE tokens
|
||||
bpe_indices.append(j)
|
||||
bpe_tok = bpe_tok[len(other_tok) :]
|
||||
other_tok = ""
|
||||
else:
|
||||
raise Exception('Cannot align "{}" and "{}"'.format(other_tok, bpe_tok))
|
||||
if other_tok == "":
|
||||
break
|
||||
assert len(bpe_indices) > 0
|
||||
alignment.append(bpe_indices)
|
||||
assert len(alignment) == len(other_tokens)
|
||||
|
||||
return alignment
|
||||
|
||||
|
||||
def align_features_to_words(roberta, features, alignment):
|
||||
"""
|
||||
Align given features to words.
|
||||
|
||||
Args:
|
||||
roberta (RobertaHubInterface): RoBERTa instance
|
||||
features (torch.Tensor): features to align of shape `(T_bpe x C)`
|
||||
alignment: alignment between BPE tokens and words returned by
|
||||
func:`align_bpe_to_words`.
|
||||
"""
|
||||
assert features.dim() == 2
|
||||
|
||||
bpe_counts = Counter(j for bpe_indices in alignment for j in bpe_indices)
|
||||
assert bpe_counts[0] == 0 # <s> shouldn't be aligned
|
||||
denom = features.new([bpe_counts.get(j, 1) for j in range(len(features))])
|
||||
weighted_features = features / denom.unsqueeze(-1)
|
||||
|
||||
output = [weighted_features[0]]
|
||||
largest_j = -1
|
||||
for bpe_indices in alignment:
|
||||
output.append(weighted_features[bpe_indices].sum(dim=0))
|
||||
largest_j = max(largest_j, *bpe_indices)
|
||||
for j in range(largest_j + 1, len(features)):
|
||||
output.append(weighted_features[j])
|
||||
output = torch.stack(output)
|
||||
assert torch.all(torch.abs(output.sum(dim=0) - features.sum(dim=0)) < 1e-4)
|
||||
return output
|
||||
|
||||
|
||||
def spacy_nlp():
|
||||
if getattr(spacy_nlp, "_nlp", None) is None:
|
||||
try:
|
||||
from spacy.lang.en import English
|
||||
|
||||
spacy_nlp._nlp = English()
|
||||
except ImportError:
|
||||
raise ImportError("Please install spacy with: pip install spacy")
|
||||
return spacy_nlp._nlp
|
||||
|
||||
|
||||
def spacy_tokenizer():
|
||||
if getattr(spacy_tokenizer, "_tokenizer", None) is None:
|
||||
try:
|
||||
nlp = spacy_nlp()
|
||||
spacy_tokenizer._tokenizer = nlp.Defaults.create_tokenizer(nlp)
|
||||
except ImportError:
|
||||
raise ImportError("Please install spacy with: pip install spacy")
|
||||
return spacy_tokenizer._tokenizer
|
||||
@@ -0,0 +1,235 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.data import encoders
|
||||
|
||||
|
||||
class RobertaHubInterface(nn.Module):
|
||||
"""A simple PyTorch Hub interface to RoBERTa.
|
||||
|
||||
Usage: https://github.com/pytorch/fairseq/tree/master/examples/roberta
|
||||
"""
|
||||
|
||||
def __init__(self, cfg, task, model):
|
||||
super().__init__()
|
||||
self.cfg = cfg
|
||||
self.task = task
|
||||
self.model = model
|
||||
|
||||
self.bpe = encoders.build_bpe(cfg.bpe)
|
||||
|
||||
# this is useful for determining the device
|
||||
self.register_buffer("_float_tensor", torch.tensor([0], dtype=torch.float))
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
return self._float_tensor.device
|
||||
|
||||
def encode(
|
||||
self, sentence: str, *addl_sentences, no_separator=False
|
||||
) -> torch.LongTensor:
|
||||
"""
|
||||
BPE-encode a sentence (or multiple sentences).
|
||||
|
||||
Every sequence begins with a beginning-of-sentence (`<s>`) symbol.
|
||||
Every sentence ends with an end-of-sentence (`</s>`) and we use an
|
||||
extra end-of-sentence (`</s>`) as a separator.
|
||||
|
||||
Example (single sentence): `<s> a b c </s>`
|
||||
Example (sentence pair): `<s> d e f </s> </s> 1 2 3 </s>`
|
||||
|
||||
The BPE encoding follows GPT-2. One subtle detail is that the GPT-2 BPE
|
||||
requires leading spaces. For example::
|
||||
|
||||
>>> roberta.encode('Hello world').tolist()
|
||||
[0, 31414, 232, 2]
|
||||
>>> roberta.encode(' world').tolist()
|
||||
[0, 232, 2]
|
||||
>>> roberta.encode('world').tolist()
|
||||
[0, 8331, 2]
|
||||
"""
|
||||
bpe_sentence = "<s> " + self.bpe.encode(sentence) + " </s>"
|
||||
for s in addl_sentences:
|
||||
bpe_sentence += " </s>" if not no_separator else ""
|
||||
bpe_sentence += " " + self.bpe.encode(s) + " </s>"
|
||||
tokens = self.task.source_dictionary.encode_line(
|
||||
bpe_sentence, append_eos=False, add_if_not_exist=False
|
||||
)
|
||||
return tokens.long()
|
||||
|
||||
def decode(self, tokens: torch.LongTensor):
|
||||
assert tokens.dim() == 1
|
||||
tokens = tokens.numpy()
|
||||
if tokens[0] == self.task.source_dictionary.bos():
|
||||
tokens = tokens[1:] # remove <s>
|
||||
eos_mask = tokens == self.task.source_dictionary.eos()
|
||||
doc_mask = eos_mask[1:] & eos_mask[:-1]
|
||||
sentences = np.split(tokens, doc_mask.nonzero()[0] + 1)
|
||||
sentences = [
|
||||
self.bpe.decode(self.task.source_dictionary.string(s)) for s in sentences
|
||||
]
|
||||
if len(sentences) == 1:
|
||||
return sentences[0]
|
||||
return sentences
|
||||
|
||||
def extract_features(
|
||||
self, tokens: torch.LongTensor, return_all_hiddens: bool = False
|
||||
) -> torch.Tensor:
|
||||
if tokens.dim() == 1:
|
||||
tokens = tokens.unsqueeze(0)
|
||||
if tokens.size(-1) > self.model.max_positions():
|
||||
raise ValueError(
|
||||
"tokens exceeds maximum length: {} > {}".format(
|
||||
tokens.size(-1), self.model.max_positions()
|
||||
)
|
||||
)
|
||||
features, extra = self.model(
|
||||
tokens.to(device=self.device),
|
||||
features_only=True,
|
||||
return_all_hiddens=return_all_hiddens,
|
||||
)
|
||||
if return_all_hiddens:
|
||||
# convert from T x B x C -> B x T x C
|
||||
inner_states = extra["inner_states"]
|
||||
return [inner_state.transpose(0, 1) for inner_state in inner_states]
|
||||
else:
|
||||
return features # just the last layer's features
|
||||
|
||||
def register_classification_head(
|
||||
self, name: str, num_classes: int = None, embedding_size: int = None, **kwargs
|
||||
):
|
||||
self.model.register_classification_head(
|
||||
name, num_classes=num_classes, embedding_size=embedding_size, **kwargs
|
||||
)
|
||||
|
||||
def predict(self, head: str, tokens: torch.LongTensor, return_logits: bool = False):
|
||||
features = self.extract_features(tokens.to(device=self.device))
|
||||
logits = self.model.classification_heads[head](features)
|
||||
if return_logits:
|
||||
return logits
|
||||
return F.log_softmax(logits, dim=-1)
|
||||
|
||||
def extract_features_aligned_to_words(
|
||||
self, sentence: str, return_all_hiddens: bool = False
|
||||
) -> torch.Tensor:
|
||||
"""Extract RoBERTa features, aligned to spaCy's word-level tokenizer."""
|
||||
from fairseq.models.roberta import alignment_utils
|
||||
from spacy.tokens import Doc
|
||||
|
||||
nlp = alignment_utils.spacy_nlp()
|
||||
tokenizer = alignment_utils.spacy_tokenizer()
|
||||
|
||||
# tokenize both with GPT-2 BPE and spaCy
|
||||
bpe_toks = self.encode(sentence)
|
||||
spacy_toks = tokenizer(sentence)
|
||||
spacy_toks_ws = [t.text_with_ws for t in tokenizer(sentence)]
|
||||
alignment = alignment_utils.align_bpe_to_words(self, bpe_toks, spacy_toks_ws)
|
||||
|
||||
# extract features and align them
|
||||
features = self.extract_features(
|
||||
bpe_toks, return_all_hiddens=return_all_hiddens
|
||||
)
|
||||
features = features.squeeze(0)
|
||||
aligned_feats = alignment_utils.align_features_to_words(
|
||||
self, features, alignment
|
||||
)
|
||||
|
||||
# wrap in spaCy Doc
|
||||
doc = Doc(
|
||||
nlp.vocab,
|
||||
words=["<s>"] + [x.text for x in spacy_toks] + ["</s>"],
|
||||
spaces=[True]
|
||||
+ [x.endswith(" ") for x in spacy_toks_ws[:-1]]
|
||||
+ [True, False],
|
||||
)
|
||||
assert len(doc) == aligned_feats.size(0)
|
||||
doc.user_token_hooks["vector"] = lambda token: aligned_feats[token.i]
|
||||
return doc
|
||||
|
||||
def fill_mask(self, masked_input: str, topk: int = 5):
|
||||
masked_token = "<mask>"
|
||||
assert (
|
||||
masked_token in masked_input and masked_input.count(masked_token) == 1
|
||||
), "Please add one {0} token for the input, eg: 'He is a {0} guy'".format(
|
||||
masked_token
|
||||
)
|
||||
|
||||
text_spans = masked_input.split(masked_token)
|
||||
text_spans_bpe = (
|
||||
(" {0} ".format(masked_token))
|
||||
.join([self.bpe.encode(text_span.rstrip()) for text_span in text_spans])
|
||||
.strip()
|
||||
)
|
||||
tokens = self.task.source_dictionary.encode_line(
|
||||
"<s> " + text_spans_bpe + " </s>",
|
||||
append_eos=False,
|
||||
add_if_not_exist=False,
|
||||
)
|
||||
|
||||
masked_index = (tokens == self.task.mask_idx).nonzero(as_tuple=False)
|
||||
if tokens.dim() == 1:
|
||||
tokens = tokens.unsqueeze(0)
|
||||
|
||||
with utils.model_eval(self.model):
|
||||
features, extra = self.model(
|
||||
tokens.long().to(device=self.device),
|
||||
features_only=False,
|
||||
return_all_hiddens=False,
|
||||
)
|
||||
logits = features[0, masked_index, :].squeeze()
|
||||
prob = logits.softmax(dim=0)
|
||||
values, index = prob.topk(k=topk, dim=0)
|
||||
topk_predicted_token_bpe = self.task.source_dictionary.string(index)
|
||||
|
||||
topk_filled_outputs = []
|
||||
for index, predicted_token_bpe in enumerate(
|
||||
topk_predicted_token_bpe.split(" ")
|
||||
):
|
||||
predicted_token = self.bpe.decode(predicted_token_bpe)
|
||||
# Quick hack to fix https://github.com/pytorch/fairseq/issues/1306
|
||||
if predicted_token_bpe.startswith("\u2581"):
|
||||
predicted_token = " " + predicted_token
|
||||
if " {0}".format(masked_token) in masked_input:
|
||||
topk_filled_outputs.append(
|
||||
(
|
||||
masked_input.replace(
|
||||
" {0}".format(masked_token), predicted_token
|
||||
),
|
||||
values[index].item(),
|
||||
predicted_token,
|
||||
)
|
||||
)
|
||||
else:
|
||||
topk_filled_outputs.append(
|
||||
(
|
||||
masked_input.replace(masked_token, predicted_token),
|
||||
values[index].item(),
|
||||
predicted_token,
|
||||
)
|
||||
)
|
||||
return topk_filled_outputs
|
||||
|
||||
def disambiguate_pronoun(self, sentence: str) -> bool:
|
||||
"""
|
||||
Usage::
|
||||
|
||||
>>> disambiguate_pronoun('The _trophy_ would not fit in the brown suitcase because [it] was too big.')
|
||||
True
|
||||
|
||||
>>> disambiguate_pronoun('The trophy would not fit in the brown suitcase because [it] was too big.')
|
||||
'The trophy'
|
||||
"""
|
||||
assert hasattr(
|
||||
self.task, "disambiguate_pronoun"
|
||||
), "roberta.disambiguate_pronoun() requires a model trained with the WSC task."
|
||||
with utils.model_eval(self.model):
|
||||
return self.task.disambiguate_pronoun(
|
||||
self.model, sentence, use_cuda=self.device.type == "cuda"
|
||||
)
|
||||
@@ -0,0 +1,569 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
RoBERTa: A Robustly Optimized BERT Pretraining Approach.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.transformer import TransformerEncoder
|
||||
from fairseq.modules import LayerNorm
|
||||
from fairseq.modules.quant_noise import quant_noise as apply_quant_noise_
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
|
||||
from .hub_interface import RobertaHubInterface
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_model("roberta")
|
||||
class RobertaModel(FairseqEncoderModel):
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
"roberta.base": "http://dl.fbaipublicfiles.com/fairseq/models/roberta.base.tar.gz",
|
||||
"roberta.large": "http://dl.fbaipublicfiles.com/fairseq/models/roberta.large.tar.gz",
|
||||
"roberta.large.mnli": "http://dl.fbaipublicfiles.com/fairseq/models/roberta.large.mnli.tar.gz",
|
||||
"roberta.large.wsc": "http://dl.fbaipublicfiles.com/fairseq/models/roberta.large.wsc.tar.gz",
|
||||
}
|
||||
|
||||
def __init__(self, args, encoder):
|
||||
super().__init__(encoder)
|
||||
self.args = args
|
||||
|
||||
# We follow BERT's random weight initialization
|
||||
self.apply(init_bert_params)
|
||||
|
||||
self.classification_heads = nn.ModuleDict()
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
parser.add_argument(
|
||||
"--encoder-layers", type=int, metavar="L", help="num encoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-embed-dim",
|
||||
type=int,
|
||||
metavar="H",
|
||||
help="encoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="F",
|
||||
help="encoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-attention-heads",
|
||||
type=int,
|
||||
metavar="A",
|
||||
help="num encoder attention heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pooler-activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use for pooler layer",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each encoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--layernorm-embedding",
|
||||
action="store_true",
|
||||
help="add layernorm to embedding",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dropout", type=float, metavar="D", help="dropout probability"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for attention weights",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--activation-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability after activation in FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pooler-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability in the masked_lm pooler layers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-positions", type=int, help="number of positional embeddings to learn"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-checkpoint-heads",
|
||||
action="store_true",
|
||||
help="(re-)register and load heads when loading checkpoints",
|
||||
)
|
||||
# args for "Reducing Transformer Depth on Demand with Structured Dropout" (Fan et al., 2019)
|
||||
parser.add_argument(
|
||||
"--encoder-layerdrop",
|
||||
type=float,
|
||||
metavar="D",
|
||||
default=0,
|
||||
help="LayerDrop probability for encoder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-layers-to-keep",
|
||||
default=None,
|
||||
help="which layers to *keep* when pruning as a comma-separated list",
|
||||
)
|
||||
# args for Training with Quantization Noise for Extreme Model Compression ({Fan*, Stock*} et al., 2020)
|
||||
parser.add_argument(
|
||||
"--quant-noise-pq",
|
||||
type=float,
|
||||
metavar="D",
|
||||
default=0,
|
||||
help="iterative PQ quantization noise at training time",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--quant-noise-pq-block-size",
|
||||
type=int,
|
||||
metavar="D",
|
||||
default=8,
|
||||
help="block size of quantization noise at training time",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--quant-noise-scalar",
|
||||
type=float,
|
||||
metavar="D",
|
||||
default=0,
|
||||
help="scalar quantization noise and scalar quantization at training time",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--untie-weights-roberta",
|
||||
action="store_true",
|
||||
help="Untie weights between embeddings and classifiers in RoBERTa",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--spectral-norm-classification-head",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Apply spectral normalization on the classification head",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present
|
||||
base_architecture(args)
|
||||
|
||||
if not hasattr(args, "max_positions"):
|
||||
args.max_positions = args.tokens_per_sample
|
||||
|
||||
encoder = RobertaEncoder(args, task.source_dictionary)
|
||||
return cls(args, encoder)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
src_tokens,
|
||||
features_only=False,
|
||||
return_all_hiddens=False,
|
||||
classification_head_name=None,
|
||||
**kwargs
|
||||
):
|
||||
if classification_head_name is not None:
|
||||
features_only = True
|
||||
|
||||
x, extra = self.encoder(src_tokens, features_only, return_all_hiddens, **kwargs)
|
||||
|
||||
if classification_head_name is not None:
|
||||
x = self.classification_heads[classification_head_name](x)
|
||||
return x, extra
|
||||
|
||||
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
logits = net_output[0].float()
|
||||
if log_probs:
|
||||
return F.log_softmax(logits, dim=-1)
|
||||
else:
|
||||
return F.softmax(logits, dim=-1)
|
||||
|
||||
def register_classification_head(
|
||||
self, name, num_classes=None, inner_dim=None, **kwargs
|
||||
):
|
||||
"""Register a classification head."""
|
||||
if name in self.classification_heads:
|
||||
prev_num_classes = self.classification_heads[name].out_proj.out_features
|
||||
prev_inner_dim = self.classification_heads[name].dense.out_features
|
||||
if num_classes != prev_num_classes or inner_dim != prev_inner_dim:
|
||||
logger.warning(
|
||||
're-registering head "{}" with num_classes {} (prev: {}) '
|
||||
"and inner_dim {} (prev: {})".format(
|
||||
name, num_classes, prev_num_classes, inner_dim, prev_inner_dim
|
||||
)
|
||||
)
|
||||
self.classification_heads[name] = RobertaClassificationHead(
|
||||
input_dim=self.args.encoder_embed_dim,
|
||||
inner_dim=inner_dim or self.args.encoder_embed_dim,
|
||||
num_classes=num_classes,
|
||||
activation_fn=self.args.pooler_activation_fn,
|
||||
pooler_dropout=self.args.pooler_dropout,
|
||||
q_noise=self.args.quant_noise_pq,
|
||||
qn_block_size=self.args.quant_noise_pq_block_size,
|
||||
do_spectral_norm=self.args.spectral_norm_classification_head,
|
||||
)
|
||||
|
||||
@property
|
||||
def supported_targets(self):
|
||||
return {"self"}
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(
|
||||
cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file="model.pt",
|
||||
data_name_or_path=".",
|
||||
bpe="gpt2",
|
||||
**kwargs
|
||||
):
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
bpe=bpe,
|
||||
load_checkpoint_heads=True,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
logger.info(x["args"])
|
||||
return RobertaHubInterface(x["args"], x["task"], x["models"][0])
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
prefix = name + "." if name != "" else ""
|
||||
|
||||
# rename decoder -> encoder before upgrading children modules
|
||||
for k in list(state_dict.keys()):
|
||||
if k.startswith(prefix + "decoder"):
|
||||
new_k = prefix + "encoder" + k[len(prefix + "decoder") :]
|
||||
state_dict[new_k] = state_dict[k]
|
||||
del state_dict[k]
|
||||
|
||||
# rename emb_layer_norm -> layernorm_embedding
|
||||
for k in list(state_dict.keys()):
|
||||
if ".emb_layer_norm." in k:
|
||||
new_k = k.replace(".emb_layer_norm.", ".layernorm_embedding.")
|
||||
state_dict[new_k] = state_dict[k]
|
||||
del state_dict[k]
|
||||
|
||||
# upgrade children modules
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
|
||||
# Handle new classification heads present in the state dict.
|
||||
current_head_names = (
|
||||
[]
|
||||
if not hasattr(self, "classification_heads")
|
||||
else self.classification_heads.keys()
|
||||
)
|
||||
keys_to_delete = []
|
||||
for k in state_dict.keys():
|
||||
if not k.startswith(prefix + "classification_heads."):
|
||||
continue
|
||||
|
||||
head_name = k[len(prefix + "classification_heads.") :].split(".")[0]
|
||||
num_classes = state_dict[
|
||||
prefix + "classification_heads." + head_name + ".out_proj.weight"
|
||||
].size(0)
|
||||
inner_dim = state_dict[
|
||||
prefix + "classification_heads." + head_name + ".dense.weight"
|
||||
].size(0)
|
||||
|
||||
if getattr(self.args, "load_checkpoint_heads", False):
|
||||
if head_name not in current_head_names:
|
||||
self.register_classification_head(head_name, num_classes, inner_dim)
|
||||
else:
|
||||
if head_name not in current_head_names:
|
||||
logger.warning(
|
||||
"deleting classification head ({}) from checkpoint "
|
||||
"not present in current model: {}".format(head_name, k)
|
||||
)
|
||||
keys_to_delete.append(k)
|
||||
elif (
|
||||
num_classes
|
||||
!= self.classification_heads[head_name].out_proj.out_features
|
||||
or inner_dim
|
||||
!= self.classification_heads[head_name].dense.out_features
|
||||
):
|
||||
logger.warning(
|
||||
"deleting classification head ({}) from checkpoint "
|
||||
"with different dimensions than current model: {}".format(
|
||||
head_name, k
|
||||
)
|
||||
)
|
||||
keys_to_delete.append(k)
|
||||
for k in keys_to_delete:
|
||||
del state_dict[k]
|
||||
|
||||
# Copy any newly-added classification heads into the state dict
|
||||
# with their current weights.
|
||||
if hasattr(self, "classification_heads"):
|
||||
cur_state = self.classification_heads.state_dict()
|
||||
for k, v in cur_state.items():
|
||||
if prefix + "classification_heads." + k not in state_dict:
|
||||
logger.info("Overwriting " + prefix + "classification_heads." + k)
|
||||
state_dict[prefix + "classification_heads." + k] = v
|
||||
|
||||
|
||||
class RobertaLMHead(nn.Module):
|
||||
"""Head for masked language modeling."""
|
||||
|
||||
def __init__(self, embed_dim, output_dim, activation_fn, weight=None):
|
||||
super().__init__()
|
||||
self.dense = nn.Linear(embed_dim, embed_dim)
|
||||
self.activation_fn = utils.get_activation_fn(activation_fn)
|
||||
self.layer_norm = LayerNorm(embed_dim)
|
||||
|
||||
if weight is None:
|
||||
weight = nn.Linear(embed_dim, output_dim, bias=False).weight
|
||||
self.weight = weight
|
||||
self.bias = nn.Parameter(torch.zeros(output_dim))
|
||||
|
||||
def forward(self, features, masked_tokens=None, **kwargs):
|
||||
# Only project the masked tokens while training,
|
||||
# saves both memory and computation
|
||||
if masked_tokens is not None:
|
||||
features = features[masked_tokens, :]
|
||||
|
||||
x = self.dense(features)
|
||||
x = self.activation_fn(x)
|
||||
x = self.layer_norm(x)
|
||||
# project back to size of vocabulary with bias
|
||||
x = F.linear(x, self.weight) + self.bias
|
||||
return x
|
||||
|
||||
|
||||
class RobertaClassificationHead(nn.Module):
|
||||
"""Head for sentence-level classification tasks."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
input_dim,
|
||||
inner_dim,
|
||||
num_classes,
|
||||
activation_fn,
|
||||
pooler_dropout,
|
||||
q_noise=0,
|
||||
qn_block_size=8,
|
||||
do_spectral_norm=False,
|
||||
):
|
||||
super().__init__()
|
||||
self.dense = nn.Linear(input_dim, inner_dim)
|
||||
self.activation_fn = utils.get_activation_fn(activation_fn)
|
||||
self.dropout = nn.Dropout(p=pooler_dropout)
|
||||
self.out_proj = apply_quant_noise_(
|
||||
nn.Linear(inner_dim, num_classes), q_noise, qn_block_size
|
||||
)
|
||||
if do_spectral_norm:
|
||||
if q_noise != 0:
|
||||
raise NotImplementedError(
|
||||
"Attempting to use Spectral Normalization with Quant Noise. This is not officially supported"
|
||||
)
|
||||
self.out_proj = torch.nn.utils.spectral_norm(self.out_proj)
|
||||
|
||||
def forward(self, features, **kwargs):
|
||||
x = features[:, 0, :] # take <s> token (equiv. to [CLS])
|
||||
x = self.dropout(x)
|
||||
x = self.dense(x)
|
||||
x = self.activation_fn(x)
|
||||
x = self.dropout(x)
|
||||
x = self.out_proj(x)
|
||||
return x
|
||||
|
||||
|
||||
class RobertaEncoder(FairseqEncoder):
|
||||
"""RoBERTa encoder."""
|
||||
|
||||
def __init__(self, args, dictionary):
|
||||
super().__init__(dictionary)
|
||||
|
||||
# set any missing default values
|
||||
base_architecture(args)
|
||||
self.args = args
|
||||
|
||||
if args.encoder_layers_to_keep:
|
||||
args.encoder_layers = len(args.encoder_layers_to_keep.split(","))
|
||||
|
||||
embed_tokens = self.build_embedding(
|
||||
len(dictionary), args.encoder_embed_dim, dictionary.pad()
|
||||
)
|
||||
|
||||
self.sentence_encoder = self.build_encoder(args, dictionary, embed_tokens)
|
||||
|
||||
self.lm_head = self.build_lm_head(
|
||||
embed_dim=args.encoder_embed_dim,
|
||||
output_dim=len(dictionary),
|
||||
activation_fn=args.activation_fn,
|
||||
weight=(
|
||||
self.sentence_encoder.embed_tokens.weight
|
||||
if not args.untie_weights_roberta
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
def build_embedding(self, vocab_size, embedding_dim, padding_idx):
|
||||
return nn.Embedding(vocab_size, embedding_dim, padding_idx)
|
||||
|
||||
def build_encoder(self, args, dictionary, embed_tokens):
|
||||
encoder = TransformerEncoder(args, dictionary, embed_tokens)
|
||||
encoder.apply(init_bert_params)
|
||||
return encoder
|
||||
|
||||
def build_lm_head(self, embed_dim, output_dim, activation_fn, weight):
|
||||
return RobertaLMHead(embed_dim, output_dim, activation_fn, weight)
|
||||
|
||||
def build_lm_head(self, embed_dim, output_dim, activation_fn, weight):
|
||||
return RobertaLMHead(embed_dim, output_dim, activation_fn, weight)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
src_tokens,
|
||||
features_only=False,
|
||||
return_all_hiddens=False,
|
||||
masked_tokens=None,
|
||||
**unused
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
src_tokens (LongTensor): input tokens of shape `(batch, src_len)`
|
||||
features_only (bool, optional): skip LM head and just return
|
||||
features. If True, the output will be of shape
|
||||
`(batch, src_len, embed_dim)`.
|
||||
return_all_hiddens (bool, optional): also return all of the
|
||||
intermediate hidden states (default: False).
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the LM output of shape `(batch, src_len, vocab)`
|
||||
- a dictionary of additional data, where 'inner_states'
|
||||
is a list of hidden states. Note that the hidden
|
||||
states have shape `(src_len, batch, vocab)`.
|
||||
"""
|
||||
x, extra = self.extract_features(
|
||||
src_tokens, return_all_hiddens=return_all_hiddens
|
||||
)
|
||||
if not features_only:
|
||||
x = self.output_layer(x, masked_tokens=masked_tokens)
|
||||
return x, extra
|
||||
|
||||
def extract_features(self, src_tokens, return_all_hiddens=False, **kwargs):
|
||||
encoder_out = self.sentence_encoder(
|
||||
src_tokens,
|
||||
return_all_hiddens=return_all_hiddens,
|
||||
token_embeddings=kwargs.get("token_embeddings", None),
|
||||
)
|
||||
# T x B x C -> B x T x C
|
||||
features = encoder_out["encoder_out"][0].transpose(0, 1)
|
||||
inner_states = encoder_out["encoder_states"] if return_all_hiddens else None
|
||||
return features, {"inner_states": inner_states}
|
||||
|
||||
def output_layer(self, features, masked_tokens=None, **unused):
|
||||
return self.lm_head(features, masked_tokens)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the encoder."""
|
||||
return self.args.max_positions
|
||||
|
||||
|
||||
@register_model_architecture("roberta", "roberta")
|
||||
def base_architecture(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 768)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 3072)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 12)
|
||||
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_dropout = getattr(args, "activation_dropout", 0.0)
|
||||
args.pooler_dropout = getattr(args, "pooler_dropout", 0.0)
|
||||
|
||||
args.max_source_positions = getattr(args, "max_positions", 512)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
|
||||
# BERT has a few structural differences compared to the original Transformer
|
||||
args.encoder_learned_pos = getattr(args, "encoder_learned_pos", True)
|
||||
args.layernorm_embedding = getattr(args, "layernorm_embedding", True)
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", True)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
args.pooler_activation_fn = getattr(args, "pooler_activation_fn", "tanh")
|
||||
args.untie_weights_roberta = getattr(args, "untie_weights_roberta", False)
|
||||
|
||||
# Adaptive input config
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
|
||||
# LayerDrop config
|
||||
args.encoder_layerdrop = getattr(args, "encoder_layerdrop", 0.0)
|
||||
args.encoder_layers_to_keep = getattr(args, "encoder_layers_to_keep", None)
|
||||
|
||||
# Quantization noise config
|
||||
args.quant_noise_pq = getattr(args, "quant_noise_pq", 0)
|
||||
args.quant_noise_pq_block_size = getattr(args, "quant_noise_pq_block_size", 8)
|
||||
args.quant_noise_scalar = getattr(args, "quant_noise_scalar", 0)
|
||||
|
||||
# R4F config
|
||||
args.spectral_norm_classification_head = getattr(
|
||||
args, "spectral_norm_classification_head", False
|
||||
)
|
||||
|
||||
|
||||
@register_model_architecture("roberta", "roberta_prenorm")
|
||||
def roberta_prenorm_architecture(args):
|
||||
args.layernorm_embedding = getattr(args, "layernorm_embedding", False)
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", True)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("roberta", "roberta_base")
|
||||
def roberta_base_architecture(args):
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("roberta", "roberta_large")
|
||||
def roberta_large_architecture(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 24)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 4096)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("roberta", "xlm")
|
||||
def xlm_architecture(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 16)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1280)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 1280 * 4)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
||||
base_architecture(args)
|
||||
@@ -0,0 +1,50 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
CamemBERT: a Tasty French Language Model
|
||||
"""
|
||||
|
||||
from fairseq.models import register_model
|
||||
|
||||
from .hub_interface import RobertaHubInterface
|
||||
from .model import RobertaModel
|
||||
|
||||
|
||||
@register_model("camembert")
|
||||
class CamembertModel(RobertaModel):
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
"camembert": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base.tar.gz",
|
||||
"camembert.v0": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base.tar.gz",
|
||||
"camembert-base": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base.tar.gz",
|
||||
"camembert-large": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-large.tar.gz",
|
||||
"camembert-base-ccnet": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base-ccnet.tar.gz",
|
||||
"camembert-base-ccnet-4gb": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base-ccnet-4gb.tar.gz",
|
||||
"camembert-base-wikipedia-4gb": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base-wikipedia-4gb.tar.gz",
|
||||
"camembert-base-oscar-4gb": "http://dl.fbaipublicfiles.com/fairseq/models/camembert-base-oscar-4gb.tar.gz",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(
|
||||
cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file="model.pt",
|
||||
data_name_or_path=".",
|
||||
bpe="sentencepiece",
|
||||
**kwargs
|
||||
):
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
bpe=bpe,
|
||||
load_checkpoint_heads=True,
|
||||
**kwargs,
|
||||
)
|
||||
return RobertaHubInterface(x["args"], x["task"], x["models"][0])
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
GottBERT: a pure German Language Model
|
||||
"""
|
||||
|
||||
from fairseq.models import register_model
|
||||
|
||||
from .hub_interface import RobertaHubInterface
|
||||
from .model import RobertaModel
|
||||
|
||||
|
||||
@register_model('gottbert')
|
||||
class GottbertModel(RobertaModel):
|
||||
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
'gottbert-base': 'https://dl.gottbert.de/fairseq/models/gottbert-base.tar.gz',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file='model.pt',
|
||||
data_name_or_path='.',
|
||||
bpe='hf_byte_bpe',
|
||||
bpe_vocab='vocab.json',
|
||||
bpe_merges='merges.txt',
|
||||
bpe_add_prefix_space=False,
|
||||
**kwargs
|
||||
):
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
bpe=bpe,
|
||||
load_checkpoint_heads=True,
|
||||
bpe_vocab=bpe_vocab,
|
||||
bpe_merges=bpe_merges,
|
||||
bpe_add_prefix_space=bpe_add_prefix_space,
|
||||
**kwargs,
|
||||
)
|
||||
return RobertaHubInterface(x['args'], x['task'], x['models'][0])
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
"""
|
||||
Unsupervised Cross-lingual Representation Learning at Scale
|
||||
"""
|
||||
|
||||
from fairseq.models import register_model
|
||||
|
||||
from .hub_interface import RobertaHubInterface
|
||||
from .model import RobertaModel
|
||||
|
||||
|
||||
@register_model("xlmr")
|
||||
class XLMRModel(RobertaModel):
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
return {
|
||||
"xlmr.base": "http://dl.fbaipublicfiles.com/fairseq/models/xlmr.base.tar.gz",
|
||||
"xlmr.large": "http://dl.fbaipublicfiles.com/fairseq/models/xlmr.large.tar.gz",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_pretrained(
|
||||
cls,
|
||||
model_name_or_path,
|
||||
checkpoint_file="model.pt",
|
||||
data_name_or_path=".",
|
||||
bpe="sentencepiece",
|
||||
**kwargs
|
||||
):
|
||||
from fairseq import hub_utils
|
||||
|
||||
x = hub_utils.from_pretrained(
|
||||
model_name_or_path,
|
||||
checkpoint_file,
|
||||
data_name_or_path,
|
||||
archive_map=cls.hub_models(),
|
||||
bpe=bpe,
|
||||
load_checkpoint_heads=True,
|
||||
**kwargs,
|
||||
)
|
||||
return RobertaHubInterface(x["args"], x["task"], x["models"][0])
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from .berard import * # noqa
|
||||
from .convtransformer import * # noqa
|
||||
from .s2t_transformer import * # noqa
|
||||
@@ -0,0 +1,606 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from ast import literal_eval
|
||||
from typing import List, Tuple
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import checkpoint_utils, utils
|
||||
from fairseq.data.data_utils import lengths_to_padding_mask
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
FairseqIncrementalDecoder,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
|
||||
|
||||
@register_model("s2t_berard")
|
||||
class BerardModel(FairseqEncoderDecoderModel):
|
||||
"""Implementation of a model similar to https://arxiv.org/abs/1802.04200
|
||||
|
||||
Paper title: End-to-End Automatic Speech Translation of Audiobooks
|
||||
An implementation is available in tensorflow at
|
||||
https://github.com/eske/seq2seq
|
||||
Relevant files in this implementation are the config
|
||||
(https://github.com/eske/seq2seq/blob/master/config/LibriSpeech/AST.yaml)
|
||||
and the model code
|
||||
(https://github.com/eske/seq2seq/blob/master/translate/models.py).
|
||||
The encoder and decoder try to be close to the original implementation.
|
||||
The attention is an MLP as in Bahdanau et al.
|
||||
(https://arxiv.org/abs/1409.0473).
|
||||
There is no state initialization by averaging the encoder outputs.
|
||||
"""
|
||||
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
parser.add_argument(
|
||||
"--input-layers",
|
||||
type=str,
|
||||
metavar="EXPR",
|
||||
help="List of linear layer dimensions. These "
|
||||
"layers are applied to the input features and "
|
||||
"are followed by tanh and possibly dropout.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="Dropout probability to use in the encoder/decoder. "
|
||||
"Note that this parameters control dropout in various places, "
|
||||
"there is no fine-grained control for dropout for embeddings "
|
||||
"vs LSTM layers for example.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--in-channels",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Number of encoder input channels. " "Typically value is 1.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--conv-layers",
|
||||
type=str,
|
||||
metavar="EXPR",
|
||||
help="List of conv layers " "(format: (channels, kernel, stride)).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num-blstm-layers",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Number of encoder bi-LSTM layers.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lstm-size", type=int, metavar="N", help="LSTM hidden size."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Embedding dimension of the decoder target tokens.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-hidden-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Decoder LSTM hidden dimension.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-num-layers",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Number of decoder LSTM layers.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Hidden layer dimension in MLP attention.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-layer-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="Hidden layer dim for linear layer prior to output projection.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-pretrained-encoder-from",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="model to take encoder weights from (for initialization)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-pretrained-decoder-from",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="model to take decoder weights from (for initialization)",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args, task):
|
||||
encoder = BerardEncoder(
|
||||
input_layers=literal_eval(args.input_layers),
|
||||
conv_layers=literal_eval(args.conv_layers),
|
||||
in_channels=args.input_channels,
|
||||
input_feat_per_channel=args.input_feat_per_channel,
|
||||
num_blstm_layers=args.num_blstm_layers,
|
||||
lstm_size=args.lstm_size,
|
||||
dropout=args.dropout,
|
||||
)
|
||||
if getattr(args, "load_pretrained_encoder_from", None):
|
||||
encoder = checkpoint_utils.load_pretrained_component_from_model(
|
||||
component=encoder, checkpoint=args.load_pretrained_encoder_from
|
||||
)
|
||||
return encoder
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, task):
|
||||
decoder = LSTMDecoder(
|
||||
dictionary=task.target_dictionary,
|
||||
embed_dim=args.decoder_embed_dim,
|
||||
num_layers=args.decoder_num_layers,
|
||||
hidden_size=args.decoder_hidden_dim,
|
||||
dropout=args.dropout,
|
||||
encoder_output_dim=2 * args.lstm_size, # bidirectional
|
||||
attention_dim=args.attention_dim,
|
||||
output_layer_dim=args.output_layer_dim,
|
||||
)
|
||||
if getattr(args, "load_pretrained_decoder_from", None):
|
||||
decoder = checkpoint_utils.load_pretrained_component_from_model(
|
||||
component=decoder, checkpoint=args.load_pretrained_decoder_from
|
||||
)
|
||||
return decoder
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
encoder = cls.build_encoder(args, task)
|
||||
decoder = cls.build_decoder(args, task)
|
||||
|
||||
return cls(encoder, decoder)
|
||||
|
||||
def get_normalized_probs(self, net_output, log_probs, sample=None):
|
||||
# net_output['encoder_out'] is a (B, T, D) tensor
|
||||
lprobs = super().get_normalized_probs(net_output, log_probs, sample)
|
||||
# lprobs is a (B, T, D) tensor
|
||||
lprobs.batch_first = True
|
||||
return lprobs
|
||||
|
||||
|
||||
class BerardEncoder(FairseqEncoder):
|
||||
def __init__(
|
||||
self,
|
||||
input_layers: List[int],
|
||||
conv_layers: List[Tuple[int]],
|
||||
in_channels: int,
|
||||
input_feat_per_channel: int,
|
||||
num_blstm_layers: int,
|
||||
lstm_size: int,
|
||||
dropout: float,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
input_layers: list of linear layer dimensions. These layers are
|
||||
applied to the input features and are followed by tanh and
|
||||
possibly dropout.
|
||||
conv_layers: list of conv2d layer configurations. A configuration is
|
||||
a tuple (out_channels, conv_kernel_size, stride).
|
||||
in_channels: number of input channels.
|
||||
input_feat_per_channel: number of input features per channel. These
|
||||
are speech features, typically 40 or 80.
|
||||
num_blstm_layers: number of bidirectional LSTM layers.
|
||||
lstm_size: size of the LSTM hidden (and cell) size.
|
||||
dropout: dropout probability. Dropout can be applied after the
|
||||
linear layers and LSTM layers but not to the convolutional
|
||||
layers.
|
||||
"""
|
||||
super().__init__(None)
|
||||
|
||||
self.input_layers = nn.ModuleList()
|
||||
in_features = input_feat_per_channel
|
||||
for out_features in input_layers:
|
||||
if dropout > 0:
|
||||
self.input_layers.append(
|
||||
nn.Sequential(
|
||||
nn.Linear(in_features, out_features), nn.Dropout(p=dropout)
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.input_layers.append(nn.Linear(in_features, out_features))
|
||||
in_features = out_features
|
||||
|
||||
self.in_channels = in_channels
|
||||
self.input_dim = input_feat_per_channel
|
||||
self.conv_kernel_sizes_and_strides = []
|
||||
self.conv_layers = nn.ModuleList()
|
||||
lstm_input_dim = input_layers[-1]
|
||||
for conv_layer in conv_layers:
|
||||
out_channels, conv_kernel_size, conv_stride = conv_layer
|
||||
self.conv_layers.append(
|
||||
nn.Conv2d(
|
||||
in_channels,
|
||||
out_channels,
|
||||
conv_kernel_size,
|
||||
stride=conv_stride,
|
||||
padding=conv_kernel_size // 2,
|
||||
)
|
||||
)
|
||||
self.conv_kernel_sizes_and_strides.append((conv_kernel_size, conv_stride))
|
||||
in_channels = out_channels
|
||||
lstm_input_dim //= conv_stride
|
||||
|
||||
lstm_input_dim *= conv_layers[-1][0]
|
||||
self.lstm_size = lstm_size
|
||||
self.num_blstm_layers = num_blstm_layers
|
||||
self.lstm = nn.LSTM(
|
||||
input_size=lstm_input_dim,
|
||||
hidden_size=lstm_size,
|
||||
num_layers=num_blstm_layers,
|
||||
dropout=dropout,
|
||||
bidirectional=True,
|
||||
)
|
||||
self.output_dim = 2 * lstm_size # bidirectional
|
||||
if dropout > 0:
|
||||
self.dropout = nn.Dropout(p=dropout)
|
||||
else:
|
||||
self.dropout = None
|
||||
|
||||
def forward(self, src_tokens, src_lengths=None, **kwargs):
|
||||
"""
|
||||
Args
|
||||
src_tokens: padded tensor (B, T, C * feat)
|
||||
src_lengths: tensor of original lengths of input utterances (B,)
|
||||
"""
|
||||
bsz, max_seq_len, _ = src_tokens.size()
|
||||
# (B, C, T, feat)
|
||||
x = (
|
||||
src_tokens.view(bsz, max_seq_len, self.in_channels, self.input_dim)
|
||||
.transpose(1, 2)
|
||||
.contiguous()
|
||||
)
|
||||
|
||||
for input_layer in self.input_layers:
|
||||
x = input_layer(x)
|
||||
x = torch.tanh(x)
|
||||
|
||||
for conv_layer in self.conv_layers:
|
||||
x = conv_layer(x)
|
||||
|
||||
bsz, _, output_seq_len, _ = x.size()
|
||||
|
||||
# (B, C, T, feat) -> (B, T, C, feat) -> (T, B, C, feat) ->
|
||||
# (T, B, C * feat)
|
||||
x = x.transpose(1, 2).transpose(0, 1).contiguous().view(output_seq_len, bsz, -1)
|
||||
|
||||
input_lengths = src_lengths.clone()
|
||||
for k, s in self.conv_kernel_sizes_and_strides:
|
||||
p = k // 2
|
||||
input_lengths = (input_lengths.float() + 2 * p - k) / s + 1
|
||||
input_lengths = input_lengths.floor().long()
|
||||
|
||||
packed_x = nn.utils.rnn.pack_padded_sequence(x, input_lengths)
|
||||
|
||||
h0 = x.new(2 * self.num_blstm_layers, bsz, self.lstm_size).zero_()
|
||||
c0 = x.new(2 * self.num_blstm_layers, bsz, self.lstm_size).zero_()
|
||||
packed_outs, _ = self.lstm(packed_x, (h0, c0))
|
||||
|
||||
# unpack outputs and apply dropout
|
||||
x, output_lengths = nn.utils.rnn.pad_packed_sequence(packed_outs)
|
||||
if self.dropout is not None:
|
||||
x = self.dropout(x)
|
||||
|
||||
encoder_padding_mask = (
|
||||
lengths_to_padding_mask(output_lengths).to(src_tokens.device).t()
|
||||
)
|
||||
|
||||
return {
|
||||
"encoder_out": x, # (T, B, C)
|
||||
"encoder_padding_mask": encoder_padding_mask, # (T, B)
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
encoder_out["encoder_out"] = encoder_out["encoder_out"].index_select(
|
||||
1, new_order
|
||||
)
|
||||
encoder_out["encoder_padding_mask"] = encoder_out[
|
||||
"encoder_padding_mask"
|
||||
].index_select(1, new_order)
|
||||
return encoder_out
|
||||
|
||||
|
||||
class MLPAttention(nn.Module):
|
||||
"""The original attention from Badhanau et al. (2014)
|
||||
|
||||
https://arxiv.org/abs/1409.0473, based on a Multi-Layer Perceptron.
|
||||
The attention score between position i in the encoder and position j in the
|
||||
decoder is: alpha_ij = V_a * tanh(W_ae * enc_i + W_ad * dec_j + b_a)
|
||||
"""
|
||||
|
||||
def __init__(self, decoder_hidden_state_dim, context_dim, attention_dim):
|
||||
super().__init__()
|
||||
|
||||
self.context_dim = context_dim
|
||||
self.attention_dim = attention_dim
|
||||
# W_ae and b_a
|
||||
self.encoder_proj = nn.Linear(context_dim, self.attention_dim, bias=True)
|
||||
# W_ad
|
||||
self.decoder_proj = nn.Linear(
|
||||
decoder_hidden_state_dim, self.attention_dim, bias=False
|
||||
)
|
||||
# V_a
|
||||
self.to_scores = nn.Linear(self.attention_dim, 1, bias=False)
|
||||
|
||||
def forward(self, decoder_state, source_hids, encoder_padding_mask):
|
||||
"""The expected input dimensions are:
|
||||
decoder_state: bsz x decoder_hidden_state_dim
|
||||
source_hids: src_len x bsz x context_dim
|
||||
encoder_padding_mask: src_len x bsz
|
||||
"""
|
||||
src_len, bsz, _ = source_hids.size()
|
||||
# (src_len*bsz) x context_dim (to feed through linear)
|
||||
flat_source_hids = source_hids.view(-1, self.context_dim)
|
||||
# (src_len*bsz) x attention_dim
|
||||
encoder_component = self.encoder_proj(flat_source_hids)
|
||||
# src_len x bsz x attention_dim
|
||||
encoder_component = encoder_component.view(src_len, bsz, self.attention_dim)
|
||||
# 1 x bsz x attention_dim
|
||||
decoder_component = self.decoder_proj(decoder_state).unsqueeze(0)
|
||||
# Sum with broadcasting and apply the non linearity
|
||||
# src_len x bsz x attention_dim
|
||||
hidden_att = torch.tanh(
|
||||
(decoder_component + encoder_component).view(-1, self.attention_dim)
|
||||
)
|
||||
# Project onto the reals to get attentions scores (src_len x bsz)
|
||||
attn_scores = self.to_scores(hidden_att).view(src_len, bsz)
|
||||
|
||||
# Mask + softmax (src_len x bsz)
|
||||
if encoder_padding_mask is not None:
|
||||
attn_scores = (
|
||||
attn_scores.float()
|
||||
.masked_fill_(encoder_padding_mask, float("-inf"))
|
||||
.type_as(attn_scores)
|
||||
) # FP16 support: cast to float and back
|
||||
# srclen x bsz
|
||||
normalized_masked_attn_scores = F.softmax(attn_scores, dim=0)
|
||||
|
||||
# Sum weighted sources (bsz x context_dim)
|
||||
attn_weighted_context = (
|
||||
source_hids * normalized_masked_attn_scores.unsqueeze(2)
|
||||
).sum(dim=0)
|
||||
|
||||
return attn_weighted_context, normalized_masked_attn_scores
|
||||
|
||||
|
||||
class LSTMDecoder(FairseqIncrementalDecoder):
|
||||
def __init__(
|
||||
self,
|
||||
dictionary,
|
||||
embed_dim,
|
||||
num_layers,
|
||||
hidden_size,
|
||||
dropout,
|
||||
encoder_output_dim,
|
||||
attention_dim,
|
||||
output_layer_dim,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
dictionary: target text dictionary.
|
||||
embed_dim: embedding dimension for target tokens.
|
||||
num_layers: number of LSTM layers.
|
||||
hidden_size: hidden size for LSTM layers.
|
||||
dropout: dropout probability. Dropout can be applied to the
|
||||
embeddings, the LSTM layers, and the context vector.
|
||||
encoder_output_dim: encoder output dimension (hidden size of
|
||||
encoder LSTM).
|
||||
attention_dim: attention dimension for MLP attention.
|
||||
output_layer_dim: size of the linear layer prior to output
|
||||
projection.
|
||||
"""
|
||||
super().__init__(dictionary)
|
||||
self.num_layers = num_layers
|
||||
self.hidden_size = hidden_size
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
self.embed_tokens = nn.Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
if dropout > 0:
|
||||
self.dropout = nn.Dropout(p=dropout)
|
||||
else:
|
||||
self.dropout = None
|
||||
|
||||
self.layers = nn.ModuleList()
|
||||
for layer_id in range(num_layers):
|
||||
input_size = embed_dim if layer_id == 0 else encoder_output_dim
|
||||
self.layers.append(
|
||||
nn.LSTMCell(input_size=input_size, hidden_size=hidden_size)
|
||||
)
|
||||
|
||||
self.context_dim = encoder_output_dim
|
||||
self.attention = MLPAttention(
|
||||
decoder_hidden_state_dim=hidden_size,
|
||||
context_dim=encoder_output_dim,
|
||||
attention_dim=attention_dim,
|
||||
)
|
||||
|
||||
self.deep_output_layer = nn.Linear(
|
||||
hidden_size + encoder_output_dim + embed_dim, output_layer_dim
|
||||
)
|
||||
self.output_projection = nn.Linear(output_layer_dim, num_embeddings)
|
||||
|
||||
def forward(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **kwargs
|
||||
):
|
||||
encoder_padding_mask = encoder_out["encoder_padding_mask"]
|
||||
encoder_outs = encoder_out["encoder_out"]
|
||||
|
||||
if incremental_state is not None:
|
||||
prev_output_tokens = prev_output_tokens[:, -1:]
|
||||
bsz, seqlen = prev_output_tokens.size()
|
||||
|
||||
srclen = encoder_outs.size(0)
|
||||
|
||||
# embed tokens
|
||||
embeddings = self.embed_tokens(prev_output_tokens)
|
||||
x = embeddings
|
||||
if self.dropout is not None:
|
||||
x = self.dropout(x)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
# initialize previous states (or get from cache during incremental
|
||||
# generation)
|
||||
cached_state = utils.get_incremental_state(
|
||||
self, incremental_state, "cached_state"
|
||||
)
|
||||
if cached_state is not None:
|
||||
prev_hiddens, prev_cells = cached_state
|
||||
else:
|
||||
prev_hiddens = [encoder_out["encoder_out"].mean(dim=0)] * self.num_layers
|
||||
prev_cells = [x.new_zeros(bsz, self.hidden_size)] * self.num_layers
|
||||
|
||||
attn_scores = x.new_zeros(bsz, srclen)
|
||||
attention_outs = []
|
||||
outs = []
|
||||
for j in range(seqlen):
|
||||
input = x[j, :, :]
|
||||
attention_out = None
|
||||
for i, layer in enumerate(self.layers):
|
||||
# the previous state is one layer below except for the bottom
|
||||
# layer where the previous state is the state emitted by the
|
||||
# top layer
|
||||
hidden, cell = layer(
|
||||
input,
|
||||
(
|
||||
prev_hiddens[(i - 1) % self.num_layers],
|
||||
prev_cells[(i - 1) % self.num_layers],
|
||||
),
|
||||
)
|
||||
if self.dropout is not None:
|
||||
hidden = self.dropout(hidden)
|
||||
prev_hiddens[i] = hidden
|
||||
prev_cells[i] = cell
|
||||
if attention_out is None:
|
||||
attention_out, attn_scores = self.attention(
|
||||
hidden, encoder_outs, encoder_padding_mask
|
||||
)
|
||||
if self.dropout is not None:
|
||||
attention_out = self.dropout(attention_out)
|
||||
attention_outs.append(attention_out)
|
||||
input = attention_out
|
||||
|
||||
# collect the output of the top layer
|
||||
outs.append(hidden)
|
||||
|
||||
# cache previous states (no-op except during incremental generation)
|
||||
utils.set_incremental_state(
|
||||
self, incremental_state, "cached_state", (prev_hiddens, prev_cells)
|
||||
)
|
||||
|
||||
# collect outputs across time steps
|
||||
x = torch.cat(outs, dim=0).view(seqlen, bsz, self.hidden_size)
|
||||
attention_outs_concat = torch.cat(attention_outs, dim=0).view(
|
||||
seqlen, bsz, self.context_dim
|
||||
)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(0, 1)
|
||||
attention_outs_concat = attention_outs_concat.transpose(0, 1)
|
||||
|
||||
# concat LSTM output, attention output and embedding
|
||||
# before output projection
|
||||
x = torch.cat((x, attention_outs_concat, embeddings), dim=2)
|
||||
x = self.deep_output_layer(x)
|
||||
x = torch.tanh(x)
|
||||
if self.dropout is not None:
|
||||
x = self.dropout(x)
|
||||
# project back to size of vocabulary
|
||||
x = self.output_projection(x)
|
||||
|
||||
# to return the full attn_scores tensor, we need to fix the decoder
|
||||
# to account for subsampling input frames
|
||||
# return x, attn_scores
|
||||
return x, None
|
||||
|
||||
def reorder_incremental_state(self, incremental_state, new_order):
|
||||
super().reorder_incremental_state(incremental_state, new_order)
|
||||
cached_state = utils.get_incremental_state(
|
||||
self, incremental_state, "cached_state"
|
||||
)
|
||||
if cached_state is None:
|
||||
return
|
||||
|
||||
def reorder_state(state):
|
||||
if isinstance(state, list):
|
||||
return [reorder_state(state_i) for state_i in state]
|
||||
return state.index_select(0, new_order)
|
||||
|
||||
new_state = tuple(map(reorder_state, cached_state))
|
||||
utils.set_incremental_state(self, incremental_state, "cached_state", new_state)
|
||||
|
||||
|
||||
@register_model_architecture(model_name="s2t_berard", arch_name="s2t_berard")
|
||||
def berard(args):
|
||||
"""The original version: "End-to-End Automatic Speech Translation of
|
||||
Audiobooks" (https://arxiv.org/abs/1802.04200)
|
||||
"""
|
||||
args.input_layers = getattr(args, "input_layers", "[256, 128]")
|
||||
args.conv_layers = getattr(args, "conv_layers", "[(16, 3, 2), (16, 3, 2)]")
|
||||
args.num_blstm_layers = getattr(args, "num_blstm_layers", 3)
|
||||
args.lstm_size = getattr(args, "lstm_size", 256)
|
||||
args.dropout = getattr(args, "dropout", 0.2)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 128)
|
||||
args.decoder_num_layers = getattr(args, "decoder_num_layers", 2)
|
||||
args.decoder_hidden_dim = getattr(args, "decoder_hidden_dim", 512)
|
||||
args.attention_dim = getattr(args, "attention_dim", 512)
|
||||
args.output_layer_dim = getattr(args, "output_layer_dim", 128)
|
||||
args.load_pretrained_encoder_from = getattr(
|
||||
args, "load_pretrained_encoder_from", None
|
||||
)
|
||||
args.load_pretrained_decoder_from = getattr(
|
||||
args, "load_pretrained_decoder_from", None
|
||||
)
|
||||
|
||||
|
||||
@register_model_architecture(model_name="s2t_berard", arch_name="s2t_berard_256_3_3")
|
||||
def berard_256_3_3(args):
|
||||
"""Used in
|
||||
* "Harnessing Indirect Training Data for End-to-End Automatic Speech
|
||||
Translation: Tricks of the Trade" (https://arxiv.org/abs/1909.06515)
|
||||
* "CoVoST: A Diverse Multilingual Speech-To-Text Translation Corpus"
|
||||
(https://arxiv.org/pdf/2002.01320.pdf)
|
||||
* "Self-Supervised Representations Improve End-to-End Speech Translation"
|
||||
(https://arxiv.org/abs/2006.12124)
|
||||
"""
|
||||
args.decoder_num_layers = getattr(args, "decoder_num_layers", 3)
|
||||
berard(args)
|
||||
|
||||
|
||||
@register_model_architecture(model_name="s2t_berard", arch_name="s2t_berard_512_3_2")
|
||||
def berard_512_3_2(args):
|
||||
args.num_blstm_layers = getattr(args, "num_blstm_layers", 3)
|
||||
args.lstm_size = getattr(args, "lstm_size", 512)
|
||||
args.dropout = getattr(args, "dropout", 0.3)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 256)
|
||||
args.decoder_num_layers = getattr(args, "decoder_num_layers", 2)
|
||||
args.decoder_hidden_dim = getattr(args, "decoder_hidden_dim", 1024)
|
||||
args.attention_dim = getattr(args, "attention_dim", 512)
|
||||
args.output_layer_dim = getattr(args, "output_layer_dim", 256)
|
||||
berard(args)
|
||||
|
||||
|
||||
@register_model_architecture(model_name="s2t_berard", arch_name="s2t_berard_512_5_3")
|
||||
def berard_512_5_3(args):
|
||||
args.num_blstm_layers = getattr(args, "num_blstm_layers", 5)
|
||||
args.lstm_size = getattr(args, "lstm_size", 512)
|
||||
args.dropout = getattr(args, "dropout", 0.3)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 256)
|
||||
args.decoder_num_layers = getattr(args, "decoder_num_layers", 3)
|
||||
args.decoder_hidden_dim = getattr(args, "decoder_hidden_dim", 1024)
|
||||
args.attention_dim = getattr(args, "attention_dim", 512)
|
||||
args.output_layer_dim = getattr(args, "output_layer_dim", 256)
|
||||
berard(args)
|
||||
@@ -0,0 +1,447 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
import math
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq.data.data_utils import lengths_to_padding_mask
|
||||
from fairseq import checkpoint_utils, utils
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.transformer import Embedding, TransformerDecoder
|
||||
from fairseq.modules import LayerNorm, PositionalEmbedding, TransformerEncoderLayer
|
||||
from torch import Tensor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register_model("convtransformer")
|
||||
class ConvTransformerModel(FairseqEncoderDecoderModel):
|
||||
"""
|
||||
Transformer-based Speech translation model from ESPNet-ST
|
||||
https://arxiv.org/abs/2004.10234
|
||||
"""
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
parser.add_argument(
|
||||
"--input-feat-per-channel",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder input dimension per input channel",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--activation-fn",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dropout", type=float, metavar="D", help="dropout probability"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for attention weights",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--activation-dropout",
|
||||
"--relu-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability after activation in FFN.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-layers", type=int, metavar="N", help="num encoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num encoder attention heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each encoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-layers", type=int, metavar="N", help="num decoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num decoder attention heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each decoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-output-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder output dimension (extra linear layer if different from decoder embed dim)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-decoder-input-output-embed",
|
||||
action="store_true",
|
||||
help="share decoder input and output embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--layernorm-embedding",
|
||||
action="store_true",
|
||||
help="add layernorm to embedding",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-scale-embedding",
|
||||
action="store_true",
|
||||
help="if True, dont scale embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-pretrained-encoder-from",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="model to take encoder weights from (for initialization)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-pretrained-decoder-from",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="model to take decoder weights from (for initialization)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--conv-out-channels",
|
||||
type=int,
|
||||
metavar="INT",
|
||||
help="the number of output channels of conv layer",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args):
|
||||
encoder = ConvTransformerEncoder(args)
|
||||
if getattr(args, "load_pretrained_encoder_from", None):
|
||||
encoder = checkpoint_utils.load_pretrained_component_from_model(
|
||||
component=encoder, checkpoint=args.load_pretrained_encoder_from
|
||||
)
|
||||
return encoder
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, task, embed_tokens):
|
||||
decoder = TransformerDecoderNoExtra(args, task.target_dictionary, embed_tokens)
|
||||
if getattr(args, "load_pretrained_decoder_from", None):
|
||||
decoder = checkpoint_utils.load_pretrained_component_from_model(
|
||||
component=decoder, checkpoint=args.load_pretrained_decoder_from
|
||||
)
|
||||
return decoder
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_architecture(args)
|
||||
|
||||
def build_embedding(dictionary, embed_dim):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
return Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
|
||||
decoder_embed_tokens = build_embedding(
|
||||
task.target_dictionary, args.decoder_embed_dim
|
||||
)
|
||||
encoder = cls.build_encoder(args)
|
||||
decoder = cls.build_decoder(args, task, decoder_embed_tokens)
|
||||
return cls(encoder, decoder)
|
||||
|
||||
@staticmethod
|
||||
@torch.jit.unused
|
||||
def set_batch_first(lprobs):
|
||||
lprobs.batch_first = True
|
||||
|
||||
def get_normalized_probs(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
# net_output['encoder_out'] is a (B, T, D) tensor
|
||||
lprobs = self.get_normalized_probs_scriptable(net_output, log_probs, sample)
|
||||
if self.training:
|
||||
self.set_batch_first(lprobs)
|
||||
return lprobs
|
||||
|
||||
def output_layout(self):
|
||||
return "BTD"
|
||||
|
||||
"""
|
||||
The forward method inherited from the base class has a **kwargs argument in
|
||||
its input, which is not supported in torchscript. This method overrites the forward
|
||||
method definition without **kwargs.
|
||||
"""
|
||||
|
||||
def forward(self, src_tokens, src_lengths, prev_output_tokens):
|
||||
encoder_out = self.encoder(src_tokens=src_tokens, src_lengths=src_lengths)
|
||||
decoder_out = self.decoder(
|
||||
prev_output_tokens=prev_output_tokens, encoder_out=encoder_out
|
||||
)
|
||||
return decoder_out
|
||||
|
||||
|
||||
class ConvTransformerEncoder(FairseqEncoder):
|
||||
"""Conv + Transformer encoder"""
|
||||
|
||||
def __init__(self, args):
|
||||
"""Construct an Encoder object."""
|
||||
super().__init__(None)
|
||||
|
||||
self.dropout = args.dropout
|
||||
self.embed_scale = (
|
||||
1.0 if args.no_scale_embedding else math.sqrt(args.encoder_embed_dim)
|
||||
)
|
||||
self.padding_idx = 1
|
||||
self.in_channels = 1
|
||||
self.input_dim = args.input_feat_per_channel
|
||||
self.conv = torch.nn.Sequential(
|
||||
torch.nn.Conv2d(1, args.conv_out_channels, 3, stride=2, padding=3 // 2),
|
||||
torch.nn.ReLU(),
|
||||
torch.nn.Conv2d(
|
||||
args.conv_out_channels,
|
||||
args.conv_out_channels,
|
||||
3,
|
||||
stride=2,
|
||||
padding=3 // 2,
|
||||
),
|
||||
torch.nn.ReLU(),
|
||||
)
|
||||
transformer_input_dim = self.infer_conv_output_dim(
|
||||
self.in_channels, self.input_dim, args.conv_out_channels
|
||||
)
|
||||
self.out = torch.nn.Linear(transformer_input_dim, args.encoder_embed_dim)
|
||||
self.embed_positions = PositionalEmbedding(
|
||||
args.max_source_positions,
|
||||
args.encoder_embed_dim,
|
||||
self.padding_idx,
|
||||
learned=False,
|
||||
)
|
||||
|
||||
self.transformer_layers = nn.ModuleList([])
|
||||
self.transformer_layers.extend(
|
||||
[TransformerEncoderLayer(args) for i in range(args.encoder_layers)]
|
||||
)
|
||||
if args.encoder_normalize_before:
|
||||
self.layer_norm = LayerNorm(args.encoder_embed_dim)
|
||||
else:
|
||||
self.layer_norm = None
|
||||
|
||||
def pooling_ratio(self):
|
||||
return 4
|
||||
|
||||
def infer_conv_output_dim(self, in_channels, input_dim, out_channels):
|
||||
sample_seq_len = 200
|
||||
sample_bsz = 10
|
||||
x = torch.randn(sample_bsz, in_channels, sample_seq_len, input_dim)
|
||||
x = torch.nn.Conv2d(1, out_channels, 3, stride=2, padding=3 // 2)(x)
|
||||
x = torch.nn.Conv2d(out_channels, out_channels, 3, stride=2, padding=3 // 2)(x)
|
||||
x = x.transpose(1, 2)
|
||||
mb, seq = x.size()[:2]
|
||||
return x.contiguous().view(mb, seq, -1).size(-1)
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
"""Encode input sequence.
|
||||
:param torch.Tensor xs: input tensor
|
||||
:param torch.Tensor masks: input mask
|
||||
:return: position embedded tensor and mask
|
||||
:rtype Tuple[torch.Tensor, torch.Tensor]:
|
||||
"""
|
||||
bsz, max_seq_len, _ = src_tokens.size()
|
||||
x = (
|
||||
src_tokens.view(bsz, max_seq_len, self.in_channels, self.input_dim)
|
||||
.transpose(1, 2)
|
||||
.contiguous()
|
||||
)
|
||||
x = self.conv(x)
|
||||
bsz, _, output_seq_len, _ = x.size()
|
||||
x = x.transpose(1, 2).transpose(0, 1).contiguous().view(output_seq_len, bsz, -1)
|
||||
x = self.out(x)
|
||||
x = self.embed_scale * x
|
||||
|
||||
subsampling_factor = int(max_seq_len * 1.0 / output_seq_len + 0.5)
|
||||
|
||||
input_lengths = torch.min(
|
||||
(src_lengths.float() / subsampling_factor).ceil().long(),
|
||||
x.size(0) * src_lengths.new_ones([src_lengths.size(0)]).long()
|
||||
)
|
||||
|
||||
encoder_padding_mask = lengths_to_padding_mask(input_lengths)
|
||||
|
||||
positions = self.embed_positions(encoder_padding_mask).transpose(0, 1)
|
||||
x += positions
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
|
||||
for layer in self.transformer_layers:
|
||||
x = layer(x, encoder_padding_mask)
|
||||
|
||||
if not encoder_padding_mask.any():
|
||||
maybe_encoder_padding_mask = None
|
||||
else:
|
||||
maybe_encoder_padding_mask = encoder_padding_mask
|
||||
|
||||
return {
|
||||
"encoder_out": [x],
|
||||
"encoder_padding_mask": [maybe_encoder_padding_mask]
|
||||
if maybe_encoder_padding_mask is not None
|
||||
else [],
|
||||
"encoder_embedding": [],
|
||||
"encoder_states": [],
|
||||
"src_tokens": [],
|
||||
"src_lengths": [],
|
||||
}
|
||||
|
||||
@torch.jit.export
|
||||
def reorder_encoder_out(self, encoder_out: Dict[str, List[Tensor]], new_order):
|
||||
"""
|
||||
Reorder encoder output according to *new_order*.
|
||||
|
||||
Args:
|
||||
encoder_out: output from the ``forward()`` method
|
||||
new_order (LongTensor): desired order
|
||||
|
||||
Returns:
|
||||
*encoder_out* rearranged according to *new_order*
|
||||
"""
|
||||
new_encoder_out = [encoder_out["encoder_out"][0].index_select(1, new_order)]
|
||||
if len(encoder_out["encoder_padding_mask"]) == 0:
|
||||
new_encoder_padding_mask = []
|
||||
else:
|
||||
new_encoder_padding_mask = [
|
||||
(encoder_out["encoder_padding_mask"][0]).index_select(0, new_order)
|
||||
]
|
||||
if len(encoder_out["encoder_embedding"]) == 0:
|
||||
new_encoder_embedding = []
|
||||
else:
|
||||
new_encoder_embedding = [
|
||||
(encoder_out["encoder_embedding"][0]).index_select(0, new_order)
|
||||
]
|
||||
encoder_states = encoder_out["encoder_states"]
|
||||
if len(encoder_states) > 0:
|
||||
for idx, state in enumerate(encoder_states):
|
||||
encoder_states[idx] = state.index_select(1, new_order)
|
||||
|
||||
return {
|
||||
"encoder_out": new_encoder_out,
|
||||
"encoder_padding_mask": new_encoder_padding_mask,
|
||||
"encoder_embedding": new_encoder_embedding,
|
||||
"encoder_states": encoder_states,
|
||||
"src_tokens": [],
|
||||
"src_lengths": [],
|
||||
}
|
||||
|
||||
|
||||
class TransformerDecoderNoExtra(TransformerDecoder):
|
||||
def extract_features(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out: Optional[Dict[str, List[Tensor]]],
|
||||
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
||||
full_context_alignment: bool = False,
|
||||
alignment_layer: Optional[int] = None,
|
||||
alignment_heads: Optional[int] = None,
|
||||
):
|
||||
# call scriptable method from parent class
|
||||
x, _ = self.extract_features_scriptable(
|
||||
prev_output_tokens,
|
||||
encoder_out,
|
||||
incremental_state,
|
||||
full_context_alignment,
|
||||
alignment_layer,
|
||||
alignment_heads,
|
||||
)
|
||||
return x, None
|
||||
|
||||
|
||||
@register_model_architecture(model_name="convtransformer", arch_name="convtransformer")
|
||||
def base_architecture(args):
|
||||
args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 2048)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", False)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", args.encoder_embed_dim)
|
||||
args.decoder_ffn_embed_dim = getattr(
|
||||
args, "decoder_ffn_embed_dim", args.encoder_ffn_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.decoder_normalize_before = getattr(args, "decoder_normalize_before", False)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
||||
args.activation_dropout = getattr(args, "activation_dropout", 0.0)
|
||||
args.activation_fn = getattr(args, "activation_fn", "relu")
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
args.decoder_layerdrop = getattr(args, "decoder_layerdrop", 0.0)
|
||||
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
||||
args.quant_noise_pq = getattr(args, "quant_noise_pq", 0)
|
||||
args.max_source_positions = getattr(args, "max_source_positions", 3000)
|
||||
args.max_target_positions = getattr(args, "max_target_positions", 1024)
|
||||
args.tie_adaptive_weights = getattr(args, "tie_adaptive_weights", False)
|
||||
args.conv_out_channels = getattr(args, "conv_out_channels", args.encoder_embed_dim)
|
||||
|
||||
|
||||
@register_model_architecture("convtransformer", "convtransformer_espnet")
|
||||
def convtransformer_espnet(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 256)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 4)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 4)
|
||||
@@ -0,0 +1,469 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
import math
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import torch.nn as nn
|
||||
from fairseq import checkpoint_utils, utils
|
||||
from fairseq.data.data_utils import lengths_to_padding_mask
|
||||
from fairseq.models import (
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.transformer import Embedding, TransformerDecoder
|
||||
from fairseq.modules import (
|
||||
FairseqDropout,
|
||||
LayerNorm,
|
||||
PositionalEmbedding,
|
||||
TransformerEncoderLayer,
|
||||
)
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Conv1dSubsampler(nn.Module):
|
||||
"""Convolutional subsampler: a stack of 1D convolution (along temporal
|
||||
dimension) followed by non-linear activation via gated linear units
|
||||
(https://arxiv.org/abs/1911.08460)
|
||||
|
||||
Args:
|
||||
in_channels (int): the number of input channels
|
||||
mid_channels (int): the number of intermediate channels
|
||||
out_channels (int): the number of output channels
|
||||
kernel_sizes (List[int]): the kernel size for each convolutional layer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
mid_channels: int,
|
||||
out_channels: int,
|
||||
kernel_sizes: List[int] = (3, 3),
|
||||
):
|
||||
super(Conv1dSubsampler, self).__init__()
|
||||
self.n_layers = len(kernel_sizes)
|
||||
self.conv_layers = nn.ModuleList(
|
||||
nn.Conv1d(
|
||||
in_channels if i == 0 else mid_channels // 2,
|
||||
mid_channels if i < self.n_layers - 1 else out_channels * 2,
|
||||
k,
|
||||
stride=2,
|
||||
padding=k // 2,
|
||||
)
|
||||
for i, k in enumerate(kernel_sizes)
|
||||
)
|
||||
|
||||
def get_out_seq_lens_tensor(self, in_seq_lens_tensor):
|
||||
out = in_seq_lens_tensor.clone()
|
||||
for _ in range(self.n_layers):
|
||||
out = ((out.float() - 1) / 2 + 1).floor().long()
|
||||
return out
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
bsz, in_seq_len, _ = src_tokens.size() # B x T x (C x D)
|
||||
x = src_tokens.transpose(1, 2).contiguous() # -> B x (C x D) x T
|
||||
for conv in self.conv_layers:
|
||||
x = conv(x)
|
||||
x = nn.functional.glu(x, dim=1)
|
||||
_, _, out_seq_len = x.size()
|
||||
x = x.transpose(1, 2).transpose(0, 1).contiguous() # -> T x B x (C x D)
|
||||
return x, self.get_out_seq_lens_tensor(src_lengths)
|
||||
|
||||
|
||||
@register_model("s2t_transformer")
|
||||
class S2TTransformerModel(FairseqEncoderDecoderModel):
|
||||
"""Adapted Transformer model (https://arxiv.org/abs/1706.03762) for
|
||||
speech-to-text tasks. The Transformer encoder/decoder remains the same.
|
||||
A trainable input subsampler is prepended to the Transformer encoder to
|
||||
project inputs into the encoder dimension as well as downsample input
|
||||
sequence for computational efficiency."""
|
||||
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
# input
|
||||
parser.add_argument(
|
||||
"--conv-kernel-sizes",
|
||||
type=str,
|
||||
metavar="N",
|
||||
help="kernel sizes of Conv1d subsampling layers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--conv-channels",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="# of channels in Conv1d subsampling layers",
|
||||
)
|
||||
# Transformer
|
||||
parser.add_argument(
|
||||
"--activation-fn",
|
||||
type=str,
|
||||
default="relu",
|
||||
choices=utils.get_available_activation_fns(),
|
||||
help="activation function to use",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dropout", type=float, metavar="D", help="dropout probability"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--attention-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability for attention weights",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--activation-dropout",
|
||||
"--relu-dropout",
|
||||
type=float,
|
||||
metavar="D",
|
||||
help="dropout probability after activation in FFN.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="encoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-layers", type=int, metavar="N", help="num encoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num encoder attention heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--encoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each encoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-ffn-embed-dim",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="decoder embedding dimension for FFN",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-layers", type=int, metavar="N", help="num decoder layers"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-attention-heads",
|
||||
type=int,
|
||||
metavar="N",
|
||||
help="num decoder attention heads",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--decoder-normalize-before",
|
||||
action="store_true",
|
||||
help="apply layernorm before each decoder block",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--share-decoder-input-output-embed",
|
||||
action="store_true",
|
||||
help="share decoder input and output embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--layernorm-embedding",
|
||||
action="store_true",
|
||||
help="add layernorm to embedding",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-scale-embedding",
|
||||
action="store_true",
|
||||
help="if True, dont scale embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--load-pretrained-encoder-from",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="model to take encoder weights from (for initialization)",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args):
|
||||
encoder = S2TTransformerEncoder(args)
|
||||
if getattr(args, "load_pretrained_encoder_from", None):
|
||||
encoder = checkpoint_utils.load_pretrained_component_from_model(
|
||||
component=encoder, checkpoint=args.load_pretrained_encoder_from
|
||||
)
|
||||
logger.info(
|
||||
f"loaded pretrained encoder from: "
|
||||
f"{args.load_pretrained_encoder_from}"
|
||||
)
|
||||
return encoder
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, task, embed_tokens):
|
||||
return TransformerDecoderScriptable(args, task.target_dictionary, embed_tokens)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_architecture(args)
|
||||
|
||||
def build_embedding(dictionary, embed_dim):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
return Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
|
||||
decoder_embed_tokens = build_embedding(
|
||||
task.target_dictionary, args.decoder_embed_dim
|
||||
)
|
||||
encoder = cls.build_encoder(args)
|
||||
decoder = cls.build_decoder(args, task, decoder_embed_tokens)
|
||||
return cls(encoder, decoder)
|
||||
|
||||
def get_normalized_probs(
|
||||
self,
|
||||
net_output: Tuple[Tensor, Optional[Dict[str, List[Optional[Tensor]]]]],
|
||||
log_probs: bool,
|
||||
sample: Optional[Dict[str, Tensor]] = None,
|
||||
):
|
||||
# net_output['encoder_out'] is a (B, T, D) tensor
|
||||
lprobs = self.get_normalized_probs_scriptable(net_output, log_probs, sample)
|
||||
lprobs.batch_first = True
|
||||
return lprobs
|
||||
|
||||
def forward(self, src_tokens, src_lengths, prev_output_tokens):
|
||||
"""
|
||||
The forward method inherited from the base class has a **kwargs
|
||||
argument in its input, which is not supported in torchscript. This
|
||||
method overwrites the forward method definition without **kwargs.
|
||||
"""
|
||||
encoder_out = self.encoder(src_tokens=src_tokens, src_lengths=src_lengths)
|
||||
decoder_out = self.decoder(
|
||||
prev_output_tokens=prev_output_tokens, encoder_out=encoder_out
|
||||
)
|
||||
return decoder_out
|
||||
|
||||
|
||||
class S2TTransformerEncoder(FairseqEncoder):
|
||||
"""Speech-to-text Transformer encoder that consists of input subsampler and
|
||||
Transformer encoder."""
|
||||
|
||||
def __init__(self, args):
|
||||
super().__init__(None)
|
||||
|
||||
self.dropout_module = FairseqDropout(
|
||||
p=args.dropout, module_name=self.__class__.__name__
|
||||
)
|
||||
self.embed_scale = math.sqrt(args.encoder_embed_dim)
|
||||
if args.no_scale_embedding:
|
||||
self.embed_scale = 1.0
|
||||
self.padding_idx = 1
|
||||
|
||||
self.subsample = Conv1dSubsampler(
|
||||
args.input_feat_per_channel * args.input_channels,
|
||||
args.conv_channels,
|
||||
args.encoder_embed_dim,
|
||||
[int(k) for k in args.conv_kernel_sizes.split(",")],
|
||||
)
|
||||
|
||||
self.embed_positions = PositionalEmbedding(
|
||||
args.max_source_positions, args.encoder_embed_dim, self.padding_idx
|
||||
)
|
||||
|
||||
self.transformer_layers = nn.ModuleList(
|
||||
[TransformerEncoderLayer(args) for _ in range(args.encoder_layers)]
|
||||
)
|
||||
if args.encoder_normalize_before:
|
||||
self.layer_norm = LayerNorm(args.encoder_embed_dim)
|
||||
else:
|
||||
self.layer_norm = None
|
||||
|
||||
def forward(self, src_tokens, src_lengths):
|
||||
x, input_lengths = self.subsample(src_tokens, src_lengths)
|
||||
x = self.embed_scale * x
|
||||
|
||||
encoder_padding_mask = lengths_to_padding_mask(input_lengths)
|
||||
positions = self.embed_positions(encoder_padding_mask).transpose(0, 1)
|
||||
x += positions
|
||||
x = self.dropout_module(x)
|
||||
|
||||
for layer in self.transformer_layers:
|
||||
x = layer(x, encoder_padding_mask)
|
||||
|
||||
if self.layer_norm is not None:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
return {
|
||||
"encoder_out": [x], # T x B x C
|
||||
"encoder_padding_mask": [encoder_padding_mask] if encoder_padding_mask.any() else [], # B x T
|
||||
"encoder_embedding": [], # B x T x C
|
||||
"encoder_states": [], # List[T x B x C]
|
||||
"src_tokens": [],
|
||||
"src_lengths": [],
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
new_encoder_out = (
|
||||
[] if len(encoder_out["encoder_out"]) == 0
|
||||
else [x.index_select(1, new_order) for x in encoder_out["encoder_out"]]
|
||||
)
|
||||
|
||||
new_encoder_padding_mask = (
|
||||
[] if len(encoder_out["encoder_padding_mask"]) == 0
|
||||
else [x.index_select(0, new_order) for x in encoder_out["encoder_padding_mask"]]
|
||||
)
|
||||
|
||||
new_encoder_embedding = (
|
||||
[] if len(encoder_out["encoder_embedding"]) == 0
|
||||
else [x.index_select(0, new_order) for x in encoder_out["encoder_embedding"]]
|
||||
)
|
||||
|
||||
encoder_states = encoder_out["encoder_states"]
|
||||
if len(encoder_states) > 0:
|
||||
for idx, state in enumerate(encoder_states):
|
||||
encoder_states[idx] = state.index_select(1, new_order)
|
||||
|
||||
return {
|
||||
"encoder_out": new_encoder_out, # T x B x C
|
||||
"encoder_padding_mask": new_encoder_padding_mask, # B x T
|
||||
"encoder_embedding": new_encoder_embedding, # B x T x C
|
||||
"encoder_states": encoder_states, # List[T x B x C]
|
||||
"src_tokens": [], # B x T
|
||||
"src_lengths": [], # B x 1
|
||||
}
|
||||
|
||||
|
||||
class TransformerDecoderScriptable(TransformerDecoder):
|
||||
def extract_features(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out: Optional[Dict[str, List[Tensor]]] = None,
|
||||
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
||||
full_context_alignment: bool = False,
|
||||
alignment_layer: Optional[int] = None,
|
||||
alignment_heads: Optional[int] = None,
|
||||
):
|
||||
# call scriptable method from parent class
|
||||
x, _ = self.extract_features_scriptable(
|
||||
prev_output_tokens,
|
||||
encoder_out,
|
||||
incremental_state,
|
||||
full_context_alignment,
|
||||
alignment_layer,
|
||||
alignment_heads,
|
||||
)
|
||||
return x, None
|
||||
|
||||
|
||||
@register_model_architecture(model_name="s2t_transformer", arch_name="s2t_transformer")
|
||||
def base_architecture(args):
|
||||
# Convolutional subsampler
|
||||
args.conv_kernel_sizes = getattr(args, "conv_kernel_sizes", "5,5")
|
||||
args.conv_channels = getattr(args, "conv_channels", 1024)
|
||||
# Transformer
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 2048)
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 12)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
args.encoder_normalize_before = getattr(args, "encoder_normalize_before", True)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", args.encoder_embed_dim)
|
||||
args.decoder_ffn_embed_dim = getattr(
|
||||
args, "decoder_ffn_embed_dim", args.encoder_ffn_embed_dim
|
||||
)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.decoder_normalize_before = getattr(args, "decoder_normalize_before", True)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", args.dropout)
|
||||
args.activation_dropout = getattr(args, "activation_dropout", args.dropout)
|
||||
args.activation_fn = getattr(args, "activation_fn", "relu")
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
args.decoder_layerdrop = getattr(args, "decoder_layerdrop", 0.0)
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
||||
args.quant_noise_pq = getattr(args, "quant_noise_pq", 0)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_s")
|
||||
def s2t_transformer_s(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 256)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 256 * 8)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 4)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 4)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_xs")
|
||||
def s2t_transformer_xs(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 6)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 3)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 256 * 4)
|
||||
args.dropout = getattr(args, "dropout", 0.3)
|
||||
s2t_transformer_s(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_sp")
|
||||
def s2t_transformer_sp(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 16)
|
||||
s2t_transformer_s(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_m")
|
||||
def s2t_transformer_m(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 512)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 512 * 4)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 8)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.dropout = getattr(args, "dropout", 0.15)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_mp")
|
||||
def s2t_transformer_mp(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 16)
|
||||
s2t_transformer_m(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_l")
|
||||
def s2t_transformer_l(args):
|
||||
args.encoder_embed_dim = getattr(args, "encoder_embed_dim", 1024)
|
||||
args.encoder_ffn_embed_dim = getattr(args, "encoder_ffn_embed_dim", 1024 * 4)
|
||||
args.encoder_attention_heads = getattr(args, "encoder_attention_heads", 16)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
||||
args.dropout = getattr(args, "dropout", 0.2)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("s2t_transformer", "s2t_transformer_lp")
|
||||
def s2t_transformer_lp(args):
|
||||
args.encoder_layers = getattr(args, "encoder_layers", 16)
|
||||
s2t_transformer_l(args)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from fairseq.models import register_model, register_model_architecture
|
||||
from fairseq.models.transformer import (
|
||||
TransformerModel,
|
||||
base_architecture,
|
||||
transformer_wmt_en_de_big,
|
||||
)
|
||||
|
||||
|
||||
@register_model("transformer_align")
|
||||
class TransformerAlignModel(TransformerModel):
|
||||
"""
|
||||
See "Jointly Learning to Align and Translate with Transformer
|
||||
Models" (Garg et al., EMNLP 2019).
|
||||
"""
|
||||
|
||||
def __init__(self, encoder, decoder, args):
|
||||
super().__init__(args, encoder, decoder)
|
||||
self.alignment_heads = args.alignment_heads
|
||||
self.alignment_layer = args.alignment_layer
|
||||
self.full_context_alignment = args.full_context_alignment
|
||||
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
# fmt: off
|
||||
super(TransformerAlignModel, TransformerAlignModel).add_args(parser)
|
||||
parser.add_argument('--alignment-heads', type=int, metavar='D',
|
||||
help='Number of cross attention heads per layer to supervised with alignments')
|
||||
parser.add_argument('--alignment-layer', type=int, metavar='D',
|
||||
help='Layer number which has to be supervised. 0 corresponding to the bottommost layer.')
|
||||
parser.add_argument('--full-context-alignment', action='store_true',
|
||||
help='Whether or not alignment is supervised conditioned on the full target context.')
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
# set any default arguments
|
||||
transformer_align(args)
|
||||
|
||||
transformer_model = TransformerModel.build_model(args, task)
|
||||
return TransformerAlignModel(
|
||||
transformer_model.encoder, transformer_model.decoder, args
|
||||
)
|
||||
|
||||
def forward(self, src_tokens, src_lengths, prev_output_tokens):
|
||||
encoder_out = self.encoder(src_tokens, src_lengths)
|
||||
return self.forward_decoder(prev_output_tokens, encoder_out)
|
||||
|
||||
def forward_decoder(
|
||||
self,
|
||||
prev_output_tokens,
|
||||
encoder_out=None,
|
||||
incremental_state=None,
|
||||
features_only=False,
|
||||
**extra_args,
|
||||
):
|
||||
attn_args = {
|
||||
"alignment_layer": self.alignment_layer,
|
||||
"alignment_heads": self.alignment_heads,
|
||||
}
|
||||
decoder_out = self.decoder(prev_output_tokens, encoder_out, **attn_args)
|
||||
|
||||
if self.full_context_alignment:
|
||||
attn_args["full_context_alignment"] = self.full_context_alignment
|
||||
_, alignment_out = self.decoder(
|
||||
prev_output_tokens,
|
||||
encoder_out,
|
||||
features_only=True,
|
||||
**attn_args,
|
||||
**extra_args,
|
||||
)
|
||||
decoder_out[1]["attn"] = alignment_out["attn"]
|
||||
|
||||
return decoder_out
|
||||
|
||||
|
||||
@register_model_architecture("transformer_align", "transformer_align")
|
||||
def transformer_align(args):
|
||||
args.alignment_heads = getattr(args, "alignment_heads", 1)
|
||||
args.alignment_layer = getattr(args, "alignment_layer", 4)
|
||||
args.full_context_alignment = getattr(args, "full_context_alignment", False)
|
||||
base_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_align", "transformer_wmt_en_de_big_align")
|
||||
def transformer_wmt_en_de_big_align(args):
|
||||
args.alignment_heads = getattr(args, "alignment_heads", 1)
|
||||
args.alignment_layer = getattr(args, "alignment_layer", 4)
|
||||
transformer_wmt_en_de_big(args)
|
||||
@@ -0,0 +1,152 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import os
|
||||
from typing import Any, Dict
|
||||
|
||||
from fairseq import checkpoint_utils
|
||||
from fairseq.data.legacy.masked_lm_dictionary import MaskedLMDictionary
|
||||
from fairseq.models import register_model, register_model_architecture
|
||||
from fairseq.models.transformer import (
|
||||
TransformerDecoder,
|
||||
TransformerEncoder,
|
||||
TransformerModel,
|
||||
base_architecture as transformer_base_architecture,
|
||||
)
|
||||
|
||||
|
||||
@register_model("transformer_from_pretrained_xlm")
|
||||
class TransformerFromPretrainedXLMModel(TransformerModel):
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
"""Add model-specific arguments to the parser."""
|
||||
TransformerModel.add_args(parser)
|
||||
parser.add_argument(
|
||||
"--pretrained-xlm-checkpoint",
|
||||
type=str,
|
||||
metavar="STR",
|
||||
help="XLM model to use for initializing transformer encoder and/or decoder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--init-encoder-only",
|
||||
action="store_true",
|
||||
help="if set, don't load the XLM weights and embeddings into decoder",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--init-decoder-only",
|
||||
action="store_true",
|
||||
help="if set, don't load the XLM weights and embeddings into encoder",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def build_model(self, args, task, cls_dictionary=MaskedLMDictionary):
|
||||
assert hasattr(args, "pretrained_xlm_checkpoint"), (
|
||||
"You must specify a path for --pretrained-xlm-checkpoint to use "
|
||||
"--arch transformer_from_pretrained_xlm"
|
||||
)
|
||||
assert isinstance(task.source_dictionary, cls_dictionary) and isinstance(
|
||||
task.target_dictionary, cls_dictionary
|
||||
), (
|
||||
"You should use a MaskedLMDictionary when using --arch "
|
||||
"transformer_from_pretrained_xlm because the pretrained XLM model "
|
||||
"was trained using data binarized with MaskedLMDictionary. "
|
||||
"For translation, you may want to use --task "
|
||||
"translation_from_pretrained_xlm"
|
||||
)
|
||||
assert not (
|
||||
getattr(args, "init_encoder_only", False)
|
||||
and getattr(args, "init_decoder_only", False)
|
||||
), "Only one of --init-encoder-only and --init-decoder-only can be set."
|
||||
return super().build_model(args, task)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, args, src_dict, embed_tokens):
|
||||
return TransformerEncoderFromPretrainedXLM(args, src_dict, embed_tokens)
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, args, tgt_dict, embed_tokens):
|
||||
return TransformerDecoderFromPretrainedXLM(args, tgt_dict, embed_tokens)
|
||||
|
||||
|
||||
def upgrade_state_dict_with_xlm_weights(
|
||||
state_dict: Dict[str, Any], pretrained_xlm_checkpoint: str
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Load XLM weights into a Transformer encoder or decoder model.
|
||||
|
||||
Args:
|
||||
state_dict: state dict for either TransformerEncoder or
|
||||
TransformerDecoder
|
||||
pretrained_xlm_checkpoint: checkpoint to load XLM weights from
|
||||
|
||||
Raises:
|
||||
AssertionError: If architecture (num layers, attention heads, etc.)
|
||||
does not match between the current Transformer encoder or
|
||||
decoder and the pretrained_xlm_checkpoint
|
||||
"""
|
||||
if not os.path.exists(pretrained_xlm_checkpoint):
|
||||
raise IOError("Model file not found: {}".format(pretrained_xlm_checkpoint))
|
||||
|
||||
state = checkpoint_utils.load_checkpoint_to_cpu(pretrained_xlm_checkpoint)
|
||||
xlm_state_dict = state["model"]
|
||||
for key in xlm_state_dict.keys():
|
||||
|
||||
for search_key in ["embed_tokens", "embed_positions", "layers"]:
|
||||
if search_key in key:
|
||||
subkey = key[key.find(search_key) :]
|
||||
assert subkey in state_dict, (
|
||||
"{} Transformer encoder / decoder "
|
||||
"state_dict does not contain {}. Cannot "
|
||||
"load {} from pretrained XLM checkpoint "
|
||||
"{} into Transformer.".format(
|
||||
str(state_dict.keys()), subkey, key, pretrained_xlm_checkpoint
|
||||
)
|
||||
)
|
||||
|
||||
state_dict[subkey] = xlm_state_dict[key]
|
||||
return state_dict
|
||||
|
||||
|
||||
class TransformerEncoderFromPretrainedXLM(TransformerEncoder):
|
||||
def __init__(self, args, dictionary, embed_tokens):
|
||||
super().__init__(args, dictionary, embed_tokens)
|
||||
if getattr(args, "init_decoder_only", False):
|
||||
# Don't load XLM weights for encoder if --init-decoder-only
|
||||
return
|
||||
|
||||
assert hasattr(args, "pretrained_xlm_checkpoint"), (
|
||||
"--pretrained-xlm-checkpoint must be specified to load Transformer "
|
||||
"encoder from pretrained XLM"
|
||||
)
|
||||
xlm_loaded_state_dict = upgrade_state_dict_with_xlm_weights(
|
||||
state_dict=self.state_dict(),
|
||||
pretrained_xlm_checkpoint=args.pretrained_xlm_checkpoint,
|
||||
)
|
||||
self.load_state_dict(xlm_loaded_state_dict, strict=True)
|
||||
|
||||
|
||||
class TransformerDecoderFromPretrainedXLM(TransformerDecoder):
|
||||
def __init__(self, args, dictionary, embed_tokens, no_encoder_attn=False):
|
||||
super().__init__(args, dictionary, embed_tokens, no_encoder_attn)
|
||||
if getattr(args, "init_encoder_only", False):
|
||||
# Don't load XLM weights for decoder if --init-encoder-only
|
||||
return
|
||||
assert hasattr(args, "pretrained_xlm_checkpoint"), (
|
||||
"--pretrained-xlm-checkpoint must be specified to load Transformer "
|
||||
"decoder from pretrained XLM"
|
||||
)
|
||||
|
||||
xlm_loaded_state_dict = upgrade_state_dict_with_xlm_weights(
|
||||
state_dict=self.state_dict(),
|
||||
pretrained_xlm_checkpoint=args.pretrained_xlm_checkpoint,
|
||||
)
|
||||
self.load_state_dict(xlm_loaded_state_dict, strict=True)
|
||||
|
||||
|
||||
@register_model_architecture(
|
||||
"transformer_from_pretrained_xlm", "transformer_from_pretrained_xlm"
|
||||
)
|
||||
def base_architecture(args):
|
||||
transformer_base_architecture(args)
|
||||
@@ -0,0 +1,430 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from fairseq import options, utils
|
||||
from fairseq.dataclass import ChoiceEnum, FairseqDataclass
|
||||
from fairseq.models import (
|
||||
FairseqLanguageModel,
|
||||
register_model,
|
||||
register_model_architecture,
|
||||
)
|
||||
from fairseq.models.transformer import Embedding, TransformerDecoder
|
||||
from fairseq.modules import AdaptiveInput, CharacterTokenEmbedder
|
||||
from omegaconf import II
|
||||
|
||||
|
||||
DEFAULT_MAX_TARGET_POSITIONS = 1024
|
||||
|
||||
|
||||
@dataclass
|
||||
class TransformerLanguageModelConfig(FairseqDataclass):
|
||||
activation_fn: ChoiceEnum(utils.get_available_activation_fns()) = field(
|
||||
default="relu", metadata={"help": "activation function to use"}
|
||||
)
|
||||
dropout: float = field(default=0.1, metadata={"help": "dropout probability"})
|
||||
attention_dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability for attention weights"}
|
||||
)
|
||||
activation_dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability after activation in FFN."}
|
||||
)
|
||||
relu_dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability after activation in FFN."}
|
||||
)
|
||||
decoder_embed_dim: int = field(
|
||||
default=512, metadata={"help": "decoder embedding dimension"}
|
||||
)
|
||||
decoder_output_dim: int = field(
|
||||
default=512, metadata={"help": "decoder output dimension"}
|
||||
)
|
||||
decoder_input_dim: int = field(
|
||||
default=512, metadata={"help": "decoder input dimension"}
|
||||
)
|
||||
decoder_ffn_embed_dim: int = field(
|
||||
default=2048, metadata={"help": "decoder embedding dimension for FFN"}
|
||||
)
|
||||
decoder_layers: int = field(default=6, metadata={"help": "num decoder layers"})
|
||||
decoder_attention_heads: int = field(
|
||||
default=8, metadata={"help": "num decoder attention heads"}
|
||||
)
|
||||
decoder_normalize_before: bool = field(
|
||||
default=False, metadata={"help": "apply layernorm before each decoder block"}
|
||||
)
|
||||
no_decoder_final_norm: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "don't add an extra layernorm after the last decoder block"},
|
||||
)
|
||||
adaptive_softmax_cutoff: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"help": "comma separated list of adaptive softmax cutoff points. "
|
||||
"Must be used with adaptive_loss criterion"
|
||||
},
|
||||
)
|
||||
adaptive_softmax_dropout: float = field(
|
||||
default=0,
|
||||
metadata={"help": "sets adaptive softmax dropout for the tail projections"},
|
||||
)
|
||||
adaptive_softmax_factor: float = field(
|
||||
default=4, metadata={"help": "adaptive input factor"}
|
||||
)
|
||||
no_token_positional_embeddings: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "if set, disables positional embeddings (outside self attention)"
|
||||
},
|
||||
)
|
||||
share_decoder_input_output_embed: bool = field(
|
||||
default=False, metadata={"help": "share decoder input and output embeddings"}
|
||||
)
|
||||
character_embeddings: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "if set, uses character embedding convolutions to produce token embeddings"
|
||||
},
|
||||
)
|
||||
character_filters: str = field(
|
||||
default="[(1, 64), (2, 128), (3, 192), (4, 256), (5, 256), (6, 256), (7, 256)]",
|
||||
metadata={"help": "size of character embeddings"},
|
||||
)
|
||||
character_embedding_dim: int = field(
|
||||
default=4, metadata={"help": "size of character embeddings"}
|
||||
)
|
||||
char_embedder_highway_layers: int = field(
|
||||
default=2,
|
||||
metadata={"help": "number of highway layers for character token embeddder"},
|
||||
)
|
||||
adaptive_input: bool = field(
|
||||
default=False, metadata={"help": "if set, uses adaptive input"}
|
||||
)
|
||||
adaptive_input_factor: float = field(
|
||||
default=4, metadata={"help": "adaptive input factor"}
|
||||
)
|
||||
adaptive_input_cutoff: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={"help": "comma separated list of adaptive input cutoff points."},
|
||||
)
|
||||
tie_adaptive_weights: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "if set, ties the weights of adaptive softmax and adaptive input"
|
||||
},
|
||||
)
|
||||
tie_adaptive_proj: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "if set, ties the projection weights of adaptive softmax and adaptive input"
|
||||
},
|
||||
)
|
||||
decoder_learned_pos: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "use learned positional embeddings in the decoder"},
|
||||
)
|
||||
decoder_layerdrop: float = field(
|
||||
default=0.0, metadata={"help": "LayerDrop probability for decoder"}
|
||||
)
|
||||
decoder_layers_to_keep: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"help": "which layers to *keep* when pruning as a comma-separated list"
|
||||
},
|
||||
)
|
||||
layernorm_embedding: bool = field(
|
||||
default=False, metadata={"help": "add layernorm to embedding"}
|
||||
)
|
||||
no_scale_embedding: bool = field(
|
||||
default=False, metadata={"help": "if True, dont scale embeddings"}
|
||||
)
|
||||
checkpoint_activations: bool = field(
|
||||
default=False, metadata={"help": "checkpoint activations at each layer"}
|
||||
)
|
||||
offload_activations: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "move checkpointed activations to CPU after they are used."},
|
||||
)
|
||||
quant_noise_pq: float = field(
|
||||
default=0.0,
|
||||
metadata={"help": "iterative PQ quantization noise at training time"},
|
||||
)
|
||||
quant_noise_pq_block_size: int = field(
|
||||
default=8,
|
||||
metadata={"help": "block size of quantization noise at training time"},
|
||||
)
|
||||
# TODO common var add to parent
|
||||
quant_noise_scalar: float = field(
|
||||
default=0.0,
|
||||
metadata={
|
||||
"help": "scalar quantization noise and scalar quantization at training time"
|
||||
},
|
||||
)
|
||||
add_bos_token: bool = II("task.add_bos_token")
|
||||
tokens_per_sample: int = II("task.tokens_per_sample")
|
||||
max_target_positions: Optional[int] = II("task.max_target_positions")
|
||||
tpu: bool = II("common.tpu")
|
||||
|
||||
|
||||
@register_model("transformer_lm", dataclass=TransformerLanguageModelConfig)
|
||||
class TransformerLanguageModel(FairseqLanguageModel):
|
||||
@classmethod
|
||||
def hub_models(cls):
|
||||
def moses_fastbpe(path):
|
||||
return {"path": path, "tokenizer": "moses", "bpe": "fastbpe"}
|
||||
|
||||
def spm(path):
|
||||
return {"path": path, "tokenizer": "space", "bpe": "sentencepiece"}
|
||||
|
||||
return {
|
||||
"transformer_lm.gbw.adaptive_huge": "https://dl.fbaipublicfiles.com/fairseq/models/lm/adaptive_lm_gbw_huge.tar.bz2",
|
||||
"transformer_lm.wiki103.adaptive": "https://dl.fbaipublicfiles.com/fairseq/models/lm/adaptive_lm_wiki103.v2.tar.bz2",
|
||||
"transformer_lm.wmt19.en": moses_fastbpe(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.en.tar.bz2"
|
||||
),
|
||||
"transformer_lm.wmt19.de": moses_fastbpe(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.de.tar.bz2"
|
||||
),
|
||||
"transformer_lm.wmt19.ru": moses_fastbpe(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt19.ru.tar.bz2"
|
||||
),
|
||||
"transformer_lm.wmt20.en": spm(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt20.en.tar.gz"
|
||||
),
|
||||
"transformer_lm.wmt20.ta": spm(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt20.ta.tar.gz"
|
||||
),
|
||||
"transformer_lm.wmt20.iu.news": spm(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt20.iu.news.tar.gz"
|
||||
),
|
||||
"transformer_lm.wmt20.iu.nh": spm(
|
||||
"https://dl.fbaipublicfiles.com/fairseq/models/lm/wmt20.iu.nh.tar.gz"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, decoder):
|
||||
super().__init__(decoder)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, args, task):
|
||||
"""Build a new model instance."""
|
||||
|
||||
# make sure all arguments are present in older models
|
||||
base_lm_architecture(args)
|
||||
|
||||
if args.decoder_layers_to_keep:
|
||||
args.decoder_layers = len(args.decoder_layers_to_keep.split(","))
|
||||
|
||||
if getattr(args, "max_target_positions", None) is None:
|
||||
args.max_target_positions = getattr(
|
||||
args, "tokens_per_sample", DEFAULT_MAX_TARGET_POSITIONS
|
||||
)
|
||||
|
||||
if args.character_embeddings:
|
||||
embed_tokens = CharacterTokenEmbedder(
|
||||
task.source_dictionary,
|
||||
eval(args.character_filters),
|
||||
args.character_embedding_dim,
|
||||
args.decoder_embed_dim,
|
||||
args.char_embedder_highway_layers,
|
||||
)
|
||||
elif args.adaptive_input:
|
||||
embed_tokens = AdaptiveInput(
|
||||
len(task.source_dictionary),
|
||||
task.source_dictionary.pad(),
|
||||
args.decoder_input_dim,
|
||||
args.adaptive_input_factor,
|
||||
args.decoder_embed_dim,
|
||||
options.eval_str_list(args.adaptive_input_cutoff, type=int),
|
||||
args.quant_noise_pq,
|
||||
args.quant_noise_pq_block_size,
|
||||
)
|
||||
else:
|
||||
embed_tokens = cls.build_embedding(
|
||||
args, task.source_dictionary, args.decoder_input_dim
|
||||
)
|
||||
|
||||
if args.tie_adaptive_weights:
|
||||
assert args.adaptive_input
|
||||
assert args.adaptive_input_factor == args.adaptive_softmax_factor
|
||||
assert (
|
||||
args.adaptive_softmax_cutoff == args.adaptive_input_cutoff
|
||||
), "{} != {}".format(
|
||||
args.adaptive_softmax_cutoff, args.adaptive_input_cutoff
|
||||
)
|
||||
assert args.decoder_input_dim == args.decoder_output_dim
|
||||
|
||||
decoder = TransformerDecoder(
|
||||
args, task.target_dictionary, embed_tokens, no_encoder_attn=True
|
||||
)
|
||||
return cls(decoder)
|
||||
|
||||
@classmethod
|
||||
def build_embedding(cls, args, dictionary, embed_dim, path=None):
|
||||
embed_tokens = Embedding(len(dictionary), embed_dim, dictionary.pad())
|
||||
return embed_tokens
|
||||
|
||||
|
||||
def base_lm_architecture(args):
|
||||
# backward compatibility for older model checkpoints
|
||||
if hasattr(args, "no_tie_adaptive_proj"):
|
||||
# previous models defined --no-tie-adaptive-proj, so use the existence of
|
||||
# that option to determine if this is an "old" model checkpoint
|
||||
args.no_decoder_final_norm = True # old models always set this to True
|
||||
if args.no_tie_adaptive_proj is False:
|
||||
args.tie_adaptive_proj = True
|
||||
if hasattr(args, "decoder_final_norm"):
|
||||
args.no_decoder_final_norm = not args.decoder_final_norm
|
||||
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.0)
|
||||
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 2048)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 6)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.adaptive_softmax_cutoff = getattr(args, "adaptive_softmax_cutoff", None)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0)
|
||||
args.adaptive_softmax_factor = getattr(args, "adaptive_softmax_factor", 4)
|
||||
args.decoder_learned_pos = getattr(args, "decoder_learned_pos", False)
|
||||
args.activation_fn = getattr(args, "activation_fn", "relu")
|
||||
|
||||
args.decoder_layerdrop = getattr(args, "decoder_layerdrop", 0)
|
||||
args.decoder_layers_to_keep = getattr(args, "decoder_layers_to_keep", None)
|
||||
args.quant_noise_pq = getattr(args, "quant_noise_pq", 0)
|
||||
args.quant_noise_pq_block_size = getattr(args, "quant_noise_pq_block_size", 8)
|
||||
args.quant_noise_scalar = getattr(args, "quant_noise_scalar", 0)
|
||||
|
||||
args.add_bos_token = getattr(args, "add_bos_token", False)
|
||||
args.no_token_positional_embeddings = getattr(
|
||||
args, "no_token_positional_embeddings", False
|
||||
)
|
||||
args.share_decoder_input_output_embed = getattr(
|
||||
args, "share_decoder_input_output_embed", False
|
||||
)
|
||||
args.character_embeddings = getattr(args, "character_embeddings", False)
|
||||
|
||||
args.decoder_output_dim = getattr(
|
||||
args, "decoder_output_dim", args.decoder_embed_dim
|
||||
)
|
||||
args.decoder_input_dim = getattr(args, "decoder_input_dim", args.decoder_embed_dim)
|
||||
|
||||
# Model training is not stable without this
|
||||
args.decoder_normalize_before = True
|
||||
args.no_decoder_final_norm = getattr(args, "no_decoder_final_norm", False)
|
||||
|
||||
args.adaptive_input = getattr(args, "adaptive_input", False)
|
||||
args.adaptive_input_factor = getattr(args, "adaptive_input_factor", 4)
|
||||
args.adaptive_input_cutoff = getattr(args, "adaptive_input_cutoff", None)
|
||||
|
||||
args.tie_adaptive_weights = getattr(args, "tie_adaptive_weights", False)
|
||||
args.tie_adaptive_proj = getattr(args, "tie_adaptive_proj", False)
|
||||
|
||||
args.no_scale_embedding = getattr(args, "no_scale_embedding", False)
|
||||
args.layernorm_embedding = getattr(args, "layernorm_embedding", False)
|
||||
args.checkpoint_activations = getattr(args, "checkpoint_activations", False)
|
||||
args.offload_activations = getattr(args, "offload_activations", False)
|
||||
if args.offload_activations:
|
||||
args.checkpoint_activations = True
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_big")
|
||||
def transformer_lm_big(args):
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 12)
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 1024)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 4096)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_wiki103")
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_baevski_wiki103")
|
||||
def transformer_lm_baevski_wiki103(args):
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 16)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 8)
|
||||
args.dropout = getattr(args, "dropout", 0.3)
|
||||
args.adaptive_input = getattr(args, "adaptive_input", True)
|
||||
args.tie_adaptive_weights = getattr(args, "tie_adaptive_weights", True)
|
||||
args.adaptive_input_cutoff = getattr(args, "adaptive_input_cutoff", "20000,60000")
|
||||
args.adaptive_softmax_cutoff = getattr(
|
||||
args, "adaptive_softmax_cutoff", "20000,60000"
|
||||
)
|
||||
args.adaptive_softmax_dropout = getattr(args, "adaptive_softmax_dropout", 0.2)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_dropout = getattr(args, "activation_dropout", 0.1)
|
||||
args.no_decoder_final_norm = getattr(args, "no_decoder_final_norm", True)
|
||||
args.tie_adaptive_proj = getattr(args, "tie_adaptive_proj", True)
|
||||
transformer_lm_big(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gbw")
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_baevski_gbw")
|
||||
def transformer_lm_baevski_gbw(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 512)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.no_decoder_final_norm = getattr(args, "no_decoder_final_norm", True)
|
||||
transformer_lm_big(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gpt")
|
||||
def transformer_lm_gpt(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 768)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 3072)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 12)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 12)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gpt2_small")
|
||||
def transformer_lm_gpt2_small(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 1024)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 4096)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 24)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 16)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gpt2_tiny")
|
||||
def transformer_lm_gpt2_tiny(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 64)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 64)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 2)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 1)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gpt2_medium")
|
||||
def transformer_lm_gpt2_medium(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 1280)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 5120)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 36)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 20)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
base_lm_architecture(args)
|
||||
|
||||
|
||||
@register_model_architecture("transformer_lm", "transformer_lm_gpt2_big")
|
||||
def transformer_lm_gpt2_big(args):
|
||||
args.decoder_embed_dim = getattr(args, "decoder_embed_dim", 1600)
|
||||
args.decoder_ffn_embed_dim = getattr(args, "decoder_ffn_embed_dim", 6400)
|
||||
args.decoder_layers = getattr(args, "decoder_layers", 48)
|
||||
args.decoder_attention_heads = getattr(args, "decoder_attention_heads", 25)
|
||||
args.dropout = getattr(args, "dropout", 0.1)
|
||||
args.attention_dropout = getattr(args, "attention_dropout", 0.1)
|
||||
args.activation_fn = getattr(args, "activation_fn", "gelu")
|
||||
base_lm_architecture(args)
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from .wav2vec import * # noqa
|
||||
from .wav2vec2 import * # noqa
|
||||
from .wav2vec2_asr import * # noqa
|
||||
@@ -0,0 +1,630 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
import math
|
||||
from typing import Optional, Tuple
|
||||
from omegaconf import II
|
||||
import sys
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq.dataclass import ChoiceEnum, FairseqDataclass
|
||||
from fairseq.models import BaseFairseqModel, register_model
|
||||
from fairseq.modules import (
|
||||
Fp32GroupNorm,
|
||||
Fp32LayerNorm,
|
||||
GumbelVectorQuantizer,
|
||||
KmeansVectorQuantizer,
|
||||
TransposeLast,
|
||||
)
|
||||
from fairseq.tasks import FairseqTask
|
||||
from fairseq.utils import buffered_arange
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
AGGREGATOR_CHOICES = ChoiceEnum(["cnn", "gru"])
|
||||
PROJECT_FEATURES_CHOICES = ChoiceEnum(["none", "same", "new"])
|
||||
ACTIVATION_CHOICES = ChoiceEnum(["relu", "gelu"])
|
||||
VQ_TYPE_CHOICES = ChoiceEnum(["none", "gumbel", "kmeans"])
|
||||
|
||||
|
||||
@dataclass
|
||||
class Wav2VecConfig(FairseqDataclass):
|
||||
prediction_steps: int = field(
|
||||
default=12, metadata={"help": "number of steps ahead to predict"}
|
||||
)
|
||||
sample_distance: Optional[int] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"help": "sample distance from target. does not work properly with cross-sampling"
|
||||
},
|
||||
)
|
||||
cross_sample_negatives: int = field(
|
||||
default=0, metadata={"help": "num of cross sampled negatives"}
|
||||
)
|
||||
num_negatives: int = field(
|
||||
default=10, metadata={"help": "num of cross sampled negatives"}
|
||||
)
|
||||
conv_feature_layers: str = field(
|
||||
default="[(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1), (512, 1, 1)]",
|
||||
metadata={
|
||||
"help": "convolutional feature extraction layers [(dim, kernel_size, stride), ...]"
|
||||
},
|
||||
)
|
||||
conv_aggregator_layers: str = field(
|
||||
default="[(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)]",
|
||||
metadata={
|
||||
"help": "convolutional aggregator layers [(dim, kernel_size, stride), ...]"
|
||||
},
|
||||
)
|
||||
dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout to apply within the model"}
|
||||
)
|
||||
dropout_features: float = field(
|
||||
default=0.0, metadata={"help": "dropout to apply to the features"}
|
||||
)
|
||||
dropout_agg: float = field(
|
||||
default=0.0, metadata={"help": "dropout to apply after aggregation step"}
|
||||
)
|
||||
aggregator: AGGREGATOR_CHOICES = field(
|
||||
default="cnn", metadata={"help": "type of aggregator to use"}
|
||||
)
|
||||
gru_dim: int = field(default=512, metadata={"help": "GRU dimensionality"})
|
||||
no_conv_bias: bool = field(
|
||||
default=False, metadata={"help": "if set, does not learn bias for conv layers"}
|
||||
)
|
||||
agg_zero_pad: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "if set, zero pads in aggregator instead of repl pad"},
|
||||
)
|
||||
skip_connections_feat: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "if set, adds skip connections to the feature extractor"},
|
||||
)
|
||||
skip_connections_agg: bool = field(
|
||||
default=True,
|
||||
metadata={"help": "if set, adds skip connections to the aggregator"},
|
||||
)
|
||||
residual_scale: float = field(
|
||||
default=0.5, metadata={"help": "scales residual by sqrt(value)"}
|
||||
)
|
||||
log_compression: bool = field(
|
||||
default=True,
|
||||
metadata={"help": "if set, adds a log compression to feature extractor"},
|
||||
)
|
||||
balanced_classes: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "if set, loss is scaled to balance for number of negatives"},
|
||||
)
|
||||
project_features: PROJECT_FEATURES_CHOICES = field(
|
||||
default="none",
|
||||
metadata={
|
||||
"help": "if not none, features are projected using the (same or new) aggregator"
|
||||
},
|
||||
)
|
||||
non_affine_group_norm: bool = field(
|
||||
default=False, metadata={"help": "if set, group norm is not affine"}
|
||||
)
|
||||
offset: str = field(
|
||||
default="auto",
|
||||
metadata={
|
||||
"help": "if set to 'auto', it is computed automatically from the receptive field, else set to int value"
|
||||
},
|
||||
)
|
||||
activation: ACTIVATION_CHOICES = field(
|
||||
default="relu",
|
||||
metadata={
|
||||
"help": "if set to 'auto', it is computed automatically from the receptive field, else set to int value"
|
||||
},
|
||||
)
|
||||
vq_type: VQ_TYPE_CHOICES = field(
|
||||
default="none", metadata={"help": "which type of quantizer to use"}
|
||||
)
|
||||
vq_vars: int = field(
|
||||
default=320,
|
||||
metadata={"help": "project to this many vector quantized variables per group"},
|
||||
)
|
||||
vq_groups: int = field(
|
||||
default=2, metadata={"help": "number of groups of latent variables"}
|
||||
)
|
||||
vq_dim: int = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "uses this dimensionality for quantized vectors. 0 to use model dim // groups"
|
||||
},
|
||||
)
|
||||
vq_depth: int = field(
|
||||
default=1, metadata={"help": "number of layers for vq weight projection"}
|
||||
)
|
||||
combine_groups: bool = field(
|
||||
default=False, metadata={"help": "if set, variables are shared among groups"}
|
||||
)
|
||||
vq_temp: Tuple[float, float, float] = field(
|
||||
default=(2.0, 0.5, 0.999995),
|
||||
metadata={
|
||||
"help": "temperature for latent variable sampling with gumbel softmax. should be a tuple of 3 values (start, end, decay)"
|
||||
},
|
||||
)
|
||||
vq_gamma: float = field(
|
||||
default=0.25,
|
||||
metadata={"help": "gamma parameter for kmeans style vector quantization"},
|
||||
)
|
||||
infonce: bool = II("criterion.infonce")
|
||||
|
||||
|
||||
@register_model("wav2vec", dataclass=Wav2VecConfig)
|
||||
class Wav2VecModel(BaseFairseqModel):
|
||||
@classmethod
|
||||
def build_model(cls, cfg: Wav2VecConfig, task: FairseqTask):
|
||||
"""Build a new model instance."""
|
||||
|
||||
model = Wav2VecModel(cfg)
|
||||
logger.info(model)
|
||||
return model
|
||||
|
||||
def __init__(self, cfg: Wav2VecConfig):
|
||||
super().__init__()
|
||||
|
||||
self.prediction_steps = cfg.prediction_steps
|
||||
offset = cfg.offset
|
||||
|
||||
if cfg.activation == "relu":
|
||||
activation = nn.ReLU()
|
||||
elif cfg.activation == "gelu":
|
||||
activation = nn.GELU()
|
||||
else:
|
||||
raise Exception("unknown activation " + cfg.activation)
|
||||
|
||||
feature_enc_layers = eval(cfg.conv_feature_layers)
|
||||
self.feature_extractor = ConvFeatureExtractionModel(
|
||||
conv_layers=feature_enc_layers,
|
||||
dropout=0.0,
|
||||
log_compression=cfg.log_compression,
|
||||
skip_connections=cfg.skip_connections_feat,
|
||||
residual_scale=cfg.residual_scale,
|
||||
non_affine_group_norm=cfg.non_affine_group_norm,
|
||||
activation=activation,
|
||||
)
|
||||
embed = feature_enc_layers[-1][0]
|
||||
|
||||
self.vector_quantizer = None
|
||||
if cfg.vq_type == "gumbel":
|
||||
self.vector_quantizer = GumbelVectorQuantizer(
|
||||
dim=embed,
|
||||
num_vars=cfg.vq_vars,
|
||||
temp=cfg.vq_temp,
|
||||
groups=cfg.vq_groups,
|
||||
combine_groups=cfg.combine_groups,
|
||||
vq_dim=cfg.vq_dim if cfg.vq_dim > 0 else embed,
|
||||
time_first=False,
|
||||
activation=activation,
|
||||
weight_proj_depth=cfg.vq_depth,
|
||||
weight_proj_factor=2,
|
||||
)
|
||||
elif cfg.vq_type == "kmeans":
|
||||
self.vector_quantizer = KmeansVectorQuantizer(
|
||||
dim=embed,
|
||||
num_vars=cfg.vq_vars,
|
||||
groups=cfg.vq_groups,
|
||||
combine_groups=cfg.combine_groups,
|
||||
vq_dim=cfg.vq_dim if cfg.vq_dim > 0 else embed,
|
||||
time_first=False,
|
||||
gamma=cfg.vq_gamma,
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
cfg.vq_type == "none" or cfg.vq_type is None
|
||||
), "Unknown quantizer type"
|
||||
|
||||
if cfg.offset == "auto":
|
||||
jin = 0
|
||||
rin = 0
|
||||
for _, k, stride in feature_enc_layers:
|
||||
if rin == 0:
|
||||
rin = k
|
||||
rin = rin + (k - 1) * jin
|
||||
if jin == 0:
|
||||
jin = stride
|
||||
else:
|
||||
jin *= stride
|
||||
offset = math.ceil(rin / jin)
|
||||
|
||||
offset = int(offset)
|
||||
|
||||
def make_aggregator():
|
||||
if cfg.aggregator == "cnn":
|
||||
agg_layers = eval(cfg.conv_aggregator_layers)
|
||||
agg_dim = agg_layers[-1][0]
|
||||
feature_aggregator = ConvAggegator(
|
||||
conv_layers=agg_layers,
|
||||
embed=embed,
|
||||
dropout=cfg.dropout,
|
||||
skip_connections=cfg.skip_connections_agg,
|
||||
residual_scale=cfg.residual_scale,
|
||||
non_affine_group_norm=cfg.non_affine_group_norm,
|
||||
conv_bias=not cfg.no_conv_bias,
|
||||
zero_pad=cfg.agg_zero_pad,
|
||||
activation=activation,
|
||||
)
|
||||
elif cfg.aggregator == "gru":
|
||||
agg_dim = cfg.gru_dim
|
||||
feature_aggregator = nn.Sequential(
|
||||
TransposeLast(),
|
||||
nn.GRU(
|
||||
input_size=embed,
|
||||
hidden_size=agg_dim,
|
||||
num_layers=1,
|
||||
dropout=cfg.dropout,
|
||||
),
|
||||
TransposeLast(deconstruct_idx=0),
|
||||
)
|
||||
else:
|
||||
raise Exception("unknown aggregator type " + cfg.aggregator)
|
||||
|
||||
return feature_aggregator, agg_dim
|
||||
|
||||
self.feature_aggregator, agg_dim = make_aggregator()
|
||||
|
||||
self.wav2vec_predictions = Wav2VecPredictionsModel(
|
||||
in_dim=agg_dim,
|
||||
out_dim=embed,
|
||||
prediction_steps=cfg.prediction_steps,
|
||||
n_negatives=cfg.num_negatives,
|
||||
cross_sample_negatives=cfg.cross_sample_negatives,
|
||||
sample_distance=cfg.sample_distance,
|
||||
dropout=cfg.dropout,
|
||||
offset=offset,
|
||||
balanced_classes=cfg.balanced_classes,
|
||||
infonce=cfg.infonce,
|
||||
)
|
||||
|
||||
self.dropout_feats = nn.Dropout(p=cfg.dropout_features)
|
||||
self.dropout_agg = nn.Dropout(p=cfg.dropout_agg)
|
||||
|
||||
if cfg.project_features == "none":
|
||||
self.project_features = None
|
||||
elif cfg.project_features == "same":
|
||||
self.project_features = self.feature_aggregator
|
||||
elif cfg.project_features == "new":
|
||||
self.project_features, _ = make_aggregator()
|
||||
|
||||
def forward(self, source):
|
||||
result = {}
|
||||
|
||||
features = self.feature_extractor(source)
|
||||
if self.vector_quantizer:
|
||||
q_res = self.vector_quantizer(features)
|
||||
features = q_res["x"]
|
||||
for k in q_res.keys():
|
||||
if k != "x":
|
||||
result[k] = q_res[k]
|
||||
|
||||
x = self.dropout_feats(features)
|
||||
x = self.feature_aggregator(x)
|
||||
x = self.dropout_agg(x)
|
||||
|
||||
if self.project_features is not None:
|
||||
features = self.project_features(features)
|
||||
x, targets = self.wav2vec_predictions(x, features)
|
||||
result["cpc_logits"] = x
|
||||
result["cpc_targets"] = targets
|
||||
|
||||
return result
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum length supported by the model."""
|
||||
return sys.maxsize
|
||||
|
||||
def get_logits(self, net_output):
|
||||
logits = net_output["cpc_logits"]
|
||||
return logits
|
||||
|
||||
def get_targets(self, sample, net_output):
|
||||
t = net_output["cpc_targets"]
|
||||
if isinstance(t, tuple):
|
||||
t = t[0]
|
||||
return t.contiguous()
|
||||
|
||||
def get_target_weights(self, targets, net_output):
|
||||
targets = net_output["cpc_targets"]
|
||||
if isinstance(targets, tuple) and targets[-1] is not None:
|
||||
return targets[-1]
|
||||
return None
|
||||
|
||||
def get_extra_losses(self, net_output):
|
||||
loss = None
|
||||
if "prob_perplexity" in net_output:
|
||||
loss = net_output["num_vars"] - net_output["prob_perplexity"]
|
||||
elif "kmeans_loss" in net_output:
|
||||
loss = net_output["kmeans_loss"]
|
||||
|
||||
return loss
|
||||
|
||||
|
||||
def norm_block(is_layer_norm, dim, affine=True):
|
||||
if is_layer_norm:
|
||||
mod = nn.Sequential(
|
||||
TransposeLast(),
|
||||
Fp32LayerNorm(dim, elementwise_affine=affine),
|
||||
TransposeLast(),
|
||||
)
|
||||
else:
|
||||
mod = Fp32GroupNorm(1, dim, affine=affine)
|
||||
|
||||
return mod
|
||||
|
||||
|
||||
class ConvFeatureExtractionModel(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
conv_layers,
|
||||
dropout,
|
||||
log_compression,
|
||||
skip_connections,
|
||||
residual_scale,
|
||||
non_affine_group_norm,
|
||||
activation,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
def block(n_in, n_out, k, stride):
|
||||
return nn.Sequential(
|
||||
nn.Conv1d(n_in, n_out, k, stride=stride, bias=False),
|
||||
nn.Dropout(p=dropout),
|
||||
norm_block(
|
||||
is_layer_norm=False, dim=n_out, affine=not non_affine_group_norm
|
||||
),
|
||||
activation,
|
||||
)
|
||||
|
||||
in_d = 1
|
||||
self.conv_layers = nn.ModuleList()
|
||||
for dim, k, stride in conv_layers:
|
||||
self.conv_layers.append(block(in_d, dim, k, stride))
|
||||
in_d = dim
|
||||
|
||||
self.log_compression = log_compression
|
||||
self.skip_connections = skip_connections
|
||||
self.residual_scale = math.sqrt(residual_scale)
|
||||
|
||||
def forward(self, x):
|
||||
# BxT -> BxCxT
|
||||
x = x.unsqueeze(1)
|
||||
|
||||
for conv in self.conv_layers:
|
||||
residual = x
|
||||
x = conv(x)
|
||||
if self.skip_connections and x.size(1) == residual.size(1):
|
||||
tsz = x.size(2)
|
||||
r_tsz = residual.size(2)
|
||||
residual = residual[..., :: r_tsz // tsz][..., :tsz]
|
||||
x = (x + residual) * self.residual_scale
|
||||
|
||||
if self.log_compression:
|
||||
x = x.abs()
|
||||
x = x + 1
|
||||
x = x.log()
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class ZeroPad1d(nn.Module):
|
||||
def __init__(self, pad_left, pad_right):
|
||||
super().__init__()
|
||||
self.pad_left = pad_left
|
||||
self.pad_right = pad_right
|
||||
|
||||
def forward(self, x):
|
||||
return F.pad(x, (self.pad_left, self.pad_right))
|
||||
|
||||
|
||||
class ConvAggegator(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
conv_layers,
|
||||
embed,
|
||||
dropout,
|
||||
skip_connections,
|
||||
residual_scale,
|
||||
non_affine_group_norm,
|
||||
conv_bias,
|
||||
zero_pad,
|
||||
activation,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
def block(n_in, n_out, k, stride):
|
||||
# padding dims only really make sense for stride = 1
|
||||
ka = k // 2
|
||||
kb = ka - 1 if k % 2 == 0 else ka
|
||||
|
||||
pad = (
|
||||
ZeroPad1d(ka + kb, 0) if zero_pad else nn.ReplicationPad1d((ka + kb, 0))
|
||||
)
|
||||
|
||||
return nn.Sequential(
|
||||
pad,
|
||||
nn.Conv1d(n_in, n_out, k, stride=stride, bias=conv_bias),
|
||||
nn.Dropout(p=dropout),
|
||||
norm_block(False, n_out, affine=not non_affine_group_norm),
|
||||
activation,
|
||||
)
|
||||
|
||||
in_d = embed
|
||||
self.conv_layers = nn.ModuleList()
|
||||
self.residual_proj = nn.ModuleList()
|
||||
for dim, k, stride in conv_layers:
|
||||
if in_d != dim and skip_connections:
|
||||
self.residual_proj.append(nn.Conv1d(in_d, dim, 1, bias=False))
|
||||
else:
|
||||
self.residual_proj.append(None)
|
||||
|
||||
self.conv_layers.append(block(in_d, dim, k, stride))
|
||||
in_d = dim
|
||||
self.conv_layers = nn.Sequential(*self.conv_layers)
|
||||
self.skip_connections = skip_connections
|
||||
self.residual_scale = math.sqrt(residual_scale)
|
||||
|
||||
def forward(self, x):
|
||||
for rproj, conv in zip(self.residual_proj, self.conv_layers):
|
||||
residual = x
|
||||
x = conv(x)
|
||||
if self.skip_connections:
|
||||
if rproj is not None:
|
||||
residual = rproj(residual)
|
||||
x = (x + residual) * self.residual_scale
|
||||
return x
|
||||
|
||||
|
||||
class Wav2VecPredictionsModel(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
in_dim,
|
||||
out_dim,
|
||||
prediction_steps,
|
||||
n_negatives,
|
||||
cross_sample_negatives,
|
||||
sample_distance,
|
||||
dropout,
|
||||
offset,
|
||||
balanced_classes,
|
||||
infonce,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.n_negatives = n_negatives
|
||||
self.cross_sample_negatives = cross_sample_negatives
|
||||
self.sample_distance = sample_distance
|
||||
self.project_to_steps = nn.ConvTranspose2d(
|
||||
in_dim, out_dim, (1, prediction_steps)
|
||||
)
|
||||
self.dropout = nn.Dropout(p=dropout)
|
||||
self.offset = offset
|
||||
self.balanced_classes = balanced_classes
|
||||
self.infonce = infonce
|
||||
|
||||
def sample_negatives(self, y):
|
||||
bsz, fsz, tsz = y.shape
|
||||
|
||||
y = y.transpose(0, 1) # BCT -> CBT
|
||||
y = y.contiguous().view(fsz, -1) # CBT => C(BxT)
|
||||
|
||||
cross_high = tsz * bsz
|
||||
high = tsz if self.sample_distance is None else min(tsz, self.sample_distance)
|
||||
assert high > 1
|
||||
|
||||
neg_idxs = torch.randint(low=0, high=high, size=(bsz, self.n_negatives * tsz))
|
||||
|
||||
with torch.no_grad():
|
||||
if self.n_negatives > 0:
|
||||
tszs = (
|
||||
buffered_arange(tsz)
|
||||
.unsqueeze(-1)
|
||||
.expand(-1, self.n_negatives)
|
||||
.flatten()
|
||||
)
|
||||
|
||||
neg_idxs = torch.randint(
|
||||
low=0, high=high - 1, size=(bsz, self.n_negatives * tsz)
|
||||
)
|
||||
neg_idxs[neg_idxs >= tszs] += 1
|
||||
|
||||
if self.cross_sample_negatives > 0:
|
||||
tszs = (
|
||||
buffered_arange(tsz)
|
||||
.unsqueeze(-1)
|
||||
.expand(-1, self.cross_sample_negatives)
|
||||
.flatten()
|
||||
)
|
||||
|
||||
cross_neg_idxs = torch.randint(
|
||||
low=0,
|
||||
high=cross_high - 1,
|
||||
size=(bsz, self.cross_sample_negatives * tsz),
|
||||
)
|
||||
cross_neg_idxs[cross_neg_idxs >= tszs] += 1
|
||||
|
||||
if self.n_negatives > 0:
|
||||
for i in range(1, bsz):
|
||||
neg_idxs[i] += i * high
|
||||
else:
|
||||
neg_idxs = cross_neg_idxs
|
||||
|
||||
if self.cross_sample_negatives > 0 and self.n_negatives > 0:
|
||||
neg_idxs = torch.cat([neg_idxs, cross_neg_idxs], dim=1)
|
||||
|
||||
negs = y[..., neg_idxs.view(-1)]
|
||||
negs = negs.view(
|
||||
fsz, bsz, self.n_negatives + self.cross_sample_negatives, tsz
|
||||
).permute(
|
||||
2, 1, 0, 3
|
||||
) # to NxBxCxT
|
||||
|
||||
return negs
|
||||
|
||||
def forward(self, x, y):
|
||||
|
||||
x = x.unsqueeze(-1)
|
||||
x = self.project_to_steps(x) # BxCxTxS
|
||||
x = self.dropout(x)
|
||||
|
||||
negatives = self.sample_negatives(y)
|
||||
y = y.unsqueeze(0)
|
||||
targets = torch.cat([y, negatives], dim=0) # Copies x B x C x T
|
||||
|
||||
copies = targets.size(0)
|
||||
bsz, dim, tsz, steps = x.shape
|
||||
steps = min(steps, tsz - self.offset)
|
||||
|
||||
predictions = x.new(
|
||||
bsz * copies * (tsz - self.offset + 1) * steps
|
||||
- ((steps + 1) * steps // 2) * copies * bsz
|
||||
)
|
||||
if self.infonce:
|
||||
labels = predictions.new_full(
|
||||
(predictions.shape[0] // copies,), 0, dtype=torch.long
|
||||
)
|
||||
else:
|
||||
labels = torch.zeros_like(predictions)
|
||||
weights = (
|
||||
torch.full_like(labels, 1 / self.n_negatives)
|
||||
if self.balanced_classes and not self.infonce
|
||||
else None
|
||||
)
|
||||
|
||||
start = end = 0
|
||||
for i in range(steps):
|
||||
offset = i + self.offset
|
||||
end = start + (tsz - offset) * bsz * copies
|
||||
if self.infonce:
|
||||
predictions[start:end] = torch.einsum(
|
||||
"bct,nbct->tbn", x[..., :-offset, i], targets[..., offset:]
|
||||
).flatten()
|
||||
else:
|
||||
pos_num = (end - start) // copies
|
||||
predictions[start:end] = torch.einsum(
|
||||
"bct,nbct->nbt", x[..., :-offset, i], targets[..., offset:]
|
||||
).flatten()
|
||||
labels[start : start + pos_num] = 1.0
|
||||
if weights is not None:
|
||||
weights[start : start + pos_num] = 1.0
|
||||
start = end
|
||||
assert end == predictions.numel(), "{} != {}".format(end, predictions.numel())
|
||||
|
||||
if self.infonce:
|
||||
predictions = predictions.view(-1, copies)
|
||||
else:
|
||||
if weights is not None:
|
||||
labels = (labels, weights)
|
||||
|
||||
return predictions, labels
|
||||
@@ -0,0 +1,900 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Tuple
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from fairseq import utils
|
||||
from fairseq.data.data_utils import compute_mask_indices
|
||||
from fairseq.dataclass import ChoiceEnum, FairseqDataclass
|
||||
from fairseq.models import BaseFairseqModel, register_model
|
||||
from fairseq.modules import (
|
||||
Fp32GroupNorm,
|
||||
Fp32LayerNorm,
|
||||
GradMultiply,
|
||||
GumbelVectorQuantizer,
|
||||
LayerNorm,
|
||||
MultiheadAttention,
|
||||
SamePad,
|
||||
TransposeLast,
|
||||
)
|
||||
from fairseq.modules.transformer_sentence_encoder import init_bert_params
|
||||
from fairseq.utils import buffered_arange
|
||||
|
||||
|
||||
EXTRACTOR_MODE_CHOICES = ChoiceEnum(["default", "layer_norm"])
|
||||
MASKING_DISTRIBUTION_CHOICES = ChoiceEnum(["static", "uniform", "normal", "poisson"])
|
||||
|
||||
|
||||
@dataclass
|
||||
class Wav2Vec2Config(FairseqDataclass):
|
||||
extractor_mode: EXTRACTOR_MODE_CHOICES = field(
|
||||
default="default",
|
||||
metadata={
|
||||
"help": "mode for feature extractor. default has a single group norm with d "
|
||||
"groups in the first conv block, whereas layer_norm has layer norms in "
|
||||
"every block (meant to use with normalize=True)"
|
||||
},
|
||||
)
|
||||
encoder_layers: int = field(
|
||||
default=12, metadata={"help": "num encoder layers in the transformer"}
|
||||
)
|
||||
encoder_embed_dim: int = field(
|
||||
default=768, metadata={"help": "encoder embedding dimension"}
|
||||
)
|
||||
encoder_ffn_embed_dim: int = field(
|
||||
default=3072, metadata={"help": "encoder embedding dimension for FFN"}
|
||||
)
|
||||
encoder_attention_heads: int = field(
|
||||
default=12, metadata={"help": "num encoder attention heads"}
|
||||
)
|
||||
activation_fn: ChoiceEnum(utils.get_available_activation_fns()) = field(
|
||||
default="gelu", metadata={"help": "activation function to use"}
|
||||
)
|
||||
|
||||
# dropouts
|
||||
dropout: float = field(
|
||||
default=0.1, metadata={"help": "dropout probability for the transformer"}
|
||||
)
|
||||
attention_dropout: float = field(
|
||||
default=0.1, metadata={"help": "dropout probability for attention weights"}
|
||||
)
|
||||
activation_dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability after activation in FFN"}
|
||||
)
|
||||
encoder_layerdrop: float = field(
|
||||
default=0.0, metadata={"help": "probability of dropping a tarnsformer layer"}
|
||||
)
|
||||
dropout_input: float = field(
|
||||
default=0.0,
|
||||
metadata={"help": "dropout to apply to the input (after feat extr)"},
|
||||
)
|
||||
dropout_features: float = field(
|
||||
default=0.0,
|
||||
metadata={"help": "dropout to apply to the features (after feat extr)"},
|
||||
)
|
||||
|
||||
final_dim: int = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "project final representations and targets to this many dimensions."
|
||||
"set to encoder_embed_dim is <= 0"
|
||||
},
|
||||
)
|
||||
layer_norm_first: bool = field(
|
||||
default=False, metadata={"help": "apply layernorm first in the transformer"}
|
||||
)
|
||||
conv_feature_layers: str = field(
|
||||
default="[(512, 10, 5)] + [(512, 3, 2)] * 4 + [(512,2,2)] + [(512,2,2)]",
|
||||
metadata={
|
||||
"help": "string describing convolutional feature extraction layers in form of a python list that contains "
|
||||
"[(dim, kernel_size, stride), ...]"
|
||||
},
|
||||
)
|
||||
conv_bias: bool = field(
|
||||
default=False, metadata={"help": "include bias in conv encoder"}
|
||||
)
|
||||
logit_temp: float = field(
|
||||
default=0.1, metadata={"help": "temperature to divide logits by"}
|
||||
)
|
||||
quantize_targets: bool = field(
|
||||
default=False, metadata={"help": "use quantized targets"}
|
||||
)
|
||||
quantize_input: bool = field(
|
||||
default=False, metadata={"help": "use quantized inputs"}
|
||||
)
|
||||
same_quantizer: bool = field(
|
||||
default=False, metadata={"help": "use same quantizer for inputs and targets"}
|
||||
)
|
||||
target_glu: bool = field(
|
||||
default=False, metadata={"help": "adds projection + glu to targets"}
|
||||
)
|
||||
feature_grad_mult: float = field(
|
||||
default=1.0, metadata={"help": "multiply feature extractor var grads by this"}
|
||||
)
|
||||
latent_vars: int = field(
|
||||
default=320,
|
||||
metadata={"help": "number of latent variables V in each group of the codebook"},
|
||||
)
|
||||
latent_groups: int = field(
|
||||
default=2,
|
||||
metadata={"help": "number of groups G of latent variables in the codebook"},
|
||||
)
|
||||
latent_dim: int = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "if > 0, uses this dimensionality for latent variables. "
|
||||
"otherwise uses final_dim / latent_groups"
|
||||
},
|
||||
)
|
||||
|
||||
# masking
|
||||
mask_length: int = field(default=10, metadata={"help": "mask length"})
|
||||
mask_prob: float = field(
|
||||
default=0.65, metadata={"help": "probability of replacing a token with mask"}
|
||||
)
|
||||
mask_selection: MASKING_DISTRIBUTION_CHOICES = field(
|
||||
default="static", metadata={"help": "how to choose mask length"}
|
||||
)
|
||||
mask_other: float = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "secondary mask argument (used for more complex distributions), "
|
||||
"see help in compute_mask_indices"
|
||||
},
|
||||
)
|
||||
no_mask_overlap: bool = field(
|
||||
default=False, metadata={"help": "whether to allow masks to overlap"}
|
||||
)
|
||||
mask_min_space: int = field(
|
||||
default=1,
|
||||
metadata={"help": "min space between spans (if no overlap is enabled)"},
|
||||
)
|
||||
|
||||
# channel masking
|
||||
mask_channel_length: int = field(
|
||||
default=10, metadata={"help": "length of the mask for features (channels)"}
|
||||
)
|
||||
mask_channel_prob: float = field(
|
||||
default=0.0, metadata={"help": "probability of replacing a feature with 0"}
|
||||
)
|
||||
mask_channel_selection: MASKING_DISTRIBUTION_CHOICES = field(
|
||||
default="static",
|
||||
metadata={"help": "how to choose mask length for channel masking"},
|
||||
)
|
||||
mask_channel_other: float = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "secondary mask argument (used for more complex distributions), "
|
||||
"see help in compute_mask_indicesh"
|
||||
},
|
||||
)
|
||||
no_mask_channel_overlap: bool = field(
|
||||
default=False, metadata={"help": "whether to allow channel masks to overlap"}
|
||||
)
|
||||
mask_channel_min_space: int = field(
|
||||
default=1,
|
||||
metadata={"help": "min space between spans (if no overlap is enabled)"},
|
||||
)
|
||||
|
||||
# negative selection
|
||||
num_negatives: int = field(
|
||||
default=100,
|
||||
metadata={"help": "number of negative examples from the same sample"},
|
||||
)
|
||||
negatives_from_everywhere: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "sample negatives from everywhere, not just masked states"},
|
||||
)
|
||||
cross_sample_negatives: int = field(
|
||||
default=0, metadata={"help": "number of negative examples from the any sample"}
|
||||
)
|
||||
codebook_negatives: int = field(
|
||||
default=0, metadata={"help": "number of negative examples codebook"}
|
||||
)
|
||||
|
||||
# positional embeddings
|
||||
conv_pos: int = field(
|
||||
default=128,
|
||||
metadata={"help": "number of filters for convolutional positional embeddings"},
|
||||
)
|
||||
conv_pos_groups: int = field(
|
||||
default=16,
|
||||
metadata={"help": "number of groups for convolutional positional embedding"},
|
||||
)
|
||||
|
||||
latent_temp: Tuple[float, float, float] = field(
|
||||
default=(2, 0.5, 0.999995),
|
||||
metadata={
|
||||
"help": "temperature for latent variable sampling. "
|
||||
"can be tuple of 3 values (start, end, decay)"
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@register_model("wav2vec2", dataclass=Wav2Vec2Config)
|
||||
class Wav2Vec2Model(BaseFairseqModel):
|
||||
def __init__(self, cfg: Wav2Vec2Config):
|
||||
super().__init__()
|
||||
self.cfg = cfg
|
||||
|
||||
feature_enc_layers = eval(cfg.conv_feature_layers)
|
||||
self.embed = feature_enc_layers[-1][0]
|
||||
|
||||
self.feature_extractor = ConvFeatureExtractionModel(
|
||||
conv_layers=feature_enc_layers,
|
||||
dropout=0.0,
|
||||
mode=cfg.extractor_mode,
|
||||
conv_bias=cfg.conv_bias,
|
||||
)
|
||||
|
||||
self.post_extract_proj = (
|
||||
nn.Linear(self.embed, cfg.encoder_embed_dim)
|
||||
if self.embed != cfg.encoder_embed_dim and not cfg.quantize_input
|
||||
else None
|
||||
)
|
||||
|
||||
self.mask_prob = cfg.mask_prob
|
||||
self.mask_selection = cfg.mask_selection
|
||||
self.mask_other = cfg.mask_other
|
||||
self.mask_length = cfg.mask_length
|
||||
self.no_mask_overlap = cfg.no_mask_overlap
|
||||
self.mask_min_space = cfg.mask_min_space
|
||||
|
||||
self.mask_channel_prob = cfg.mask_channel_prob
|
||||
self.mask_channel_selection = cfg.mask_channel_selection
|
||||
self.mask_channel_other = cfg.mask_channel_other
|
||||
self.mask_channel_length = cfg.mask_channel_length
|
||||
self.no_mask_channel_overlap = cfg.no_mask_channel_overlap
|
||||
self.mask_channel_min_space = cfg.mask_channel_min_space
|
||||
|
||||
self.dropout_input = nn.Dropout(cfg.dropout_input)
|
||||
self.dropout_features = nn.Dropout(cfg.dropout_features)
|
||||
|
||||
self.feature_grad_mult = cfg.feature_grad_mult
|
||||
|
||||
self.quantizer = None
|
||||
self.input_quantizer = None
|
||||
|
||||
self.n_negatives = cfg.num_negatives
|
||||
self.cross_sample_negatives = cfg.cross_sample_negatives
|
||||
self.codebook_negatives = cfg.codebook_negatives
|
||||
self.negatives_from_everywhere = cfg.negatives_from_everywhere
|
||||
|
||||
self.logit_temp = cfg.logit_temp
|
||||
|
||||
final_dim = cfg.final_dim if cfg.final_dim > 0 else cfg.encoder_embed_dim
|
||||
|
||||
if cfg.quantize_targets:
|
||||
vq_dim = cfg.latent_dim if cfg.latent_dim > 0 else final_dim
|
||||
self.quantizer = GumbelVectorQuantizer(
|
||||
dim=self.embed,
|
||||
num_vars=cfg.latent_vars,
|
||||
temp=cfg.latent_temp,
|
||||
groups=cfg.latent_groups,
|
||||
combine_groups=False,
|
||||
vq_dim=vq_dim,
|
||||
time_first=True,
|
||||
)
|
||||
self.project_q = nn.Linear(vq_dim, final_dim)
|
||||
else:
|
||||
self.project_q = nn.Linear(self.embed, final_dim)
|
||||
|
||||
if cfg.quantize_input:
|
||||
if cfg.same_quantizer and self.quantizer is not None:
|
||||
vq_dim = final_dim
|
||||
self.input_quantizer = self.quantizer
|
||||
else:
|
||||
vq_dim = cfg.latent_dim if cfg.latent_dim > 0 else cfg.encoder_embed_dim
|
||||
self.input_quantizer = GumbelVectorQuantizer(
|
||||
dim=self.embed,
|
||||
num_vars=cfg.latent_vars,
|
||||
temp=cfg.latent_temp,
|
||||
groups=cfg.latent_groups,
|
||||
combine_groups=False,
|
||||
vq_dim=vq_dim,
|
||||
time_first=True,
|
||||
)
|
||||
self.project_inp = nn.Linear(vq_dim, cfg.encoder_embed_dim)
|
||||
|
||||
self.mask_emb = nn.Parameter(
|
||||
torch.FloatTensor(cfg.encoder_embed_dim).uniform_()
|
||||
)
|
||||
|
||||
self.encoder = TransformerEncoder(cfg)
|
||||
self.layer_norm = LayerNorm(self.embed)
|
||||
|
||||
self.target_glu = None
|
||||
if cfg.target_glu:
|
||||
self.target_glu = nn.Sequential(
|
||||
nn.Linear(final_dim, final_dim * 2), nn.GLU()
|
||||
)
|
||||
|
||||
self.final_proj = nn.Linear(cfg.encoder_embed_dim, final_dim)
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
"""Upgrade a (possibly old) state dict for new versions of fairseq."""
|
||||
return state_dict
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, cfg: Wav2Vec2Config, task=None):
|
||||
"""Build a new model instance."""
|
||||
|
||||
return cls(cfg)
|
||||
|
||||
def apply_mask(self, x, padding_mask):
|
||||
B, T, C = x.shape
|
||||
if self.mask_prob > 0:
|
||||
mask_indices = compute_mask_indices(
|
||||
(B, T),
|
||||
padding_mask,
|
||||
self.mask_prob,
|
||||
self.mask_length,
|
||||
self.mask_selection,
|
||||
self.mask_other,
|
||||
min_masks=2,
|
||||
no_overlap=self.no_mask_overlap,
|
||||
min_space=self.mask_min_space,
|
||||
)
|
||||
mask_indices = torch.from_numpy(mask_indices).to(x.device)
|
||||
x[mask_indices] = self.mask_emb
|
||||
else:
|
||||
mask_indices = None
|
||||
|
||||
if self.mask_channel_prob > 0:
|
||||
mask_channel_indices = compute_mask_indices(
|
||||
(B, C),
|
||||
None,
|
||||
self.mask_channel_prob,
|
||||
self.mask_channel_length,
|
||||
self.mask_channel_selection,
|
||||
self.mask_channel_other,
|
||||
no_overlap=self.no_mask_channel_overlap,
|
||||
min_space=self.mask_channel_min_space,
|
||||
)
|
||||
mask_channel_indices = (
|
||||
torch.from_numpy(mask_channel_indices)
|
||||
.to(x.device)
|
||||
.unsqueeze(1)
|
||||
.expand(-1, T, -1)
|
||||
)
|
||||
x[mask_channel_indices] = 0
|
||||
|
||||
return x, mask_indices
|
||||
|
||||
def sample_negatives(self, y, num):
|
||||
|
||||
if self.n_negatives == 0 and self.cross_sample_negatives == 0:
|
||||
return y.new(0)
|
||||
|
||||
bsz, tsz, fsz = y.shape
|
||||
y = y.view(-1, fsz) # BTC => (BxT)C
|
||||
|
||||
cross_high = tsz * bsz
|
||||
high = tsz
|
||||
with torch.no_grad():
|
||||
assert high > 1, f"{bsz,tsz,fsz}"
|
||||
|
||||
if self.n_negatives > 0:
|
||||
tszs = (
|
||||
buffered_arange(num)
|
||||
.unsqueeze(-1)
|
||||
.expand(-1, self.n_negatives)
|
||||
.flatten()
|
||||
)
|
||||
|
||||
neg_idxs = torch.randint(
|
||||
low=0, high=high - 1, size=(bsz, self.n_negatives * num)
|
||||
)
|
||||
neg_idxs[neg_idxs >= tszs] += 1
|
||||
|
||||
if self.cross_sample_negatives > 0:
|
||||
tszs = (
|
||||
buffered_arange(num)
|
||||
.unsqueeze(-1)
|
||||
.expand(-1, self.cross_sample_negatives)
|
||||
.flatten()
|
||||
)
|
||||
|
||||
cross_neg_idxs = torch.randint(
|
||||
low=0,
|
||||
high=cross_high - 1,
|
||||
size=(bsz, self.cross_sample_negatives * num),
|
||||
)
|
||||
cross_neg_idxs[cross_neg_idxs >= tszs] += 1
|
||||
|
||||
if self.n_negatives > 0:
|
||||
for i in range(1, bsz):
|
||||
neg_idxs[i] += i * high
|
||||
else:
|
||||
neg_idxs = cross_neg_idxs
|
||||
|
||||
if self.cross_sample_negatives > 0 and self.n_negatives > 0:
|
||||
neg_idxs = torch.cat([neg_idxs, cross_neg_idxs], dim=1)
|
||||
|
||||
negs = y[neg_idxs.view(-1)]
|
||||
negs = negs.view(
|
||||
bsz, num, self.n_negatives + self.cross_sample_negatives, fsz
|
||||
).permute(
|
||||
2, 0, 1, 3
|
||||
) # to NxBxTxC
|
||||
return negs, neg_idxs
|
||||
|
||||
def compute_preds(self, x, y, negatives):
|
||||
|
||||
neg_is_pos = (y == negatives).all(-1)
|
||||
y = y.unsqueeze(0)
|
||||
targets = torch.cat([y, negatives], dim=0)
|
||||
|
||||
logits = torch.cosine_similarity(x.float(), targets.float(), dim=-1).type_as(x)
|
||||
|
||||
logits /= self.logit_temp
|
||||
|
||||
if neg_is_pos.any():
|
||||
logits[1:][neg_is_pos] = float("-inf")
|
||||
|
||||
return logits
|
||||
|
||||
def _get_feat_extract_output_lengths(self, input_lengths: torch.LongTensor):
|
||||
"""
|
||||
Computes the output length of the convolutional layers
|
||||
"""
|
||||
|
||||
def _conv_out_length(input_length, kernel_size, stride):
|
||||
return torch.floor((input_length - kernel_size) / stride + 1)
|
||||
|
||||
conv_cfg_list = eval(self.cfg.conv_feature_layers)
|
||||
|
||||
for i in range(len(conv_cfg_list)):
|
||||
input_lengths = _conv_out_length(input_lengths, conv_cfg_list[i][1], conv_cfg_list[i][2])
|
||||
|
||||
return input_lengths.to(torch.long)
|
||||
|
||||
def forward(self, source, padding_mask=None, mask=True, features_only=False):
|
||||
|
||||
if self.feature_grad_mult > 0:
|
||||
features = self.feature_extractor(source)
|
||||
if self.feature_grad_mult != 1.0:
|
||||
features = GradMultiply.apply(features, self.feature_grad_mult)
|
||||
else:
|
||||
with torch.no_grad():
|
||||
features = self.feature_extractor(source)
|
||||
|
||||
features_pen = features.float().pow(2).mean()
|
||||
|
||||
features = features.transpose(1, 2)
|
||||
features = self.layer_norm(features)
|
||||
unmasked_features = features.clone()
|
||||
|
||||
if padding_mask is not None:
|
||||
input_lengths = (1 - padding_mask.long()).sum(-1)
|
||||
# apply conv formula to get real output_lengths
|
||||
output_lengths = self._get_feat_extract_output_lengths(input_lengths)
|
||||
|
||||
padding_mask = torch.zeros(
|
||||
features.shape[:2], dtype=features.dtype, device=features.device
|
||||
)
|
||||
|
||||
# these two operations makes sure that all values
|
||||
# before the output lengths indices are attended to
|
||||
padding_mask[(torch.arange(padding_mask.shape[0], device=padding_mask.device), output_lengths - 1)] = 1
|
||||
padding_mask = (1 - padding_mask.flip([-1]).cumsum(-1).flip([-1])).bool()
|
||||
|
||||
if self.post_extract_proj is not None:
|
||||
features = self.post_extract_proj(features)
|
||||
|
||||
features = self.dropout_input(features)
|
||||
unmasked_features = self.dropout_features(unmasked_features)
|
||||
|
||||
num_vars = None
|
||||
code_ppl = None
|
||||
prob_ppl = None
|
||||
curr_temp = None
|
||||
|
||||
if self.input_quantizer:
|
||||
q = self.input_quantizer(features, produce_targets=False)
|
||||
features = q["x"]
|
||||
num_vars = q["num_vars"]
|
||||
code_ppl = q["code_perplexity"]
|
||||
prob_ppl = q["prob_perplexity"]
|
||||
curr_temp = q["temp"]
|
||||
features = self.project_inp(features)
|
||||
|
||||
if mask:
|
||||
x, mask_indices = self.apply_mask(features, padding_mask)
|
||||
if mask_indices is not None:
|
||||
y = unmasked_features[mask_indices].view(
|
||||
unmasked_features.size(0), -1, unmasked_features.size(-1)
|
||||
)
|
||||
else:
|
||||
y = unmasked_features
|
||||
else:
|
||||
x = features
|
||||
y = unmasked_features
|
||||
mask_indices = None
|
||||
|
||||
x = self.encoder(x, padding_mask=padding_mask)
|
||||
|
||||
if features_only:
|
||||
return {"x": x, "padding_mask": padding_mask}
|
||||
|
||||
if self.quantizer:
|
||||
q = self.quantizer(y, produce_targets=False)
|
||||
y = q["x"]
|
||||
num_vars = q["num_vars"]
|
||||
code_ppl = q["code_perplexity"]
|
||||
prob_ppl = q["prob_perplexity"]
|
||||
curr_temp = q["temp"]
|
||||
|
||||
y = self.project_q(y)
|
||||
|
||||
if self.negatives_from_everywhere:
|
||||
neg_cands, *_ = self.quantizer(unmasked_features, produce_targets=False)
|
||||
negs, _ = self.sample_negatives(neg_cands, y.size(1))
|
||||
negs = self.project_q(negs)
|
||||
|
||||
else:
|
||||
negs, _ = self.sample_negatives(y, y.size(1))
|
||||
|
||||
if self.codebook_negatives > 0:
|
||||
cb_negs = self.quantizer.sample_from_codebook(
|
||||
y.size(0) * y.size(1), self.codebook_negatives
|
||||
)
|
||||
cb_negs = cb_negs.view(
|
||||
self.codebook_negatives, y.size(0), y.size(1), -1
|
||||
) # order doesnt matter
|
||||
cb_negs = self.project_q(cb_negs)
|
||||
negs = torch.cat([negs, cb_negs], dim=0)
|
||||
else:
|
||||
y = self.project_q(y)
|
||||
|
||||
if self.negatives_from_everywhere:
|
||||
negs, _ = self.sample_negatives(unmasked_features, y.size(1))
|
||||
negs = self.project_q(negs)
|
||||
else:
|
||||
negs, _ = self.sample_negatives(y, y.size(1))
|
||||
|
||||
x = x[mask_indices].view(x.size(0), -1, x.size(-1))
|
||||
|
||||
if self.target_glu:
|
||||
y = self.target_glu(y)
|
||||
negs = self.target_glu(negs)
|
||||
|
||||
x = self.final_proj(x)
|
||||
x = self.compute_preds(x, y, negs)
|
||||
|
||||
result = {"x": x, "padding_mask": padding_mask, "features_pen": features_pen}
|
||||
|
||||
if prob_ppl is not None:
|
||||
result["prob_perplexity"] = prob_ppl
|
||||
result["code_perplexity"] = code_ppl
|
||||
result["num_vars"] = num_vars
|
||||
result["temp"] = curr_temp
|
||||
|
||||
return result
|
||||
|
||||
def quantize(self, x):
|
||||
assert self.quantizer is not None
|
||||
x = self.feature_extractor(x)
|
||||
x = x.transpose(1, 2)
|
||||
x = self.layer_norm(x)
|
||||
return self.quantizer.forward_idx(x)
|
||||
|
||||
def extract_features(self, source, padding_mask, mask=False):
|
||||
res = self.forward(source, padding_mask, mask=mask, features_only=True)
|
||||
return res["x"], res["padding_mask"]
|
||||
|
||||
def get_logits(self, net_output):
|
||||
logits = net_output["x"]
|
||||
logits = logits.transpose(0, 2)
|
||||
logits = logits.reshape(-1, logits.size(-1))
|
||||
return logits
|
||||
|
||||
def get_targets(self, sample, net_output, expand_steps=True):
|
||||
x = net_output["x"]
|
||||
return x.new_zeros(x.size(1) * x.size(2), dtype=torch.long)
|
||||
|
||||
def get_extra_losses(self, net_output):
|
||||
pen = []
|
||||
|
||||
if "prob_perplexity" in net_output:
|
||||
pen.append(
|
||||
(net_output["num_vars"] - net_output["prob_perplexity"])
|
||||
/ net_output["num_vars"]
|
||||
)
|
||||
|
||||
if "features_pen" in net_output:
|
||||
pen.append(net_output["features_pen"])
|
||||
|
||||
return pen
|
||||
|
||||
def remove_pretraining_modules(self):
|
||||
self.quantizer = None
|
||||
self.project_q = None
|
||||
self.target_glu = None
|
||||
self.final_proj = None
|
||||
|
||||
|
||||
class ConvFeatureExtractionModel(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
conv_layers: List[Tuple[int, int, int]],
|
||||
dropout: float = 0.0,
|
||||
mode: str = "default",
|
||||
conv_bias: bool = False,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
assert mode in {"default", "layer_norm"}
|
||||
|
||||
def block(
|
||||
n_in,
|
||||
n_out,
|
||||
k,
|
||||
stride,
|
||||
is_layer_norm=False,
|
||||
is_group_norm=False,
|
||||
conv_bias=False,
|
||||
):
|
||||
def make_conv():
|
||||
conv = nn.Conv1d(n_in, n_out, k, stride=stride, bias=conv_bias)
|
||||
nn.init.kaiming_normal_(conv.weight)
|
||||
return conv
|
||||
|
||||
assert (
|
||||
is_layer_norm and is_group_norm
|
||||
) == False, "layer norm and group norm are exclusive"
|
||||
|
||||
if is_layer_norm:
|
||||
return nn.Sequential(
|
||||
make_conv(),
|
||||
nn.Dropout(p=dropout),
|
||||
nn.Sequential(
|
||||
TransposeLast(),
|
||||
Fp32LayerNorm(dim, elementwise_affine=True),
|
||||
TransposeLast(),
|
||||
),
|
||||
nn.GELU(),
|
||||
)
|
||||
elif is_group_norm:
|
||||
return nn.Sequential(
|
||||
make_conv(),
|
||||
nn.Dropout(p=dropout),
|
||||
Fp32GroupNorm(dim, dim, affine=True),
|
||||
nn.GELU(),
|
||||
)
|
||||
else:
|
||||
return nn.Sequential(make_conv(), nn.Dropout(p=dropout), nn.GELU())
|
||||
|
||||
in_d = 1
|
||||
self.conv_layers = nn.ModuleList()
|
||||
for i, cl in enumerate(conv_layers):
|
||||
assert len(cl) == 3, "invalid conv definition: " + str(cl)
|
||||
(dim, k, stride) = cl
|
||||
|
||||
self.conv_layers.append(
|
||||
block(
|
||||
in_d,
|
||||
dim,
|
||||
k,
|
||||
stride,
|
||||
is_layer_norm=mode == "layer_norm",
|
||||
is_group_norm=mode == "default" and i == 0,
|
||||
conv_bias=conv_bias,
|
||||
)
|
||||
)
|
||||
in_d = dim
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
# BxT -> BxCxT
|
||||
x = x.unsqueeze(1)
|
||||
|
||||
for conv in self.conv_layers:
|
||||
x = conv(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class TransformerEncoder(nn.Module):
|
||||
def __init__(self, args):
|
||||
super().__init__()
|
||||
|
||||
self.dropout = args.dropout
|
||||
self.embedding_dim = args.encoder_embed_dim
|
||||
|
||||
self.pos_conv = nn.Conv1d(
|
||||
self.embedding_dim,
|
||||
self.embedding_dim,
|
||||
kernel_size=args.conv_pos,
|
||||
padding=args.conv_pos // 2,
|
||||
groups=args.conv_pos_groups,
|
||||
)
|
||||
dropout = 0
|
||||
std = math.sqrt((4 * (1.0 - dropout)) / (args.conv_pos * self.embedding_dim))
|
||||
nn.init.normal_(self.pos_conv.weight, mean=0, std=std)
|
||||
nn.init.constant_(self.pos_conv.bias, 0)
|
||||
|
||||
self.pos_conv = nn.utils.weight_norm(self.pos_conv, name="weight", dim=2)
|
||||
self.pos_conv = nn.Sequential(self.pos_conv, SamePad(args.conv_pos), nn.GELU())
|
||||
|
||||
self.layers = nn.ModuleList(
|
||||
[
|
||||
TransformerSentenceEncoderLayer(
|
||||
embedding_dim=self.embedding_dim,
|
||||
ffn_embedding_dim=args.encoder_ffn_embed_dim,
|
||||
num_attention_heads=args.encoder_attention_heads,
|
||||
dropout=self.dropout,
|
||||
attention_dropout=args.attention_dropout,
|
||||
activation_dropout=args.activation_dropout,
|
||||
activation_fn=args.activation_fn,
|
||||
layer_norm_first=args.layer_norm_first,
|
||||
)
|
||||
for _ in range(args.encoder_layers)
|
||||
]
|
||||
)
|
||||
|
||||
self.layer_norm_first = args.layer_norm_first
|
||||
self.layer_norm = LayerNorm(self.embedding_dim)
|
||||
self.layerdrop = args.encoder_layerdrop
|
||||
|
||||
self.apply(init_bert_params)
|
||||
|
||||
def forward(self, x, padding_mask=None):
|
||||
x = self.extract_features(x, padding_mask)
|
||||
|
||||
if self.layer_norm_first:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
return x
|
||||
|
||||
def extract_features(self, x, padding_mask=None):
|
||||
|
||||
if padding_mask is not None:
|
||||
x[padding_mask] = 0
|
||||
|
||||
x_conv = self.pos_conv(x.transpose(1, 2))
|
||||
x_conv = x_conv.transpose(1, 2)
|
||||
x += x_conv
|
||||
|
||||
if not self.layer_norm_first:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
layer_results = []
|
||||
for i, layer in enumerate(self.layers):
|
||||
dropout_probability = np.random.random()
|
||||
if not self.training or (dropout_probability > self.layerdrop):
|
||||
x, z = layer(x, self_attn_padding_mask=padding_mask, need_weights=False)
|
||||
layer_results.append(x)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
return x
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the encoder."""
|
||||
return self.args.max_positions
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
"""Upgrade a (possibly old) state dict for new versions of fairseq."""
|
||||
return state_dict
|
||||
|
||||
|
||||
class TransformerSentenceEncoderLayer(nn.Module):
|
||||
"""
|
||||
Implements a Transformer Encoder Layer used in BERT/XLM style pre-trained
|
||||
models.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
embedding_dim: float = 768,
|
||||
ffn_embedding_dim: float = 3072,
|
||||
num_attention_heads: float = 8,
|
||||
dropout: float = 0.1,
|
||||
attention_dropout: float = 0.1,
|
||||
activation_dropout: float = 0.1,
|
||||
activation_fn: str = "relu",
|
||||
layer_norm_first: bool = False,
|
||||
) -> None:
|
||||
|
||||
super().__init__()
|
||||
# Initialize parameters
|
||||
self.embedding_dim = embedding_dim
|
||||
self.dropout = dropout
|
||||
self.activation_dropout = activation_dropout
|
||||
|
||||
# Initialize blocks
|
||||
self.activation_fn = utils.get_activation_fn(activation_fn)
|
||||
self.self_attn = MultiheadAttention(
|
||||
self.embedding_dim,
|
||||
num_attention_heads,
|
||||
dropout=attention_dropout,
|
||||
self_attention=True,
|
||||
)
|
||||
|
||||
self.dropout1 = nn.Dropout(dropout)
|
||||
self.dropout2 = nn.Dropout(self.activation_dropout)
|
||||
self.dropout3 = nn.Dropout(dropout)
|
||||
|
||||
self.layer_norm_first = layer_norm_first
|
||||
|
||||
# layer norm associated with the self attention layer
|
||||
self.self_attn_layer_norm = LayerNorm(self.embedding_dim)
|
||||
self.fc1 = nn.Linear(self.embedding_dim, ffn_embedding_dim)
|
||||
self.fc2 = nn.Linear(ffn_embedding_dim, self.embedding_dim)
|
||||
|
||||
# layer norm associated with the position wise feed-forward NN
|
||||
self.final_layer_norm = LayerNorm(self.embedding_dim)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
x: torch.Tensor,
|
||||
self_attn_mask: torch.Tensor = None,
|
||||
self_attn_padding_mask: torch.Tensor = None,
|
||||
need_weights: bool = False,
|
||||
att_args=None,
|
||||
):
|
||||
"""
|
||||
LayerNorm is applied either before or after the self-attention/ffn
|
||||
modules similar to the original Transformer imlementation.
|
||||
"""
|
||||
residual = x
|
||||
|
||||
if self.layer_norm_first:
|
||||
x = self.self_attn_layer_norm(x)
|
||||
x, attn = self.self_attn(
|
||||
query=x,
|
||||
key=x,
|
||||
value=x,
|
||||
key_padding_mask=self_attn_padding_mask,
|
||||
need_weights=False,
|
||||
attn_mask=self_attn_mask,
|
||||
)
|
||||
x = self.dropout1(x)
|
||||
x = residual + x
|
||||
|
||||
residual = x
|
||||
x = self.final_layer_norm(x)
|
||||
x = self.activation_fn(self.fc1(x))
|
||||
x = self.dropout2(x)
|
||||
x = self.fc2(x)
|
||||
x = self.dropout3(x)
|
||||
x = residual + x
|
||||
else:
|
||||
x, attn = self.self_attn(
|
||||
query=x,
|
||||
key=x,
|
||||
value=x,
|
||||
key_padding_mask=self_attn_padding_mask,
|
||||
need_weights=need_weights,
|
||||
)
|
||||
|
||||
x = self.dropout1(x)
|
||||
x = residual + x
|
||||
|
||||
x = self.self_attn_layer_norm(x)
|
||||
|
||||
residual = x
|
||||
x = self.activation_fn(self.fc1(x))
|
||||
x = self.dropout2(x)
|
||||
x = self.fc2(x)
|
||||
x = self.dropout3(x)
|
||||
x = residual + x
|
||||
x = self.final_layer_norm(x)
|
||||
|
||||
return x, attn
|
||||
@@ -0,0 +1,606 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from argparse import Namespace
|
||||
import contextlib
|
||||
import copy
|
||||
import math
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from dataclasses import dataclass, field
|
||||
from omegaconf import MISSING, II, open_dict
|
||||
from typing import Any
|
||||
|
||||
from fairseq import checkpoint_utils, tasks, utils
|
||||
from fairseq.dataclass import FairseqDataclass
|
||||
from fairseq.dataclass.utils import convert_namespace_to_omegaconf
|
||||
from fairseq.tasks import FairseqTask
|
||||
from fairseq.models import (
|
||||
BaseFairseqModel,
|
||||
FairseqEncoder,
|
||||
FairseqEncoderDecoderModel,
|
||||
FairseqIncrementalDecoder,
|
||||
register_model,
|
||||
)
|
||||
from fairseq.models.wav2vec.wav2vec2 import MASKING_DISTRIBUTION_CHOICES
|
||||
from fairseq.modules import LayerNorm, PositionalEmbedding, TransformerDecoderLayer
|
||||
|
||||
|
||||
@dataclass
|
||||
class Wav2Vec2AsrConfig(FairseqDataclass):
|
||||
w2v_path: str = field(
|
||||
default=MISSING, metadata={"help": "path to wav2vec 2.0 model"}
|
||||
)
|
||||
no_pretrained_weights: bool = field(
|
||||
default=False, metadata={"help": "if true, does not load pretrained weights"}
|
||||
)
|
||||
dropout_input: float = field(
|
||||
default=0.0,
|
||||
metadata={"help": "dropout to apply to the input (after feat extr)"},
|
||||
)
|
||||
final_dropout: float = field(
|
||||
default=0.0,
|
||||
metadata={"help": "dropout after transformer and before final projection"},
|
||||
)
|
||||
dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability inside wav2vec 2.0 model"}
|
||||
)
|
||||
attention_dropout: float = field(
|
||||
default=0.0,
|
||||
metadata={
|
||||
"help": "dropout probability for attention weights inside wav2vec 2.0 model"
|
||||
},
|
||||
)
|
||||
activation_dropout: float = field(
|
||||
default=0.0,
|
||||
metadata={
|
||||
"help": "dropout probability after activation in FFN inside wav2vec 2.0 model"
|
||||
},
|
||||
)
|
||||
|
||||
# masking
|
||||
apply_mask: bool = field(
|
||||
default=False, metadata={"help": "apply masking during fine-tuning"}
|
||||
)
|
||||
mask_length: int = field(
|
||||
default=10, metadata={"help": "repeat the mask indices multiple times"}
|
||||
)
|
||||
mask_prob: float = field(
|
||||
default=0.5,
|
||||
metadata={
|
||||
"help": "probability of replacing a token with mask (normalized by length)"
|
||||
},
|
||||
)
|
||||
mask_selection: MASKING_DISTRIBUTION_CHOICES = field(
|
||||
default="static", metadata={"help": "how to choose masks"}
|
||||
)
|
||||
mask_other: float = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "secondary mask argument (used for more complex distributions), "
|
||||
"see help in compute_mask_indices"
|
||||
},
|
||||
)
|
||||
no_mask_overlap: bool = field(
|
||||
default=False, metadata={"help": "whether to allow masks to overlap"}
|
||||
)
|
||||
|
||||
# channel masking
|
||||
mask_channel_length: int = field(
|
||||
default=10, metadata={"help": "length of the mask for features (channels)"}
|
||||
)
|
||||
mask_channel_prob: float = field(
|
||||
default=0.0, metadata={"help": "probability of replacing a feature with 0"}
|
||||
)
|
||||
mask_channel_selection: MASKING_DISTRIBUTION_CHOICES = field(
|
||||
default="static",
|
||||
metadata={"help": "how to choose mask length for channel masking"},
|
||||
)
|
||||
mask_channel_other: float = field(
|
||||
default=0,
|
||||
metadata={
|
||||
"help": "secondary mask argument (used for more complex distributions), "
|
||||
"see help in compute_mask_indicesh"
|
||||
},
|
||||
)
|
||||
no_mask_channel_overlap: bool = field(
|
||||
default=False, metadata={"help": "whether to allow channel masks to overlap"}
|
||||
)
|
||||
freeze_finetune_updates: int = field(
|
||||
default=0, metadata={"help": "dont finetune wav2vec for this many updates"}
|
||||
)
|
||||
feature_grad_mult: float = field(
|
||||
default=0.0, metadata={"help": "reset feature grad mult in wav2vec 2.0 to this"}
|
||||
)
|
||||
layerdrop: float = field(
|
||||
default=0.0, metadata={"help": "probability of dropping a layer in wav2vec 2.0"}
|
||||
)
|
||||
normalize: bool = II("task.normalize")
|
||||
data: str = II("task.data")
|
||||
# this holds the loaded wav2vec args
|
||||
w2v_args: Any = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Wav2Vec2CtcConfig(Wav2Vec2AsrConfig):
|
||||
pass
|
||||
|
||||
|
||||
@register_model("wav2vec_ctc", dataclass=Wav2Vec2CtcConfig)
|
||||
class Wav2VecCtc(BaseFairseqModel):
|
||||
def __init__(self, cfg: Wav2Vec2CtcConfig, w2v_encoder: BaseFairseqModel):
|
||||
super().__init__()
|
||||
self.cfg = cfg
|
||||
self.w2v_encoder = w2v_encoder
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
return state_dict
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, cfg: Wav2Vec2CtcConfig, task: FairseqTask):
|
||||
"""Build a new model instance."""
|
||||
w2v_encoder = Wav2VecEncoder(cfg, task.target_dictionary)
|
||||
return cls(cfg, w2v_encoder)
|
||||
|
||||
def get_normalized_probs(self, net_output, log_probs):
|
||||
"""Get normalized probabilities (or log probs) from a net's output."""
|
||||
|
||||
logits = net_output["encoder_out"]
|
||||
if log_probs:
|
||||
return utils.log_softmax(logits.float(), dim=-1)
|
||||
else:
|
||||
return utils.softmax(logits.float(), dim=-1)
|
||||
|
||||
def get_logits(self, net_output):
|
||||
logits = net_output["encoder_out"]
|
||||
padding = net_output["padding_mask"]
|
||||
if padding is not None and padding.any():
|
||||
padding = padding.T
|
||||
logits[padding][...,0] = 0
|
||||
logits[padding][...,1:] = float('-inf')
|
||||
|
||||
return logits
|
||||
|
||||
def forward(self, **kwargs):
|
||||
x = self.w2v_encoder(**kwargs)
|
||||
return x
|
||||
|
||||
|
||||
@dataclass
|
||||
class Wav2Vec2Seq2SeqConfig(Wav2Vec2AsrConfig):
|
||||
decoder_embed_dim: int = field(
|
||||
default=768, metadata={"help": "decoder embedding dimension"}
|
||||
)
|
||||
decoder_ffn_embed_dim: int = field(
|
||||
default=3072, metadata={"help": "decoder embedding dimension for FFN"}
|
||||
)
|
||||
decoder_layers: int = field(default=6, metadata={"help": "num of decoder layers"})
|
||||
decoder_layerdrop: float = field(
|
||||
default=0.0, metadata={"help": "decoder layerdrop chance"}
|
||||
)
|
||||
decoder_attention_heads: int = field(
|
||||
default=4, metadata={"help": "num decoder attention heads"}
|
||||
)
|
||||
decoder_learned_pos: bool = field(
|
||||
default=False,
|
||||
metadata={"help": "use learned positional embeddings in the decoder"},
|
||||
)
|
||||
decoder_normalize_before: bool = field(
|
||||
default=False, metadata={"help": "apply layernorm before each decoder block"}
|
||||
)
|
||||
no_token_positional_embeddings: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"help": "if set, disables positional embeddings (outside self attention)"
|
||||
},
|
||||
)
|
||||
decoder_dropout: float = field(
|
||||
default=0.0, metadata={"help": "dropout probability in the decoder"}
|
||||
)
|
||||
decoder_attention_dropout: float = field(
|
||||
default=0.0,
|
||||
metadata={
|
||||
"help": "dropout probability for attention weights inside the decoder"
|
||||
},
|
||||
)
|
||||
decoder_activation_dropout: float = field(
|
||||
default=0.0,
|
||||
metadata={
|
||||
"help": "dropout probability after activation in FFN inside the decoder"
|
||||
},
|
||||
)
|
||||
max_target_positions: int = field(
|
||||
default=2048, metadata={"help": "max target positions"}
|
||||
)
|
||||
share_decoder_input_output_embed: bool = field(
|
||||
default=False, metadata={"help": "share decoder input and output embeddings"}
|
||||
)
|
||||
autoregressive: bool = II("task.autoregressive")
|
||||
|
||||
|
||||
@register_model("wav2vec_seq2seq", dataclass=Wav2Vec2Seq2SeqConfig)
|
||||
class Wav2Vec2Seq2SeqModel(FairseqEncoderDecoderModel):
|
||||
def __init__(self, encoder, decoder):
|
||||
super().__init__(encoder, decoder)
|
||||
|
||||
@classmethod
|
||||
def build_model(cls, cfg: Wav2Vec2Seq2SeqConfig, task: FairseqTask):
|
||||
"""Build a new model instance."""
|
||||
|
||||
assert cfg.autoregressive, "Please set task.autoregressive=true for seq2seq asr models"
|
||||
|
||||
src_dict, tgt_dict = task.source_dictionary, task.target_dictionary
|
||||
|
||||
def build_embedding(dictionary, embed_dim):
|
||||
num_embeddings = len(dictionary)
|
||||
padding_idx = dictionary.pad()
|
||||
emb = Embedding(num_embeddings, embed_dim, padding_idx)
|
||||
return emb
|
||||
|
||||
decoder_embed_tokens = build_embedding(tgt_dict, cfg.decoder_embed_dim)
|
||||
|
||||
encoder = cls.build_encoder(cfg)
|
||||
decoder = cls.build_decoder(cfg, tgt_dict, decoder_embed_tokens)
|
||||
|
||||
return Wav2Vec2Seq2SeqModel(encoder, decoder)
|
||||
|
||||
@classmethod
|
||||
def build_encoder(cls, cfg: Wav2Vec2AsrConfig):
|
||||
return Wav2VecEncoder(cfg)
|
||||
|
||||
@classmethod
|
||||
def build_decoder(cls, cfg: Wav2Vec2Seq2SeqConfig, tgt_dict, embed_tokens):
|
||||
return TransformerDecoder(cfg, tgt_dict, embed_tokens)
|
||||
|
||||
def forward(self, **kwargs):
|
||||
encoder_out = self.encoder(tbc=False, **kwargs)
|
||||
decoder_out = self.decoder(encoder_out=encoder_out, **kwargs)
|
||||
return decoder_out
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
super().upgrade_state_dict_named(state_dict, name)
|
||||
return state_dict
|
||||
|
||||
|
||||
class Wav2VecEncoder(FairseqEncoder):
|
||||
def __init__(self, cfg: Wav2Vec2AsrConfig, tgt_dict=None):
|
||||
self.apply_mask = cfg.apply_mask
|
||||
|
||||
arg_overrides = {
|
||||
"dropout": cfg.dropout,
|
||||
"activation_dropout": cfg.activation_dropout,
|
||||
"dropout_input": cfg.dropout_input,
|
||||
"attention_dropout": cfg.attention_dropout,
|
||||
"mask_length": cfg.mask_length,
|
||||
"mask_prob": cfg.mask_prob,
|
||||
"mask_selection": cfg.mask_selection,
|
||||
"mask_other": cfg.mask_other,
|
||||
"no_mask_overlap": cfg.no_mask_overlap,
|
||||
"mask_channel_length": cfg.mask_channel_length,
|
||||
"mask_channel_prob": cfg.mask_channel_prob,
|
||||
"mask_channel_selection": cfg.mask_channel_selection,
|
||||
"mask_channel_other": cfg.mask_channel_other,
|
||||
"no_mask_channel_overlap": cfg.no_mask_channel_overlap,
|
||||
"encoder_layerdrop": cfg.layerdrop,
|
||||
"feature_grad_mult": cfg.feature_grad_mult,
|
||||
}
|
||||
|
||||
if cfg.w2v_args is None:
|
||||
state = checkpoint_utils.load_checkpoint_to_cpu(cfg.w2v_path, arg_overrides)
|
||||
w2v_args = state.get("cfg", None)
|
||||
if w2v_args is None:
|
||||
w2v_args = convert_namespace_to_omegaconf(state["args"])
|
||||
cfg.w2v_args = w2v_args
|
||||
else:
|
||||
state = None
|
||||
w2v_args = cfg.w2v_args
|
||||
if isinstance(w2v_args, Namespace):
|
||||
cfg.w2v_args = w2v_args = convert_namespace_to_omegaconf(w2v_args)
|
||||
|
||||
assert cfg.normalize == w2v_args.task.normalize, (
|
||||
"Fine-tuning works best when data normalization is the same. "
|
||||
"Please check that --normalize is set or unset for both pre-training and here"
|
||||
)
|
||||
|
||||
w2v_args.task.data = cfg.data
|
||||
task = tasks.setup_task(w2v_args.task)
|
||||
model = task.build_model(w2v_args.model)
|
||||
|
||||
if state is not None and not cfg.no_pretrained_weights:
|
||||
model.load_state_dict(state["model"], strict=True)
|
||||
|
||||
model.remove_pretraining_modules()
|
||||
|
||||
super().__init__(task.source_dictionary)
|
||||
|
||||
d = w2v_args.model.encoder_embed_dim
|
||||
|
||||
self.w2v_model = model
|
||||
|
||||
self.final_dropout = nn.Dropout(cfg.final_dropout)
|
||||
self.freeze_finetune_updates = cfg.freeze_finetune_updates
|
||||
self.num_updates = 0
|
||||
|
||||
if tgt_dict is not None:
|
||||
self.proj = Linear(d, len(tgt_dict))
|
||||
elif getattr(cfg, "decoder_embed_dim", d) != d:
|
||||
self.proj = Linear(d, cfg.decoder_embed_dim)
|
||||
else:
|
||||
self.proj = None
|
||||
|
||||
def set_num_updates(self, num_updates):
|
||||
"""Set the number of parameters updates."""
|
||||
super().set_num_updates(num_updates)
|
||||
self.num_updates = num_updates
|
||||
|
||||
def forward(self, source, padding_mask, tbc=True, **kwargs):
|
||||
|
||||
w2v_args = {
|
||||
"source": source,
|
||||
"padding_mask": padding_mask,
|
||||
"mask": self.apply_mask and self.training,
|
||||
}
|
||||
|
||||
ft = self.freeze_finetune_updates <= self.num_updates
|
||||
|
||||
with torch.no_grad() if not ft else contextlib.ExitStack():
|
||||
x, padding_mask = self.w2v_model.extract_features(**w2v_args)
|
||||
|
||||
if tbc:
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
x = self.final_dropout(x)
|
||||
|
||||
if self.proj:
|
||||
x = self.proj(x)
|
||||
|
||||
return {
|
||||
"encoder_out": x, # T x B x C
|
||||
"encoder_padding_mask": padding_mask.transpose(0, 1), # T x B
|
||||
"padding_mask": padding_mask,
|
||||
}
|
||||
|
||||
def reorder_encoder_out(self, encoder_out, new_order):
|
||||
if encoder_out["encoder_out"] is not None:
|
||||
encoder_out["encoder_out"] = encoder_out["encoder_out"].index_select(
|
||||
1, new_order
|
||||
)
|
||||
if encoder_out["encoder_padding_mask"] is not None:
|
||||
encoder_out["encoder_padding_mask"] = encoder_out[
|
||||
"encoder_padding_mask"
|
||||
].index_select(0, new_order)
|
||||
return encoder_out
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum input length supported by the encoder."""
|
||||
return None
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
return state_dict
|
||||
|
||||
|
||||
class TransformerDecoder(FairseqIncrementalDecoder):
|
||||
"""
|
||||
Transformer decoder consisting of *args.decoder_layers* layers. Each layer
|
||||
is a :class:`TransformerDecoderLayer`.
|
||||
|
||||
Args:
|
||||
args (argparse.Namespace): parsed command-line arguments
|
||||
dictionary (~fairseq.data.Dictionary): decoding dictionary
|
||||
embed_tokens (torch.nn.Embedding): output embedding
|
||||
no_encoder_attn (bool, optional): whether to attend to encoder outputs
|
||||
(default: False).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
cfg: Wav2Vec2Seq2SeqConfig,
|
||||
dictionary,
|
||||
embed_tokens,
|
||||
no_encoder_attn=False,
|
||||
):
|
||||
super().__init__(dictionary)
|
||||
|
||||
self.dropout = cfg.decoder_dropout
|
||||
self.share_input_output_embed = cfg.share_decoder_input_output_embed
|
||||
|
||||
input_embed_dim = embed_tokens.embedding_dim
|
||||
embed_dim = cfg.decoder_embed_dim
|
||||
self.output_embed_dim = cfg.decoder_embed_dim
|
||||
|
||||
self.layerdrop = cfg.decoder_layerdrop
|
||||
|
||||
padding_idx = embed_tokens.padding_idx
|
||||
self.max_target_positions = cfg.max_target_positions
|
||||
|
||||
self.embed_tokens = embed_tokens
|
||||
self.embed_scale = math.sqrt(embed_dim) # todo: try with input_embed_dim
|
||||
|
||||
self.project_in_dim = (
|
||||
Linear(input_embed_dim, embed_dim, bias=False)
|
||||
if embed_dim != input_embed_dim
|
||||
else None
|
||||
)
|
||||
|
||||
self.embed_positions = (
|
||||
PositionalEmbedding(
|
||||
cfg.max_target_positions,
|
||||
embed_dim,
|
||||
padding_idx,
|
||||
learned=cfg.decoder_learned_pos,
|
||||
)
|
||||
if not cfg.no_token_positional_embeddings
|
||||
else None
|
||||
)
|
||||
|
||||
# TODO: update this when transformer gets converted to dataclass configs
|
||||
transformer_cfg = copy.deepcopy(cfg)
|
||||
with open_dict(transformer_cfg):
|
||||
transformer_cfg.dropout = transformer_cfg.decoder_dropout
|
||||
transformer_cfg.attention_dropout = (
|
||||
transformer_cfg.decoder_attention_dropout
|
||||
)
|
||||
transformer_cfg.activation_dropout = (
|
||||
transformer_cfg.decoder_activation_dropout
|
||||
)
|
||||
|
||||
self.layers = nn.ModuleList([])
|
||||
self.layers.extend(
|
||||
[
|
||||
TransformerDecoderLayer(transformer_cfg, no_encoder_attn)
|
||||
for _ in range(transformer_cfg.decoder_layers)
|
||||
]
|
||||
)
|
||||
|
||||
if not self.share_input_output_embed:
|
||||
self.embed_out = nn.Parameter(
|
||||
torch.Tensor(len(dictionary), self.output_embed_dim)
|
||||
)
|
||||
nn.init.normal_(self.embed_out, mean=0, std=self.output_embed_dim ** -0.5)
|
||||
|
||||
if transformer_cfg.decoder_normalize_before:
|
||||
self.layer_norm = LayerNorm(embed_dim)
|
||||
else:
|
||||
self.layer_norm = None
|
||||
|
||||
def forward(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **unused
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
prev_output_tokens (LongTensor): previous decoder outputs of shape
|
||||
`(batch, tgt_len)`, for teacher forcing
|
||||
encoder_out (Tensor, optional): output from the encoder, used for
|
||||
encoder-side attention
|
||||
incremental_state (dict): dictionary used for storing state during
|
||||
:ref:`Incremental decoding`
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
prev_output_tokens = prev_output_tokens.long()
|
||||
x, extra = self.extract_features(
|
||||
prev_output_tokens, encoder_out, incremental_state
|
||||
)
|
||||
x = self.output_layer(x)
|
||||
return x, extra
|
||||
|
||||
def extract_features(
|
||||
self, prev_output_tokens, encoder_out=None, incremental_state=None, **unused
|
||||
):
|
||||
"""
|
||||
Similar to *forward* but only return features.
|
||||
|
||||
Returns:
|
||||
tuple:
|
||||
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
||||
- a dictionary with any model-specific outputs
|
||||
"""
|
||||
|
||||
# embed positions
|
||||
positions = (
|
||||
self.embed_positions(
|
||||
prev_output_tokens, incremental_state=incremental_state
|
||||
)
|
||||
if self.embed_positions is not None
|
||||
else None
|
||||
)
|
||||
|
||||
if incremental_state is not None:
|
||||
prev_output_tokens = prev_output_tokens[:, -1:]
|
||||
if positions is not None:
|
||||
positions = positions[:, -1:]
|
||||
|
||||
# embed tokens and positions
|
||||
x = self.embed_scale * self.embed_tokens(prev_output_tokens)
|
||||
|
||||
if self.project_in_dim is not None:
|
||||
x = self.project_in_dim(x)
|
||||
|
||||
if positions is not None:
|
||||
x += positions
|
||||
x = F.dropout(x, p=self.dropout, training=self.training)
|
||||
|
||||
# B x T x C -> T x B x C
|
||||
x = x.transpose(0, 1)
|
||||
attn = None
|
||||
|
||||
inner_states = [x]
|
||||
|
||||
# decoder layers
|
||||
for layer in self.layers:
|
||||
dropout_probability = np.random.random()
|
||||
if not self.training or (dropout_probability > self.layerdrop):
|
||||
x, attn, _ = layer(
|
||||
x,
|
||||
encoder_out["encoder_out"] if encoder_out is not None else None,
|
||||
encoder_out["padding_mask"]
|
||||
if encoder_out is not None
|
||||
else None,
|
||||
incremental_state,
|
||||
self_attn_mask=self.buffered_future_mask(x)
|
||||
if incremental_state is None
|
||||
else None,
|
||||
)
|
||||
inner_states.append(x)
|
||||
|
||||
if self.layer_norm:
|
||||
x = self.layer_norm(x)
|
||||
|
||||
# T x B x C -> B x T x C
|
||||
x = x.transpose(0, 1)
|
||||
|
||||
return x, {"attn": attn, "inner_states": inner_states}
|
||||
|
||||
def output_layer(self, features, **kwargs):
|
||||
"""Project features to the vocabulary size."""
|
||||
# project back to size of vocabulary
|
||||
if self.share_input_output_embed:
|
||||
return F.linear(features, self.embed_tokens.weight)
|
||||
else:
|
||||
return F.linear(features, self.embed_out)
|
||||
|
||||
def max_positions(self):
|
||||
"""Maximum output length supported by the decoder."""
|
||||
if self.embed_positions is None:
|
||||
return self.max_target_positions
|
||||
return min(self.max_target_positions, self.embed_positions.max_positions)
|
||||
|
||||
def buffered_future_mask(self, tensor):
|
||||
dim = tensor.size(0)
|
||||
if (
|
||||
not hasattr(self, "_future_mask")
|
||||
or self._future_mask is None
|
||||
or self._future_mask.device != tensor.device
|
||||
or self._future_mask.size(0) < dim
|
||||
):
|
||||
self._future_mask = torch.triu(
|
||||
utils.fill_with_neg_inf(tensor.new(dim, dim)), 1
|
||||
)
|
||||
return self._future_mask[:dim, :dim]
|
||||
|
||||
def upgrade_state_dict_named(self, state_dict, name):
|
||||
return state_dict
|
||||
|
||||
|
||||
def Embedding(num_embeddings, embedding_dim, padding_idx):
|
||||
m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
|
||||
nn.init.normal_(m.weight, mean=0, std=embedding_dim ** -0.5)
|
||||
nn.init.constant_(m.weight[padding_idx], 0)
|
||||
return m
|
||||
|
||||
|
||||
def Linear(in_features, out_features, bias=True):
|
||||
m = nn.Linear(in_features, out_features, bias)
|
||||
nn.init.xavier_uniform_(m.weight)
|
||||
if bias:
|
||||
nn.init.constant_(m.bias, 0.0)
|
||||
return m
|
||||
Reference in New Issue
Block a user