sonithoiningombam commited on
Commit
99b970a
·
verified ·
1 Parent(s): 376f14d

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. data/fairseq/data-bin/preprocess.log +1 -0
  2. data/fairseq/docs/Makefile +20 -0
  3. data/fairseq/docs/command_line_tools.rst +85 -0
  4. data/fairseq/docs/conf.py +98 -0
  5. data/fairseq/docs/criterions.rst +31 -0
  6. data/fairseq/docs/data.rst +58 -0
  7. data/fairseq/docs/docutils.conf +2 -0
  8. data/fairseq/docs/fairseq_logo.png +0 -0
  9. data/fairseq/docs/getting_started.rst +230 -0
  10. data/fairseq/docs/hydra_integration.md +284 -0
  11. data/fairseq/docs/index.rst +49 -0
  12. data/fairseq/docs/lr_scheduler.rst +34 -0
  13. data/fairseq/docs/make.bat +36 -0
  14. data/fairseq/docs/models.rst +104 -0
  15. data/fairseq/docs/modules.rst +9 -0
  16. data/fairseq/docs/optim.rst +38 -0
  17. data/fairseq/docs/overview.rst +74 -0
  18. data/fairseq/docs/tasks.rst +61 -0
  19. data/fairseq/docs/tutorial_classifying_names.rst +415 -0
  20. data/fairseq/docs/tutorial_simple_lstm.rst +518 -0
  21. data/fairseq/scripts/constraints/validate.py +34 -0
  22. data/fairseq/tests/__init__.py +0 -0
  23. data/fairseq/tests/test_activation_checkpointing.py +79 -0
  24. data/fairseq/tests/test_amp_optimizer.py +75 -0
  25. data/fairseq/tests/test_average_checkpoints.py +134 -0
  26. data/fairseq/tests/test_backtranslation_dataset.py +123 -0
  27. data/fairseq/tests/test_binaries.py +1915 -0
  28. data/fairseq/tests/test_binarizer.py +122 -0
  29. data/fairseq/tests/test_character_token_embedder.py +48 -0
  30. data/fairseq/tests/test_checkpoint_utils.py +125 -0
  31. data/fairseq/tests/test_checkpoint_utils_for_task_level_attributes.py +172 -0
  32. data/fairseq/tests/test_concat_dataset.py +58 -0
  33. data/fairseq/tests/test_dataset.py +66 -0
  34. data/fairseq/tests/test_ema.py +275 -0
  35. data/fairseq/tests/test_export.py +120 -0
  36. data/fairseq/tests/test_hf_hub.py +29 -0
  37. data/fairseq/tests/test_iopath.py +28 -0
  38. data/fairseq/tests/test_lstm_jitable.py +115 -0
  39. data/fairseq/tests/test_multi_corpus_dataset.py +82 -0
  40. data/fairseq/tests/test_multi_corpus_sampled_dataset.py +95 -0
  41. data/fairseq/tests/test_multihead_attention.py +488 -0
  42. data/fairseq/tests/test_positional_encoding.py +63 -0
  43. data/fairseq/tests/test_reproducibility.py +148 -0
  44. data/fairseq/tests/test_resampling_dataset.py +103 -0
  45. data/fairseq/tests/test_rotary_positional_embedding.py +85 -0
  46. data/fairseq/tests/test_sequence_generator.py +744 -0
  47. data/fairseq/tests/test_sequence_scorer.py +120 -0
  48. data/fairseq/tests/test_sparse_multihead_attention.py +114 -0
  49. data/fairseq/tests/test_token_block_dataset.py +92 -0
  50. data/fairseq/tests/test_transformer.py +65 -0
data/fairseq/data-bin/preprocess.log ADDED
@@ -0,0 +1 @@
 
 
1
+ Namespace(aim_repo=None, aim_run_hash=None, align_suffix=None, alignfile=None, all_gather_list_size=16384, amp=False, amp_batch_retries=2, amp_init_scale=128, amp_scale_window=None, azureml_logging=False, bf16=False, bpe=None, cpu=False, criterion='cross_entropy', dataset_impl='mmap', destdir='data-bin', dict_only=False, empty_cache_freq=0, fp16=False, fp16_init_scale=128, fp16_no_flatten_grads=False, fp16_scale_tolerance=0.0, fp16_scale_window=None, joined_dictionary=False, log_file=None, log_format=None, log_interval=100, lr_scheduler='fixed', memory_efficient_bf16=False, memory_efficient_fp16=False, min_loss_scale=0.0001, model_parallel_size=1, no_progress_bar=False, nwordssrc=-1, nwordstgt=-1, on_cpu_convert_precision=False, only_source=False, optimizer=None, padding_factor=8, plasma_path='/tmp/plasma', profile=False, quantization_config_path=None, reset_logging=False, scoring='bleu', seed=1, source_lang='eng_Latn', srcdict=None, suppress_crashes=False, target_lang='mni_Beng', task='translation', tensorboard_logdir=None, testpref='new/data/test', tgtdict=None, threshold_loss_scale=None, thresholdsrc=0, thresholdtgt=0, tokenizer=None, tpu=False, trainpref='new/data/train', use_plasma_view=False, user_dir=None, validpref='new/data/dev', wandb_project=None, workers=1)
data/fairseq/docs/Makefile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line.
5
+ SPHINXOPTS =
6
+ SPHINXBUILD = python -msphinx
7
+ SPHINXPROJ = fairseq
8
+ SOURCEDIR = .
9
+ BUILDDIR = _build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ # Catch-all target: route all unknown targets to Sphinx using the new
18
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19
+ %: Makefile
20
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
data/fairseq/docs/command_line_tools.rst ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. _Command-line Tools:
2
+
3
+ Command-line Tools
4
+ ==================
5
+
6
+ Fairseq provides several command-line tools for training and evaluating models:
7
+
8
+ - :ref:`fairseq-preprocess`: Data pre-processing: build vocabularies and binarize training data
9
+ - :ref:`fairseq-train`: Train a new model on one or multiple GPUs
10
+ - :ref:`fairseq-generate`: Translate pre-processed data with a trained model
11
+ - :ref:`fairseq-interactive`: Translate raw text with a trained model
12
+ - :ref:`fairseq-score`: BLEU scoring of generated translations against reference translations
13
+ - :ref:`fairseq-eval-lm`: Language model evaluation
14
+
15
+
16
+ .. _fairseq-preprocess:
17
+
18
+ fairseq-preprocess
19
+ ~~~~~~~~~~~~~~~~~~
20
+ .. automodule:: fairseq_cli.preprocess
21
+
22
+ .. argparse::
23
+ :module: fairseq.options
24
+ :func: get_preprocessing_parser
25
+ :prog: fairseq-preprocess
26
+
27
+
28
+ .. _fairseq-train:
29
+
30
+ fairseq-train
31
+ ~~~~~~~~~~~~~
32
+ .. automodule:: fairseq_cli.train
33
+
34
+ .. argparse::
35
+ :module: fairseq.options
36
+ :func: get_training_parser
37
+ :prog: fairseq-train
38
+
39
+
40
+ .. _fairseq-generate:
41
+
42
+ fairseq-generate
43
+ ~~~~~~~~~~~~~~~~
44
+ .. automodule:: fairseq_cli.generate
45
+
46
+ .. argparse::
47
+ :module: fairseq.options
48
+ :func: get_generation_parser
49
+ :prog: fairseq-generate
50
+
51
+
52
+ .. _fairseq-interactive:
53
+
54
+ fairseq-interactive
55
+ ~~~~~~~~~~~~~~~~~~~
56
+ .. automodule:: fairseq_cli.interactive
57
+
58
+ .. argparse::
59
+ :module: fairseq.options
60
+ :func: get_interactive_generation_parser
61
+ :prog: fairseq-interactive
62
+
63
+
64
+ .. _fairseq-score:
65
+
66
+ fairseq-score
67
+ ~~~~~~~~~~~~~
68
+ .. automodule:: fairseq_cli.score
69
+
70
+ .. argparse::
71
+ :module: fairseq_cli.score
72
+ :func: get_parser
73
+ :prog: fairseq-score
74
+
75
+
76
+ .. _fairseq-eval-lm:
77
+
78
+ fairseq-eval-lm
79
+ ~~~~~~~~~~~~~~~
80
+ .. automodule:: fairseq_cli.eval_lm
81
+
82
+ .. argparse::
83
+ :module: fairseq.options
84
+ :func: get_eval_lm_parser
85
+ :prog: fairseq-eval-lm
data/fairseq/docs/conf.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # fairseq documentation build configuration file, created by
5
+ # sphinx-quickstart on Fri Aug 17 21:45:30 2018.
6
+ #
7
+ # This file is execfile()d with the current directory set to its
8
+ # containing dir.
9
+ #
10
+ # Note that not all possible configuration values are present in this
11
+ # autogenerated file.
12
+ #
13
+ # All configuration values have a default; values that are commented out
14
+ # serve to show the default.
15
+
16
+ # If extensions (or modules to document with autodoc) are in another directory,
17
+ # add these directories to sys.path here. If the directory is relative to the
18
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
19
+
20
+ import os
21
+ import sys
22
+ from fairseq import __version__
23
+
24
+
25
+ # source code directory, relative to this file, for sphinx-autobuild
26
+ sys.path.insert(0, os.path.abspath(".."))
27
+
28
+ source_suffix = [".rst"]
29
+
30
+ # -- General configuration ------------------------------------------------
31
+
32
+ # If your documentation needs a minimal Sphinx version, state it here.
33
+ #
34
+ # needs_sphinx = '1.0'
35
+
36
+ # Add any Sphinx extension module names here, as strings. They can be
37
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
38
+ # ones.
39
+ extensions = [
40
+ "sphinx.ext.autodoc",
41
+ "sphinx.ext.intersphinx",
42
+ "sphinx.ext.viewcode",
43
+ "sphinx.ext.napoleon",
44
+ "sphinxarg.ext",
45
+ ]
46
+
47
+ # Add any paths that contain templates here, relative to this directory.
48
+ templates_path = ["_templates"]
49
+
50
+ # The master toctree document.
51
+ master_doc = "index"
52
+
53
+ # General information about the project.
54
+ project = "fairseq"
55
+ copyright = "Facebook AI Research (FAIR)"
56
+ author = "Facebook AI Research (FAIR)"
57
+
58
+ github_doc_root = "https://github.com/pytorch/fairseq/tree/main/docs/"
59
+
60
+ # The version info for the project you're documenting, acts as replacement for
61
+ # |version| and |release|, also used in various other places throughout the
62
+ # built documents.
63
+ #
64
+ # The short X.Y version.
65
+ version = __version__
66
+ # The full version, including alpha/beta/rc tags.
67
+ release = __version__
68
+
69
+ # The language for content autogenerated by Sphinx. Refer to documentation
70
+ # for a list of supported languages.
71
+ #
72
+ # This is also used if you do content translation via gettext catalogs.
73
+ # Usually you set "language" from the command line for these cases.
74
+ language = None
75
+
76
+ # List of patterns, relative to source directory, that match files and
77
+ # directories to ignore when looking for source files.
78
+ # This patterns also effect to html_static_path and html_extra_path
79
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
80
+
81
+ # The name of the Pygments (syntax highlighting) style to use.
82
+ pygments_style = "sphinx"
83
+ highlight_language = "python"
84
+
85
+ # If true, `todo` and `todoList` produce output, else they produce nothing.
86
+ todo_include_todos = False
87
+
88
+
89
+ # -- Options for HTML output ----------------------------------------------
90
+
91
+ html_theme = "classic"
92
+
93
+ # Example configuration for intersphinx: refer to the Python standard library.
94
+ intersphinx_mapping = {
95
+ "numpy": ("http://docs.scipy.org/doc/numpy/", None),
96
+ "python": ("https://docs.python.org/", None),
97
+ "torch": ("https://pytorch.org/docs/master/", None),
98
+ }
data/fairseq/docs/criterions.rst ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. _Criterions:
5
+
6
+ Criterions
7
+ ==========
8
+
9
+ Criterions compute the loss function given the model and batch, roughly::
10
+
11
+ loss = criterion(model, batch)
12
+
13
+ .. automodule:: fairseq.criterions
14
+ :members:
15
+
16
+ .. autoclass:: fairseq.criterions.FairseqCriterion
17
+ :members:
18
+ :undoc-members:
19
+
20
+ .. autoclass:: fairseq.criterions.adaptive_loss.AdaptiveLoss
21
+ :members:
22
+ :undoc-members:
23
+ .. autoclass:: fairseq.criterions.composite_loss.CompositeLoss
24
+ :members:
25
+ :undoc-members:
26
+ .. autoclass:: fairseq.criterions.cross_entropy.CrossEntropyCriterion
27
+ :members:
28
+ :undoc-members:
29
+ .. autoclass:: fairseq.criterions.label_smoothed_cross_entropy.LabelSmoothedCrossEntropyCriterion
30
+ :members:
31
+ :undoc-members:
data/fairseq/docs/data.rst ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. module:: fairseq.data
5
+
6
+ Data Loading and Utilities
7
+ ==========================
8
+
9
+ .. _datasets:
10
+
11
+ Datasets
12
+ --------
13
+
14
+ **Datasets** define the data format and provide helpers for creating
15
+ mini-batches.
16
+
17
+ .. autoclass:: fairseq.data.FairseqDataset
18
+ :members:
19
+ .. autoclass:: fairseq.data.LanguagePairDataset
20
+ :members:
21
+ .. autoclass:: fairseq.data.MonolingualDataset
22
+ :members:
23
+
24
+ **Helper Datasets**
25
+
26
+ These datasets wrap other :class:`fairseq.data.FairseqDataset` instances and
27
+ provide additional functionality:
28
+
29
+ .. autoclass:: fairseq.data.BacktranslationDataset
30
+ :members:
31
+ .. autoclass:: fairseq.data.ConcatDataset
32
+ :members:
33
+ .. autoclass:: fairseq.data.ResamplingDataset
34
+ :members:
35
+ .. autoclass:: fairseq.data.RoundRobinZipDatasets
36
+ :members:
37
+ .. autoclass:: fairseq.data.TransformEosDataset
38
+ :members:
39
+
40
+
41
+ Dictionary
42
+ ----------
43
+
44
+ .. autoclass:: fairseq.data.Dictionary
45
+ :members:
46
+
47
+
48
+ Iterators
49
+ ---------
50
+
51
+ .. autoclass:: fairseq.data.CountingIterator
52
+ :members:
53
+ .. autoclass:: fairseq.data.EpochBatchIterator
54
+ :members:
55
+ .. autoclass:: fairseq.data.GroupedIterator
56
+ :members:
57
+ .. autoclass:: fairseq.data.ShardedIterator
58
+ :members:
data/fairseq/docs/docutils.conf ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [writers]
2
+ option-limit=0
data/fairseq/docs/fairseq_logo.png ADDED
data/fairseq/docs/getting_started.rst ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Evaluating Pre-trained Models
2
+ =============================
3
+
4
+ First, download a pre-trained model along with its vocabularies:
5
+
6
+ .. code-block:: console
7
+
8
+ > curl https://dl.fbaipublicfiles.com/fairseq/models/wmt14.v2.en-fr.fconv-py.tar.bz2 | tar xvjf -
9
+
10
+ This model uses a `Byte Pair Encoding (BPE)
11
+ vocabulary <https://arxiv.org/abs/1508.07909>`__, so we'll have to apply
12
+ the encoding to the source text before it can be translated. This can be
13
+ done with the
14
+ `apply\_bpe.py <https://github.com/rsennrich/subword-nmt/blob/master/subword_nmt/apply_bpe.py>`__
15
+ script using the ``wmt14.en-fr.fconv-cuda/bpecodes`` file. ``@@`` is
16
+ used as a continuation marker and the original text can be easily
17
+ recovered with e.g. ``sed s/@@ //g`` or by passing the ``--remove-bpe``
18
+ flag to :ref:`fairseq-generate`. Prior to BPE, input text needs to be tokenized
19
+ using ``tokenizer.perl`` from
20
+ `mosesdecoder <https://github.com/moses-smt/mosesdecoder>`__.
21
+
22
+ Let's use :ref:`fairseq-interactive` to generate translations interactively.
23
+ Here, we use a beam size of 5 and preprocess the input with the Moses
24
+ tokenizer and the given Byte-Pair Encoding vocabulary. It will automatically
25
+ remove the BPE continuation markers and detokenize the output.
26
+
27
+ .. code-block:: console
28
+
29
+ > MODEL_DIR=wmt14.en-fr.fconv-py
30
+ > fairseq-interactive \
31
+ --path $MODEL_DIR/model.pt $MODEL_DIR \
32
+ --beam 5 --source-lang en --target-lang fr \
33
+ --tokenizer moses \
34
+ --bpe subword_nmt --bpe-codes $MODEL_DIR/bpecodes
35
+ | loading model(s) from wmt14.en-fr.fconv-py/model.pt
36
+ | [en] dictionary: 44206 types
37
+ | [fr] dictionary: 44463 types
38
+ | Type the input sentence and press return:
39
+ Why is it rare to discover new marine mammal species?
40
+ S-0 Why is it rare to discover new marine mam@@ mal species ?
41
+ H-0 -0.0643349438905716 Pourquoi est-il rare de découvrir de nouvelles espèces de mammifères marins?
42
+ P-0 -0.0763 -0.1849 -0.0956 -0.0946 -0.0735 -0.1150 -0.1301 -0.0042 -0.0321 -0.0171 -0.0052 -0.0062 -0.0015
43
+
44
+ This generation script produces three types of outputs: a line prefixed
45
+ with *O* is a copy of the original source sentence; *H* is the
46
+ hypothesis along with an average log-likelihood; and *P* is the
47
+ positional score per token position, including the
48
+ end-of-sentence marker which is omitted from the text.
49
+
50
+ Other types of output lines you might see are *D*, the detokenized hypothesis,
51
+ *T*, the reference target, *A*, alignment info, *E* the history of generation steps.
52
+
53
+ See the `README <https://github.com/pytorch/fairseq#pre-trained-models>`__ for a
54
+ full list of pre-trained models available.
55
+
56
+ Training a New Model
57
+ ====================
58
+
59
+ The following tutorial is for machine translation. For an example of how
60
+ to use Fairseq for other tasks, such as :ref:`language modeling`, please see the
61
+ ``examples/`` directory.
62
+
63
+ Data Pre-processing
64
+ -------------------
65
+
66
+ Fairseq contains example pre-processing scripts for several translation
67
+ datasets: IWSLT 2014 (German-English), WMT 2014 (English-French) and WMT
68
+ 2014 (English-German). To pre-process and binarize the IWSLT dataset:
69
+
70
+ .. code-block:: console
71
+
72
+ > cd examples/translation/
73
+ > bash prepare-iwslt14.sh
74
+ > cd ../..
75
+ > TEXT=examples/translation/iwslt14.tokenized.de-en
76
+ > fairseq-preprocess --source-lang de --target-lang en \
77
+ --trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
78
+ --destdir data-bin/iwslt14.tokenized.de-en
79
+
80
+ This will write binarized data that can be used for model training to
81
+ ``data-bin/iwslt14.tokenized.de-en``.
82
+
83
+ Training
84
+ --------
85
+
86
+ Use :ref:`fairseq-train` to train a new model. Here a few example settings that work
87
+ well for the IWSLT 2014 dataset:
88
+
89
+ .. code-block:: console
90
+
91
+ > mkdir -p checkpoints/fconv
92
+ > CUDA_VISIBLE_DEVICES=0 fairseq-train data-bin/iwslt14.tokenized.de-en \
93
+ --optimizer nag --lr 0.25 --clip-norm 0.1 --dropout 0.2 --max-tokens 4000 \
94
+ --arch fconv_iwslt_de_en --save-dir checkpoints/fconv
95
+
96
+ By default, :ref:`fairseq-train` will use all available GPUs on your machine. Use the
97
+ ``CUDA_VISIBLE_DEVICES`` environment variable to select specific GPUs and/or to
98
+ change the number of GPU devices that will be used.
99
+
100
+ Also note that the batch size is specified in terms of the maximum
101
+ number of tokens per batch (``--max-tokens``). You may need to use a
102
+ smaller value depending on the available GPU memory on your system.
103
+
104
+ Generation
105
+ ----------
106
+
107
+ Once your model is trained, you can generate translations using
108
+ :ref:`fairseq-generate` **(for binarized data)** or
109
+ :ref:`fairseq-interactive` **(for raw text)**:
110
+
111
+ .. code-block:: console
112
+
113
+ > fairseq-generate data-bin/iwslt14.tokenized.de-en \
114
+ --path checkpoints/fconv/checkpoint_best.pt \
115
+ --batch-size 128 --beam 5
116
+ | [de] dictionary: 35475 types
117
+ | [en] dictionary: 24739 types
118
+ | data-bin/iwslt14.tokenized.de-en test 6750 examples
119
+ | model fconv
120
+ | loaded checkpoint trainings/fconv/checkpoint_best.pt
121
+ S-721 danke .
122
+ T-721 thank you .
123
+ ...
124
+
125
+ To generate translations with only a CPU, use the ``--cpu`` flag. BPE
126
+ continuation markers can be removed with the ``--remove-bpe`` flag.
127
+
128
+ Advanced Training Options
129
+ =========================
130
+
131
+ Large mini-batch training with delayed updates
132
+ ----------------------------------------------
133
+
134
+ The ``--update-freq`` option can be used to accumulate gradients from
135
+ multiple mini-batches and delay updating, creating a larger effective
136
+ batch size. Delayed updates can also improve training speed by reducing
137
+ inter-GPU communication costs and by saving idle time caused by variance
138
+ in workload across GPUs. See `Ott et al.
139
+ (2018) <https://arxiv.org/abs/1806.00187>`__ for more details.
140
+
141
+ To train on a single GPU with an effective batch size that is equivalent
142
+ to training on 8 GPUs:
143
+
144
+ .. code-block:: console
145
+
146
+ > CUDA_VISIBLE_DEVICES=0 fairseq-train --update-freq 8 (...)
147
+
148
+ Training with half precision floating point (FP16)
149
+ --------------------------------------------------
150
+
151
+ .. note::
152
+
153
+ FP16 training requires a Volta GPU and CUDA 9.1 or greater
154
+
155
+ Recent GPUs enable efficient half precision floating point computation,
156
+ e.g., using `Nvidia Tensor Cores
157
+ <https://docs.nvidia.com/deeplearning/sdk/mixed-precision-training/index.html>`__.
158
+ Fairseq supports FP16 training with the ``--fp16`` flag:
159
+
160
+ .. code-block:: console
161
+
162
+ > fairseq-train --fp16 (...)
163
+
164
+ Distributed training
165
+ --------------------
166
+
167
+ Distributed training in fairseq is implemented on top of ``torch.distributed``.
168
+ The easiest way to launch jobs is with the `torch.distributed.launch
169
+ <https://pytorch.org/docs/stable/distributed.html#launch-utility>`__ tool.
170
+
171
+ For example, to train a large English-German Transformer model on 2 nodes each
172
+ with 8 GPUs (in total 16 GPUs), run the following command on each node,
173
+ replacing ``node_rank=0`` with ``node_rank=1`` on the second node and making
174
+ sure to update ``--master_addr`` to the IP address of the first node:
175
+
176
+ .. code-block:: console
177
+
178
+ > python -m torch.distributed.launch --nproc_per_node=8 \
179
+ --nnodes=2 --node_rank=0 --master_addr="192.168.1.1" \
180
+ --master_port=12345 \
181
+ $(which fairseq-train) data-bin/wmt16_en_de_bpe32k \
182
+ --arch transformer_vaswani_wmt_en_de_big --share-all-embeddings \
183
+ --optimizer adam --adam-betas '(0.9, 0.98)' --clip-norm 0.0 \
184
+ --lr-scheduler inverse_sqrt --warmup-init-lr 1e-07 --warmup-updates 4000 \
185
+ --lr 0.0005 \
186
+ --dropout 0.3 --weight-decay 0.0 --criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
187
+ --max-tokens 3584 \
188
+ --max-epoch 70 \
189
+ --fp16
190
+
191
+ On SLURM clusters, fairseq will automatically detect the number of nodes and
192
+ GPUs, but a port number must be provided:
193
+
194
+ .. code-block:: console
195
+
196
+ > salloc --gpus=16 --nodes 2 (...)
197
+ > srun fairseq-train --distributed-port 12345 (...).
198
+
199
+
200
+ .. warning::
201
+
202
+ PyTorch Distributed features used in fairseq are intended for internal
203
+ communication only. They are not built for use in untrusted environments or
204
+ networks.
205
+
206
+ For performance reasons, none of the PyTorch Distributed primitives include
207
+ any authorization protocol and will send messages unencrypted. They accept
208
+ connections from anywhere, and execute the workload sent without performing
209
+ any checks. Therefore, if you run a distributed fairseq job on your network,
210
+ anybody with access to the network can execute arbitrary code with the
211
+ privileges of the user running the job.
212
+
213
+ Sharding very large datasets
214
+ ----------------------------
215
+
216
+ It can be challenging to train over very large datasets, particularly if your
217
+ machine does not have much system RAM. Most tasks in fairseq support training
218
+ over "sharded" datasets, in which the original dataset has been preprocessed
219
+ into non-overlapping chunks (or "shards").
220
+
221
+ For example, instead of preprocessing all your data into a single "data-bin"
222
+ directory, you can split the data and create "data-bin1", "data-bin2", etc.
223
+ Then you can adapt your training command like so:
224
+
225
+ .. code-block:: console
226
+
227
+ > fairseq-train data-bin1:data-bin2:data-bin3 (...)
228
+
229
+ Training will now iterate over each shard, one by one, with each shard
230
+ corresponding to an "epoch", thus reducing system memory usage.
data/fairseq/docs/hydra_integration.md ADDED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Hydra
2
+
3
+ [Hydra](https://github.com/facebookresearch/hydra) is an open-source Python
4
+ framework that simplifies the development of research and other complex
5
+ applications. The key feature is the ability to dynamically create a
6
+ hierarchical configuration by composition and override it through config files
7
+ and the command line. The name Hydra comes from its ability to run multiple
8
+ similar jobs - much like a Hydra with multiple heads.
9
+
10
+ ## Motivation
11
+
12
+ Until recently, all components in fairseq were configured through a shared
13
+ `args` namespace that was created at application startup. Components declared
14
+ their own `add_args` method to update the argparse parser, hoping that the names
15
+ would not clash with arguments from other components. While this model works for
16
+ smaller applications, as fairseq grew and became integrated into other
17
+ applications, this became problematic. In order to determine how to configure
18
+ each component, one needed to a) examine what args were added by this component,
19
+ and b) read the code to figure out what shared arguments it is using that were
20
+ added in other places. Reproducing models involved sharing commands that often
21
+ contained dozens of command line switches.
22
+
23
+ The model described above is still supported by fairseq for backward
24
+ compatibility, but will be deprecated some time in the future.
25
+
26
+ New components in fairseq should now create a dataclass that encapsulates all
27
+ parameters required to configure this component. The dataclass is registered
28
+ along with the component, and fairseq takes care of constructing and providing
29
+ this configuration object to the component's constructor. Note that sharing
30
+ parameters can optionally still work, but one has to explicitly point to the
31
+ "source of truth" (see inheritance example below). These changes make components
32
+ in fairseq more independent and re-usable by other applications: all that is
33
+ needed to create a component is to initialize its dataclass and overwrite some
34
+ of the defaults.
35
+
36
+ While configuring fairseq through command line (using either the legacy argparse
37
+ based or the new Hydra based entry points) is still fully supported, you can now
38
+ take advantage of configuring fairseq completely or piece-by-piece through
39
+ hierarchical YAML configuration files. These files can also be shipped as
40
+ examples that others can use to run an identically configured job.
41
+
42
+ Additionally, Hydra has a rich and growing [library of
43
+ plugins](https://github.com/facebookresearch/hydra/tree/master/plugins) that
44
+ provide functionality such as hyperparameter sweeping (including using bayesian
45
+ optimization through the [Ax](https://github.com/facebook/Ax) library), job
46
+ launching across various platforms, and more.
47
+
48
+ ## Creating or migrating components
49
+
50
+ In general, each new (or updated) component should provide a companion
51
+ [dataclass](https://www.python.org/dev/peps/pep-0557/). These dataclass are
52
+ typically located in the same file as the component and are passed as arguments
53
+ to the `register_*()` functions. Top-level configs that should be present in
54
+ every fairseq application are placed in the
55
+ [global](fairseq/dataclass/configs.py) config file and added to the
56
+ `FairseqConfig` object.
57
+
58
+ Each dataclass is a plain-old-data object, similar to a `NamedTuple`. These
59
+ classes are decorated with a `@dataclass` decorator, and typically inherit from
60
+ `FairseqDataclass` (which adds some functionality for backward compatibility).
61
+ Each field must have a type, and generally has metadata (such as a help string)
62
+ and a default value. Only primitive types or other config objects are allowed as
63
+ data types for each field.
64
+
65
+ #### Example:
66
+
67
+ ```python
68
+ from dataclasses import dataclass, field
69
+ from fairseq.dataclass import FairseqDataclass
70
+
71
+ @dataclass
72
+ class InteractiveConfig(FairseqDataclass):
73
+ buffer_size: int = field(
74
+ default=0,
75
+ metadata={
76
+ "help": "read this many sentences into a buffer before processing them"
77
+ },
78
+ )
79
+ input: str = field(
80
+ default="-",
81
+ metadata={"help": "file to read from; use - for stdin"},
82
+ )
83
+ ```
84
+
85
+ ### Inherting values
86
+
87
+ Some components require sharing a value. For example, a learning rate scheduler
88
+ and an optimizer may both need to know the initial learning rate value. One can
89
+ declare a field that, by default, will inherit its value from another config
90
+ node in the same hierarchy:
91
+
92
+ ```python
93
+ @dataclass
94
+ FairseqAdamConfig(FairseqDataclass):
95
+ ...
96
+ lr: List[float] = II("optimization.lr")
97
+ ...
98
+ ```
99
+
100
+ `II("optimization.lr")` is syntactic sugar for `"${optimization.lr}"`, which is
101
+ the value one can use in a YAML config file or through command line to achieve
102
+ the same effect. Note that this assumes that there is an "optimization" config
103
+ object in the root config and it has a field called "lr".
104
+
105
+ ### Tasks and Models
106
+
107
+ Creating Tasks and Models works same as before, except that legacy
108
+ implementations now inherit from `LegacyFairseq*` base classes, while new
109
+ components inherit from `FairseqTask` and `FairseqModel` and provide a dataclass
110
+ to the `register_*()` functions.
111
+
112
+ #### Task example:
113
+
114
+ ```python
115
+ @dataclass
116
+ class LanguageModelingConfig(FairseqDataclass):
117
+ data: Optional[str] = field(
118
+ default=None, metadata={"help": "path to data directory"}
119
+ )
120
+ ...
121
+
122
+ @register_task("language_modeling", dataclass=LanguageModelingConfig)
123
+ class LanguageModelingTask(FairseqTask):
124
+ ...
125
+ @classmethod
126
+ def setup_task(cls, cfg: LanguageModelingConfig):
127
+ ...
128
+ ```
129
+
130
+ #### Model example:
131
+
132
+ ```python
133
+ @dataclass
134
+ class TransformerLanguageModelConfig(FairseqDataclass):
135
+ activation_fn: ChoiceEnum(utils.get_available_activation_fns()) = field(
136
+ default="relu", metadata={"help": "activation function to use"}
137
+ )
138
+ dropout: float = field(default=0.1, metadata={"help": "dropout probability"})
139
+ ...
140
+
141
+ @register_model("transformer_lm", dataclass=TransformerLanguageModelConfig)
142
+ class TransformerLanguageModel(FairseqLanguageModel):
143
+ ...
144
+ @classmethod
145
+ def build_model(cls, cfg: TransformerLanguageModelConfig, task: FairseqTask):
146
+ ...
147
+ ```
148
+
149
+ ### Other components
150
+
151
+ Other components work as before, but they now take their configuration dataclass
152
+ as the only constructor argument:
153
+
154
+ ```python
155
+ @dataclass
156
+ class MosesTokenizerConfig(FairseqDataclass):
157
+ source_lang: str = field(default="en", metadata={"help": "source language"})
158
+ ...
159
+
160
+ @register_tokenizer("moses", dataclass=MosesTokenizerConfig)
161
+ class MosesTokenizer(object):
162
+ def __init__(self, cfg: MosesTokenizerConfig):
163
+ ...
164
+ ```
165
+
166
+ Note that if you are adding a new registry for a new set of components, you need
167
+ to add it to the `FairseqConfig` object in `fairseq/dataclass/configs.py`:
168
+
169
+ ```python
170
+ @dataclass
171
+ class FairseqConfig(object):
172
+ ...
173
+ my_new_registry: Any = None
174
+ ```
175
+
176
+ ## Training with `fairseq-hydra-train`
177
+
178
+ To fully take advantage of configuration flexibility offered by Hydra, you may
179
+ want to train new models using the `fairseq-hydra-train` entry point. Legacy CLI
180
+ tools such as `fairseq-train` will remain supported for the foreseeable future
181
+ but will be deprecated eventually.
182
+
183
+ On startup, Hydra will create a configuration object that contains a hierarchy
184
+ of all the necessary dataclasses populated with their default values in the
185
+ code. The default values are overwritten by values found in YAML files in
186
+ `fairseq/config` directory (which currently sets minimal defaults) and then
187
+ further overwritten by values provided through command line arguments.
188
+
189
+ Some of the most common use cases are shown below:
190
+
191
+ ### 1. Override default values through command line:
192
+
193
+ ```shell script
194
+ $ fairseq-hydra-train \
195
+ distributed_training.distributed_world_size=1 \
196
+ dataset.batch_size=2 \
197
+ task.data=data-bin \
198
+ model=transformer_lm/transformer_lm_gpt \
199
+ task=language_modeling \
200
+ optimization.max_update=5000
201
+ ```
202
+
203
+ Note that along with explicitly providing values for parameters such as
204
+ `dataset.batch_size`, this also tells Hydra to overlay configuration found in
205
+ `fairseq/config/model/transformer_lm/transformer_lm_gpt.yaml` over the default
206
+ values in the dataclass. If you want to train a model without specifying a
207
+ particular architecture you can simply specify `model=transformer_lm`. This only
208
+ works for migrated tasks and models.
209
+
210
+ ### 2. Replace bundled configs with an external config:
211
+
212
+ ```shell script
213
+ $ fairseq-hydra-train \
214
+ --config-dir /path/to/external/configs \
215
+ --config-name wiki103
216
+ ```
217
+
218
+ where `/path/to/external/configs/wiki103.yaml` contains:
219
+
220
+ ```yaml
221
+ # @package _group_
222
+
223
+ model:
224
+ _name: transformer_lm
225
+ distributed_training:
226
+ distributed_world_size: 1
227
+ dataset:
228
+ batch_size: 2
229
+ task:
230
+ _name: language_modeling
231
+ data: /path/to/data
232
+ add_bos_token: false
233
+ max_target_positions: 1024
234
+ optimization:
235
+ max_update: 50000
236
+ lr: [ 0.25 ]
237
+ criterion: cross_entropy
238
+ optimizer: adam
239
+ lr_scheduler:
240
+ _name: cosine
241
+ ```
242
+
243
+ Note that here bundled configs from `fairseq/config` directory are not used,
244
+ however the defaults from each dataclass will still be used (unless overwritten
245
+ by your external config).
246
+
247
+ Additionally you can choose to break up your configs by creating a directory
248
+ structure in the same location as your main config file, with the names of the
249
+ top-level fields (such as "model", "dataset", etc), and placing config files
250
+ with meaningful names that would populate that specific section of your
251
+ top-level config file (for example, you might have
252
+ `model/small_transformer_lm.yaml`, `model/big_transformer_lm.yaml`, etc). You
253
+ can then specify the correct configuration via command line, defaults in the
254
+ main config, or even launch all of them as a sweep (see Hydra documentation on
255
+ how to do this).
256
+
257
+ ### 3. Add an external config directory to Hydra search path:
258
+
259
+ This allows combining default configuration (including using any bundled config
260
+ files), while specifying your own config files for some parts of the
261
+ configuration.
262
+
263
+ ```shell script
264
+ $ fairseq-hydra-train \
265
+ distributed_training.distributed_world_size=1 \
266
+ dataset.batch_size=2 \
267
+ task.data=/path/to/data/ \
268
+ model=transformer_lm/2_layers \
269
+ task=language_modeling \
270
+ optimization.max_update=5000 \
271
+ --config-dir /path/to/external/configs
272
+ ```
273
+
274
+ where `/path/to/external/configs` has the following structure:
275
+ ```
276
+ .
277
+ +-- model
278
+ | +-- transformer_lm
279
+ | | +-- 2_layers.yaml
280
+ ```
281
+
282
+ and `2_layers.yaml` contains a copy of `transformer_lm_gpt.yaml` but with
283
+ `decoder_layers` set to 2. You can add other configs to configure other
284
+ components as well.
data/fairseq/docs/index.rst ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. fairseq documentation master file, created by
2
+ sphinx-quickstart on Fri Aug 17 21:45:30 2018.
3
+ You can adapt this file completely to your liking, but it should at least
4
+ contain the root `toctree` directive.
5
+
6
+ :github_url: https://github.com/pytorch/fairseq
7
+
8
+
9
+ fairseq documentation
10
+ =====================
11
+
12
+ Fairseq is a sequence modeling toolkit written in `PyTorch
13
+ <http://pytorch.org/>`_ that allows researchers and developers to
14
+ train custom models for translation, summarization, language modeling and other
15
+ text generation tasks.
16
+
17
+ .. toctree::
18
+ :maxdepth: 1
19
+ :caption: Getting Started
20
+
21
+ getting_started
22
+ command_line_tools
23
+
24
+ .. toctree::
25
+ :maxdepth: 1
26
+ :caption: Extending Fairseq
27
+
28
+ overview
29
+ tutorial_simple_lstm
30
+ tutorial_classifying_names
31
+
32
+ .. toctree::
33
+ :maxdepth: 2
34
+ :caption: Library Reference
35
+
36
+ tasks
37
+ models
38
+ criterions
39
+ optim
40
+ lr_scheduler
41
+ data
42
+ modules
43
+
44
+
45
+ Indices and tables
46
+ ==================
47
+
48
+ * :ref:`genindex`
49
+ * :ref:`search`
data/fairseq/docs/lr_scheduler.rst ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. _Learning Rate Schedulers:
5
+
6
+ Learning Rate Schedulers
7
+ ========================
8
+
9
+ Learning Rate Schedulers update the learning rate over the course of training.
10
+ Learning rates can be updated after each update via :func:`step_update` or at
11
+ epoch boundaries via :func:`step`.
12
+
13
+ .. automodule:: fairseq.optim.lr_scheduler
14
+ :members:
15
+
16
+ .. autoclass:: fairseq.optim.lr_scheduler.FairseqLRScheduler
17
+ :members:
18
+ :undoc-members:
19
+
20
+ .. autoclass:: fairseq.optim.lr_scheduler.cosine_lr_scheduler.CosineSchedule
21
+ :members:
22
+ :undoc-members:
23
+ .. autoclass:: fairseq.optim.lr_scheduler.fixed_schedule.FixedSchedule
24
+ :members:
25
+ :undoc-members:
26
+ .. autoclass:: fairseq.optim.lr_scheduler.inverse_square_root_schedule.InverseSquareRootSchedule
27
+ :members:
28
+ :undoc-members:
29
+ .. autoclass:: fairseq.optim.lr_scheduler.reduce_lr_on_plateau.ReduceLROnPlateau
30
+ :members:
31
+ :undoc-members:
32
+ .. autoclass:: fairseq.optim.lr_scheduler.triangular_lr_scheduler.TriangularSchedule
33
+ :members:
34
+ :undoc-members:
data/fairseq/docs/make.bat ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @ECHO OFF
2
+
3
+ pushd %~dp0
4
+
5
+ REM Command file for Sphinx documentation
6
+
7
+ if "%SPHINXBUILD%" == "" (
8
+ set SPHINXBUILD=python -msphinx
9
+ )
10
+ set SOURCEDIR=.
11
+ set BUILDDIR=_build
12
+ set SPHINXPROJ=fairseq
13
+
14
+ if "%1" == "" goto help
15
+
16
+ %SPHINXBUILD% >NUL 2>NUL
17
+ if errorlevel 9009 (
18
+ echo.
19
+ echo.The Sphinx module was not found. Make sure you have Sphinx installed,
20
+ echo.then set the SPHINXBUILD environment variable to point to the full
21
+ echo.path of the 'sphinx-build' executable. Alternatively you may add the
22
+ echo.Sphinx directory to PATH.
23
+ echo.
24
+ echo.If you don't have Sphinx installed, grab it from
25
+ echo.http://sphinx-doc.org/
26
+ exit /b 1
27
+ )
28
+
29
+ %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30
+ goto end
31
+
32
+ :help
33
+ %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34
+
35
+ :end
36
+ popd
data/fairseq/docs/models.rst ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. module:: fairseq.models
5
+
6
+ .. _Models:
7
+
8
+ Models
9
+ ======
10
+
11
+ A Model defines the neural network's ``forward()`` method and encapsulates all
12
+ of the learnable parameters in the network. Each model also provides a set of
13
+ named *architectures* that define the precise network configuration (e.g.,
14
+ embedding dimension, number of layers, etc.).
15
+
16
+ Both the model type and architecture are selected via the ``--arch``
17
+ command-line argument. Once selected, a model may expose additional command-line
18
+ arguments for further configuration.
19
+
20
+ .. note::
21
+
22
+ All fairseq Models extend :class:`BaseFairseqModel`, which in turn extends
23
+ :class:`torch.nn.Module`. Thus any fairseq Model can be used as a
24
+ stand-alone Module in other PyTorch code.
25
+
26
+
27
+ Convolutional Neural Networks (CNN)
28
+ -----------------------------------
29
+
30
+ .. module:: fairseq.models.fconv
31
+ .. autoclass:: fairseq.models.fconv.FConvModel
32
+ :members:
33
+ .. autoclass:: fairseq.models.fconv.FConvEncoder
34
+ :members:
35
+ :undoc-members:
36
+ .. autoclass:: fairseq.models.fconv.FConvDecoder
37
+ :members:
38
+
39
+
40
+ Long Short-Term Memory (LSTM) networks
41
+ --------------------------------------
42
+
43
+ .. module:: fairseq.models.lstm
44
+ .. autoclass:: fairseq.models.lstm.LSTMModel
45
+ :members:
46
+ .. autoclass:: fairseq.models.lstm.LSTMEncoder
47
+ :members:
48
+ .. autoclass:: fairseq.models.lstm.LSTMDecoder
49
+ :members:
50
+
51
+
52
+ Transformer (self-attention) networks
53
+ -------------------------------------
54
+
55
+ .. module:: fairseq.models.transformer
56
+ .. autoclass:: fairseq.models.transformer.TransformerModel
57
+ :members:
58
+ .. autoclass:: fairseq.models.transformer.TransformerEncoder
59
+ :members:
60
+ .. autoclass:: fairseq.models.transformer.TransformerEncoderLayer
61
+ :members:
62
+ .. autoclass:: fairseq.models.transformer.TransformerDecoder
63
+ :members:
64
+ .. autoclass:: fairseq.models.transformer.TransformerDecoderLayer
65
+ :members:
66
+
67
+
68
+ Adding new models
69
+ -----------------
70
+
71
+ .. currentmodule:: fairseq.models
72
+ .. autofunction:: fairseq.models.register_model
73
+ .. autofunction:: fairseq.models.register_model_architecture
74
+ .. autoclass:: fairseq.models.BaseFairseqModel
75
+ :members:
76
+ :undoc-members:
77
+ .. autoclass:: fairseq.models.FairseqEncoderDecoderModel
78
+ :members:
79
+ :undoc-members:
80
+ .. autoclass:: fairseq.models.FairseqEncoderModel
81
+ :members:
82
+ :undoc-members:
83
+ .. autoclass:: fairseq.models.FairseqLanguageModel
84
+ :members:
85
+ :undoc-members:
86
+ .. autoclass:: fairseq.models.FairseqMultiModel
87
+ :members:
88
+ :undoc-members:
89
+ .. autoclass:: fairseq.models.FairseqEncoder
90
+ :members:
91
+ .. autoclass:: fairseq.models.CompositeEncoder
92
+ :members:
93
+ .. autoclass:: fairseq.models.FairseqDecoder
94
+ :members:
95
+
96
+
97
+ .. _Incremental decoding:
98
+
99
+ Incremental decoding
100
+ --------------------
101
+
102
+ .. autoclass:: fairseq.models.FairseqIncrementalDecoder
103
+ :members:
104
+ :undoc-members:
data/fairseq/docs/modules.rst ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ Modules
2
+ =======
3
+
4
+ Fairseq provides several stand-alone :class:`torch.nn.Module` classes that may
5
+ be helpful when implementing a new :class:`~fairseq.models.BaseFairseqModel`.
6
+
7
+ .. automodule:: fairseq.modules
8
+ :members:
9
+ :undoc-members:
data/fairseq/docs/optim.rst ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. _optimizers:
5
+
6
+ Optimizers
7
+ ==========
8
+
9
+ Optimizers update the Model parameters based on the gradients.
10
+
11
+ .. automodule:: fairseq.optim
12
+ :members:
13
+
14
+ .. autoclass:: fairseq.optim.FairseqOptimizer
15
+ :members:
16
+ :undoc-members:
17
+
18
+ .. autoclass:: fairseq.optim.adadelta.Adadelta
19
+ :members:
20
+ :undoc-members:
21
+ .. autoclass:: fairseq.optim.adagrad.Adagrad
22
+ :members:
23
+ :undoc-members:
24
+ .. autoclass:: fairseq.optim.adafactor.FairseqAdafactor
25
+ :members:
26
+ :undoc-members:
27
+ .. autoclass:: fairseq.optim.adam.FairseqAdam
28
+ :members:
29
+ :undoc-members:
30
+ .. autoclass:: fairseq.optim.fp16_optimizer.FP16Optimizer
31
+ :members:
32
+ :undoc-members:
33
+ .. autoclass:: fairseq.optim.nag.FairseqNAG
34
+ :members:
35
+ :undoc-members:
36
+ .. autoclass:: fairseq.optim.sgd.SGD
37
+ :members:
38
+ :undoc-members:
data/fairseq/docs/overview.rst ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Overview
2
+ ========
3
+
4
+ Fairseq can be extended through user-supplied `plug-ins
5
+ <https://en.wikipedia.org/wiki/Plug-in_(computing)>`_. We support five kinds of
6
+ plug-ins:
7
+
8
+ - :ref:`Models` define the neural network architecture and encapsulate all of the
9
+ learnable parameters.
10
+ - :ref:`Criterions` compute the loss function given the model outputs and targets.
11
+ - :ref:`Tasks` store dictionaries and provide helpers for loading/iterating over
12
+ Datasets, initializing the Model/Criterion and calculating the loss.
13
+ - :ref:`Optimizers` update the Model parameters based on the gradients.
14
+ - :ref:`Learning Rate Schedulers` update the learning rate over the course of
15
+ training.
16
+
17
+ **Training Flow**
18
+
19
+ Given a ``model``, ``criterion``, ``task``, ``optimizer`` and ``lr_scheduler``,
20
+ fairseq implements the following high-level training flow::
21
+
22
+ for epoch in range(num_epochs):
23
+ itr = task.get_batch_iterator(task.dataset('train'))
24
+ for num_updates, batch in enumerate(itr):
25
+ task.train_step(batch, model, criterion, optimizer)
26
+ average_and_clip_gradients()
27
+ optimizer.step()
28
+ lr_scheduler.step_update(num_updates)
29
+ lr_scheduler.step(epoch)
30
+
31
+ where the default implementation for ``task.train_step`` is roughly::
32
+
33
+ def train_step(self, batch, model, criterion, optimizer, **unused):
34
+ loss = criterion(model, batch)
35
+ optimizer.backward(loss)
36
+ return loss
37
+
38
+ **Registering new plug-ins**
39
+
40
+ New plug-ins are *registered* through a set of ``@register`` function
41
+ decorators, for example::
42
+
43
+ @register_model('my_lstm')
44
+ class MyLSTM(FairseqEncoderDecoderModel):
45
+ (...)
46
+
47
+ Once registered, new plug-ins can be used with the existing :ref:`Command-line
48
+ Tools`. See the Tutorial sections for more detailed walkthroughs of how to add
49
+ new plug-ins.
50
+
51
+ **Loading plug-ins from another directory**
52
+
53
+ New plug-ins can be defined in a custom module stored in the user system. In
54
+ order to import the module, and make the plugin available to *fairseq*, the
55
+ command line supports the ``--user-dir`` flag that can be used to specify a
56
+ custom location for additional modules to load into *fairseq*.
57
+
58
+ For example, assuming this directory tree::
59
+
60
+ /home/user/my-module/
61
+ └── __init__.py
62
+
63
+ with ``__init__.py``::
64
+
65
+ from fairseq.models import register_model_architecture
66
+ from fairseq.models.transformer import transformer_vaswani_wmt_en_de_big
67
+
68
+ @register_model_architecture('transformer', 'my_transformer')
69
+ def transformer_mmt_big(args):
70
+ transformer_vaswani_wmt_en_de_big(args)
71
+
72
+ it is possible to invoke the :ref:`fairseq-train` script with the new architecture with::
73
+
74
+ fairseq-train ... --user-dir /home/user/my-module -a my_transformer --task translation
data/fairseq/docs/tasks.rst ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .. role:: hidden
2
+ :class: hidden-section
3
+
4
+ .. module:: fairseq.tasks
5
+
6
+ .. _Tasks:
7
+
8
+ Tasks
9
+ =====
10
+
11
+ Tasks store dictionaries and provide helpers for loading/iterating over
12
+ Datasets, initializing the Model/Criterion and calculating the loss.
13
+
14
+ Tasks can be selected via the ``--task`` command-line argument. Once selected, a
15
+ task may expose additional command-line arguments for further configuration.
16
+
17
+ Example usage::
18
+
19
+ # setup the task (e.g., load dictionaries)
20
+ task = fairseq.tasks.setup_task(args)
21
+
22
+ # build model and criterion
23
+ model = task.build_model(args)
24
+ criterion = task.build_criterion(args)
25
+
26
+ # load datasets
27
+ task.load_dataset('train')
28
+ task.load_dataset('valid')
29
+
30
+ # iterate over mini-batches of data
31
+ batch_itr = task.get_batch_iterator(
32
+ task.dataset('train'), max_tokens=4096,
33
+ )
34
+ for batch in batch_itr:
35
+ # compute the loss
36
+ loss, sample_size, logging_output = task.get_loss(
37
+ model, criterion, batch,
38
+ )
39
+ loss.backward()
40
+
41
+
42
+ Translation
43
+ -----------
44
+
45
+ .. autoclass:: fairseq.tasks.translation.TranslationTask
46
+
47
+ .. _language modeling:
48
+
49
+ Language Modeling
50
+ -----------------
51
+
52
+ .. autoclass:: fairseq.tasks.language_modeling.LanguageModelingTask
53
+
54
+
55
+ Adding new tasks
56
+ ----------------
57
+
58
+ .. autofunction:: fairseq.tasks.register_task
59
+ .. autoclass:: fairseq.tasks.FairseqTask
60
+ :members:
61
+ :undoc-members:
data/fairseq/docs/tutorial_classifying_names.rst ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tutorial: Classifying Names with a Character-Level RNN
2
+ ======================================================
3
+
4
+ In this tutorial we will extend fairseq to support *classification* tasks. In
5
+ particular we will re-implement the PyTorch tutorial for `Classifying Names with
6
+ a Character-Level RNN <https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html>`_
7
+ in fairseq. It is recommended to quickly skim that tutorial before beginning
8
+ this one.
9
+
10
+ This tutorial covers:
11
+
12
+ 1. **Preprocessing the data** to create dictionaries.
13
+ 2. **Registering a new Model** that encodes an input sentence with a simple RNN
14
+ and predicts the output label.
15
+ 3. **Registering a new Task** that loads our dictionaries and dataset.
16
+ 4. **Training the Model** using the existing command-line tools.
17
+ 5. **Writing an evaluation script** that imports fairseq and allows us to
18
+ interactively evaluate our model on new inputs.
19
+
20
+
21
+ 1. Preprocessing the data
22
+ -------------------------
23
+
24
+ The original tutorial provides raw data, but we'll work with a modified version
25
+ of the data that is already tokenized into characters and split into separate
26
+ train, valid and test sets.
27
+
28
+ Download and extract the data from here:
29
+ `tutorial_names.tar.gz <https://dl.fbaipublicfiles.com/fairseq/data/tutorial_names.tar.gz>`_
30
+
31
+ Once extracted, let's preprocess the data using the :ref:`fairseq-preprocess`
32
+ command-line tool to create the dictionaries. While this tool is primarily
33
+ intended for sequence-to-sequence problems, we're able to reuse it here by
34
+ treating the label as a "target" sequence of length 1. We'll also output the
35
+ preprocessed files in "raw" format using the ``--dataset-impl`` option to
36
+ enhance readability:
37
+
38
+ .. code-block:: console
39
+
40
+ > fairseq-preprocess \
41
+ --trainpref names/train --validpref names/valid --testpref names/test \
42
+ --source-lang input --target-lang label \
43
+ --destdir names-bin --dataset-impl raw
44
+
45
+ After running the above command you should see a new directory,
46
+ :file:`names-bin/`, containing the dictionaries for *inputs* and *labels*.
47
+
48
+
49
+ 2. Registering a new Model
50
+ --------------------------
51
+
52
+ Next we'll register a new model in fairseq that will encode an input sentence
53
+ with a simple RNN and predict the output label. Compared to the original PyTorch
54
+ tutorial, our version will also work with batches of data and GPU Tensors.
55
+
56
+ First let's copy the simple RNN module implemented in the `PyTorch tutorial
57
+ <https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html#creating-the-network>`_.
58
+ Create a new file named :file:`fairseq/models/rnn_classifier.py` with the
59
+ following contents::
60
+
61
+ import torch
62
+ import torch.nn as nn
63
+
64
+ class RNN(nn.Module):
65
+
66
+ def __init__(self, input_size, hidden_size, output_size):
67
+ super(RNN, self).__init__()
68
+
69
+ self.hidden_size = hidden_size
70
+
71
+ self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
72
+ self.i2o = nn.Linear(input_size + hidden_size, output_size)
73
+ self.softmax = nn.LogSoftmax(dim=1)
74
+
75
+ def forward(self, input, hidden):
76
+ combined = torch.cat((input, hidden), 1)
77
+ hidden = self.i2h(combined)
78
+ output = self.i2o(combined)
79
+ output = self.softmax(output)
80
+ return output, hidden
81
+
82
+ def initHidden(self):
83
+ return torch.zeros(1, self.hidden_size)
84
+
85
+ We must also *register* this model with fairseq using the
86
+ :func:`~fairseq.models.register_model` function decorator. Once the model is
87
+ registered we'll be able to use it with the existing :ref:`Command-line Tools`.
88
+
89
+ All registered models must implement the :class:`~fairseq.models.BaseFairseqModel`
90
+ interface, so we'll create a small wrapper class in the same file and register
91
+ it in fairseq with the name ``'rnn_classifier'``::
92
+
93
+ from fairseq.models import BaseFairseqModel, register_model
94
+
95
+ # Note: the register_model "decorator" should immediately precede the
96
+ # definition of the Model class.
97
+
98
+ @register_model('rnn_classifier')
99
+ class FairseqRNNClassifier(BaseFairseqModel):
100
+
101
+ @staticmethod
102
+ def add_args(parser):
103
+ # Models can override this method to add new command-line arguments.
104
+ # Here we'll add a new command-line argument to configure the
105
+ # dimensionality of the hidden state.
106
+ parser.add_argument(
107
+ '--hidden-dim', type=int, metavar='N',
108
+ help='dimensionality of the hidden state',
109
+ )
110
+
111
+ @classmethod
112
+ def build_model(cls, args, task):
113
+ # Fairseq initializes models by calling the ``build_model()``
114
+ # function. This provides more flexibility, since the returned model
115
+ # instance can be of a different type than the one that was called.
116
+ # In this case we'll just return a FairseqRNNClassifier instance.
117
+
118
+ # Initialize our RNN module
119
+ rnn = RNN(
120
+ # We'll define the Task in the next section, but for now just
121
+ # notice that the task holds the dictionaries for the "source"
122
+ # (i.e., the input sentence) and "target" (i.e., the label).
123
+ input_size=len(task.source_dictionary),
124
+ hidden_size=args.hidden_dim,
125
+ output_size=len(task.target_dictionary),
126
+ )
127
+
128
+ # Return the wrapped version of the module
129
+ return FairseqRNNClassifier(
130
+ rnn=rnn,
131
+ input_vocab=task.source_dictionary,
132
+ )
133
+
134
+ def __init__(self, rnn, input_vocab):
135
+ super(FairseqRNNClassifier, self).__init__()
136
+
137
+ self.rnn = rnn
138
+ self.input_vocab = input_vocab
139
+
140
+ # The RNN module in the tutorial expects one-hot inputs, so we can
141
+ # precompute the identity matrix to help convert from indices to
142
+ # one-hot vectors. We register it as a buffer so that it is moved to
143
+ # the GPU when ``cuda()`` is called.
144
+ self.register_buffer('one_hot_inputs', torch.eye(len(input_vocab)))
145
+
146
+ def forward(self, src_tokens, src_lengths):
147
+ # The inputs to the ``forward()`` function are determined by the
148
+ # Task, and in particular the ``'net_input'`` key in each
149
+ # mini-batch. We'll define the Task in the next section, but for
150
+ # now just know that *src_tokens* has shape `(batch, src_len)` and
151
+ # *src_lengths* has shape `(batch)`.
152
+ bsz, max_src_len = src_tokens.size()
153
+
154
+ # Initialize the RNN hidden state. Compared to the original PyTorch
155
+ # tutorial we'll also handle batched inputs and work on the GPU.
156
+ hidden = self.rnn.initHidden()
157
+ hidden = hidden.repeat(bsz, 1) # expand for batched inputs
158
+ hidden = hidden.to(src_tokens.device) # move to GPU
159
+
160
+ for i in range(max_src_len):
161
+ # WARNING: The inputs have padding, so we should mask those
162
+ # elements here so that padding doesn't affect the results.
163
+ # This is left as an exercise for the reader. The padding symbol
164
+ # is given by ``self.input_vocab.pad()`` and the unpadded length
165
+ # of each input is given by *src_lengths*.
166
+
167
+ # One-hot encode a batch of input characters.
168
+ input = self.one_hot_inputs[src_tokens[:, i].long()]
169
+
170
+ # Feed the input to our RNN.
171
+ output, hidden = self.rnn(input, hidden)
172
+
173
+ # Return the final output state for making a prediction
174
+ return output
175
+
176
+ Finally let's define a *named architecture* with the configuration for our
177
+ model. This is done with the :func:`~fairseq.models.register_model_architecture`
178
+ function decorator. Thereafter this named architecture can be used with the
179
+ ``--arch`` command-line argument, e.g., ``--arch pytorch_tutorial_rnn``::
180
+
181
+ from fairseq.models import register_model_architecture
182
+
183
+ # The first argument to ``register_model_architecture()`` should be the name
184
+ # of the model we registered above (i.e., 'rnn_classifier'). The function we
185
+ # register here should take a single argument *args* and modify it in-place
186
+ # to match the desired architecture.
187
+
188
+ @register_model_architecture('rnn_classifier', 'pytorch_tutorial_rnn')
189
+ def pytorch_tutorial_rnn(args):
190
+ # We use ``getattr()`` to prioritize arguments that are explicitly given
191
+ # on the command-line, so that the defaults defined below are only used
192
+ # when no other value has been specified.
193
+ args.hidden_dim = getattr(args, 'hidden_dim', 128)
194
+
195
+
196
+ 3. Registering a new Task
197
+ -------------------------
198
+
199
+ Now we'll register a new :class:`~fairseq.tasks.FairseqTask` that will load our
200
+ dictionaries and dataset. Tasks can also control how the data is batched into
201
+ mini-batches, but in this tutorial we'll reuse the batching provided by
202
+ :class:`fairseq.data.LanguagePairDataset`.
203
+
204
+ Create a new file named :file:`fairseq/tasks/simple_classification.py` with the
205
+ following contents::
206
+
207
+ import os
208
+ import torch
209
+
210
+ from fairseq.data import Dictionary, LanguagePairDataset
211
+ from fairseq.tasks import LegacyFairseqTask, register_task
212
+
213
+
214
+ @register_task('simple_classification')
215
+ class SimpleClassificationTask(LegacyFairseqTask):
216
+
217
+ @staticmethod
218
+ def add_args(parser):
219
+ # Add some command-line arguments for specifying where the data is
220
+ # located and the maximum supported input length.
221
+ parser.add_argument('data', metavar='FILE',
222
+ help='file prefix for data')
223
+ parser.add_argument('--max-positions', default=1024, type=int,
224
+ help='max input length')
225
+
226
+ @classmethod
227
+ def setup_task(cls, args, **kwargs):
228
+ # Here we can perform any setup required for the task. This may include
229
+ # loading Dictionaries, initializing shared Embedding layers, etc.
230
+ # In this case we'll just load the Dictionaries.
231
+ input_vocab = Dictionary.load(os.path.join(args.data, 'dict.input.txt'))
232
+ label_vocab = Dictionary.load(os.path.join(args.data, 'dict.label.txt'))
233
+ print('| [input] dictionary: {} types'.format(len(input_vocab)))
234
+ print('| [label] dictionary: {} types'.format(len(label_vocab)))
235
+
236
+ return SimpleClassificationTask(args, input_vocab, label_vocab)
237
+
238
+ def __init__(self, args, input_vocab, label_vocab):
239
+ super().__init__(args)
240
+ self.input_vocab = input_vocab
241
+ self.label_vocab = label_vocab
242
+
243
+ def load_dataset(self, split, **kwargs):
244
+ """Load a given dataset split (e.g., train, valid, test)."""
245
+
246
+ prefix = os.path.join(self.args.data, '{}.input-label'.format(split))
247
+
248
+ # Read input sentences.
249
+ sentences, lengths = [], []
250
+ with open(prefix + '.input', encoding='utf-8') as file:
251
+ for line in file:
252
+ sentence = line.strip()
253
+
254
+ # Tokenize the sentence, splitting on spaces
255
+ tokens = self.input_vocab.encode_line(
256
+ sentence, add_if_not_exist=False,
257
+ )
258
+
259
+ sentences.append(tokens)
260
+ lengths.append(tokens.numel())
261
+
262
+ # Read labels.
263
+ labels = []
264
+ with open(prefix + '.label', encoding='utf-8') as file:
265
+ for line in file:
266
+ label = line.strip()
267
+ labels.append(
268
+ # Convert label to a numeric ID.
269
+ torch.LongTensor([self.label_vocab.add_symbol(label)])
270
+ )
271
+
272
+ assert len(sentences) == len(labels)
273
+ print('| {} {} {} examples'.format(self.args.data, split, len(sentences)))
274
+
275
+ # We reuse LanguagePairDataset since classification can be modeled as a
276
+ # sequence-to-sequence task where the target sequence has length 1.
277
+ self.datasets[split] = LanguagePairDataset(
278
+ src=sentences,
279
+ src_sizes=lengths,
280
+ src_dict=self.input_vocab,
281
+ tgt=labels,
282
+ tgt_sizes=torch.ones(len(labels)), # targets have length 1
283
+ tgt_dict=self.label_vocab,
284
+ left_pad_source=False,
285
+ # Since our target is a single class label, there's no need for
286
+ # teacher forcing. If we set this to ``True`` then our Model's
287
+ # ``forward()`` method would receive an additional argument called
288
+ # *prev_output_tokens* that would contain a shifted version of the
289
+ # target sequence.
290
+ input_feeding=False,
291
+ )
292
+
293
+ def max_positions(self):
294
+ """Return the max input length allowed by the task."""
295
+ # The source should be less than *args.max_positions* and the "target"
296
+ # has max length 1.
297
+ return (self.args.max_positions, 1)
298
+
299
+ @property
300
+ def source_dictionary(self):
301
+ """Return the source :class:`~fairseq.data.Dictionary`."""
302
+ return self.input_vocab
303
+
304
+ @property
305
+ def target_dictionary(self):
306
+ """Return the target :class:`~fairseq.data.Dictionary`."""
307
+ return self.label_vocab
308
+
309
+ # We could override this method if we wanted more control over how batches
310
+ # are constructed, but it's not necessary for this tutorial since we can
311
+ # reuse the batching provided by LanguagePairDataset.
312
+ #
313
+ # def get_batch_iterator(
314
+ # self, dataset, max_tokens=None, max_sentences=None, max_positions=None,
315
+ # ignore_invalid_inputs=False, required_batch_size_multiple=1,
316
+ # seed=1, num_shards=1, shard_id=0, num_workers=0, epoch=1,
317
+ # data_buffer_size=0, disable_iterator_cache=False,
318
+ # ):
319
+ # (...)
320
+
321
+
322
+ 4. Training the Model
323
+ ---------------------
324
+
325
+ Now we're ready to train the model. We can use the existing :ref:`fairseq-train`
326
+ command-line tool for this, making sure to specify our new Task (``--task
327
+ simple_classification``) and Model architecture (``--arch
328
+ pytorch_tutorial_rnn``):
329
+
330
+ .. note::
331
+
332
+ You can also configure the dimensionality of the hidden state by passing the
333
+ ``--hidden-dim`` argument to :ref:`fairseq-train`.
334
+
335
+ .. code-block:: console
336
+
337
+ > fairseq-train names-bin \
338
+ --task simple_classification \
339
+ --arch pytorch_tutorial_rnn \
340
+ --optimizer adam --lr 0.001 --lr-shrink 0.5 \
341
+ --max-tokens 1000
342
+ (...)
343
+ | epoch 027 | loss 1.200 | ppl 2.30 | wps 15728 | ups 119.4 | wpb 116 | bsz 116 | num_updates 3726 | lr 1.5625e-05 | gnorm 1.290 | clip 0% | oom 0 | wall 32 | train_wall 21
344
+ | epoch 027 | valid on 'valid' subset | valid_loss 1.41304 | valid_ppl 2.66 | num_updates 3726 | best 1.41208
345
+ | done training in 31.6 seconds
346
+
347
+ The model files should appear in the :file:`checkpoints/` directory.
348
+
349
+
350
+ 5. Writing an evaluation script
351
+ -------------------------------
352
+
353
+ Finally we can write a short script to evaluate our model on new inputs. Create
354
+ a new file named :file:`eval_classifier.py` with the following contents::
355
+
356
+ from fairseq import checkpoint_utils, data, options, tasks
357
+
358
+ # Parse command-line arguments for generation
359
+ parser = options.get_generation_parser(default_task='simple_classification')
360
+ args = options.parse_args_and_arch(parser)
361
+
362
+ # Setup task
363
+ task = tasks.setup_task(args)
364
+
365
+ # Load model
366
+ print('| loading model from {}'.format(args.path))
367
+ models, _model_args = checkpoint_utils.load_model_ensemble([args.path], task=task)
368
+ model = models[0]
369
+
370
+ while True:
371
+ sentence = input('\nInput: ')
372
+
373
+ # Tokenize into characters
374
+ chars = ' '.join(list(sentence.strip()))
375
+ tokens = task.source_dictionary.encode_line(
376
+ chars, add_if_not_exist=False,
377
+ )
378
+
379
+ # Build mini-batch to feed to the model
380
+ batch = data.language_pair_dataset.collate(
381
+ samples=[{'id': -1, 'source': tokens}], # bsz = 1
382
+ pad_idx=task.source_dictionary.pad(),
383
+ eos_idx=task.source_dictionary.eos(),
384
+ left_pad_source=False,
385
+ input_feeding=False,
386
+ )
387
+
388
+ # Feed batch to the model and get predictions
389
+ preds = model(**batch['net_input'])
390
+
391
+ # Print top 3 predictions and their log-probabilities
392
+ top_scores, top_labels = preds[0].topk(k=3)
393
+ for score, label_idx in zip(top_scores, top_labels):
394
+ label_name = task.target_dictionary.string([label_idx])
395
+ print('({:.2f})\t{}'.format(score, label_name))
396
+
397
+ Now we can evaluate our model interactively. Note that we have included the
398
+ original data path (:file:`names-bin/`) so that the dictionaries can be loaded:
399
+
400
+ .. code-block:: console
401
+
402
+ > python eval_classifier.py names-bin --path checkpoints/checkpoint_best.pt
403
+ | [input] dictionary: 64 types
404
+ | [label] dictionary: 24 types
405
+ | loading model from checkpoints/checkpoint_best.pt
406
+
407
+ Input: Satoshi
408
+ (-0.61) Japanese
409
+ (-1.20) Arabic
410
+ (-2.86) Italian
411
+
412
+ Input: Sinbad
413
+ (-0.30) Arabic
414
+ (-1.76) English
415
+ (-4.08) Russian
data/fairseq/docs/tutorial_simple_lstm.rst ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tutorial: Simple LSTM
2
+ =====================
3
+
4
+ In this tutorial we will extend fairseq by adding a new
5
+ :class:`~fairseq.models.FairseqEncoderDecoderModel` that encodes a source
6
+ sentence with an LSTM and then passes the final hidden state to a second LSTM
7
+ that decodes the target sentence (without attention).
8
+
9
+ This tutorial covers:
10
+
11
+ 1. **Writing an Encoder and Decoder** to encode/decode the source/target
12
+ sentence, respectively.
13
+ 2. **Registering a new Model** so that it can be used with the existing
14
+ :ref:`Command-line tools`.
15
+ 3. **Training the Model** using the existing command-line tools.
16
+ 4. **Making generation faster** by modifying the Decoder to use
17
+ :ref:`Incremental decoding`.
18
+
19
+
20
+ 1. Building an Encoder and Decoder
21
+ ----------------------------------
22
+
23
+ In this section we'll define a simple LSTM Encoder and Decoder. All Encoders
24
+ should implement the :class:`~fairseq.models.FairseqEncoder` interface and
25
+ Decoders should implement the :class:`~fairseq.models.FairseqDecoder` interface.
26
+ These interfaces themselves extend :class:`torch.nn.Module`, so FairseqEncoders
27
+ and FairseqDecoders can be written and used in the same ways as ordinary PyTorch
28
+ Modules.
29
+
30
+
31
+ Encoder
32
+ ~~~~~~~
33
+
34
+ Our Encoder will embed the tokens in the source sentence, feed them to a
35
+ :class:`torch.nn.LSTM` and return the final hidden state. To create our encoder
36
+ save the following in a new file named :file:`fairseq/models/simple_lstm.py`::
37
+
38
+ import torch.nn as nn
39
+ from fairseq import utils
40
+ from fairseq.models import FairseqEncoder
41
+
42
+ class SimpleLSTMEncoder(FairseqEncoder):
43
+
44
+ def __init__(
45
+ self, args, dictionary, embed_dim=128, hidden_dim=128, dropout=0.1,
46
+ ):
47
+ super().__init__(dictionary)
48
+ self.args = args
49
+
50
+ # Our encoder will embed the inputs before feeding them to the LSTM.
51
+ self.embed_tokens = nn.Embedding(
52
+ num_embeddings=len(dictionary),
53
+ embedding_dim=embed_dim,
54
+ padding_idx=dictionary.pad(),
55
+ )
56
+ self.dropout = nn.Dropout(p=dropout)
57
+
58
+ # We'll use a single-layer, unidirectional LSTM for simplicity.
59
+ self.lstm = nn.LSTM(
60
+ input_size=embed_dim,
61
+ hidden_size=hidden_dim,
62
+ num_layers=1,
63
+ bidirectional=False,
64
+ batch_first=True,
65
+ )
66
+
67
+ def forward(self, src_tokens, src_lengths):
68
+ # The inputs to the ``forward()`` function are determined by the
69
+ # Task, and in particular the ``'net_input'`` key in each
70
+ # mini-batch. We discuss Tasks in the next tutorial, but for now just
71
+ # know that *src_tokens* has shape `(batch, src_len)` and *src_lengths*
72
+ # has shape `(batch)`.
73
+
74
+ # Note that the source is typically padded on the left. This can be
75
+ # configured by adding the `--left-pad-source "False"` command-line
76
+ # argument, but here we'll make the Encoder handle either kind of
77
+ # padding by converting everything to be right-padded.
78
+ if self.args.left_pad_source:
79
+ # Convert left-padding to right-padding.
80
+ src_tokens = utils.convert_padding_direction(
81
+ src_tokens,
82
+ padding_idx=self.dictionary.pad(),
83
+ left_to_right=True
84
+ )
85
+
86
+ # Embed the source.
87
+ x = self.embed_tokens(src_tokens)
88
+
89
+ # Apply dropout.
90
+ x = self.dropout(x)
91
+
92
+ # Pack the sequence into a PackedSequence object to feed to the LSTM.
93
+ x = nn.utils.rnn.pack_padded_sequence(x, src_lengths, batch_first=True)
94
+
95
+ # Get the output from the LSTM.
96
+ _outputs, (final_hidden, _final_cell) = self.lstm(x)
97
+
98
+ # Return the Encoder's output. This can be any object and will be
99
+ # passed directly to the Decoder.
100
+ return {
101
+ # this will have shape `(bsz, hidden_dim)`
102
+ 'final_hidden': final_hidden.squeeze(0),
103
+ }
104
+
105
+ # Encoders are required to implement this method so that we can rearrange
106
+ # the order of the batch elements during inference (e.g., beam search).
107
+ def reorder_encoder_out(self, encoder_out, new_order):
108
+ """
109
+ Reorder encoder output according to `new_order`.
110
+
111
+ Args:
112
+ encoder_out: output from the ``forward()`` method
113
+ new_order (LongTensor): desired order
114
+
115
+ Returns:
116
+ `encoder_out` rearranged according to `new_order`
117
+ """
118
+ final_hidden = encoder_out['final_hidden']
119
+ return {
120
+ 'final_hidden': final_hidden.index_select(0, new_order),
121
+ }
122
+
123
+
124
+ Decoder
125
+ ~~~~~~~
126
+
127
+ Our Decoder will predict the next word, conditioned on the Encoder's final
128
+ hidden state and an embedded representation of the previous target word -- which
129
+ is sometimes called *teacher forcing*. More specifically, we'll use a
130
+ :class:`torch.nn.LSTM` to produce a sequence of hidden states that we'll project
131
+ to the size of the output vocabulary to predict each target word.
132
+
133
+ ::
134
+
135
+ import torch
136
+ from fairseq.models import FairseqDecoder
137
+
138
+ class SimpleLSTMDecoder(FairseqDecoder):
139
+
140
+ def __init__(
141
+ self, dictionary, encoder_hidden_dim=128, embed_dim=128, hidden_dim=128,
142
+ dropout=0.1,
143
+ ):
144
+ super().__init__(dictionary)
145
+
146
+ # Our decoder will embed the inputs before feeding them to the LSTM.
147
+ self.embed_tokens = nn.Embedding(
148
+ num_embeddings=len(dictionary),
149
+ embedding_dim=embed_dim,
150
+ padding_idx=dictionary.pad(),
151
+ )
152
+ self.dropout = nn.Dropout(p=dropout)
153
+
154
+ # We'll use a single-layer, unidirectional LSTM for simplicity.
155
+ self.lstm = nn.LSTM(
156
+ # For the first layer we'll concatenate the Encoder's final hidden
157
+ # state with the embedded target tokens.
158
+ input_size=encoder_hidden_dim + embed_dim,
159
+ hidden_size=hidden_dim,
160
+ num_layers=1,
161
+ bidirectional=False,
162
+ )
163
+
164
+ # Define the output projection.
165
+ self.output_projection = nn.Linear(hidden_dim, len(dictionary))
166
+
167
+ # During training Decoders are expected to take the entire target sequence
168
+ # (shifted right by one position) and produce logits over the vocabulary.
169
+ # The *prev_output_tokens* tensor begins with the end-of-sentence symbol,
170
+ # ``dictionary.eos()``, followed by the target sequence.
171
+ def forward(self, prev_output_tokens, encoder_out):
172
+ """
173
+ Args:
174
+ prev_output_tokens (LongTensor): previous decoder outputs of shape
175
+ `(batch, tgt_len)`, for teacher forcing
176
+ encoder_out (Tensor, optional): output from the encoder, used for
177
+ encoder-side attention
178
+
179
+ Returns:
180
+ tuple:
181
+ - the last decoder layer's output of shape
182
+ `(batch, tgt_len, vocab)`
183
+ - the last decoder layer's attention weights of shape
184
+ `(batch, tgt_len, src_len)`
185
+ """
186
+ bsz, tgt_len = prev_output_tokens.size()
187
+
188
+ # Extract the final hidden state from the Encoder.
189
+ final_encoder_hidden = encoder_out['final_hidden']
190
+
191
+ # Embed the target sequence, which has been shifted right by one
192
+ # position and now starts with the end-of-sentence symbol.
193
+ x = self.embed_tokens(prev_output_tokens)
194
+
195
+ # Apply dropout.
196
+ x = self.dropout(x)
197
+
198
+ # Concatenate the Encoder's final hidden state to *every* embedded
199
+ # target token.
200
+ x = torch.cat(
201
+ [x, final_encoder_hidden.unsqueeze(1).expand(bsz, tgt_len, -1)],
202
+ dim=2,
203
+ )
204
+
205
+ # Using PackedSequence objects in the Decoder is harder than in the
206
+ # Encoder, since the targets are not sorted in descending length order,
207
+ # which is a requirement of ``pack_padded_sequence()``. Instead we'll
208
+ # feed nn.LSTM directly.
209
+ initial_state = (
210
+ final_encoder_hidden.unsqueeze(0), # hidden
211
+ torch.zeros_like(final_encoder_hidden).unsqueeze(0), # cell
212
+ )
213
+ output, _ = self.lstm(
214
+ x.transpose(0, 1), # convert to shape `(tgt_len, bsz, dim)`
215
+ initial_state,
216
+ )
217
+ x = output.transpose(0, 1) # convert to shape `(bsz, tgt_len, hidden)`
218
+
219
+ # Project the outputs to the size of the vocabulary.
220
+ x = self.output_projection(x)
221
+
222
+ # Return the logits and ``None`` for the attention weights
223
+ return x, None
224
+
225
+
226
+ 2. Registering the Model
227
+ ------------------------
228
+
229
+ Now that we've defined our Encoder and Decoder we must *register* our model with
230
+ fairseq using the :func:`~fairseq.models.register_model` function decorator.
231
+ Once the model is registered we'll be able to use it with the existing
232
+ :ref:`Command-line Tools`.
233
+
234
+ All registered models must implement the
235
+ :class:`~fairseq.models.BaseFairseqModel` interface. For sequence-to-sequence
236
+ models (i.e., any model with a single Encoder and Decoder), we can instead
237
+ implement the :class:`~fairseq.models.FairseqEncoderDecoderModel` interface.
238
+
239
+ Create a small wrapper class in the same file and register it in fairseq with
240
+ the name ``'simple_lstm'``::
241
+
242
+ from fairseq.models import FairseqEncoderDecoderModel, register_model
243
+
244
+ # Note: the register_model "decorator" should immediately precede the
245
+ # definition of the Model class.
246
+
247
+ @register_model('simple_lstm')
248
+ class SimpleLSTMModel(FairseqEncoderDecoderModel):
249
+
250
+ @staticmethod
251
+ def add_args(parser):
252
+ # Models can override this method to add new command-line arguments.
253
+ # Here we'll add some new command-line arguments to configure dropout
254
+ # and the dimensionality of the embeddings and hidden states.
255
+ parser.add_argument(
256
+ '--encoder-embed-dim', type=int, metavar='N',
257
+ help='dimensionality of the encoder embeddings',
258
+ )
259
+ parser.add_argument(
260
+ '--encoder-hidden-dim', type=int, metavar='N',
261
+ help='dimensionality of the encoder hidden state',
262
+ )
263
+ parser.add_argument(
264
+ '--encoder-dropout', type=float, default=0.1,
265
+ help='encoder dropout probability',
266
+ )
267
+ parser.add_argument(
268
+ '--decoder-embed-dim', type=int, metavar='N',
269
+ help='dimensionality of the decoder embeddings',
270
+ )
271
+ parser.add_argument(
272
+ '--decoder-hidden-dim', type=int, metavar='N',
273
+ help='dimensionality of the decoder hidden state',
274
+ )
275
+ parser.add_argument(
276
+ '--decoder-dropout', type=float, default=0.1,
277
+ help='decoder dropout probability',
278
+ )
279
+
280
+ @classmethod
281
+ def build_model(cls, args, task):
282
+ # Fairseq initializes models by calling the ``build_model()``
283
+ # function. This provides more flexibility, since the returned model
284
+ # instance can be of a different type than the one that was called.
285
+ # In this case we'll just return a SimpleLSTMModel instance.
286
+
287
+ # Initialize our Encoder and Decoder.
288
+ encoder = SimpleLSTMEncoder(
289
+ args=args,
290
+ dictionary=task.source_dictionary,
291
+ embed_dim=args.encoder_embed_dim,
292
+ hidden_dim=args.encoder_hidden_dim,
293
+ dropout=args.encoder_dropout,
294
+ )
295
+ decoder = SimpleLSTMDecoder(
296
+ dictionary=task.target_dictionary,
297
+ encoder_hidden_dim=args.encoder_hidden_dim,
298
+ embed_dim=args.decoder_embed_dim,
299
+ hidden_dim=args.decoder_hidden_dim,
300
+ dropout=args.decoder_dropout,
301
+ )
302
+ model = SimpleLSTMModel(encoder, decoder)
303
+
304
+ # Print the model architecture.
305
+ print(model)
306
+
307
+ return model
308
+
309
+ # We could override the ``forward()`` if we wanted more control over how
310
+ # the encoder and decoder interact, but it's not necessary for this
311
+ # tutorial since we can inherit the default implementation provided by
312
+ # the FairseqEncoderDecoderModel base class, which looks like:
313
+ #
314
+ # def forward(self, src_tokens, src_lengths, prev_output_tokens):
315
+ # encoder_out = self.encoder(src_tokens, src_lengths)
316
+ # decoder_out = self.decoder(prev_output_tokens, encoder_out)
317
+ # return decoder_out
318
+
319
+ Finally let's define a *named architecture* with the configuration for our
320
+ model. This is done with the :func:`~fairseq.models.register_model_architecture`
321
+ function decorator. Thereafter this named architecture can be used with the
322
+ ``--arch`` command-line argument, e.g., ``--arch tutorial_simple_lstm``::
323
+
324
+ from fairseq.models import register_model_architecture
325
+
326
+ # The first argument to ``register_model_architecture()`` should be the name
327
+ # of the model we registered above (i.e., 'simple_lstm'). The function we
328
+ # register here should take a single argument *args* and modify it in-place
329
+ # to match the desired architecture.
330
+
331
+ @register_model_architecture('simple_lstm', 'tutorial_simple_lstm')
332
+ def tutorial_simple_lstm(args):
333
+ # We use ``getattr()`` to prioritize arguments that are explicitly given
334
+ # on the command-line, so that the defaults defined below are only used
335
+ # when no other value has been specified.
336
+ args.encoder_embed_dim = getattr(args, 'encoder_embed_dim', 256)
337
+ args.encoder_hidden_dim = getattr(args, 'encoder_hidden_dim', 256)
338
+ args.decoder_embed_dim = getattr(args, 'decoder_embed_dim', 256)
339
+ args.decoder_hidden_dim = getattr(args, 'decoder_hidden_dim', 256)
340
+
341
+
342
+ 3. Training the Model
343
+ ---------------------
344
+
345
+ Now we're ready to train the model. We can use the existing :ref:`fairseq-train`
346
+ command-line tool for this, making sure to specify our new Model architecture
347
+ (``--arch tutorial_simple_lstm``).
348
+
349
+ .. note::
350
+
351
+ Make sure you've already preprocessed the data from the IWSLT example in the
352
+ :file:`examples/translation/` directory.
353
+
354
+ .. code-block:: console
355
+
356
+ > fairseq-train data-bin/iwslt14.tokenized.de-en \
357
+ --arch tutorial_simple_lstm \
358
+ --encoder-dropout 0.2 --decoder-dropout 0.2 \
359
+ --optimizer adam --lr 0.005 --lr-shrink 0.5 \
360
+ --max-tokens 12000
361
+ (...)
362
+ | epoch 052 | loss 4.027 | ppl 16.30 | wps 420805 | ups 39.7 | wpb 9841 | bsz 400 | num_updates 20852 | lr 1.95313e-05 | gnorm 0.218 | clip 0% | oom 0 | wall 529 | train_wall 396
363
+ | epoch 052 | valid on 'valid' subset | valid_loss 4.74989 | valid_ppl 26.91 | num_updates 20852 | best 4.74954
364
+
365
+ The model files should appear in the :file:`checkpoints/` directory. While this
366
+ model architecture is not very good, we can use the :ref:`fairseq-generate` script to
367
+ generate translations and compute our BLEU score over the test set:
368
+
369
+ .. code-block:: console
370
+
371
+ > fairseq-generate data-bin/iwslt14.tokenized.de-en \
372
+ --path checkpoints/checkpoint_best.pt \
373
+ --beam 5 \
374
+ --remove-bpe
375
+ (...)
376
+ | Translated 6750 sentences (153132 tokens) in 17.3s (389.12 sentences/s, 8827.68 tokens/s)
377
+ | Generate test with beam=5: BLEU4 = 8.18, 38.8/12.1/4.7/2.0 (BP=1.000, ratio=1.066, syslen=139865, reflen=131146)
378
+
379
+
380
+ 4. Making generation faster
381
+ ---------------------------
382
+
383
+ While autoregressive generation from sequence-to-sequence models is inherently
384
+ slow, our implementation above is especially slow because it recomputes the
385
+ entire sequence of Decoder hidden states for every output token (i.e., it is
386
+ ``O(n^2)``). We can make this significantly faster by instead caching the
387
+ previous hidden states.
388
+
389
+ In fairseq this is called :ref:`Incremental decoding`. Incremental decoding is a
390
+ special mode at inference time where the Model only receives a single timestep
391
+ of input corresponding to the immediately previous output token (for teacher
392
+ forcing) and must produce the next output incrementally. Thus the model must
393
+ cache any long-term state that is needed about the sequence, e.g., hidden
394
+ states, convolutional states, etc.
395
+
396
+ To implement incremental decoding we will modify our model to implement the
397
+ :class:`~fairseq.models.FairseqIncrementalDecoder` interface. Compared to the
398
+ standard :class:`~fairseq.models.FairseqDecoder` interface, the incremental
399
+ decoder interface allows ``forward()`` methods to take an extra keyword argument
400
+ (*incremental_state*) that can be used to cache state across time-steps.
401
+
402
+ Let's replace our ``SimpleLSTMDecoder`` with an incremental one::
403
+
404
+ import torch
405
+ from fairseq.models import FairseqIncrementalDecoder
406
+
407
+ class SimpleLSTMDecoder(FairseqIncrementalDecoder):
408
+
409
+ def __init__(
410
+ self, dictionary, encoder_hidden_dim=128, embed_dim=128, hidden_dim=128,
411
+ dropout=0.1,
412
+ ):
413
+ # This remains the same as before.
414
+ super().__init__(dictionary)
415
+ self.embed_tokens = nn.Embedding(
416
+ num_embeddings=len(dictionary),
417
+ embedding_dim=embed_dim,
418
+ padding_idx=dictionary.pad(),
419
+ )
420
+ self.dropout = nn.Dropout(p=dropout)
421
+ self.lstm = nn.LSTM(
422
+ input_size=encoder_hidden_dim + embed_dim,
423
+ hidden_size=hidden_dim,
424
+ num_layers=1,
425
+ bidirectional=False,
426
+ )
427
+ self.output_projection = nn.Linear(hidden_dim, len(dictionary))
428
+
429
+ # We now take an additional kwarg (*incremental_state*) for caching the
430
+ # previous hidden and cell states.
431
+ def forward(self, prev_output_tokens, encoder_out, incremental_state=None):
432
+ if incremental_state is not None:
433
+ # If the *incremental_state* argument is not ``None`` then we are
434
+ # in incremental inference mode. While *prev_output_tokens* will
435
+ # still contain the entire decoded prefix, we will only use the
436
+ # last step and assume that the rest of the state is cached.
437
+ prev_output_tokens = prev_output_tokens[:, -1:]
438
+
439
+ # This remains the same as before.
440
+ bsz, tgt_len = prev_output_tokens.size()
441
+ final_encoder_hidden = encoder_out['final_hidden']
442
+ x = self.embed_tokens(prev_output_tokens)
443
+ x = self.dropout(x)
444
+ x = torch.cat(
445
+ [x, final_encoder_hidden.unsqueeze(1).expand(bsz, tgt_len, -1)],
446
+ dim=2,
447
+ )
448
+
449
+ # We will now check the cache and load the cached previous hidden and
450
+ # cell states, if they exist, otherwise we will initialize them to
451
+ # zeros (as before). We will use the ``utils.get_incremental_state()``
452
+ # and ``utils.set_incremental_state()`` helpers.
453
+ initial_state = utils.get_incremental_state(
454
+ self, incremental_state, 'prev_state',
455
+ )
456
+ if initial_state is None:
457
+ # first time initialization, same as the original version
458
+ initial_state = (
459
+ final_encoder_hidden.unsqueeze(0), # hidden
460
+ torch.zeros_like(final_encoder_hidden).unsqueeze(0), # cell
461
+ )
462
+
463
+ # Run one step of our LSTM.
464
+ output, latest_state = self.lstm(x.transpose(0, 1), initial_state)
465
+
466
+ # Update the cache with the latest hidden and cell states.
467
+ utils.set_incremental_state(
468
+ self, incremental_state, 'prev_state', latest_state,
469
+ )
470
+
471
+ # This remains the same as before
472
+ x = output.transpose(0, 1)
473
+ x = self.output_projection(x)
474
+ return x, None
475
+
476
+ # The ``FairseqIncrementalDecoder`` interface also requires implementing a
477
+ # ``reorder_incremental_state()`` method, which is used during beam search
478
+ # to select and reorder the incremental state.
479
+ def reorder_incremental_state(self, incremental_state, new_order):
480
+ # Load the cached state.
481
+ prev_state = utils.get_incremental_state(
482
+ self, incremental_state, 'prev_state',
483
+ )
484
+
485
+ # Reorder batches according to *new_order*.
486
+ reordered_state = (
487
+ prev_state[0].index_select(1, new_order), # hidden
488
+ prev_state[1].index_select(1, new_order), # cell
489
+ )
490
+
491
+ # Update the cached state.
492
+ utils.set_incremental_state(
493
+ self, incremental_state, 'prev_state', reordered_state,
494
+ )
495
+
496
+ Finally, we can rerun generation and observe the speedup:
497
+
498
+ .. code-block:: console
499
+
500
+ # Before
501
+
502
+ > fairseq-generate data-bin/iwslt14.tokenized.de-en \
503
+ --path checkpoints/checkpoint_best.pt \
504
+ --beam 5 \
505
+ --remove-bpe
506
+ (...)
507
+ | Translated 6750 sentences (153132 tokens) in 17.3s (389.12 sentences/s, 8827.68 tokens/s)
508
+ | Generate test with beam=5: BLEU4 = 8.18, 38.8/12.1/4.7/2.0 (BP=1.000, ratio=1.066, syslen=139865, reflen=131146)
509
+
510
+ # After
511
+
512
+ > fairseq-generate data-bin/iwslt14.tokenized.de-en \
513
+ --path checkpoints/checkpoint_best.pt \
514
+ --beam 5 \
515
+ --remove-bpe
516
+ (...)
517
+ | Translated 6750 sentences (153132 tokens) in 5.5s (1225.54 sentences/s, 27802.94 tokens/s)
518
+ | Generate test with beam=5: BLEU4 = 8.18, 38.8/12.1/4.7/2.0 (BP=1.000, ratio=1.066, syslen=139865, reflen=131146)
data/fairseq/scripts/constraints/validate.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Copyright (c) Facebook, Inc. and its affiliates.
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ import sys
9
+
10
+
11
+ """Reads in a fairseq output file, and verifies that the constraints
12
+ (C- lines) are present in the output (the first H- line). Assumes that
13
+ constraints are listed prior to the first hypothesis.
14
+ """
15
+
16
+ constraints = []
17
+ found = 0
18
+ total = 0
19
+ for line in sys.stdin:
20
+ if line.startswith("C-"):
21
+ constraints.append(line.rstrip().split("\t")[1])
22
+ elif line.startswith("H-"):
23
+ text = line.split("\t")[2]
24
+
25
+ for constraint in constraints:
26
+ total += 1
27
+ if constraint in text:
28
+ found += 1
29
+ else:
30
+ print(f"No {constraint} in {text}", file=sys.stderr)
31
+
32
+ constraints = []
33
+
34
+ print(f"Found {found} / {total} = {100 * found / total:.1f}%")
data/fairseq/tests/__init__.py ADDED
File without changes
data/fairseq/tests/test_activation_checkpointing.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ from fairseq.modules.checkpoint_activations import checkpoint_wrapper
11
+ from torch.utils.checkpoint import checkpoint
12
+
13
+
14
+ class Model(nn.Module):
15
+ def __init__(
16
+ self, use_pytorch_checkpoint=False, use_fairseq_checkpoint=False, **kwargs
17
+ ):
18
+ super().__init__()
19
+ torch.manual_seed(0)
20
+ self.use_pytorch_checkpoint = use_pytorch_checkpoint
21
+ self.ffn = nn.Sequential(
22
+ nn.Linear(32, 128),
23
+ # add a Dropout layer to test RNG save/restore
24
+ nn.Dropout(p=0.5),
25
+ nn.Linear(128, 32),
26
+ )
27
+ if use_fairseq_checkpoint:
28
+ self.ffn = checkpoint_wrapper(self.ffn, **kwargs)
29
+ self.out = nn.Linear(32, 1)
30
+
31
+ def forward(self, x):
32
+ if self.use_pytorch_checkpoint:
33
+ x = checkpoint(self.ffn, x)
34
+ else:
35
+ x = self.ffn(x)
36
+ return self.out(x)
37
+
38
+
39
+ class TestActivationCheckpointing(unittest.TestCase):
40
+ def _test_checkpoint_wrapper(self, device, log_memory_usage=False):
41
+ def get_loss_and_gnorm(model):
42
+ torch.manual_seed(1)
43
+ input = torch.rand(2, 16, 32).requires_grad_(True).to(device)
44
+ model.zero_grad()
45
+ loss = model(input).sum()
46
+ loss.backward()
47
+ gnorm = torch.norm(
48
+ torch.stack([torch.norm(p.grad.detach()) for p in model.parameters()])
49
+ )
50
+ return {"loss": loss, "gnorm": gnorm}
51
+
52
+ model = Model().to(device)
53
+ no_cpt = get_loss_and_gnorm(model)
54
+
55
+ model = Model(use_pytorch_checkpoint=True).to(device)
56
+ pyt_cpt = get_loss_and_gnorm(model)
57
+ torch.testing.assert_allclose(no_cpt["loss"], pyt_cpt["loss"])
58
+ torch.testing.assert_allclose(no_cpt["gnorm"], pyt_cpt["gnorm"])
59
+
60
+ model = Model(use_fairseq_checkpoint=True).to(device)
61
+ fairseq_cpt = get_loss_and_gnorm(model)
62
+ torch.testing.assert_allclose(no_cpt["loss"], fairseq_cpt["loss"])
63
+ torch.testing.assert_allclose(no_cpt["gnorm"], fairseq_cpt["gnorm"])
64
+
65
+ model = Model(use_fairseq_checkpoint=True, offload_to_cpu=True).to(device)
66
+ fairseq_cpt_offload = get_loss_and_gnorm(model)
67
+ torch.testing.assert_allclose(no_cpt["loss"], fairseq_cpt_offload["loss"])
68
+ torch.testing.assert_allclose(no_cpt["gnorm"], fairseq_cpt_offload["gnorm"])
69
+
70
+ def test_checkpoint_wrapper_cpu(self):
71
+ self._test_checkpoint_wrapper(device=torch.device("cpu"))
72
+
73
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
74
+ def test_checkpoint_wrapper_cuda(self):
75
+ self._test_checkpoint_wrapper(device=torch.device("cuda"))
76
+
77
+
78
+ if __name__ == "__main__":
79
+ unittest.main()
data/fairseq/tests/test_amp_optimizer.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ import copy
8
+ import unittest
9
+
10
+ import torch
11
+ from torch.cuda.amp import GradScaler, autocast
12
+
13
+ from fairseq.optim import build_optimizer
14
+
15
+
16
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
17
+ class TestGradientScalingAMP(unittest.TestCase):
18
+ def setUp(self):
19
+ self.x = torch.tensor([2.0]).cuda().half()
20
+ weight = 3.0
21
+ bias = 5.0
22
+ self.error = 1.0
23
+ self.target = torch.tensor([self.x * weight + bias + self.error]).cuda()
24
+ self.loss_fn = torch.nn.L1Loss()
25
+
26
+ self.model = torch.nn.Linear(1, 1)
27
+ self.model.weight.data = torch.tensor([[weight]])
28
+ self.model.bias.data = torch.tensor([bias])
29
+ self.model.cuda()
30
+ self.params = list(self.model.parameters())
31
+
32
+ self.namespace_dls = argparse.Namespace(
33
+ optimizer="adam",
34
+ lr=[0.1],
35
+ adam_betas="(0.9, 0.999)",
36
+ adam_eps=1e-8,
37
+ weight_decay=0.0,
38
+ threshold_loss_scale=1,
39
+ min_loss_scale=1e-4,
40
+ )
41
+ self.scaler = GradScaler(
42
+ init_scale=1,
43
+ growth_interval=1,
44
+ )
45
+
46
+ def run_iter(self, model, params, optimizer):
47
+ optimizer.zero_grad()
48
+ with autocast():
49
+ y = model(self.x)
50
+ loss = self.loss_fn(y, self.target)
51
+ self.scaler.scale(loss).backward()
52
+ self.assertEqual(loss, torch.tensor(1.0, device="cuda:0", dtype=torch.float16))
53
+
54
+ self.scaler.unscale_(optimizer)
55
+ grad_norm = optimizer.clip_grad_norm(0)
56
+ self.assertAlmostEqual(grad_norm.item(), 2.2361, 4)
57
+
58
+ self.scaler.step(optimizer)
59
+ self.scaler.update()
60
+ self.assertEqual(
61
+ model.weight,
62
+ torch.tensor([[3.1]], device="cuda:0", requires_grad=True),
63
+ )
64
+ self.assertEqual(
65
+ model.bias,
66
+ torch.tensor([5.1], device="cuda:0", requires_grad=True),
67
+ )
68
+ self.assertEqual(self.scaler.get_scale(), 2.0)
69
+
70
+ def test_automatic_mixed_precision(self):
71
+ model = copy.deepcopy(self.model)
72
+ params = list(model.parameters())
73
+ optimizer = build_optimizer(self.namespace_dls, params)
74
+
75
+ self.run_iter(model, params, optimizer)
data/fairseq/tests/test_average_checkpoints.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import collections
7
+ import os
8
+ import shutil
9
+ import tempfile
10
+ import unittest
11
+
12
+ import numpy as np
13
+ import torch
14
+ from scripts.average_checkpoints import average_checkpoints
15
+ from torch import nn
16
+
17
+
18
+ class ModelWithSharedParameter(nn.Module):
19
+ def __init__(self):
20
+ super(ModelWithSharedParameter, self).__init__()
21
+ self.embedding = nn.Embedding(1000, 200)
22
+ self.FC1 = nn.Linear(200, 200)
23
+ self.FC2 = nn.Linear(200, 200)
24
+ # tie weight in FC2 to FC1
25
+ self.FC2.weight = nn.Parameter(self.FC1.weight)
26
+ self.FC2.bias = nn.Parameter(self.FC1.bias)
27
+
28
+ self.relu = nn.ReLU()
29
+
30
+ def forward(self, input):
31
+ return self.FC2(self.ReLU(self.FC1(input))) + self.FC1(input)
32
+
33
+
34
+ class TestAverageCheckpoints(unittest.TestCase):
35
+ def test_average_checkpoints(self):
36
+ params_0 = collections.OrderedDict(
37
+ [
38
+ ("a", torch.DoubleTensor([100.0])),
39
+ ("b", torch.FloatTensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])),
40
+ ("c", torch.IntTensor([7, 8, 9])),
41
+ ]
42
+ )
43
+ params_1 = collections.OrderedDict(
44
+ [
45
+ ("a", torch.DoubleTensor([1.0])),
46
+ ("b", torch.FloatTensor([[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]])),
47
+ ("c", torch.IntTensor([2, 2, 2])),
48
+ ]
49
+ )
50
+ params_avg = collections.OrderedDict(
51
+ [
52
+ ("a", torch.DoubleTensor([50.5])),
53
+ ("b", torch.FloatTensor([[1.0, 1.5, 2.0], [2.5, 3.0, 3.5]])),
54
+ # We expect truncation for integer division
55
+ ("c", torch.IntTensor([4, 5, 5])),
56
+ ]
57
+ )
58
+
59
+ fd_0, path_0 = tempfile.mkstemp()
60
+ fd_1, path_1 = tempfile.mkstemp()
61
+ torch.save(collections.OrderedDict([("model", params_0)]), path_0)
62
+ torch.save(collections.OrderedDict([("model", params_1)]), path_1)
63
+
64
+ output = average_checkpoints([path_0, path_1])["model"]
65
+
66
+ os.close(fd_0)
67
+ os.remove(path_0)
68
+ os.close(fd_1)
69
+ os.remove(path_1)
70
+
71
+ for (k_expected, v_expected), (k_out, v_out) in zip(
72
+ params_avg.items(), output.items()
73
+ ):
74
+ self.assertEqual(
75
+ k_expected,
76
+ k_out,
77
+ "Key mismatch - expected {} but found {}. "
78
+ "(Expected list of keys: {} vs actual list of keys: {})".format(
79
+ k_expected, k_out, params_avg.keys(), output.keys()
80
+ ),
81
+ )
82
+ np.testing.assert_allclose(
83
+ v_expected.numpy(),
84
+ v_out.numpy(),
85
+ err_msg="Tensor value mismatch for key {}".format(k_expected),
86
+ )
87
+
88
+ def test_average_checkpoints_with_shared_parameters(self):
89
+ def _construct_model_with_shared_parameters(path, value):
90
+ m = ModelWithSharedParameter()
91
+ nn.init.constant_(m.FC1.weight, value)
92
+ torch.save({"model": m.state_dict()}, path)
93
+ return m
94
+
95
+ tmpdir = tempfile.mkdtemp()
96
+ paths = []
97
+ path = os.path.join(tmpdir, "m1.pt")
98
+ m1 = _construct_model_with_shared_parameters(path, 1.0)
99
+ paths.append(path)
100
+
101
+ path = os.path.join(tmpdir, "m2.pt")
102
+ m2 = _construct_model_with_shared_parameters(path, 2.0)
103
+ paths.append(path)
104
+
105
+ path = os.path.join(tmpdir, "m3.pt")
106
+ m3 = _construct_model_with_shared_parameters(path, 3.0)
107
+ paths.append(path)
108
+
109
+ new_model = average_checkpoints(paths)
110
+ self.assertTrue(
111
+ torch.equal(
112
+ new_model["model"]["embedding.weight"],
113
+ (m1.embedding.weight + m2.embedding.weight + m3.embedding.weight) / 3.0,
114
+ )
115
+ )
116
+
117
+ self.assertTrue(
118
+ torch.equal(
119
+ new_model["model"]["FC1.weight"],
120
+ (m1.FC1.weight + m2.FC1.weight + m3.FC1.weight) / 3.0,
121
+ )
122
+ )
123
+
124
+ self.assertTrue(
125
+ torch.equal(
126
+ new_model["model"]["FC2.weight"],
127
+ (m1.FC2.weight + m2.FC2.weight + m3.FC2.weight) / 3.0,
128
+ )
129
+ )
130
+ shutil.rmtree(tmpdir)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ unittest.main()
data/fairseq/tests/test_backtranslation_dataset.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import tests.utils as test_utils
9
+ import torch
10
+ from fairseq.data import (
11
+ BacktranslationDataset,
12
+ LanguagePairDataset,
13
+ TransformEosDataset,
14
+ )
15
+ from fairseq.sequence_generator import SequenceGenerator
16
+
17
+
18
+ class TestBacktranslationDataset(unittest.TestCase):
19
+ def setUp(self):
20
+ (
21
+ self.tgt_dict,
22
+ self.w1,
23
+ self.w2,
24
+ self.src_tokens,
25
+ self.src_lengths,
26
+ self.model,
27
+ ) = test_utils.sequence_generator_setup()
28
+
29
+ dummy_src_samples = self.src_tokens
30
+
31
+ self.tgt_dataset = test_utils.TestDataset(data=dummy_src_samples)
32
+ self.cuda = torch.cuda.is_available()
33
+
34
+ def _backtranslation_dataset_helper(
35
+ self,
36
+ remove_eos_from_input_src,
37
+ remove_eos_from_output_src,
38
+ ):
39
+ tgt_dataset = LanguagePairDataset(
40
+ src=self.tgt_dataset,
41
+ src_sizes=self.tgt_dataset.sizes,
42
+ src_dict=self.tgt_dict,
43
+ tgt=None,
44
+ tgt_sizes=None,
45
+ tgt_dict=None,
46
+ )
47
+
48
+ generator = SequenceGenerator(
49
+ [self.model],
50
+ tgt_dict=self.tgt_dict,
51
+ max_len_a=0,
52
+ max_len_b=200,
53
+ beam_size=2,
54
+ unk_penalty=0,
55
+ )
56
+
57
+ backtranslation_dataset = BacktranslationDataset(
58
+ tgt_dataset=TransformEosDataset(
59
+ dataset=tgt_dataset,
60
+ eos=self.tgt_dict.eos(),
61
+ # remove eos from the input src
62
+ remove_eos_from_src=remove_eos_from_input_src,
63
+ ),
64
+ src_dict=self.tgt_dict,
65
+ backtranslation_fn=(
66
+ lambda sample: generator.generate([self.model], sample)
67
+ ),
68
+ output_collater=TransformEosDataset(
69
+ dataset=tgt_dataset,
70
+ eos=self.tgt_dict.eos(),
71
+ # if we remove eos from the input src, then we need to add it
72
+ # back to the output tgt
73
+ append_eos_to_tgt=remove_eos_from_input_src,
74
+ remove_eos_from_src=remove_eos_from_output_src,
75
+ ).collater,
76
+ cuda=self.cuda,
77
+ )
78
+ dataloader = torch.utils.data.DataLoader(
79
+ backtranslation_dataset,
80
+ batch_size=2,
81
+ collate_fn=backtranslation_dataset.collater,
82
+ )
83
+ backtranslation_batch_result = next(iter(dataloader))
84
+
85
+ eos, pad, w1, w2 = self.tgt_dict.eos(), self.tgt_dict.pad(), self.w1, self.w2
86
+
87
+ # Note that we sort by src_lengths and add left padding, so actually
88
+ # ids will look like: [1, 0]
89
+ expected_src = torch.LongTensor([[w1, w2, w1, eos], [pad, pad, w1, eos]])
90
+ if remove_eos_from_output_src:
91
+ expected_src = expected_src[:, :-1]
92
+ expected_tgt = torch.LongTensor([[w1, w2, eos], [w1, w2, eos]])
93
+ generated_src = backtranslation_batch_result["net_input"]["src_tokens"]
94
+ tgt_tokens = backtranslation_batch_result["target"]
95
+
96
+ self.assertTensorEqual(expected_src, generated_src)
97
+ self.assertTensorEqual(expected_tgt, tgt_tokens)
98
+
99
+ def test_backtranslation_dataset_no_eos_in_output_src(self):
100
+ self._backtranslation_dataset_helper(
101
+ remove_eos_from_input_src=False,
102
+ remove_eos_from_output_src=True,
103
+ )
104
+
105
+ def test_backtranslation_dataset_with_eos_in_output_src(self):
106
+ self._backtranslation_dataset_helper(
107
+ remove_eos_from_input_src=False,
108
+ remove_eos_from_output_src=False,
109
+ )
110
+
111
+ def test_backtranslation_dataset_no_eos_in_input_src(self):
112
+ self._backtranslation_dataset_helper(
113
+ remove_eos_from_input_src=True,
114
+ remove_eos_from_output_src=False,
115
+ )
116
+
117
+ def assertTensorEqual(self, t1, t2):
118
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
119
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
120
+
121
+
122
+ if __name__ == "__main__":
123
+ unittest.main()
data/fairseq/tests/test_binaries.py ADDED
@@ -0,0 +1,1915 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import contextlib
7
+ import json
8
+ import logging
9
+ import os
10
+ import random
11
+ import sys
12
+ import tempfile
13
+ import unittest
14
+ from packaging import version
15
+ from io import StringIO
16
+ from typing import Dict, List
17
+
18
+ import torch
19
+
20
+ from fairseq import options
21
+ from fairseq_cli import eval_lm, train
22
+ from tests.utils import (
23
+ create_dummy_data,
24
+ create_laser_data_and_config_json,
25
+ generate_main,
26
+ preprocess_lm_data,
27
+ preprocess_summarization_data,
28
+ preprocess_translation_data,
29
+ train_language_model,
30
+ train_translation_model,
31
+ )
32
+
33
+ try:
34
+ import transformers # noqa
35
+
36
+ has_hf_transformers = True
37
+ except ImportError:
38
+ has_hf_transformers = False
39
+
40
+
41
+ class TestTranslation(unittest.TestCase):
42
+ def setUp(self):
43
+ logging.disable(logging.CRITICAL)
44
+
45
+ def tearDown(self):
46
+ logging.disable(logging.NOTSET)
47
+
48
+ def test_fconv(self):
49
+ with contextlib.redirect_stdout(StringIO()):
50
+ with tempfile.TemporaryDirectory("test_fconv") as data_dir:
51
+ create_dummy_data(data_dir)
52
+ preprocess_translation_data(data_dir)
53
+ train_translation_model(data_dir, "fconv_iwslt_de_en")
54
+ generate_main(data_dir)
55
+
56
+ def test_raw(self):
57
+ with contextlib.redirect_stdout(StringIO()):
58
+ with tempfile.TemporaryDirectory("test_fconv_raw") as data_dir:
59
+ create_dummy_data(data_dir)
60
+ preprocess_translation_data(data_dir, ["--dataset-impl", "raw"])
61
+ train_translation_model(
62
+ data_dir, "fconv_iwslt_de_en", ["--dataset-impl", "raw"]
63
+ )
64
+ generate_main(data_dir, ["--dataset-impl", "raw"])
65
+
66
+ def test_update_freq(self):
67
+ with contextlib.redirect_stdout(StringIO()):
68
+ with tempfile.TemporaryDirectory("test_update_freq") as data_dir:
69
+ create_dummy_data(data_dir)
70
+ preprocess_translation_data(data_dir)
71
+ train_translation_model(
72
+ data_dir, "fconv_iwslt_de_en", ["--update-freq", "3"]
73
+ )
74
+ generate_main(data_dir)
75
+
76
+ def test_max_positions(self):
77
+ with contextlib.redirect_stdout(StringIO()):
78
+ with tempfile.TemporaryDirectory("test_max_positions") as data_dir:
79
+ create_dummy_data(data_dir)
80
+ preprocess_translation_data(data_dir)
81
+ with self.assertRaises(Exception) as context:
82
+ train_translation_model(
83
+ data_dir,
84
+ "fconv_iwslt_de_en",
85
+ ["--max-target-positions", "5"],
86
+ )
87
+ self.assertTrue(
88
+ "skip this example with --skip-invalid-size-inputs-valid-test"
89
+ in str(context.exception)
90
+ )
91
+ train_translation_model(
92
+ data_dir,
93
+ "fconv_iwslt_de_en",
94
+ [
95
+ "--max-target-positions",
96
+ "5",
97
+ "--skip-invalid-size-inputs-valid-test",
98
+ ],
99
+ )
100
+ with self.assertRaises(Exception) as context:
101
+ generate_main(data_dir)
102
+ generate_main(data_dir, ["--skip-invalid-size-inputs-valid-test"])
103
+
104
+ def test_generation(self):
105
+ with contextlib.redirect_stdout(StringIO()):
106
+ with tempfile.TemporaryDirectory("test_sampling") as data_dir:
107
+ create_dummy_data(data_dir)
108
+ preprocess_translation_data(data_dir)
109
+ train_translation_model(data_dir, "fconv_iwslt_de_en")
110
+ generate_main(
111
+ data_dir,
112
+ [
113
+ "--sampling",
114
+ "--temperature",
115
+ "2",
116
+ "--beam",
117
+ "2",
118
+ "--nbest",
119
+ "2",
120
+ ],
121
+ )
122
+ generate_main(
123
+ data_dir,
124
+ [
125
+ "--sampling",
126
+ "--sampling-topk",
127
+ "3",
128
+ "--beam",
129
+ "2",
130
+ "--nbest",
131
+ "2",
132
+ ],
133
+ )
134
+ generate_main(
135
+ data_dir,
136
+ [
137
+ "--sampling",
138
+ "--sampling-topp",
139
+ "0.2",
140
+ "--beam",
141
+ "2",
142
+ "--nbest",
143
+ "2",
144
+ ],
145
+ )
146
+ generate_main(
147
+ data_dir,
148
+ [
149
+ "--diversity-rate",
150
+ "0.5",
151
+ "--beam",
152
+ "6",
153
+ ],
154
+ )
155
+ with self.assertRaises(ValueError):
156
+ generate_main(
157
+ data_dir,
158
+ [
159
+ "--diverse-beam-groups",
160
+ "4",
161
+ "--match-source-len",
162
+ ],
163
+ )
164
+ generate_main(data_dir, ["--prefix-size", "2"])
165
+ generate_main(data_dir, ["--retain-dropout"])
166
+
167
+ def test_eval_bleu(self):
168
+ with contextlib.redirect_stdout(StringIO()):
169
+ with tempfile.TemporaryDirectory("test_eval_bleu") as data_dir:
170
+ create_dummy_data(data_dir)
171
+ preprocess_translation_data(data_dir)
172
+ train_translation_model(
173
+ data_dir,
174
+ "fconv_iwslt_de_en",
175
+ [
176
+ "--eval-bleu",
177
+ "--eval-bleu-print-samples",
178
+ "--eval-bleu-remove-bpe",
179
+ "--eval-bleu-detok",
180
+ "space",
181
+ "--eval-bleu-args",
182
+ '{"beam": 4, "min_len": 10}',
183
+ ],
184
+ )
185
+
186
+ def test_lstm(self):
187
+ with contextlib.redirect_stdout(StringIO()):
188
+ with tempfile.TemporaryDirectory("test_lstm") as data_dir:
189
+ create_dummy_data(data_dir)
190
+ preprocess_translation_data(data_dir)
191
+ train_translation_model(
192
+ data_dir,
193
+ "lstm_wiseman_iwslt_de_en",
194
+ [
195
+ "--encoder-layers",
196
+ "2",
197
+ "--decoder-layers",
198
+ "2",
199
+ "--encoder-embed-dim",
200
+ "8",
201
+ "--decoder-embed-dim",
202
+ "8",
203
+ "--decoder-out-embed-dim",
204
+ "8",
205
+ ],
206
+ )
207
+ generate_main(data_dir)
208
+
209
+ def test_lstm_bidirectional(self):
210
+ with contextlib.redirect_stdout(StringIO()):
211
+ with tempfile.TemporaryDirectory("test_lstm_bidirectional") as data_dir:
212
+ create_dummy_data(data_dir)
213
+ preprocess_translation_data(data_dir)
214
+ train_translation_model(
215
+ data_dir,
216
+ "lstm",
217
+ [
218
+ "--encoder-layers",
219
+ "2",
220
+ "--encoder-bidirectional",
221
+ "--encoder-hidden-size",
222
+ "16",
223
+ "--encoder-embed-dim",
224
+ "8",
225
+ "--decoder-embed-dim",
226
+ "8",
227
+ "--decoder-out-embed-dim",
228
+ "8",
229
+ "--decoder-layers",
230
+ "2",
231
+ ],
232
+ )
233
+ generate_main(data_dir)
234
+
235
+ def test_transformer(self):
236
+ with contextlib.redirect_stdout(StringIO()):
237
+ with tempfile.TemporaryDirectory("test_transformer") as data_dir:
238
+ create_dummy_data(data_dir)
239
+ preprocess_translation_data(data_dir)
240
+ train_translation_model(
241
+ data_dir,
242
+ "transformer_iwslt_de_en",
243
+ [
244
+ "--encoder-layers",
245
+ "2",
246
+ "--decoder-layers",
247
+ "2",
248
+ "--encoder-embed-dim",
249
+ "8",
250
+ "--decoder-embed-dim",
251
+ "8",
252
+ ],
253
+ run_validation=True,
254
+ )
255
+ generate_main(data_dir)
256
+
257
+ def test_multilingual_transformer(self):
258
+ # test with all combinations of encoder/decoder lang tokens
259
+ encoder_langtok_flags = [
260
+ [],
261
+ ["--encoder-langtok", "src"],
262
+ ["--encoder-langtok", "tgt"],
263
+ ]
264
+ decoder_langtok_flags = [[], ["--decoder-langtok"]]
265
+ with contextlib.redirect_stdout(StringIO()):
266
+ for i in range(len(encoder_langtok_flags)):
267
+ for j in range(len(decoder_langtok_flags)):
268
+ enc_ltok_flag = encoder_langtok_flags[i]
269
+ dec_ltok_flag = decoder_langtok_flags[j]
270
+ with tempfile.TemporaryDirectory(
271
+ f"test_multilingual_transformer_{i}_{j}"
272
+ ) as data_dir:
273
+ create_dummy_data(data_dir)
274
+ preprocess_translation_data(data_dir)
275
+ train_translation_model(
276
+ data_dir,
277
+ arch="multilingual_transformer",
278
+ task="multilingual_translation",
279
+ extra_flags=[
280
+ "--encoder-layers",
281
+ "2",
282
+ "--decoder-layers",
283
+ "2",
284
+ "--encoder-embed-dim",
285
+ "8",
286
+ "--decoder-embed-dim",
287
+ "8",
288
+ ]
289
+ + enc_ltok_flag
290
+ + dec_ltok_flag,
291
+ lang_flags=["--lang-pairs", "in-out,out-in"],
292
+ run_validation=True,
293
+ extra_valid_flags=enc_ltok_flag + dec_ltok_flag,
294
+ )
295
+ generate_main(
296
+ data_dir,
297
+ extra_flags=[
298
+ "--task",
299
+ "multilingual_translation",
300
+ "--lang-pairs",
301
+ "in-out,out-in",
302
+ "--source-lang",
303
+ "in",
304
+ "--target-lang",
305
+ "out",
306
+ ]
307
+ + enc_ltok_flag
308
+ + dec_ltok_flag,
309
+ )
310
+
311
+ @unittest.skipIf(
312
+ sys.platform.lower() == "darwin", "skip latent depth test on MacOS"
313
+ )
314
+ def test_multilingual_translation_latent_depth(self):
315
+ # test with latent depth in encoder, decoder, or both
316
+ encoder_latent_layer = [[], ["--encoder-latent-layer"]]
317
+ decoder_latent_layer = [[], ["--decoder-latent-layer"]]
318
+ with contextlib.redirect_stdout(StringIO()):
319
+ for i in range(len(encoder_latent_layer)):
320
+ for j in range(len(decoder_latent_layer)):
321
+ if i == 0 and j == 0:
322
+ continue
323
+ enc_ll_flag = encoder_latent_layer[i]
324
+ dec_ll_flag = decoder_latent_layer[j]
325
+ with tempfile.TemporaryDirectory(
326
+ f"test_multilingual_translation_latent_depth_{i}_{j}"
327
+ ) as data_dir:
328
+ create_dummy_data(data_dir)
329
+ preprocess_translation_data(
330
+ data_dir, extra_flags=["--joined-dictionary"]
331
+ )
332
+ train_translation_model(
333
+ data_dir,
334
+ arch="latent_multilingual_transformer",
335
+ task="multilingual_translation_latent_depth",
336
+ extra_flags=[
337
+ "--user-dir",
338
+ "examples/latent_depth/latent_depth_src",
339
+ "--encoder-layers",
340
+ "2",
341
+ "--decoder-layers",
342
+ "2",
343
+ "--encoder-embed-dim",
344
+ "8",
345
+ "--decoder-embed-dim",
346
+ "8",
347
+ "--share-encoders",
348
+ "--share-decoders",
349
+ "--sparsity-weight",
350
+ "0.1",
351
+ ]
352
+ + enc_ll_flag
353
+ + dec_ll_flag,
354
+ lang_flags=["--lang-pairs", "in-out,out-in"],
355
+ run_validation=True,
356
+ extra_valid_flags=[
357
+ "--user-dir",
358
+ "examples/latent_depth/latent_depth_src",
359
+ ]
360
+ + enc_ll_flag
361
+ + dec_ll_flag,
362
+ )
363
+ generate_main(
364
+ data_dir,
365
+ extra_flags=[
366
+ "--user-dir",
367
+ "examples/latent_depth/latent_depth_src",
368
+ "--task",
369
+ "multilingual_translation_latent_depth",
370
+ "--lang-pairs",
371
+ "in-out,out-in",
372
+ "--source-lang",
373
+ "in",
374
+ "--target-lang",
375
+ "out",
376
+ ]
377
+ + enc_ll_flag
378
+ + dec_ll_flag,
379
+ )
380
+
381
+ def test_translation_multi_simple_epoch(self):
382
+ # test with all combinations of encoder/decoder lang tokens
383
+ encoder_langtok_flags = [
384
+ [],
385
+ ["--encoder-langtok", "src"],
386
+ ["--encoder-langtok", "tgt"],
387
+ ]
388
+ decoder_langtok_flags = [[], ["--decoder-langtok"]]
389
+ with contextlib.redirect_stdout(StringIO()):
390
+ for i in range(len(encoder_langtok_flags)):
391
+ for j in range(len(decoder_langtok_flags)):
392
+ enc_ltok_flag = encoder_langtok_flags[i]
393
+ dec_ltok_flag = decoder_langtok_flags[j]
394
+ with tempfile.TemporaryDirectory(
395
+ f"test_translation_multi_simple_epoch_{i}_{j}"
396
+ ) as data_dir:
397
+ create_dummy_data(data_dir)
398
+ preprocess_translation_data(
399
+ data_dir, extra_flags=["--joined-dictionary"]
400
+ )
401
+ train_translation_model(
402
+ data_dir,
403
+ arch="transformer",
404
+ task="translation_multi_simple_epoch",
405
+ extra_flags=[
406
+ "--encoder-layers",
407
+ "2",
408
+ "--decoder-layers",
409
+ "2",
410
+ "--encoder-embed-dim",
411
+ "8",
412
+ "--decoder-embed-dim",
413
+ "8",
414
+ "--sampling-method",
415
+ "temperature",
416
+ "--sampling-temperature",
417
+ "1.5",
418
+ "--virtual-epoch-size",
419
+ "1000",
420
+ ]
421
+ + enc_ltok_flag
422
+ + dec_ltok_flag,
423
+ lang_flags=["--lang-pairs", "in-out,out-in"],
424
+ run_validation=True,
425
+ extra_valid_flags=enc_ltok_flag + dec_ltok_flag,
426
+ )
427
+ generate_main(
428
+ data_dir,
429
+ extra_flags=[
430
+ "--task",
431
+ "translation_multi_simple_epoch",
432
+ "--lang-pairs",
433
+ "in-out,out-in",
434
+ "--source-lang",
435
+ "in",
436
+ "--target-lang",
437
+ "out",
438
+ ]
439
+ + enc_ltok_flag
440
+ + dec_ltok_flag,
441
+ )
442
+
443
+ def test_translation_multi_simple_epoch_no_vepoch(self):
444
+ # test with all combinations of encoder/decoder lang tokens
445
+ with contextlib.redirect_stdout(StringIO()):
446
+ enc_ltok_flag = ["--encoder-langtok", "src"]
447
+ dec_ltok_flag = ["--decoder-langtok"]
448
+ with tempfile.TemporaryDirectory(
449
+ "test_translation_multi_simple_epoch_dict"
450
+ ) as data_dir:
451
+ create_dummy_data(data_dir)
452
+ preprocess_translation_data(data_dir, extra_flags=[])
453
+ train_translation_model(
454
+ data_dir,
455
+ arch="transformer",
456
+ task="translation_multi_simple_epoch",
457
+ extra_flags=[
458
+ "--encoder-layers",
459
+ "2",
460
+ "--decoder-layers",
461
+ "2",
462
+ "--encoder-embed-dim",
463
+ "8",
464
+ "--decoder-embed-dim",
465
+ "8",
466
+ "--sampling-method",
467
+ "temperature",
468
+ "--sampling-temperature",
469
+ "1.5",
470
+ ]
471
+ + enc_ltok_flag
472
+ + dec_ltok_flag,
473
+ lang_flags=["--lang-pairs", "in-out"],
474
+ run_validation=True,
475
+ extra_valid_flags=enc_ltok_flag + dec_ltok_flag,
476
+ )
477
+ generate_main(
478
+ data_dir,
479
+ extra_flags=[
480
+ "--task",
481
+ "translation_multi_simple_epoch",
482
+ "--lang-pairs",
483
+ "in-out",
484
+ "--source-lang",
485
+ "in",
486
+ "--target-lang",
487
+ "out",
488
+ ]
489
+ + enc_ltok_flag
490
+ + dec_ltok_flag,
491
+ )
492
+
493
+ def test_translation_multi_simple_epoch_dicts(self):
494
+ # test with all combinations of encoder/decoder lang tokens
495
+ with contextlib.redirect_stdout(StringIO()):
496
+ enc_ltok_flag = ["--encoder-langtok", "src"]
497
+ dec_ltok_flag = ["--decoder-langtok"]
498
+ with tempfile.TemporaryDirectory(
499
+ "test_translation_multi_simple_epoch_dict"
500
+ ) as data_dir:
501
+ create_dummy_data(data_dir)
502
+ preprocess_translation_data(data_dir, extra_flags=[])
503
+ train_translation_model(
504
+ data_dir,
505
+ arch="transformer",
506
+ task="translation_multi_simple_epoch",
507
+ extra_flags=[
508
+ "--encoder-layers",
509
+ "2",
510
+ "--decoder-layers",
511
+ "2",
512
+ "--encoder-embed-dim",
513
+ "8",
514
+ "--decoder-embed-dim",
515
+ "8",
516
+ "--sampling-method",
517
+ "temperature",
518
+ "--sampling-temperature",
519
+ "1.5",
520
+ "--virtual-epoch-size",
521
+ "1000",
522
+ ]
523
+ + enc_ltok_flag
524
+ + dec_ltok_flag,
525
+ lang_flags=["--lang-pairs", "in-out"],
526
+ run_validation=True,
527
+ extra_valid_flags=enc_ltok_flag + dec_ltok_flag,
528
+ )
529
+ generate_main(
530
+ data_dir,
531
+ extra_flags=[
532
+ "--task",
533
+ "translation_multi_simple_epoch",
534
+ "--lang-pairs",
535
+ "in-out",
536
+ "--source-lang",
537
+ "in",
538
+ "--target-lang",
539
+ "out",
540
+ ]
541
+ + enc_ltok_flag
542
+ + dec_ltok_flag,
543
+ )
544
+
545
+ def test_translation_multi_simple_epoch_src_tgt_dict_spec(self):
546
+ # test the specification of explicit --src-dict and --tgt-dict
547
+ with contextlib.redirect_stdout(StringIO()):
548
+ enc_ltok_flag = ["--encoder-langtok", "src"]
549
+ dec_ltok_flag = ["--decoder-langtok"]
550
+ with tempfile.TemporaryDirectory(
551
+ "test_translation_multi_simple_epoch_dict"
552
+ ) as data_dir:
553
+ create_dummy_data(data_dir)
554
+ preprocess_translation_data(data_dir, extra_flags=[])
555
+ train_translation_model(
556
+ data_dir,
557
+ arch="transformer",
558
+ task="translation_multi_simple_epoch",
559
+ extra_flags=[
560
+ "--source-dict",
561
+ f"{data_dir}/dict.in.txt",
562
+ "--target-dict",
563
+ f"{data_dir}/dict.out.txt",
564
+ "--encoder-layers",
565
+ "2",
566
+ "--decoder-layers",
567
+ "2",
568
+ "--encoder-embed-dim",
569
+ "8",
570
+ "--decoder-embed-dim",
571
+ "8",
572
+ "--sampling-method",
573
+ "temperature",
574
+ "--sampling-temperature",
575
+ "1.5",
576
+ "--virtual-epoch-size",
577
+ "1000",
578
+ ]
579
+ + enc_ltok_flag
580
+ + dec_ltok_flag,
581
+ lang_flags=["--lang-pairs", "in-out"],
582
+ run_validation=True,
583
+ extra_valid_flags=enc_ltok_flag + dec_ltok_flag,
584
+ )
585
+ generate_main(
586
+ data_dir,
587
+ extra_flags=[
588
+ "--task",
589
+ "translation_multi_simple_epoch",
590
+ "--lang-pairs",
591
+ "in-out",
592
+ "--source-lang",
593
+ "in",
594
+ "--target-lang",
595
+ "out",
596
+ ]
597
+ + enc_ltok_flag
598
+ + dec_ltok_flag,
599
+ )
600
+
601
+ def test_transformer_cross_self_attention(self):
602
+ with contextlib.redirect_stdout(StringIO()):
603
+ with tempfile.TemporaryDirectory(
604
+ "test_transformer_cross_self_attention"
605
+ ) as data_dir:
606
+ create_dummy_data(data_dir)
607
+ preprocess_translation_data(data_dir)
608
+ train_translation_model(
609
+ data_dir,
610
+ "transformer_iwslt_de_en",
611
+ [
612
+ "--encoder-layers",
613
+ "2",
614
+ "--decoder-layers",
615
+ "2",
616
+ "--encoder-embed-dim",
617
+ "8",
618
+ "--decoder-embed-dim",
619
+ "8",
620
+ "--decoder-embed-dim",
621
+ "8",
622
+ "--no-cross-attention",
623
+ "--cross-self-attention",
624
+ ],
625
+ run_validation=True,
626
+ )
627
+ generate_main(data_dir, extra_flags=[])
628
+
629
+ @unittest.skipIf(
630
+ version.parse(torch.__version__) > version.parse("1.8"),
631
+ "skip for latest torch versions",
632
+ )
633
+ def test_transformer_pointer_generator(self):
634
+ with contextlib.redirect_stdout(StringIO()):
635
+ with tempfile.TemporaryDirectory(
636
+ "test_transformer_pointer_generator"
637
+ ) as data_dir:
638
+ create_dummy_data(data_dir)
639
+ preprocess_summarization_data(data_dir)
640
+ train_translation_model(
641
+ data_dir,
642
+ "transformer_pointer_generator",
643
+ extra_flags=[
644
+ "--user-dir",
645
+ "examples/pointer_generator/pointer_generator_src",
646
+ "--encoder-layers",
647
+ "2",
648
+ "--decoder-layers",
649
+ "2",
650
+ "--encoder-embed-dim",
651
+ "8",
652
+ "--decoder-embed-dim",
653
+ "8",
654
+ "--alignment-layer",
655
+ "-1",
656
+ "--alignment-heads",
657
+ "1",
658
+ "--source-position-markers",
659
+ "0",
660
+ ],
661
+ run_validation=True,
662
+ extra_valid_flags=[
663
+ "--user-dir",
664
+ "examples/pointer_generator/pointer_generator_src",
665
+ ],
666
+ )
667
+ generate_main(
668
+ data_dir,
669
+ extra_flags=[
670
+ "--user-dir",
671
+ "examples/pointer_generator/pointer_generator_src",
672
+ ],
673
+ )
674
+
675
+ def test_lightconv(self):
676
+ with contextlib.redirect_stdout(StringIO()):
677
+ with tempfile.TemporaryDirectory("test_lightconv") as data_dir:
678
+ create_dummy_data(data_dir)
679
+ preprocess_translation_data(data_dir)
680
+ train_translation_model(
681
+ data_dir,
682
+ "lightconv_iwslt_de_en",
683
+ [
684
+ "--encoder-conv-type",
685
+ "lightweight",
686
+ "--decoder-conv-type",
687
+ "lightweight",
688
+ "--encoder-embed-dim",
689
+ "8",
690
+ "--decoder-embed-dim",
691
+ "8",
692
+ ],
693
+ )
694
+ generate_main(data_dir)
695
+
696
+ def test_dynamicconv(self):
697
+ with contextlib.redirect_stdout(StringIO()):
698
+ with tempfile.TemporaryDirectory("test_dynamicconv") as data_dir:
699
+ create_dummy_data(data_dir)
700
+ preprocess_translation_data(data_dir)
701
+ train_translation_model(
702
+ data_dir,
703
+ "lightconv_iwslt_de_en",
704
+ [
705
+ "--encoder-conv-type",
706
+ "dynamic",
707
+ "--decoder-conv-type",
708
+ "dynamic",
709
+ "--encoder-embed-dim",
710
+ "8",
711
+ "--decoder-embed-dim",
712
+ "8",
713
+ ],
714
+ )
715
+ generate_main(data_dir)
716
+
717
+ def test_cmlm_transformer(self):
718
+ with contextlib.redirect_stdout(StringIO()):
719
+ with tempfile.TemporaryDirectory("test_cmlm_transformer") as data_dir:
720
+ create_dummy_data(data_dir)
721
+ preprocess_translation_data(data_dir, ["--joined-dictionary"])
722
+ train_translation_model(
723
+ data_dir,
724
+ "cmlm_transformer",
725
+ [
726
+ "--apply-bert-init",
727
+ "--criterion",
728
+ "nat_loss",
729
+ "--noise",
730
+ "full_mask",
731
+ "--pred-length-offset",
732
+ "--length-loss-factor",
733
+ "0.1",
734
+ ],
735
+ task="translation_lev",
736
+ )
737
+ generate_main(
738
+ data_dir,
739
+ [
740
+ "--task",
741
+ "translation_lev",
742
+ "--iter-decode-max-iter",
743
+ "9",
744
+ "--iter-decode-eos-penalty",
745
+ "0",
746
+ "--print-step",
747
+ ],
748
+ )
749
+
750
+ def test_nonautoregressive_transformer(self):
751
+ with contextlib.redirect_stdout(StringIO()):
752
+ with tempfile.TemporaryDirectory(
753
+ "test_nonautoregressive_transformer"
754
+ ) as data_dir:
755
+ create_dummy_data(data_dir)
756
+ preprocess_translation_data(data_dir, ["--joined-dictionary"])
757
+ train_translation_model(
758
+ data_dir,
759
+ "nonautoregressive_transformer",
760
+ [
761
+ "--apply-bert-init",
762
+ "--src-embedding-copy",
763
+ "--criterion",
764
+ "nat_loss",
765
+ "--noise",
766
+ "full_mask",
767
+ "--pred-length-offset",
768
+ "--length-loss-factor",
769
+ "0.1",
770
+ ],
771
+ task="translation_lev",
772
+ )
773
+ generate_main(
774
+ data_dir,
775
+ [
776
+ "--task",
777
+ "translation_lev",
778
+ "--iter-decode-max-iter",
779
+ "0",
780
+ "--iter-decode-eos-penalty",
781
+ "0",
782
+ "--print-step",
783
+ ],
784
+ )
785
+
786
+ # def test_nat_crf_transformer(self):
787
+ # with contextlib.redirect_stdout(StringIO()):
788
+ # with tempfile.TemporaryDirectory('test_nat_crf_transformer') as data_dir:
789
+ # create_dummy_data(data_dir)
790
+ # preprocess_translation_data(data_dir, ['--joined-dictionary'])
791
+ # train_translation_model(data_dir, 'nacrf_transformer', [
792
+ # '--apply-bert-init', '--criterion',
793
+ # 'nat_loss', '--noise', 'full_mask', '--pred-length-offset',
794
+ # '--length-loss-factor', '0.1',
795
+ # '--word-ins-loss-factor', '0.5',
796
+ # '--crf-lowrank-approx', '1',
797
+ # '--crf-beam-approx', '1'
798
+ # ], task='translation_lev')
799
+ # generate_main(data_dir, [
800
+ # '--task', 'translation_lev',
801
+ # '--iter-decode-max-iter', '0',
802
+ # '--iter-decode-eos-penalty', '0',
803
+ # '--print-step',
804
+ # ])
805
+
806
+ def test_iterative_nonautoregressive_transformer(self):
807
+ with contextlib.redirect_stdout(StringIO()):
808
+ with tempfile.TemporaryDirectory(
809
+ "test_iterative_nonautoregressive_transformer"
810
+ ) as data_dir:
811
+ create_dummy_data(data_dir)
812
+ preprocess_translation_data(data_dir, ["--joined-dictionary"])
813
+ train_translation_model(
814
+ data_dir,
815
+ "iterative_nonautoregressive_transformer",
816
+ [
817
+ "--apply-bert-init",
818
+ "--src-embedding-copy",
819
+ "--criterion",
820
+ "nat_loss",
821
+ "--noise",
822
+ "full_mask",
823
+ "--stochastic-approx",
824
+ "--dae-ratio",
825
+ "0.5",
826
+ "--train-step",
827
+ "3",
828
+ ],
829
+ task="translation_lev",
830
+ )
831
+ generate_main(
832
+ data_dir,
833
+ [
834
+ "--task",
835
+ "translation_lev",
836
+ "--iter-decode-max-iter",
837
+ "9",
838
+ "--iter-decode-eos-penalty",
839
+ "0",
840
+ "--print-step",
841
+ ],
842
+ )
843
+
844
+ def test_insertion_transformer(self):
845
+ with contextlib.redirect_stdout(StringIO()):
846
+ with tempfile.TemporaryDirectory("test_insertion_transformer") as data_dir:
847
+ create_dummy_data(data_dir)
848
+ preprocess_translation_data(data_dir, ["--joined-dictionary"])
849
+ train_translation_model(
850
+ data_dir,
851
+ "insertion_transformer",
852
+ [
853
+ "--apply-bert-init",
854
+ "--criterion",
855
+ "nat_loss",
856
+ "--noise",
857
+ "random_mask",
858
+ ],
859
+ task="translation_lev",
860
+ )
861
+ generate_main(
862
+ data_dir,
863
+ [
864
+ "--task",
865
+ "translation_lev",
866
+ "--iter-decode-max-iter",
867
+ "9",
868
+ "--iter-decode-eos-penalty",
869
+ "0",
870
+ "--print-step",
871
+ ],
872
+ )
873
+
874
+ def test_mixture_of_experts(self):
875
+ with contextlib.redirect_stdout(StringIO()):
876
+ with tempfile.TemporaryDirectory("test_moe") as data_dir:
877
+ create_dummy_data(data_dir)
878
+ preprocess_translation_data(data_dir)
879
+ train_translation_model(
880
+ data_dir,
881
+ "transformer_iwslt_de_en",
882
+ [
883
+ "--task",
884
+ "translation_moe",
885
+ "--user-dir",
886
+ "examples/translation_moe/translation_moe_src",
887
+ "--method",
888
+ "hMoElp",
889
+ "--mean-pool-gating-network",
890
+ "--num-experts",
891
+ "3",
892
+ "--encoder-layers",
893
+ "2",
894
+ "--decoder-layers",
895
+ "2",
896
+ "--encoder-embed-dim",
897
+ "8",
898
+ "--decoder-embed-dim",
899
+ "8",
900
+ ],
901
+ )
902
+ generate_main(
903
+ data_dir,
904
+ [
905
+ "--task",
906
+ "translation_moe",
907
+ "--user-dir",
908
+ "examples/translation_moe/translation_moe_src",
909
+ "--method",
910
+ "hMoElp",
911
+ "--mean-pool-gating-network",
912
+ "--num-experts",
913
+ "3",
914
+ "--gen-expert",
915
+ "0",
916
+ ],
917
+ )
918
+
919
+ def test_alignment(self):
920
+ with contextlib.redirect_stdout(StringIO()):
921
+ with tempfile.TemporaryDirectory("test_alignment") as data_dir:
922
+ create_dummy_data(data_dir, alignment=True)
923
+ preprocess_translation_data(data_dir, ["--align-suffix", "align"])
924
+ train_translation_model(
925
+ data_dir,
926
+ "transformer_align",
927
+ [
928
+ "--encoder-layers",
929
+ "2",
930
+ "--decoder-layers",
931
+ "2",
932
+ "--encoder-embed-dim",
933
+ "8",
934
+ "--decoder-embed-dim",
935
+ "8",
936
+ "--load-alignments",
937
+ "--alignment-layer",
938
+ "1",
939
+ "--criterion",
940
+ "label_smoothed_cross_entropy_with_alignment",
941
+ ],
942
+ run_validation=True,
943
+ )
944
+ generate_main(data_dir)
945
+
946
+ def test_laser_lstm(self):
947
+ with contextlib.redirect_stdout(StringIO()):
948
+ with tempfile.TemporaryDirectory("test_laser_lstm") as data_dir:
949
+ laser_config_file = create_laser_data_and_config_json(data_dir)
950
+ train_translation_model(
951
+ laser_config_file.name,
952
+ "laser_lstm",
953
+ [
954
+ "--user-dir",
955
+ "examples/laser/laser_src",
956
+ "--weighting-alpha",
957
+ "0.3",
958
+ "--encoder-bidirectional",
959
+ "--encoder-hidden-size",
960
+ "512",
961
+ "--encoder-layers",
962
+ "5",
963
+ "--decoder-layers",
964
+ "1",
965
+ "--encoder-embed-dim",
966
+ "320",
967
+ "--decoder-embed-dim",
968
+ "320",
969
+ "--decoder-lang-embed-dim",
970
+ "32",
971
+ "--save-dir",
972
+ data_dir,
973
+ "--disable-validation",
974
+ ],
975
+ task="laser",
976
+ lang_flags=[],
977
+ )
978
+
979
+ def test_laser_transformer(self):
980
+ with contextlib.redirect_stdout(StringIO()):
981
+ with tempfile.TemporaryDirectory("test_laser_transformer") as data_dir:
982
+ laser_config_file = create_laser_data_and_config_json(data_dir)
983
+ train_translation_model(
984
+ laser_config_file.name,
985
+ "laser_transformer",
986
+ [
987
+ "--user-dir",
988
+ "examples/laser/laser_src",
989
+ "--weighting-alpha",
990
+ "0.3",
991
+ "--encoder-embed-dim",
992
+ "320",
993
+ "--decoder-embed-dim",
994
+ "320",
995
+ "--decoder-lang-embed-dim",
996
+ "32",
997
+ "--save-dir",
998
+ data_dir,
999
+ "--disable-validation",
1000
+ ],
1001
+ task="laser",
1002
+ lang_flags=[],
1003
+ )
1004
+
1005
+ def test_alignment_full_context(self):
1006
+ with contextlib.redirect_stdout(StringIO()):
1007
+ with tempfile.TemporaryDirectory("test_alignment") as data_dir:
1008
+ create_dummy_data(data_dir, alignment=True)
1009
+ preprocess_translation_data(data_dir, ["--align-suffix", "align"])
1010
+ train_translation_model(
1011
+ data_dir,
1012
+ "transformer_align",
1013
+ [
1014
+ "--encoder-layers",
1015
+ "2",
1016
+ "--decoder-layers",
1017
+ "2",
1018
+ "--encoder-embed-dim",
1019
+ "8",
1020
+ "--decoder-embed-dim",
1021
+ "8",
1022
+ "--load-alignments",
1023
+ "--alignment-layer",
1024
+ "1",
1025
+ "--criterion",
1026
+ "label_smoothed_cross_entropy_with_alignment",
1027
+ "--full-context-alignment",
1028
+ ],
1029
+ run_validation=True,
1030
+ )
1031
+ generate_main(data_dir)
1032
+
1033
+ def test_transformer_layerdrop(self):
1034
+ with contextlib.redirect_stdout(StringIO()):
1035
+ with tempfile.TemporaryDirectory("test_transformer_layerdrop") as data_dir:
1036
+ create_dummy_data(data_dir)
1037
+ preprocess_translation_data(data_dir)
1038
+ train_translation_model(
1039
+ data_dir,
1040
+ "transformer_iwslt_de_en",
1041
+ [
1042
+ "--encoder-layers",
1043
+ "3",
1044
+ "--decoder-layers",
1045
+ "3",
1046
+ "--encoder-embed-dim",
1047
+ "8",
1048
+ "--decoder-embed-dim",
1049
+ "8",
1050
+ "--encoder-layerdrop",
1051
+ "0.01",
1052
+ "--decoder-layerdrop",
1053
+ "0.01",
1054
+ ],
1055
+ )
1056
+ generate_main(data_dir)
1057
+ generate_main(
1058
+ data_dir,
1059
+ [
1060
+ "--model-overrides",
1061
+ "{'encoder_layers_to_keep':'0,2','decoder_layers_to_keep':'1'}",
1062
+ ],
1063
+ )
1064
+
1065
+
1066
+ class TestStories(unittest.TestCase):
1067
+ def setUp(self):
1068
+ logging.disable(logging.CRITICAL)
1069
+
1070
+ def tearDown(self):
1071
+ logging.disable(logging.NOTSET)
1072
+
1073
+ def test_fconv_self_att_wp(self):
1074
+ with contextlib.redirect_stdout(StringIO()):
1075
+ with tempfile.TemporaryDirectory("test_fconv_self_att_wp") as data_dir:
1076
+ create_dummy_data(data_dir)
1077
+ preprocess_translation_data(data_dir)
1078
+ config = [
1079
+ "--encoder-layers",
1080
+ "[(128, 3)] * 2",
1081
+ "--decoder-layers",
1082
+ "[(128, 3)] * 2",
1083
+ "--decoder-attention",
1084
+ "True",
1085
+ "--encoder-attention",
1086
+ "False",
1087
+ "--gated-attention",
1088
+ "True",
1089
+ "--self-attention",
1090
+ "True",
1091
+ "--project-input",
1092
+ "True",
1093
+ "--encoder-embed-dim",
1094
+ "8",
1095
+ "--decoder-embed-dim",
1096
+ "8",
1097
+ "--decoder-out-embed-dim",
1098
+ "8",
1099
+ "--multihead-self-attention-nheads",
1100
+ "2",
1101
+ ]
1102
+ train_translation_model(data_dir, "fconv_self_att_wp", config)
1103
+ generate_main(data_dir)
1104
+
1105
+ # fusion model
1106
+ os.rename(
1107
+ os.path.join(data_dir, "checkpoint_last.pt"),
1108
+ os.path.join(data_dir, "pretrained.pt"),
1109
+ )
1110
+ config.extend(
1111
+ [
1112
+ "--pretrained",
1113
+ "True",
1114
+ "--pretrained-checkpoint",
1115
+ os.path.join(data_dir, "pretrained.pt"),
1116
+ "--save-dir",
1117
+ os.path.join(data_dir, "fusion_model"),
1118
+ ]
1119
+ )
1120
+ train_translation_model(data_dir, "fconv_self_att_wp", config)
1121
+
1122
+
1123
+ class TestLanguageModeling(unittest.TestCase):
1124
+ def setUp(self):
1125
+ logging.disable(logging.CRITICAL)
1126
+
1127
+ def tearDown(self):
1128
+ logging.disable(logging.NOTSET)
1129
+
1130
+ def test_fconv_lm(self):
1131
+ with contextlib.redirect_stdout(StringIO()):
1132
+ with tempfile.TemporaryDirectory("test_fconv_lm") as data_dir:
1133
+ create_dummy_data(data_dir)
1134
+ preprocess_lm_data(data_dir)
1135
+ train_language_model(
1136
+ data_dir,
1137
+ "fconv_lm",
1138
+ [
1139
+ "--decoder-layers",
1140
+ "[(850, 3)] * 2 + [(1024,4)]",
1141
+ "--decoder-embed-dim",
1142
+ "280",
1143
+ "--optimizer",
1144
+ "nag",
1145
+ "--lr",
1146
+ "0.1",
1147
+ ],
1148
+ )
1149
+ eval_lm_main(data_dir)
1150
+ generate_main(
1151
+ data_dir,
1152
+ [
1153
+ "--task",
1154
+ "language_modeling",
1155
+ "--sample-break-mode",
1156
+ "eos",
1157
+ "--tokens-per-sample",
1158
+ "500",
1159
+ ],
1160
+ )
1161
+
1162
+ def test_transformer_lm(self):
1163
+ with contextlib.redirect_stdout(StringIO()):
1164
+ with tempfile.TemporaryDirectory("test_transformer_lm") as data_dir:
1165
+ create_dummy_data(data_dir)
1166
+ preprocess_lm_data(data_dir)
1167
+ train_language_model(
1168
+ data_dir,
1169
+ "transformer_lm",
1170
+ ["--add-bos-token", "--nval", "1"],
1171
+ run_validation=True,
1172
+ )
1173
+ eval_lm_main(data_dir)
1174
+ eval_lm_main(data_dir, extra_flags=["--context-window", "25"])
1175
+ generate_main(
1176
+ data_dir,
1177
+ [
1178
+ "--task",
1179
+ "language_modeling",
1180
+ "--sample-break-mode",
1181
+ "eos",
1182
+ "--tokens-per-sample",
1183
+ "500",
1184
+ ],
1185
+ )
1186
+
1187
+ def test_normformer_lm(self):
1188
+ with contextlib.redirect_stdout(StringIO()):
1189
+ with tempfile.TemporaryDirectory("test_transformer_lm") as data_dir:
1190
+ create_dummy_data(data_dir)
1191
+ preprocess_lm_data(data_dir)
1192
+ train_language_model(
1193
+ data_dir,
1194
+ "transformer_lm",
1195
+ [
1196
+ "--add-bos-token",
1197
+ "--nval",
1198
+ "1",
1199
+ "--scale-fc",
1200
+ "--scale-heads",
1201
+ "--scale-attn",
1202
+ "--scale-fc",
1203
+ ],
1204
+ run_validation=True,
1205
+ )
1206
+ eval_lm_main(data_dir)
1207
+ eval_lm_main(data_dir, extra_flags=["--context-window", "25"])
1208
+ generate_main(
1209
+ data_dir,
1210
+ [
1211
+ "--task",
1212
+ "language_modeling",
1213
+ "--sample-break-mode",
1214
+ "eos",
1215
+ "--tokens-per-sample",
1216
+ "500",
1217
+ ],
1218
+ )
1219
+
1220
+ def test_transformer_lm_with_adaptive_softmax(self):
1221
+ with contextlib.redirect_stdout(StringIO()):
1222
+ with tempfile.TemporaryDirectory(
1223
+ "test_transformer_lm_with_adaptive_softmax"
1224
+ ) as data_dir:
1225
+ create_dummy_data(data_dir)
1226
+ preprocess_lm_data(data_dir)
1227
+ train_language_model(
1228
+ data_dir,
1229
+ "transformer_lm",
1230
+ [
1231
+ "--add-bos-token",
1232
+ "--criterion",
1233
+ "adaptive_loss",
1234
+ "--adaptive-softmax-cutoff",
1235
+ "5,10,15",
1236
+ ],
1237
+ run_validation=True,
1238
+ )
1239
+ eval_lm_main(data_dir)
1240
+ generate_main(
1241
+ data_dir,
1242
+ [
1243
+ "--task",
1244
+ "language_modeling",
1245
+ "--sample-break-mode",
1246
+ "eos",
1247
+ "--tokens-per-sample",
1248
+ "500",
1249
+ ],
1250
+ )
1251
+
1252
+ def test_lightconv_lm(self):
1253
+ with contextlib.redirect_stdout(StringIO()):
1254
+ with tempfile.TemporaryDirectory("test_lightconv_lm") as data_dir:
1255
+ create_dummy_data(data_dir)
1256
+ preprocess_lm_data(data_dir)
1257
+ train_language_model(
1258
+ data_dir,
1259
+ "lightconv_lm",
1260
+ ["--add-bos-token"],
1261
+ run_validation=True,
1262
+ )
1263
+ eval_lm_main(data_dir)
1264
+ generate_main(
1265
+ data_dir,
1266
+ [
1267
+ "--task",
1268
+ "language_modeling",
1269
+ "--sample-break-mode",
1270
+ "eos",
1271
+ "--tokens-per-sample",
1272
+ "500",
1273
+ ],
1274
+ )
1275
+
1276
+ def test_lstm_lm(self):
1277
+ with contextlib.redirect_stdout(StringIO()):
1278
+ with tempfile.TemporaryDirectory("test_lstm_lm") as data_dir:
1279
+ create_dummy_data(data_dir)
1280
+ preprocess_lm_data(data_dir)
1281
+ train_language_model(
1282
+ data_dir,
1283
+ "lstm_lm",
1284
+ ["--add-bos-token"],
1285
+ run_validation=True,
1286
+ )
1287
+ eval_lm_main(data_dir)
1288
+ generate_main(
1289
+ data_dir,
1290
+ [
1291
+ "--task",
1292
+ "language_modeling",
1293
+ "--sample-break-mode",
1294
+ "eos",
1295
+ "--tokens-per-sample",
1296
+ "500",
1297
+ ],
1298
+ )
1299
+
1300
+ def test_lstm_lm_residuals(self):
1301
+ with contextlib.redirect_stdout(StringIO()):
1302
+ with tempfile.TemporaryDirectory("test_lstm_lm_residuals") as data_dir:
1303
+ create_dummy_data(data_dir)
1304
+ preprocess_lm_data(data_dir)
1305
+ train_language_model(
1306
+ data_dir,
1307
+ "lstm_lm",
1308
+ ["--add-bos-token", "--residuals"],
1309
+ run_validation=True,
1310
+ )
1311
+ eval_lm_main(data_dir)
1312
+ generate_main(
1313
+ data_dir,
1314
+ [
1315
+ "--task",
1316
+ "language_modeling",
1317
+ "--sample-break-mode",
1318
+ "eos",
1319
+ "--tokens-per-sample",
1320
+ "500",
1321
+ ],
1322
+ )
1323
+
1324
+ @unittest.skipIf(not has_hf_transformers, "skip test if transformers is missing")
1325
+ def test_transformer_xl_bptt_lm(self):
1326
+ with contextlib.redirect_stdout(StringIO()):
1327
+ with tempfile.TemporaryDirectory("test_transformer_xl_bptt_lm") as data_dir:
1328
+ create_dummy_data(data_dir)
1329
+ preprocess_lm_data(data_dir)
1330
+ task_flags = [
1331
+ "--user-dir",
1332
+ "examples/truncated_bptt",
1333
+ "--task",
1334
+ "truncated_bptt_lm",
1335
+ "--batch-size",
1336
+ "2",
1337
+ "--tokens-per-sample",
1338
+ "50",
1339
+ ]
1340
+ train_language_model(
1341
+ data_dir=data_dir,
1342
+ arch="transformer_xl",
1343
+ extra_flags=task_flags
1344
+ + [
1345
+ "--n-layer",
1346
+ "2",
1347
+ ],
1348
+ task="truncated_bptt_lm",
1349
+ run_validation=True,
1350
+ extra_valid_flags=task_flags,
1351
+ )
1352
+ eval_lm_main(data_dir, extra_flags=task_flags)
1353
+ # Train with activation offloading
1354
+ train_language_model(
1355
+ data_dir=data_dir,
1356
+ arch="transformer_xl",
1357
+ extra_flags=task_flags
1358
+ + [
1359
+ "--n-layer",
1360
+ "2",
1361
+ "--offload-activations",
1362
+ ],
1363
+ task="truncated_bptt_lm",
1364
+ run_validation=True,
1365
+ extra_valid_flags=task_flags,
1366
+ )
1367
+
1368
+
1369
+ class TestMaskedLanguageModel(unittest.TestCase):
1370
+ def setUp(self):
1371
+ logging.disable(logging.CRITICAL)
1372
+
1373
+ def tearDown(self):
1374
+ logging.disable(logging.NOTSET)
1375
+
1376
+ def test_legacy_masked_lm(self):
1377
+ with contextlib.redirect_stdout(StringIO()):
1378
+ with tempfile.TemporaryDirectory("test_legacy_mlm") as data_dir:
1379
+ create_dummy_data(data_dir)
1380
+ preprocess_lm_data(data_dir)
1381
+ train_legacy_masked_language_model(data_dir, "masked_lm")
1382
+
1383
+ def test_roberta_masked_lm(self):
1384
+ with contextlib.redirect_stdout(StringIO()):
1385
+ with tempfile.TemporaryDirectory("test_roberta_mlm") as data_dir:
1386
+ create_dummy_data(data_dir)
1387
+ preprocess_lm_data(data_dir)
1388
+ train_masked_lm(
1389
+ data_dir, "roberta_base", extra_flags=["--encoder-layers", "2"]
1390
+ )
1391
+
1392
+ def test_roberta_sentence_prediction(self):
1393
+ num_classes = 3
1394
+ with contextlib.redirect_stdout(StringIO()):
1395
+ with tempfile.TemporaryDirectory("test_roberta_head") as data_dir:
1396
+ create_dummy_roberta_head_data(data_dir, num_classes=num_classes)
1397
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1398
+ preprocess_lm_data(os.path.join(data_dir, "label"))
1399
+ train_roberta_head(data_dir, "roberta_base", num_classes=num_classes)
1400
+
1401
+ def test_roberta_regression_single(self):
1402
+ num_classes = 1
1403
+ with contextlib.redirect_stdout(StringIO()):
1404
+ with tempfile.TemporaryDirectory(
1405
+ "test_roberta_regression_single"
1406
+ ) as data_dir:
1407
+ create_dummy_roberta_head_data(
1408
+ data_dir, num_classes=num_classes, regression=True
1409
+ )
1410
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1411
+ train_roberta_head(
1412
+ data_dir,
1413
+ "roberta_base",
1414
+ num_classes=num_classes,
1415
+ extra_flags=["--regression-target"],
1416
+ )
1417
+
1418
+ def test_roberta_regression_multiple(self):
1419
+ num_classes = 3
1420
+ with contextlib.redirect_stdout(StringIO()):
1421
+ with tempfile.TemporaryDirectory(
1422
+ "test_roberta_regression_multiple"
1423
+ ) as data_dir:
1424
+ create_dummy_roberta_head_data(
1425
+ data_dir, num_classes=num_classes, regression=True
1426
+ )
1427
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1428
+ train_roberta_head(
1429
+ data_dir,
1430
+ "roberta_base",
1431
+ num_classes=num_classes,
1432
+ extra_flags=["--regression-target"],
1433
+ )
1434
+
1435
+ def test_linformer_roberta_masked_lm(self):
1436
+ with contextlib.redirect_stdout(StringIO()):
1437
+ with tempfile.TemporaryDirectory("test_linformer_roberta_mlm") as data_dir:
1438
+ create_dummy_data(data_dir)
1439
+ preprocess_lm_data(data_dir)
1440
+ train_masked_lm(
1441
+ data_dir,
1442
+ "linformer_roberta_base",
1443
+ extra_flags=[
1444
+ "--user-dir",
1445
+ "examples/linformer/linformer_src",
1446
+ "--encoder-layers",
1447
+ "2",
1448
+ ],
1449
+ )
1450
+
1451
+ def test_linformer_roberta_sentence_prediction(self):
1452
+ num_classes = 3
1453
+ with contextlib.redirect_stdout(StringIO()):
1454
+ with tempfile.TemporaryDirectory("test_linformer_roberta_head") as data_dir:
1455
+ create_dummy_roberta_head_data(data_dir, num_classes=num_classes)
1456
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1457
+ preprocess_lm_data(os.path.join(data_dir, "label"))
1458
+ train_roberta_head(
1459
+ data_dir,
1460
+ "linformer_roberta_base",
1461
+ num_classes=num_classes,
1462
+ extra_flags=["--user-dir", "examples/linformer/linformer_src"],
1463
+ )
1464
+
1465
+ def test_linformer_roberta_regression_single(self):
1466
+ num_classes = 1
1467
+ with contextlib.redirect_stdout(StringIO()):
1468
+ with tempfile.TemporaryDirectory(
1469
+ "test_linformer_roberta_regression_single"
1470
+ ) as data_dir:
1471
+ create_dummy_roberta_head_data(
1472
+ data_dir, num_classes=num_classes, regression=True
1473
+ )
1474
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1475
+ train_roberta_head(
1476
+ data_dir,
1477
+ "linformer_roberta_base",
1478
+ num_classes=num_classes,
1479
+ extra_flags=[
1480
+ "--regression-target",
1481
+ "--user-dir",
1482
+ "examples/linformer/linformer_src",
1483
+ ],
1484
+ )
1485
+
1486
+ def test_linformer_roberta_regression_multiple(self):
1487
+ num_classes = 3
1488
+ with contextlib.redirect_stdout(StringIO()):
1489
+ with tempfile.TemporaryDirectory(
1490
+ "test_linformer_roberta_regression_multiple"
1491
+ ) as data_dir:
1492
+ create_dummy_roberta_head_data(
1493
+ data_dir, num_classes=num_classes, regression=True
1494
+ )
1495
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1496
+ train_roberta_head(
1497
+ data_dir,
1498
+ "linformer_roberta_base",
1499
+ num_classes=num_classes,
1500
+ extra_flags=[
1501
+ "--regression-target",
1502
+ "--user-dir",
1503
+ "examples/linformer/linformer_src",
1504
+ ],
1505
+ )
1506
+
1507
+ def _test_pretrained_masked_lm_for_translation(self, learned_pos_emb, encoder_only):
1508
+ with contextlib.redirect_stdout(StringIO()):
1509
+ with tempfile.TemporaryDirectory("test_mlm") as data_dir:
1510
+ create_dummy_data(data_dir)
1511
+ preprocess_lm_data(data_dir)
1512
+ train_legacy_masked_language_model(
1513
+ data_dir,
1514
+ arch="masked_lm",
1515
+ extra_args=("--encoder-learned-pos",) if learned_pos_emb else (),
1516
+ )
1517
+ with tempfile.TemporaryDirectory(
1518
+ "test_mlm_translation"
1519
+ ) as translation_dir:
1520
+ create_dummy_data(translation_dir)
1521
+ preprocess_translation_data(
1522
+ translation_dir, extra_flags=["--joined-dictionary"]
1523
+ )
1524
+ # Train transformer with data_dir/checkpoint_last.pt
1525
+ train_translation_model(
1526
+ translation_dir,
1527
+ arch="transformer_from_pretrained_xlm",
1528
+ extra_flags=[
1529
+ "--decoder-layers",
1530
+ "1",
1531
+ "--decoder-embed-dim",
1532
+ "32",
1533
+ "--decoder-attention-heads",
1534
+ "1",
1535
+ "--decoder-ffn-embed-dim",
1536
+ "32",
1537
+ "--encoder-layers",
1538
+ "1",
1539
+ "--encoder-embed-dim",
1540
+ "32",
1541
+ "--encoder-attention-heads",
1542
+ "1",
1543
+ "--encoder-ffn-embed-dim",
1544
+ "32",
1545
+ "--pretrained-xlm-checkpoint",
1546
+ "{}/checkpoint_last.pt".format(data_dir),
1547
+ "--activation-fn",
1548
+ "gelu",
1549
+ "--max-source-positions",
1550
+ "500",
1551
+ "--max-target-positions",
1552
+ "500",
1553
+ ]
1554
+ + (
1555
+ ["--encoder-learned-pos", "--decoder-learned-pos"]
1556
+ if learned_pos_emb
1557
+ else []
1558
+ )
1559
+ + (["--init-encoder-only"] if encoder_only else []),
1560
+ task="translation_from_pretrained_xlm",
1561
+ )
1562
+
1563
+ def test_pretrained_masked_lm_for_translation_learned_pos_emb(self):
1564
+ self._test_pretrained_masked_lm_for_translation(True, False)
1565
+
1566
+ def test_pretrained_masked_lm_for_translation_sinusoidal_pos_emb(self):
1567
+ self._test_pretrained_masked_lm_for_translation(False, False)
1568
+
1569
+ def test_pretrained_masked_lm_for_translation_encoder_only(self):
1570
+ self._test_pretrained_masked_lm_for_translation(True, True)
1571
+
1572
+ def test_r4f_roberta(self):
1573
+ num_classes = 3
1574
+ with contextlib.redirect_stdout(StringIO()):
1575
+ with tempfile.TemporaryDirectory("test_r4f_roberta_head") as data_dir:
1576
+ create_dummy_roberta_head_data(data_dir, num_classes=num_classes)
1577
+ preprocess_lm_data(os.path.join(data_dir, "input0"))
1578
+ preprocess_lm_data(os.path.join(data_dir, "label"))
1579
+ train_roberta_head(
1580
+ data_dir,
1581
+ "roberta_base",
1582
+ num_classes=num_classes,
1583
+ extra_flags=[
1584
+ "--user-dir",
1585
+ "examples/rxf/rxf_src",
1586
+ "--criterion",
1587
+ "sentence_prediction_r3f",
1588
+ "--spectral-norm-classification-head",
1589
+ ],
1590
+ )
1591
+
1592
+
1593
+ def train_legacy_masked_language_model(data_dir, arch, extra_args=()):
1594
+ train_parser = options.get_training_parser()
1595
+ # TODO: langs should be in and out right?
1596
+ train_args = options.parse_args_and_arch(
1597
+ train_parser,
1598
+ [
1599
+ "--task",
1600
+ "cross_lingual_lm",
1601
+ data_dir,
1602
+ "--arch",
1603
+ arch,
1604
+ # Optimizer args
1605
+ "--optimizer",
1606
+ "adam",
1607
+ "--lr-scheduler",
1608
+ "reduce_lr_on_plateau",
1609
+ "--lr-shrink",
1610
+ "0.5",
1611
+ "--lr",
1612
+ "0.0001",
1613
+ "--stop-min-lr",
1614
+ "1e-09",
1615
+ # dropout, attention args
1616
+ "--dropout",
1617
+ "0.1",
1618
+ "--attention-dropout",
1619
+ "0.1",
1620
+ # MLM args
1621
+ "--criterion",
1622
+ "legacy_masked_lm_loss",
1623
+ "--masked-lm-only",
1624
+ "--monolingual-langs",
1625
+ "in,out",
1626
+ "--num-segment",
1627
+ "5",
1628
+ # Transformer args: use a small transformer model for fast training
1629
+ "--encoder-layers",
1630
+ "1",
1631
+ "--encoder-embed-dim",
1632
+ "32",
1633
+ "--encoder-attention-heads",
1634
+ "1",
1635
+ "--encoder-ffn-embed-dim",
1636
+ "32",
1637
+ # Other training args
1638
+ "--max-tokens",
1639
+ "500",
1640
+ "--tokens-per-sample",
1641
+ "500",
1642
+ "--save-dir",
1643
+ data_dir,
1644
+ "--max-epoch",
1645
+ "1",
1646
+ "--no-progress-bar",
1647
+ "--distributed-world-size",
1648
+ "1",
1649
+ "--dataset-impl",
1650
+ "raw",
1651
+ "--num-workers",
1652
+ "0",
1653
+ ]
1654
+ + list(extra_args),
1655
+ )
1656
+ train.main(train_args)
1657
+
1658
+
1659
+ class TestOptimizers(unittest.TestCase):
1660
+ def setUp(self):
1661
+ logging.disable(logging.CRITICAL)
1662
+
1663
+ def tearDown(self):
1664
+ logging.disable(logging.NOTSET)
1665
+
1666
+ def test_optimizers(self):
1667
+ with contextlib.redirect_stdout(StringIO()):
1668
+ with tempfile.TemporaryDirectory("test_optimizers") as data_dir:
1669
+ # Use just a bit of data and tiny model to keep this test runtime reasonable
1670
+ create_dummy_data(data_dir, num_examples=10, maxlen=5)
1671
+ preprocess_translation_data(data_dir)
1672
+ optimizers = ["adafactor", "adam", "nag", "adagrad", "sgd", "adadelta"]
1673
+ last_checkpoint = os.path.join(data_dir, "checkpoint_last.pt")
1674
+ for optimizer in optimizers:
1675
+ if os.path.exists(last_checkpoint):
1676
+ os.remove(last_checkpoint)
1677
+ train_translation_model(
1678
+ data_dir,
1679
+ "lstm",
1680
+ [
1681
+ "--required-batch-size-multiple",
1682
+ "1",
1683
+ "--encoder-layers",
1684
+ "1",
1685
+ "--encoder-hidden-size",
1686
+ "32",
1687
+ "--decoder-layers",
1688
+ "1",
1689
+ "--optimizer",
1690
+ optimizer,
1691
+ ],
1692
+ )
1693
+ generate_main(data_dir)
1694
+
1695
+
1696
+ def read_last_log_entry(
1697
+ logs: List[logging.LogRecord], logger_name: str
1698
+ ) -> Dict[str, float]:
1699
+ for x in reversed(logs):
1700
+ if x.name == logger_name:
1701
+ return json.loads(x.message)
1702
+ raise ValueError(f"No entries from {logger_name} found in captured logs")
1703
+
1704
+
1705
+ class TestActivationCheckpointing(unittest.TestCase):
1706
+ base_flags = [
1707
+ "--encoder-layers",
1708
+ "2",
1709
+ "--decoder-layers",
1710
+ "2",
1711
+ "--encoder-embed-dim",
1712
+ "8",
1713
+ "--decoder-embed-dim",
1714
+ "8",
1715
+ "--restore-file",
1716
+ "x.pt",
1717
+ "--log-format",
1718
+ "json",
1719
+ "--log-interval",
1720
+ "1",
1721
+ "--max-update",
1722
+ "2",
1723
+ ]
1724
+
1725
+ def _train(self, data_dir, extra_flags):
1726
+ with self.assertLogs() as logs:
1727
+ train_translation_model(
1728
+ data_dir,
1729
+ "transformer_iwslt_de_en",
1730
+ self.base_flags + extra_flags,
1731
+ run_validation=True,
1732
+ extra_valid_flags=["--log-format", "json"],
1733
+ )
1734
+ return logs.records
1735
+
1736
+ def test_activation_offloading_does_not_change_metrics(self):
1737
+ """Neither ----checkpoint-activations nor --offload-activations should change loss"""
1738
+ with tempfile.TemporaryDirectory("test_transformer_with_act_cpt") as data_dir:
1739
+
1740
+ with self.assertLogs():
1741
+ create_dummy_data(data_dir, num_examples=20)
1742
+ preprocess_translation_data(data_dir)
1743
+ offload_logs = self._train(data_dir, ["--offload-activations"])
1744
+ baseline_logs = self._train(data_dir, [])
1745
+
1746
+ assert len(baseline_logs) == len(offload_logs)
1747
+
1748
+ baseline_valid_stats = read_last_log_entry(baseline_logs, "valid")
1749
+ offload_valid_stats = read_last_log_entry(offload_logs, "valid")
1750
+ baseline_train_stats = read_last_log_entry(baseline_logs, "train")
1751
+ offload_train_stats = read_last_log_entry(offload_logs, "train")
1752
+
1753
+ assert (
1754
+ baseline_train_stats["train_loss"] == offload_train_stats["train_loss"]
1755
+ )
1756
+ assert (
1757
+ baseline_valid_stats["valid_loss"] == offload_valid_stats["valid_loss"]
1758
+ )
1759
+
1760
+ def test_activation_checkpointing_does_not_change_metrics(self):
1761
+ """--checkpoint-activations should not change loss"""
1762
+
1763
+ with tempfile.TemporaryDirectory("test_transformer_with_act_cpt") as data_dir:
1764
+ with self.assertLogs():
1765
+ create_dummy_data(data_dir, num_examples=20)
1766
+ preprocess_translation_data(data_dir)
1767
+ ckpt_logs = self._train(data_dir, ["--checkpoint-activations"])
1768
+ baseline_logs = self._train(data_dir, [])
1769
+ assert len(baseline_logs) == len(ckpt_logs)
1770
+
1771
+ baseline_train_stats = read_last_log_entry(baseline_logs, "train")
1772
+ ckpt_train_stats = read_last_log_entry(ckpt_logs, "train")
1773
+ assert baseline_train_stats["train_loss"] == ckpt_train_stats["train_loss"]
1774
+
1775
+ baseline_valid_stats = read_last_log_entry(baseline_logs, "valid")
1776
+ ckpt_valid_stats = read_last_log_entry(ckpt_logs, "valid")
1777
+ assert baseline_valid_stats["valid_loss"] == ckpt_valid_stats["valid_loss"]
1778
+
1779
+
1780
+ def create_dummy_roberta_head_data(
1781
+ data_dir, num_examples=100, maxlen=10, num_classes=2, regression=False
1782
+ ):
1783
+ input_dir = "input0"
1784
+
1785
+ def _create_dummy_data(filename):
1786
+ random_data = torch.rand(num_examples * maxlen)
1787
+ input_data = 97 + torch.floor(26 * random_data).int()
1788
+ if regression:
1789
+ output_data = torch.rand((num_examples, num_classes))
1790
+ else:
1791
+ output_data = 1 + torch.floor(num_classes * torch.rand(num_examples)).int()
1792
+ with open(os.path.join(data_dir, input_dir, filename + ".out"), "w") as f_in:
1793
+ label_filename = filename + ".label" if regression else filename + ".out"
1794
+ with open(os.path.join(data_dir, "label", label_filename), "w") as f_out:
1795
+ offset = 0
1796
+ for i in range(num_examples):
1797
+ # write example input
1798
+ ex_len = random.randint(1, maxlen)
1799
+ ex_str = " ".join(map(chr, input_data[offset : offset + ex_len]))
1800
+ print(ex_str, file=f_in)
1801
+ # write example label
1802
+ if regression:
1803
+ class_str = " ".join(map(str, output_data[i].numpy()))
1804
+ print(class_str, file=f_out)
1805
+ else:
1806
+ class_str = "class{}".format(output_data[i])
1807
+ print(class_str, file=f_out)
1808
+ offset += ex_len
1809
+
1810
+ os.mkdir(os.path.join(data_dir, input_dir))
1811
+ os.mkdir(os.path.join(data_dir, "label"))
1812
+ _create_dummy_data("train")
1813
+ _create_dummy_data("valid")
1814
+ _create_dummy_data("test")
1815
+
1816
+
1817
+ def train_masked_lm(data_dir, arch, extra_flags=None):
1818
+ train_parser = options.get_training_parser()
1819
+ train_args = options.parse_args_and_arch(
1820
+ train_parser,
1821
+ [
1822
+ "--task",
1823
+ "masked_lm",
1824
+ data_dir,
1825
+ "--arch",
1826
+ arch,
1827
+ "--optimizer",
1828
+ "adam",
1829
+ "--lr",
1830
+ "0.0001",
1831
+ "--criterion",
1832
+ "masked_lm",
1833
+ "--batch-size",
1834
+ "500",
1835
+ "--required-batch-size-multiple",
1836
+ "1",
1837
+ "--save-dir",
1838
+ data_dir,
1839
+ "--max-epoch",
1840
+ "1",
1841
+ "--no-progress-bar",
1842
+ "--distributed-world-size",
1843
+ "1",
1844
+ "--ddp-backend",
1845
+ "no_c10d",
1846
+ "--num-workers",
1847
+ "0",
1848
+ ]
1849
+ + (extra_flags or []),
1850
+ )
1851
+ train.main(train_args)
1852
+
1853
+
1854
+ def train_roberta_head(data_dir, arch, num_classes=2, extra_flags=None):
1855
+ train_parser = options.get_training_parser()
1856
+ train_args = options.parse_args_and_arch(
1857
+ train_parser,
1858
+ [
1859
+ "--task",
1860
+ "sentence_prediction",
1861
+ data_dir,
1862
+ "--arch",
1863
+ arch,
1864
+ "--encoder-layers",
1865
+ "2",
1866
+ "--num-classes",
1867
+ str(num_classes),
1868
+ "--optimizer",
1869
+ "adam",
1870
+ "--lr",
1871
+ "0.0001",
1872
+ "--criterion",
1873
+ "sentence_prediction",
1874
+ "--max-tokens",
1875
+ "500",
1876
+ "--max-positions",
1877
+ "500",
1878
+ "--batch-size",
1879
+ "500",
1880
+ "--save-dir",
1881
+ data_dir,
1882
+ "--max-epoch",
1883
+ "1",
1884
+ "--no-progress-bar",
1885
+ "--distributed-world-size",
1886
+ "1",
1887
+ "--ddp-backend",
1888
+ "no_c10d",
1889
+ "--num-workers",
1890
+ "0",
1891
+ ]
1892
+ + (extra_flags or []),
1893
+ )
1894
+ train.main(train_args)
1895
+
1896
+
1897
+ def eval_lm_main(data_dir, extra_flags=None):
1898
+ eval_lm_parser = options.get_eval_lm_parser()
1899
+ eval_lm_args = options.parse_args_and_arch(
1900
+ eval_lm_parser,
1901
+ [
1902
+ data_dir,
1903
+ "--path",
1904
+ os.path.join(data_dir, "checkpoint_last.pt"),
1905
+ "--no-progress-bar",
1906
+ "--num-workers",
1907
+ "0",
1908
+ ]
1909
+ + (extra_flags or []),
1910
+ )
1911
+ eval_lm.main(eval_lm_args)
1912
+
1913
+
1914
+ if __name__ == "__main__":
1915
+ unittest.main()
data/fairseq/tests/test_binarizer.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+
7
+ import os
8
+ import typing as tp
9
+ import unittest
10
+ from tempfile import TemporaryDirectory
11
+
12
+ from fairseq.binarizer import BinarizeSummary, FileBinarizer, VocabularyDatasetBinarizer
13
+ from fairseq.data import Dictionary, indexed_dataset
14
+ from tests.utils import make_data, sizes
15
+
16
+
17
+ def build_vocab(data: tp.List[tp.List[str]]) -> Dictionary:
18
+ d = Dictionary()
19
+ for s in data:
20
+ for token in s:
21
+ d.add_symbol(token)
22
+ d.finalize()
23
+ return d
24
+
25
+
26
+ class TestBinarizer(unittest.TestCase):
27
+ def compare_ds_data(self, summary, data, prefix, impl, vocab):
28
+ self.assertEqual(summary.num_seq, len(data))
29
+ self.assertEqual(summary.num_tok, sum([len(s) for s in data]))
30
+
31
+ dataset = indexed_dataset.make_dataset(prefix, impl)
32
+
33
+ self.assertEqual(len(dataset), len(data))
34
+ decoded = [vocab.string(dataset[i]).split() for i in range(0, len(dataset))]
35
+
36
+ self.assertEqual(decoded, data)
37
+ data_sizes = [i.item() for i in dataset.sizes]
38
+ self.assertEqual(data_sizes, sizes(data))
39
+
40
+ def test_can_binarize_line(self):
41
+ data = make_data(length=1)
42
+ vocab = build_vocab(data)
43
+
44
+ binarizer = VocabularyDatasetBinarizer(
45
+ vocab,
46
+ )
47
+
48
+ sentence = data[0]
49
+ summary = BinarizeSummary()
50
+
51
+ tensor = binarizer.binarize_line(
52
+ " ".join(sentence),
53
+ summary,
54
+ )
55
+
56
+ self.assertEqual(len(tensor), len(sentence) + 1)
57
+
58
+ self.assertEqual(summary.num_tok, len(sentence) + 1)
59
+ self.assertEqual(summary.num_seq, 1)
60
+
61
+ def test_can_binarize_file_chunk(self):
62
+ # test without multiprocess logic
63
+ with TemporaryDirectory() as dirname:
64
+ raw_file = os.path.join(dirname, "raw1")
65
+ prefix = os.path.join(dirname, "test1")
66
+ impl = "mmap"
67
+
68
+ data = make_data(out_file=raw_file)
69
+ vocab = build_vocab(data)
70
+
71
+ binarizer = VocabularyDatasetBinarizer(
72
+ vocab,
73
+ append_eos=False,
74
+ )
75
+
76
+ summary = FileBinarizer._binarize_chunk_and_finalize(
77
+ binarizer,
78
+ raw_file,
79
+ offset_start=0,
80
+ offset_end=-1,
81
+ output_prefix=prefix,
82
+ dataset_impl=impl,
83
+ vocab_size=len(vocab),
84
+ )
85
+
86
+ self.compare_ds_data(summary, data, prefix, impl, vocab)
87
+
88
+ def test_can_multiprocess(self):
89
+ with TemporaryDirectory() as dirname:
90
+ raw_file = os.path.join(dirname, "raw1")
91
+ prefix = os.path.join(dirname, "test1")
92
+ impl = "mmap"
93
+ data = make_data(out_file=raw_file)
94
+ vocab = build_vocab(data)
95
+ binarizer = VocabularyDatasetBinarizer(
96
+ vocab,
97
+ append_eos=False,
98
+ )
99
+ # with one worker
100
+ summary = FileBinarizer.multiprocess_dataset(
101
+ raw_file,
102
+ impl,
103
+ binarizer,
104
+ output_prefix=prefix,
105
+ vocab_size=len(vocab),
106
+ num_workers=1,
107
+ )
108
+
109
+ self.compare_ds_data(summary, data, prefix, impl, vocab)
110
+
111
+ # with multiple worker
112
+ prefix_multi = os.path.join(dirname, "test2")
113
+ summary = FileBinarizer.multiprocess_dataset(
114
+ raw_file,
115
+ impl,
116
+ binarizer,
117
+ output_prefix=prefix_multi,
118
+ vocab_size=len(vocab),
119
+ num_workers=3,
120
+ )
121
+
122
+ self.compare_ds_data(summary, data, prefix_multi, impl, vocab)
data/fairseq/tests/test_character_token_embedder.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import torch
9
+ from fairseq.data import Dictionary
10
+ from fairseq.modules import CharacterTokenEmbedder
11
+
12
+
13
+ class TestCharacterTokenEmbedder(unittest.TestCase):
14
+ def test_character_token_embedder(self):
15
+ vocab = Dictionary()
16
+ vocab.add_symbol("hello")
17
+ vocab.add_symbol("there")
18
+
19
+ embedder = CharacterTokenEmbedder(
20
+ vocab, [(2, 16), (4, 32), (8, 64), (16, 2)], 64, 5, 2
21
+ )
22
+
23
+ test_sents = [["hello", "unk", "there"], ["there"], ["hello", "there"]]
24
+ max_len = max(len(s) for s in test_sents)
25
+ input = torch.LongTensor(len(test_sents), max_len + 2).fill_(vocab.pad())
26
+ for i in range(len(test_sents)):
27
+ input[i][0] = vocab.eos()
28
+ for j in range(len(test_sents[i])):
29
+ input[i][j + 1] = vocab.index(test_sents[i][j])
30
+ input[i][j + 2] = vocab.eos()
31
+ embs = embedder(input)
32
+
33
+ assert embs.size() == (len(test_sents), max_len + 2, 5)
34
+ self.assertAlmostEqual(embs[0][0], embs[1][0])
35
+ self.assertAlmostEqual(embs[0][0], embs[0][-1])
36
+ self.assertAlmostEqual(embs[0][1], embs[2][1])
37
+ self.assertAlmostEqual(embs[0][3], embs[1][1])
38
+
39
+ embs.sum().backward()
40
+ assert embedder.char_embeddings.weight.grad is not None
41
+
42
+ def assertAlmostEqual(self, t1, t2):
43
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
44
+ self.assertLess((t1 - t2).abs().max(), 1e-6)
45
+
46
+
47
+ if __name__ == "__main__":
48
+ unittest.main()
data/fairseq/tests/test_checkpoint_utils.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import contextlib
7
+ import logging
8
+ import os
9
+ import tempfile
10
+ import unittest
11
+ from io import StringIO
12
+ from unittest.mock import patch
13
+
14
+ from fairseq import checkpoint_utils
15
+ from tests.utils import (
16
+ create_dummy_data,
17
+ preprocess_translation_data,
18
+ train_translation_model,
19
+ )
20
+ import torch
21
+
22
+
23
+ class TestCheckpointUtils(unittest.TestCase):
24
+ def setUp(self):
25
+ logging.disable(logging.CRITICAL)
26
+
27
+ def tearDown(self):
28
+ logging.disable(logging.NOTSET)
29
+
30
+ @contextlib.contextmanager
31
+ def _train_transformer(self, seed, extra_args=None):
32
+ if extra_args is None:
33
+ extra_args = []
34
+ with tempfile.TemporaryDirectory(f"_train_transformer_seed{seed}") as data_dir:
35
+ create_dummy_data(data_dir)
36
+ preprocess_translation_data(data_dir)
37
+ train_translation_model(
38
+ data_dir,
39
+ "transformer_iwslt_de_en",
40
+ [
41
+ "--encoder-layers",
42
+ "3",
43
+ "--decoder-layers",
44
+ "3",
45
+ "--encoder-embed-dim",
46
+ "8",
47
+ "--decoder-embed-dim",
48
+ "8",
49
+ "--seed",
50
+ str(seed),
51
+ ]
52
+ + extra_args,
53
+ )
54
+ yield os.path.join(data_dir, "checkpoint_last.pt")
55
+
56
+ def test_load_model_ensemble_and_task(self):
57
+ # with contextlib.redirect_stdout(StringIO()):
58
+ with self._train_transformer(seed=123) as model1:
59
+ with self._train_transformer(seed=456) as model2:
60
+ ensemble, cfg, task = checkpoint_utils.load_model_ensemble_and_task(
61
+ filenames=[model1, model2]
62
+ )
63
+ self.assertEqual(len(ensemble), 2)
64
+
65
+ # after Transformer has been migrated to Hydra, this will probably
66
+ # become cfg.common.seed
67
+ self.assertEqual(ensemble[0].args.seed, 123)
68
+ self.assertEqual(ensemble[1].args.seed, 456)
69
+
70
+ # the task from the first model should be returned
71
+ self.assertTrue("seed123" in task.cfg.data)
72
+
73
+ # last cfg is saved
74
+ self.assertEqual(cfg.common.seed, 456)
75
+
76
+ def test_prune_state_dict(self):
77
+ with contextlib.redirect_stdout(StringIO()):
78
+ extra_args = ["--encoder-layerdrop", "0.01", "--decoder-layerdrop", "0.01"]
79
+ with self._train_transformer(seed=1, extra_args=extra_args) as model:
80
+ ensemble, cfg, task = checkpoint_utils.load_model_ensemble_and_task(
81
+ filenames=[model],
82
+ arg_overrides={
83
+ "encoder_layers_to_keep": "0,2",
84
+ "decoder_layers_to_keep": "1",
85
+ },
86
+ )
87
+ self.assertEqual(len(ensemble), 1)
88
+ self.assertEqual(len(ensemble[0].encoder.layers), 2)
89
+ self.assertEqual(len(ensemble[0].decoder.layers), 1)
90
+
91
+ def test_torch_persistent_save_async(self):
92
+ state_dict = {}
93
+ filename = "async_checkpoint.pt"
94
+
95
+ with patch(f"{checkpoint_utils.__name__}.PathManager.opena") as mock_opena:
96
+ with patch(
97
+ f"{checkpoint_utils.__name__}._torch_persistent_save"
98
+ ) as mock_save:
99
+ checkpoint_utils.torch_persistent_save(
100
+ state_dict, filename, async_write=True
101
+ )
102
+ mock_opena.assert_called_with(filename, "wb")
103
+ mock_save.assert_called()
104
+
105
+ def test_load_ema_from_checkpoint(self):
106
+ dummy_state = {"a": torch.tensor([1]), "b": torch.tensor([0.1])}
107
+ with patch(f"{checkpoint_utils.__name__}.PathManager.open") as mock_open, patch(
108
+ f"{checkpoint_utils.__name__}.torch.load"
109
+ ) as mock_load:
110
+
111
+ mock_load.return_value = {"extra_state": {"ema": dummy_state}}
112
+ filename = "ema_checkpoint.pt"
113
+ state = checkpoint_utils.load_ema_from_checkpoint(filename)
114
+
115
+ mock_open.assert_called_with(filename, "rb")
116
+ mock_load.assert_called()
117
+
118
+ self.assertIn("a", state["model"])
119
+ self.assertIn("b", state["model"])
120
+ self.assertTrue(torch.allclose(dummy_state["a"], state["model"]["a"]))
121
+ self.assertTrue(torch.allclose(dummy_state["b"], state["model"]["b"]))
122
+
123
+
124
+ if __name__ == "__main__":
125
+ unittest.main()
data/fairseq/tests/test_checkpoint_utils_for_task_level_attributes.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env fbpython
2
+ # (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
3
+
4
+ import contextlib
5
+ import logging
6
+ import unittest
7
+ from io import StringIO
8
+ from unittest.mock import MagicMock, patch
9
+
10
+ import torch
11
+ from fairseq import checkpoint_utils, data
12
+ from omegaconf import OmegaConf
13
+
14
+
15
+ def mock_trainer(epoch, num_updates, iterations_in_epoch):
16
+ trainer = MagicMock()
17
+ trainer.load_checkpoint.return_value = {
18
+ "train_iterator": {
19
+ "epoch": epoch,
20
+ "iterations_in_epoch": iterations_in_epoch,
21
+ "shuffle": False,
22
+ },
23
+ "FakeTask": checkpoint_dict()["FakeTask"],
24
+ }
25
+ trainer.get_num_updates.return_value = num_updates
26
+ trainer.task.__class__.__name__ = "FakeTask"
27
+ trainer.task.get_checkpoint_dict.return_value = checkpoint_dict()
28
+ trainer.task.set_checkpoint_dict = MagicMock()
29
+
30
+ return trainer
31
+
32
+
33
+ def checkpoint_dict():
34
+ return {
35
+ "FakeTask": {
36
+ "observer_stats": {
37
+ (
38
+ 4,
39
+ 16,
40
+ "MovingAveragePerChannelMinMax",
41
+ "MovingAveragePerChannelMinMax",
42
+ ): {"mod1": 1, "mod2": 2, "mod3": 3}
43
+ }
44
+ }
45
+ }
46
+
47
+
48
+ def mock_dict():
49
+ d = MagicMock()
50
+ d.pad.return_value = 1
51
+ d.eos.return_value = 2
52
+ d.unk.return_value = 3
53
+ return d
54
+
55
+
56
+ def get_trainer_and_epoch_itr(epoch, epoch_size, num_updates, iterations_in_epoch):
57
+ tokens = torch.LongTensor(list(range(epoch_size))).view(1, -1)
58
+ tokens_ds = data.TokenBlockDataset(
59
+ tokens,
60
+ sizes=[tokens.size(-1)],
61
+ block_size=1,
62
+ pad=0,
63
+ eos=1,
64
+ include_targets=False,
65
+ )
66
+ trainer = mock_trainer(epoch, num_updates, iterations_in_epoch)
67
+ dataset = data.LanguagePairDataset(
68
+ tokens_ds, tokens_ds.sizes, mock_dict(), shuffle=False
69
+ )
70
+ epoch_itr = data.EpochBatchIterator(
71
+ dataset=dataset,
72
+ collate_fn=dataset.collater,
73
+ batch_sampler=[[i] for i in range(epoch_size)],
74
+ )
75
+ return trainer, epoch_itr
76
+
77
+
78
+ def get_mock_cfg(finetune_from_model):
79
+ cfg_mock = OmegaConf.create(
80
+ {
81
+ "checkpoint": {
82
+ "save_dir": None,
83
+ "optimizer_overrides": "{}",
84
+ "reset_dataloader": False,
85
+ "reset_meters": False,
86
+ "reset_optimizer": False,
87
+ "reset_lr_scheduler": False,
88
+ "finetune_from_model": finetune_from_model,
89
+ "model_parallel_size": 1,
90
+ "restore_file": "checkpoint_last.pt",
91
+ "no_save": False,
92
+ "save_interval_updates": 0,
93
+ "no_last_checkpoints": False,
94
+ "keep_interval_updates": 0,
95
+ "keep_last_epochs": 0,
96
+ "keep_best_checkpoints": 0,
97
+ },
98
+ "common": {
99
+ "model_parallel_size": 1,
100
+ },
101
+ }
102
+ )
103
+ return cfg_mock
104
+
105
+
106
+ class TestCheckpointsForTaskLevelAttributes(unittest.TestCase):
107
+ def setUp(self) -> None:
108
+ self.cfg_mock = get_mock_cfg(None)
109
+ self.patches = {
110
+ "os.makedirs": MagicMock(),
111
+ "os.path.join": MagicMock(),
112
+ "os.path.isfile": MagicMock(return_value=True),
113
+ "os.path.isabs": MagicMock(return_value=False),
114
+ "fairseq.file_io.PathManager.exists": MagicMock(return_value=False),
115
+ }
116
+ self.applied_patches = [patch(p, d) for p, d in self.patches.items()]
117
+ [p.start() for p in self.applied_patches]
118
+ logging.disable(logging.CRITICAL)
119
+
120
+ self.trainer, self.epoch_itr = get_trainer_and_epoch_itr(2, 150, 200, 50)
121
+ self.trainer.get_train_iterator = MagicMock(return_value=self.epoch_itr)
122
+ self.epoch_itr.next_epoch_itr(shuffle=False)
123
+
124
+ checkpoint_utils.save_checkpoint(
125
+ self.cfg_mock.checkpoint, self.trainer, self.epoch_itr, None
126
+ )
127
+
128
+ def tearDown(self):
129
+ patch.stopall()
130
+ logging.disable(logging.NOTSET)
131
+
132
+ def test_verify_checkpoint(self) -> None:
133
+ cp_dict = self.trainer.task.get_checkpoint_dict()
134
+ self.assertTrue(len(cp_dict) == 1)
135
+ self.assertTrue("FakeTask" in cp_dict)
136
+ self.assertTrue("observer_stats" in cp_dict["FakeTask"])
137
+ self.assertTrue(len(cp_dict["FakeTask"]["observer_stats"]) == 1)
138
+ self.assertTrue(
139
+ (
140
+ 4,
141
+ 16,
142
+ "MovingAveragePerChannelMinMax",
143
+ "MovingAveragePerChannelMinMax",
144
+ )
145
+ in cp_dict["FakeTask"]["observer_stats"]
146
+ )
147
+ self.assertTrue(
148
+ cp_dict["FakeTask"]["observer_stats"][
149
+ (
150
+ 4,
151
+ 16,
152
+ "MovingAveragePerChannelMinMax",
153
+ "MovingAveragePerChannelMinMax",
154
+ )
155
+ ]
156
+ == {"mod1": 1, "mod2": 2, "mod3": 3}
157
+ )
158
+
159
+ def test_load_checkpoint(self) -> None:
160
+ with contextlib.redirect_stdout(StringIO()):
161
+ # Now, load checkpoint to ensure the respective logic works as expected
162
+ _, epoch_itr = checkpoint_utils.load_checkpoint(
163
+ self.cfg_mock.checkpoint, self.trainer
164
+ )
165
+
166
+ self.trainer.task.set_checkpoint_dict.assert_called_once_with(
167
+ checkpoint_dict()["FakeTask"]
168
+ )
169
+
170
+
171
+ if __name__ == "__main__":
172
+ unittest.main()
data/fairseq/tests/test_concat_dataset.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import torch
9
+ from fairseq.data import LanguagePairDataset, TokenBlockDataset
10
+ from fairseq.data.concat_dataset import ConcatDataset
11
+ from tests.test_train import mock_dict
12
+
13
+
14
+ class TestConcatDataset(unittest.TestCase):
15
+ def setUp(self):
16
+ d = mock_dict()
17
+ tokens_1 = torch.LongTensor([1]).view(1, -1)
18
+ tokens_ds1 = TokenBlockDataset(
19
+ tokens_1,
20
+ sizes=[tokens_1.size(-1)],
21
+ block_size=1,
22
+ pad=0,
23
+ eos=1,
24
+ include_targets=False,
25
+ )
26
+ self.dataset_1 = LanguagePairDataset(
27
+ tokens_ds1, tokens_ds1.sizes, d, shuffle=False
28
+ )
29
+ tokens_2 = torch.LongTensor([2]).view(1, -1)
30
+ tokens_ds2 = TokenBlockDataset(
31
+ tokens_2,
32
+ sizes=[tokens_2.size(-1)],
33
+ block_size=1,
34
+ pad=0,
35
+ eos=1,
36
+ include_targets=False,
37
+ )
38
+ self.dataset_2 = LanguagePairDataset(
39
+ tokens_ds2, tokens_ds2.sizes, d, shuffle=False
40
+ )
41
+
42
+ def test_concat_dataset_basics(self):
43
+ d = ConcatDataset([self.dataset_1, self.dataset_2])
44
+ assert len(d) == 2
45
+ assert d[0]["source"][0] == 1
46
+ assert d[1]["source"][0] == 2
47
+
48
+ d = ConcatDataset([self.dataset_1, self.dataset_2], sample_ratios=[1, 2])
49
+ assert len(d) == 3
50
+ assert d[0]["source"][0] == 1
51
+ assert d[1]["source"][0] == 2
52
+ assert d[2]["source"][0] == 2
53
+
54
+ d = ConcatDataset([self.dataset_1, self.dataset_2], sample_ratios=[2, 1])
55
+ assert len(d) == 3
56
+ assert d[0]["source"][0] == 1
57
+ assert d[1]["source"][0] == 1
58
+ assert d[2]["source"][0] == 2
data/fairseq/tests/test_dataset.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import logging
7
+ import unittest
8
+ from typing import Sequence
9
+
10
+ from fairseq.data import LanguagePairDataset, ListDataset, RoundRobinZipDatasets
11
+ from tests.test_train import mock_dict
12
+
13
+
14
+ def lang_pair_dataset(lengths: Sequence[int]) -> LanguagePairDataset:
15
+ tokens = [[i] * l for i, l in enumerate(lengths)]
16
+ return LanguagePairDataset(ListDataset(tokens), lengths, mock_dict())
17
+
18
+
19
+ def sample(id: int, length: int):
20
+ return {"id": id, "source": [id] * length, "target": None}
21
+
22
+
23
+ class TestDataset(unittest.TestCase):
24
+ def setUp(self):
25
+ logging.disable(logging.CRITICAL)
26
+
27
+ def tearDown(self):
28
+ logging.disable(logging.NOTSET)
29
+
30
+ def test_round_robin_zip_datasets(self):
31
+ long_dataset = lang_pair_dataset([10, 9, 8, 11])
32
+ short_dataset = lang_pair_dataset([11, 9])
33
+
34
+ dataset = RoundRobinZipDatasets({"a": long_dataset, "b": short_dataset})
35
+ # Dataset is now sorted by sentence length
36
+ dataset.ordered_indices()
37
+ assert dataset.longest_dataset is long_dataset
38
+ self.assertEqual(dict(dataset[0]), {"a": sample(2, 8), "b": sample(1, 9)})
39
+ # The item 2 of dataset 'a' is with item (2 % 2 = 0) of dataset 'b'
40
+ self.assertEqual(dict(dataset[2]), {"a": sample(0, 10), "b": sample(1, 9)})
41
+
42
+ def test_round_robin_zip_datasets_filtered(self):
43
+ long_dataset = lang_pair_dataset([10, 20, 8, 11, 1000, 7, 12])
44
+ short_dataset = lang_pair_dataset([11, 20, 9, 1000])
45
+
46
+ dataset = RoundRobinZipDatasets({"a": long_dataset, "b": short_dataset})
47
+ # Dataset is now sorted by sentence length
48
+ idx = dataset.ordered_indices()
49
+ idx, _ = dataset.filter_indices_by_size(idx, {"a": 19, "b": 900})
50
+ self.assertEqual(list(idx), [0, 1, 2, 3, 4])
51
+ self.assertEqual(dict(dataset[0]), {"a": sample(5, 7), "b": sample(2, 9)})
52
+ self.assertEqual(dict(dataset[2]), {"a": sample(0, 10), "b": sample(1, 20)})
53
+ self.assertEqual(dict(dataset[4]), {"a": sample(6, 12), "b": sample(0, 11)})
54
+
55
+ def test_round_robin_zip_datasets_filtered_with_tuple(self):
56
+ long_dataset = lang_pair_dataset([10, 20, 8, 11, 1000, 7, 12])
57
+ short_dataset = lang_pair_dataset([11, 20, 9, 1000])
58
+
59
+ dataset = RoundRobinZipDatasets({"a": long_dataset, "b": short_dataset})
60
+ # Dataset is now sorted by sentence length
61
+ idx = dataset.ordered_indices()
62
+ idx, _ = dataset.filter_indices_by_size(idx, 19)
63
+ self.assertEqual(list(idx), [0, 1, 2, 3, 4])
64
+ self.assertEqual(dict(dataset[0]), {"a": sample(5, 7), "b": sample(2, 9)})
65
+ self.assertEqual(dict(dataset[2]), {"a": sample(0, 10), "b": sample(2, 9)})
66
+ self.assertEqual(dict(dataset[4]), {"a": sample(6, 12), "b": sample(2, 9)})
data/fairseq/tests/test_ema.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+ from copy import deepcopy
8
+ from dataclasses import dataclass
9
+ import pytest
10
+ from typing import Optional
11
+ from unittest.mock import patch
12
+
13
+ import torch
14
+
15
+ from fairseq.models.ema import EMA
16
+
17
+
18
+ class DummyModule(torch.nn.Module):
19
+ def __init__(self) -> None:
20
+ """LightningModule for testing purposes
21
+
22
+ Args:
23
+ epoch_min_loss_override (int, optional): Pass in an epoch that will be set to the minimum
24
+ validation loss for testing purposes (zero based). If None this is ignored. Defaults to None.
25
+ """
26
+ super().__init__()
27
+ self.layer = torch.nn.Linear(in_features=32, out_features=2)
28
+ self.another_layer = torch.nn.Linear(in_features=2, out_features=2)
29
+
30
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
31
+ x = self.layer(x)
32
+ return self.another_layer(x)
33
+
34
+
35
+ @dataclass
36
+ class EMAConfig(object):
37
+ ema_decay: float = 0.99
38
+ ema_start_update: int = 0
39
+ ema_fp32: bool = False
40
+ ema_seed_model: Optional[str] = None
41
+ ema_update_freq: int = 1
42
+
43
+
44
+ class TestEMA(unittest.TestCase):
45
+ def assertTorchAllClose(self, x, y, atol=1e-8, rtol=1e-5, msg=None):
46
+ diff = x.float() - y.float()
47
+ diff_norm = torch.norm(diff)
48
+ other_norm = torch.norm(y.float())
49
+
50
+ if msg is None:
51
+ msg = "|input - other| > {} + {} * |other|".format(atol, rtol)
52
+
53
+ self.assertLessEqual(
54
+ diff_norm,
55
+ atol + rtol * other_norm,
56
+ msg=msg,
57
+ )
58
+
59
+ def test_ema(self):
60
+ model = DummyModule()
61
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
62
+ state = deepcopy(model.state_dict())
63
+ config = EMAConfig()
64
+ ema = EMA(model, config)
65
+
66
+ # set decay
67
+ ema._set_decay(config.ema_decay)
68
+ self.assertEqual(ema.get_decay(), config.ema_decay)
69
+
70
+ # get model
71
+ self.assertEqual(ema.get_model(), ema.model)
72
+
73
+ # Since fp32 params is not used, it should be of size 0
74
+ self.assertEqual(len(ema.fp32_params), 0)
75
+
76
+ # EMA step
77
+ x = torch.randn(32)
78
+ y = model(x)
79
+ loss = y.sum()
80
+ loss.backward()
81
+ optimizer.step()
82
+
83
+ ema.step(model)
84
+
85
+ ema_state_dict = ema.get_model().state_dict()
86
+
87
+ for key, param in model.state_dict().items():
88
+ prev_param = state[key]
89
+ ema_param = ema_state_dict[key]
90
+
91
+ if "version" in key:
92
+ # Do not decay a model.version pytorch param
93
+ continue
94
+ self.assertTorchAllClose(
95
+ ema_param,
96
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param,
97
+ )
98
+
99
+ # Since fp32 params is not used, it should be of size 0
100
+ self.assertEqual(len(ema.fp32_params), 0)
101
+
102
+ # Load EMA into model
103
+ model2 = DummyModule()
104
+ ema.reverse(model2)
105
+
106
+ for key, param in model2.state_dict().items():
107
+ ema_param = ema_state_dict[key]
108
+ self.assertTrue(torch.allclose(ema_param, param))
109
+
110
+ # Check that step_internal is called once
111
+ with patch.object(ema, "_step_internal", return_value=None) as mock_method:
112
+ ema.step(model)
113
+ mock_method.assert_called_once_with(model, None)
114
+
115
+ def _test_ema_start_update(self, updates):
116
+ model = DummyModule()
117
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
118
+ state = deepcopy(model.state_dict())
119
+ config = EMAConfig(ema_start_update=1)
120
+ ema = EMA(model, config)
121
+
122
+ # EMA step
123
+ x = torch.randn(32)
124
+ y = model(x)
125
+ loss = y.sum()
126
+ loss.backward()
127
+ optimizer.step()
128
+
129
+ ema.step(model, updates=updates)
130
+ ema_state_dict = ema.get_model().state_dict()
131
+
132
+ self.assertEqual(ema.get_decay(), 0 if updates == 0 else config.ema_decay)
133
+
134
+ for key, param in model.state_dict().items():
135
+ ema_param = ema_state_dict[key]
136
+ prev_param = state[key]
137
+
138
+ if "version" in key:
139
+ # Do not decay a model.version pytorch param
140
+ continue
141
+ if updates == 0:
142
+ self.assertTorchAllClose(
143
+ ema_param,
144
+ param,
145
+ )
146
+ else:
147
+ self.assertTorchAllClose(
148
+ ema_param,
149
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param,
150
+ )
151
+
152
+ # Check that step_internal is called once
153
+ with patch.object(ema, "_step_internal", return_value=None) as mock_method:
154
+ ema.step(model, updates=updates)
155
+ mock_method.assert_called_once_with(model, updates)
156
+
157
+ def test_ema_before_start_update(self):
158
+ self._test_ema_start_update(updates=0)
159
+
160
+ def test_ema_after_start_update(self):
161
+ self._test_ema_start_update(updates=1)
162
+
163
+ def test_ema_fp32(self):
164
+ dtype = torch.float
165
+
166
+ model = DummyModule().to(dtype)
167
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
168
+ state = deepcopy(model.state_dict())
169
+ config = EMAConfig(ema_fp32=True)
170
+ ema = EMA(model, config)
171
+
172
+ x = torch.randn(32)
173
+ y = model(x.to(dtype))
174
+ loss = y.sum()
175
+ loss.backward()
176
+ optimizer.step()
177
+
178
+ ema.step(model)
179
+
180
+ for key, param in model.state_dict().items():
181
+ prev_param = state[key]
182
+ ema_param = ema.get_model().state_dict()[key]
183
+
184
+ if "version" in key:
185
+ # Do not decay a model.version pytorch param
186
+ continue
187
+ self.assertIn(key, ema.fp32_params)
188
+
189
+ # EMA update is done in fp32, and hence the EMA param must be
190
+ # closer to the EMA update done in fp32 than in fp16.
191
+ self.assertLessEqual(
192
+ torch.norm(
193
+ ema_param.float()
194
+ - (
195
+ config.ema_decay * prev_param.float()
196
+ + (1 - config.ema_decay) * param.float()
197
+ )
198
+ .to(dtype)
199
+ .float()
200
+ ),
201
+ torch.norm(
202
+ ema_param.float()
203
+ - (
204
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param
205
+ ).float()
206
+ ),
207
+ )
208
+ self.assertTorchAllClose(
209
+ ema_param,
210
+ (
211
+ config.ema_decay * prev_param.float()
212
+ + (1 - config.ema_decay) * param.float()
213
+ ).to(dtype),
214
+ )
215
+
216
+ @pytest.mark.skipif(
217
+ not torch.cuda.is_available(),
218
+ reason="CPU no longer supports Linear in half precision",
219
+ )
220
+ def test_ema_fp16(self):
221
+ model = DummyModule().cuda().half()
222
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
223
+ state = deepcopy(model.state_dict())
224
+ config = EMAConfig(ema_fp32=False)
225
+ ema = EMA(model, config)
226
+
227
+ # Since fp32 params is not used, it should be of size 0
228
+ self.assertEqual(len(ema.fp32_params), 0)
229
+
230
+ x = torch.randn(32).cuda()
231
+ y = model(x.half())
232
+ loss = y.sum()
233
+ loss.backward()
234
+ optimizer.step()
235
+
236
+ ema.step(model)
237
+
238
+ for key, param in model.state_dict().items():
239
+ prev_param = state[key]
240
+ ema_param = ema.get_model().state_dict()[key]
241
+
242
+ if "version" in key:
243
+ # Do not decay a model.version pytorch param
244
+ continue
245
+
246
+ # EMA update is done in fp16, and hence the EMA param must be
247
+ # closer to the EMA update done in fp16 than in fp32.
248
+ self.assertLessEqual(
249
+ torch.norm(
250
+ ema_param.float()
251
+ - (
252
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param
253
+ ).float()
254
+ ),
255
+ torch.norm(
256
+ ema_param.float()
257
+ - (
258
+ config.ema_decay * prev_param.float()
259
+ + (1 - config.ema_decay) * param.float()
260
+ )
261
+ .half()
262
+ .float()
263
+ ),
264
+ )
265
+ self.assertTorchAllClose(
266
+ ema_param,
267
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param,
268
+ )
269
+
270
+ # Since fp32 params is not used, it should be of size 0
271
+ self.assertEqual(len(ema.fp32_params), 0)
272
+
273
+
274
+ if __name__ == "__main__":
275
+ unittest.main()
data/fairseq/tests/test_export.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ import argparse
8
+ import tempfile
9
+ import unittest
10
+
11
+ import torch
12
+
13
+ from fairseq.data.dictionary import Dictionary
14
+ from fairseq.models.transformer import TransformerModel
15
+ from fairseq.modules import multihead_attention, sinusoidal_positional_embedding
16
+ from fairseq.tasks.fairseq_task import LegacyFairseqTask
17
+
18
+ DEFAULT_TEST_VOCAB_SIZE = 100
19
+
20
+
21
+ class DummyTask(LegacyFairseqTask):
22
+ def __init__(self, args):
23
+ super().__init__(args)
24
+ self.dictionary = get_dummy_dictionary()
25
+ if getattr(self.args, "ctc", False):
26
+ self.dictionary.add_symbol("<ctc_blank>")
27
+ self.src_dict = self.dictionary
28
+ self.tgt_dict = self.dictionary
29
+
30
+ @property
31
+ def source_dictionary(self):
32
+ return self.src_dict
33
+
34
+ @property
35
+ def target_dictionary(self):
36
+ return self.dictionary
37
+
38
+
39
+ def get_dummy_dictionary(vocab_size=DEFAULT_TEST_VOCAB_SIZE):
40
+ dummy_dict = Dictionary()
41
+ # add dummy symbol to satisfy vocab size
42
+ for id, _ in enumerate(range(vocab_size)):
43
+ dummy_dict.add_symbol("{}".format(id), 1000)
44
+ return dummy_dict
45
+
46
+
47
+ def get_dummy_task_and_parser():
48
+ """
49
+ Return a dummy task and argument parser, which can be used to
50
+ create a model/criterion.
51
+ """
52
+ parser = argparse.ArgumentParser(
53
+ description="test_dummy_s2s_task", argument_default=argparse.SUPPRESS
54
+ )
55
+ DummyTask.add_args(parser)
56
+ args = parser.parse_args([])
57
+ task = DummyTask.setup_task(args)
58
+ return task, parser
59
+
60
+
61
+ def _test_save_and_load(scripted_module):
62
+ with tempfile.NamedTemporaryFile() as f:
63
+ scripted_module.save(f.name)
64
+ torch.jit.load(f.name)
65
+
66
+
67
+ class TestExportModels(unittest.TestCase):
68
+ def test_export_multihead_attention(self):
69
+ module = multihead_attention.MultiheadAttention(embed_dim=8, num_heads=2)
70
+ scripted = torch.jit.script(module)
71
+ _test_save_and_load(scripted)
72
+
73
+ def test_incremental_state_multihead_attention(self):
74
+ module1 = multihead_attention.MultiheadAttention(embed_dim=8, num_heads=2)
75
+ module1 = torch.jit.script(module1)
76
+ module2 = multihead_attention.MultiheadAttention(embed_dim=8, num_heads=2)
77
+ module2 = torch.jit.script(module2)
78
+
79
+ state = {}
80
+ state = module1.set_incremental_state(state, "key", {"a": torch.tensor([1])})
81
+ state = module2.set_incremental_state(state, "key", {"a": torch.tensor([2])})
82
+ v1 = module1.get_incremental_state(state, "key")["a"]
83
+ v2 = module2.get_incremental_state(state, "key")["a"]
84
+
85
+ self.assertEqual(v1, 1)
86
+ self.assertEqual(v2, 2)
87
+
88
+ def test_positional_embedding(self):
89
+ module = sinusoidal_positional_embedding.SinusoidalPositionalEmbedding(
90
+ embedding_dim=8, padding_idx=1
91
+ )
92
+ scripted = torch.jit.script(module)
93
+ _test_save_and_load(scripted)
94
+
95
+ @unittest.skipIf(
96
+ torch.__version__ < "1.6.0", "Targeting OSS scriptability for the 1.6 release"
97
+ )
98
+ def test_export_transformer(self):
99
+ task, parser = get_dummy_task_and_parser()
100
+ TransformerModel.add_args(parser)
101
+ args = parser.parse_args([])
102
+ model = TransformerModel.build_model(args, task)
103
+ scripted = torch.jit.script(model)
104
+ _test_save_and_load(scripted)
105
+
106
+ @unittest.skipIf(
107
+ torch.__version__ < "1.6.0", "Targeting OSS scriptability for the 1.6 release"
108
+ )
109
+ def test_export_transformer_no_token_pos_emb(self):
110
+ task, parser = get_dummy_task_and_parser()
111
+ TransformerModel.add_args(parser)
112
+ args = parser.parse_args([])
113
+ args.no_token_positional_embeddings = True
114
+ model = TransformerModel.build_model(args, task)
115
+ scripted = torch.jit.script(model)
116
+ _test_save_and_load(scripted)
117
+
118
+
119
+ if __name__ == "__main__":
120
+ unittest.main()
data/fairseq/tests/test_hf_hub.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ import unittest
8
+
9
+ import torch
10
+
11
+ try:
12
+ import huggingface_hub
13
+ except ImportError:
14
+ huggingface_hub = None
15
+
16
+ from fairseq.checkpoint_utils import load_model_ensemble_and_task_from_hf_hub
17
+
18
+
19
+ @unittest.skipIf(not huggingface_hub, "Requires huggingface_hub install")
20
+ class TestHuggingFaceHub(unittest.TestCase):
21
+ @torch.no_grad()
22
+ def test_hf_fastspeech2(self):
23
+ hf_model_id = "facebook/fastspeech2-en-ljspeech"
24
+ models, cfg, task = load_model_ensemble_and_task_from_hf_hub(hf_model_id)
25
+ self.assertTrue(len(models) > 0)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ unittest.main()
data/fairseq/tests/test_iopath.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+ from unittest import mock
8
+
9
+
10
+ class TestIOPath(unittest.TestCase):
11
+ def test_no_iopath(self):
12
+ from .test_reproducibility import TestReproducibility
13
+
14
+ with mock.patch.dict("sys.modules", {"iopath": None}):
15
+ # reuse reproducibility tests, which are e2e tests that should cover
16
+ # most checkpoint related functionality
17
+ TestReproducibility._test_reproducibility(self, "test_reproducibility")
18
+
19
+ def test_no_supports_rename(self):
20
+ from .test_reproducibility import TestReproducibility
21
+
22
+ with mock.patch("fairseq.file_io.PathManager.supports_rename") as mock_fn:
23
+ mock_fn.return_value = False
24
+ TestReproducibility._test_reproducibility(self, "test_reproducibility")
25
+
26
+
27
+ if __name__ == "__main__":
28
+ unittest.main()
data/fairseq/tests/test_lstm_jitable.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ import tempfile
8
+ import unittest
9
+
10
+ import torch
11
+ from fairseq.data.dictionary import Dictionary
12
+ from fairseq.models.lstm import LSTMModel
13
+ from fairseq.tasks.fairseq_task import LegacyFairseqTask
14
+
15
+
16
+ DEFAULT_TEST_VOCAB_SIZE = 100
17
+
18
+
19
+ class DummyTask(LegacyFairseqTask):
20
+ def __init__(self, args):
21
+ super().__init__(args)
22
+ self.dictionary = get_dummy_dictionary()
23
+ if getattr(self.args, "ctc", False):
24
+ self.dictionary.add_symbol("<ctc_blank>")
25
+ self.src_dict = self.dictionary
26
+ self.tgt_dict = self.dictionary
27
+
28
+ @property
29
+ def source_dictionary(self):
30
+ return self.src_dict
31
+
32
+ @property
33
+ def target_dictionary(self):
34
+ return self.dictionary
35
+
36
+
37
+ def get_dummy_dictionary(vocab_size=DEFAULT_TEST_VOCAB_SIZE):
38
+ dummy_dict = Dictionary()
39
+ # add dummy symbol to satisfy vocab size
40
+ for id, _ in enumerate(range(vocab_size)):
41
+ dummy_dict.add_symbol("{}".format(id), 1000)
42
+ return dummy_dict
43
+
44
+
45
+ def get_dummy_task_and_parser():
46
+ """
47
+ to build a fariseq model, we need some dummy parse and task. This function
48
+ is used to create dummy task and parser to faciliate model/criterion test
49
+
50
+ Note: we use FbSpeechRecognitionTask as the dummy task. You may want
51
+ to use other task by providing another function
52
+ """
53
+ parser = argparse.ArgumentParser(
54
+ description="test_dummy_s2s_task", argument_default=argparse.SUPPRESS
55
+ )
56
+ DummyTask.add_args(parser)
57
+ args = parser.parse_args([])
58
+ task = DummyTask.setup_task(args)
59
+ return task, parser
60
+
61
+
62
+ class TestJitLSTMModel(unittest.TestCase):
63
+ def _test_save_and_load(self, scripted_module):
64
+ with tempfile.NamedTemporaryFile() as f:
65
+ scripted_module.save(f.name)
66
+ torch.jit.load(f.name)
67
+
68
+ def assertTensorEqual(self, t1, t2):
69
+ t1 = t1[~torch.isnan(t1)] # can cause size mismatch errors if there are NaNs
70
+ t2 = t2[~torch.isnan(t2)]
71
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
72
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
73
+
74
+ def test_jit_and_export_lstm(self):
75
+ task, parser = get_dummy_task_and_parser()
76
+ LSTMModel.add_args(parser)
77
+ args = parser.parse_args([])
78
+ args.criterion = ""
79
+ model = LSTMModel.build_model(args, task)
80
+ scripted_model = torch.jit.script(model)
81
+ self._test_save_and_load(scripted_model)
82
+
83
+ def test_assert_jit_vs_nonjit_(self):
84
+ task, parser = get_dummy_task_and_parser()
85
+ LSTMModel.add_args(parser)
86
+ args = parser.parse_args([])
87
+ args.criterion = ""
88
+ model = LSTMModel.build_model(args, task)
89
+ model.eval()
90
+ scripted_model = torch.jit.script(model)
91
+ scripted_model.eval()
92
+ idx = len(task.source_dictionary)
93
+ iter = 100
94
+ # Inject random input and check output
95
+ seq_len_tensor = torch.randint(1, 10, (iter,))
96
+ num_samples_tensor = torch.randint(1, 10, (iter,))
97
+ for i in range(iter):
98
+ seq_len = seq_len_tensor[i]
99
+ num_samples = num_samples_tensor[i]
100
+ src_token = (torch.randint(0, idx, (num_samples, seq_len)),)
101
+ src_lengths = torch.randint(1, seq_len + 1, (num_samples,))
102
+ src_lengths, _ = torch.sort(src_lengths, descending=True)
103
+ # Force the first sample to have seq_len
104
+ src_lengths[0] = seq_len
105
+ prev_output_token = (torch.randint(0, idx, (num_samples, 1)),)
106
+ result = model(src_token[0], src_lengths, prev_output_token[0], None)
107
+ scripted_result = scripted_model(
108
+ src_token[0], src_lengths, prev_output_token[0], None
109
+ )
110
+ self.assertTensorEqual(result[0], scripted_result[0])
111
+ self.assertTensorEqual(result[1], scripted_result[1])
112
+
113
+
114
+ if __name__ == "__main__":
115
+ unittest.main()
data/fairseq/tests/test_multi_corpus_dataset.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+ from collections import OrderedDict
8
+
9
+ import torch
10
+
11
+ from fairseq.data import LanguagePairDataset, TokenBlockDataset
12
+ from fairseq.data.multi_corpus_dataset import MultiCorpusDataset
13
+ from tests.test_train import mock_dict
14
+
15
+
16
+ class TestMultiCorpusDataset(unittest.TestCase):
17
+ def setUp(self):
18
+ d = mock_dict()
19
+ tokens_1 = torch.LongTensor([i for i in range(1, 5000, 2)]).view(1, -1)
20
+ tokens_ds1 = TokenBlockDataset(
21
+ tokens_1,
22
+ sizes=[tokens_1.size(-1)],
23
+ block_size=1,
24
+ pad=0,
25
+ eos=1,
26
+ include_targets=False,
27
+ )
28
+ self.dataset_1 = LanguagePairDataset(
29
+ tokens_ds1, tokens_ds1.sizes, d, shuffle=False
30
+ )
31
+ tokens_2 = torch.LongTensor([i for i in range(0, 5000, 2)]).view(1, -1)
32
+ tokens_ds2 = TokenBlockDataset(
33
+ tokens_2,
34
+ sizes=[tokens_2.size(-1)],
35
+ block_size=1,
36
+ pad=0,
37
+ eos=1,
38
+ include_targets=False,
39
+ )
40
+ self.dataset_2 = LanguagePairDataset(
41
+ tokens_ds2, tokens_ds2.sizes, d, shuffle=False
42
+ )
43
+
44
+ def _test_sample_helper(
45
+ self,
46
+ distribution,
47
+ ):
48
+ m = MultiCorpusDataset(
49
+ OrderedDict({0: self.dataset_1, 1: self.dataset_2}),
50
+ distribution=distribution,
51
+ seed=0,
52
+ sort_indices=True,
53
+ )
54
+ m.set_epoch(1)
55
+ indices = m.ordered_indices()
56
+ count_sample_from_first_dataset = 0
57
+ items = set()
58
+ for i in indices:
59
+ item = m[i]["source"].item()
60
+ if item % 2 == 1:
61
+ count_sample_from_first_dataset += 1
62
+
63
+ items.add(item)
64
+ sample_from_first_ds_percentage = (
65
+ 1.0 * count_sample_from_first_dataset / len(indices)
66
+ )
67
+ self.assertLess(
68
+ abs(sample_from_first_ds_percentage - distribution[0]),
69
+ 0.01,
70
+ )
71
+ self.assertEqual(
72
+ len(items),
73
+ int(
74
+ min(len(self.dataset_1), len(indices) * distribution[0])
75
+ + min(len(self.dataset_1), len(indices) * distribution[1])
76
+ ),
77
+ )
78
+ print(distribution)
79
+
80
+ def test_multi_corpus_dataset(self):
81
+ for distribution in [[0.5, 0.5], [0.1, 0.9], [0.9, 0.1], [0.0, 1.0]]:
82
+ self._test_sample_helper(distribution=distribution)
data/fairseq/tests/test_multi_corpus_sampled_dataset.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+ from collections import OrderedDict
8
+
9
+ import numpy as np
10
+ import torch
11
+ from fairseq.data import LanguagePairDataset, TokenBlockDataset
12
+ from fairseq.data.multi_corpus_sampled_dataset import MultiCorpusSampledDataset
13
+ from tests.test_train import mock_dict
14
+
15
+
16
+ class TestMultiCorpusSampledDataset(unittest.TestCase):
17
+ def setUp(self):
18
+ d = mock_dict()
19
+ tokens_1 = torch.LongTensor([1]).view(1, -1)
20
+ tokens_ds1 = TokenBlockDataset(
21
+ tokens_1,
22
+ sizes=[tokens_1.size(-1)],
23
+ block_size=1,
24
+ pad=0,
25
+ eos=1,
26
+ include_targets=False,
27
+ )
28
+ self.dataset_1 = LanguagePairDataset(
29
+ tokens_ds1, tokens_ds1.sizes, d, shuffle=False
30
+ )
31
+ tokens_2 = torch.LongTensor([2]).view(1, -1)
32
+ tokens_ds2 = TokenBlockDataset(
33
+ tokens_2,
34
+ sizes=[tokens_2.size(-1)],
35
+ block_size=1,
36
+ pad=0,
37
+ eos=1,
38
+ include_targets=False,
39
+ )
40
+ self.dataset_2 = LanguagePairDataset(
41
+ tokens_ds2, tokens_ds2.sizes, d, shuffle=False
42
+ )
43
+
44
+ def _test_sample_helper(
45
+ self,
46
+ expected_sample_from_first_ds_percentage,
47
+ num_samples=1000,
48
+ sampling_func=None,
49
+ ):
50
+ # To make sure test is not flaky
51
+ np.random.seed(0)
52
+ if sampling_func is None:
53
+ m = MultiCorpusSampledDataset(
54
+ OrderedDict({0: self.dataset_1, 1: self.dataset_2}),
55
+ )
56
+ else:
57
+ m = MultiCorpusSampledDataset(
58
+ OrderedDict({0: self.dataset_1, 1: self.dataset_2}),
59
+ sampling_func=sampling_func,
60
+ )
61
+ m.ordered_indices()
62
+ count_sample_from_first_dataset = 0
63
+ for _ in range(num_samples):
64
+ if m.collater([m[0], m[1]])["net_input"]["src_tokens"][0] == 1:
65
+ count_sample_from_first_dataset += 1
66
+ sample_from_first_ds_percentage = (
67
+ 1.0 * count_sample_from_first_dataset / num_samples
68
+ )
69
+ self.assertLess(
70
+ abs(
71
+ sample_from_first_ds_percentage
72
+ - expected_sample_from_first_ds_percentage
73
+ ),
74
+ 0.01,
75
+ )
76
+
77
+ def test_multi_corpus_sampled_dataset_uniform_sample(self):
78
+ self._test_sample_helper(expected_sample_from_first_ds_percentage=0.5)
79
+
80
+ def test_multi_corpus_sampled_dataset_weighted_sample(self):
81
+ def naive_weighted_sample(weights):
82
+ def f(input):
83
+ v = np.random.random()
84
+ agg = 0
85
+ for i, weight in enumerate(weights):
86
+ agg += weight
87
+ if agg > v:
88
+ return i
89
+
90
+ return f
91
+
92
+ self._test_sample_helper(
93
+ expected_sample_from_first_ds_percentage=0.9,
94
+ sampling_func=naive_weighted_sample(weights=[0.9, 0.1]),
95
+ )
data/fairseq/tests/test_multihead_attention.py ADDED
@@ -0,0 +1,488 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import random
7
+ import unittest
8
+
9
+ import pytest
10
+ import torch
11
+
12
+ from fairseq.modules.multihead_attention import MultiheadAttention, _mask_for_xformers
13
+
14
+ BATCH = [20, 41, 97]
15
+ SEQ = [64]
16
+ EMB = [48]
17
+ HEADS = [4]
18
+ DROP = 0.1
19
+ DEVICE = ["cpu", "cuda"] if torch.cuda.is_available() else ["cpu"]
20
+ ATTN_MASK_DTYPE = [None, torch.uint8, torch.bool, torch.float]
21
+ KEY_PADDING_MASK_DTYPE = [None, torch.uint8, torch.bool]
22
+
23
+
24
+ # FIXME: some tests fail when decimal=2, fix this and set decimal to 2
25
+ def assert_almost_equal(x, y, decimal=1, err_msg=""):
26
+ import numpy.testing as npt
27
+
28
+ if isinstance(x, torch.Tensor):
29
+ x = x.cpu().detach().numpy()
30
+ if isinstance(y, torch.Tensor):
31
+ y = y.cpu().detach().numpy()
32
+ npt.assert_array_almost_equal(x, y, err_msg=err_msg, decimal=decimal)
33
+
34
+
35
+ def _reset_seeds():
36
+ torch.manual_seed(0)
37
+ torch.random.manual_seed(0)
38
+ random.seed(0)
39
+ torch.cuda.manual_seed_all(0)
40
+
41
+
42
+ def _get_mask(to_dtype: torch.dtype, dim0: int, dim1: int):
43
+ if to_dtype == torch.float:
44
+ mask = torch.randint(0, 2, (dim0, dim1)).to(dtype=torch.bool)
45
+ return mask.to(dtype=to_dtype).masked_fill(mask, -float("inf"))
46
+ return torch.randint(0, 2, (dim0, dim1)).to(dtype=to_dtype)
47
+
48
+
49
+ def test_mask_for_xformers():
50
+ # Additive Mask
51
+ m_float_add = torch.tensor([float("-inf"), 0]).to(torch.float)
52
+ m_float_add_flipped = torch.tensor([0, float("-inf")]).to(torch.float)
53
+ m_float16_add = torch.tensor([float("-inf"), 0]).to(torch.float16)
54
+ m_float16_add_flipped = torch.tensor([0, float("-inf")]).to(torch.float16)
55
+ m_uint = torch.tensor([1, 0]).to(torch.uint8)
56
+ m_uint_flipped = torch.tensor([0, 1]).to(torch.uint8)
57
+ m_bool = torch.tensor([False, True])
58
+
59
+ assert torch.equal(_mask_for_xformers(m_float_add), m_float_add)
60
+ assert torch.equal(_mask_for_xformers(m_float16_add), m_float16_add)
61
+ assert torch.equal(_mask_for_xformers(m_uint), m_uint_flipped)
62
+ assert torch.equal(_mask_for_xformers(m_bool), ~m_bool)
63
+
64
+ assert torch.equal(
65
+ _mask_for_xformers(m_float_add, to_dtype=torch.float16), m_float16_add
66
+ )
67
+ assert torch.equal(
68
+ _mask_for_xformers(m_float_add, to_dtype=torch.float), m_float_add
69
+ )
70
+ assert torch.equal(_mask_for_xformers(m_float_add, to_dtype=torch.bool), m_bool)
71
+ assert torch.equal(
72
+ _mask_for_xformers(m_float_add, to_dtype=torch.uint8), m_uint_flipped
73
+ )
74
+
75
+ assert torch.equal(
76
+ _mask_for_xformers(m_float16_add, to_dtype=torch.float16), m_float16_add
77
+ )
78
+ assert torch.equal(
79
+ _mask_for_xformers(m_float16_add, to_dtype=torch.float), m_float_add
80
+ )
81
+ assert torch.equal(_mask_for_xformers(m_float16_add, to_dtype=torch.bool), m_bool)
82
+ assert torch.equal(
83
+ _mask_for_xformers(m_float16_add, to_dtype=torch.uint8), m_uint_flipped
84
+ )
85
+
86
+ assert torch.equal(
87
+ _mask_for_xformers(m_bool, to_dtype=torch.float16), m_float16_add_flipped
88
+ )
89
+ assert torch.equal(
90
+ _mask_for_xformers(m_bool, to_dtype=torch.float), m_float_add_flipped
91
+ )
92
+ assert torch.equal(_mask_for_xformers(m_bool, to_dtype=torch.bool), ~m_bool)
93
+ assert torch.equal(_mask_for_xformers(m_bool, to_dtype=torch.uint8), m_uint)
94
+
95
+ assert torch.equal(
96
+ _mask_for_xformers(m_uint, to_dtype=torch.float16), m_float16_add
97
+ )
98
+ assert torch.equal(_mask_for_xformers(m_uint, to_dtype=torch.float), m_float_add)
99
+ assert torch.equal(_mask_for_xformers(m_uint, to_dtype=torch.bool), m_bool)
100
+ assert torch.equal(_mask_for_xformers(m_uint, to_dtype=torch.uint8), m_uint_flipped)
101
+
102
+
103
+ @pytest.mark.skipif(not torch.cuda.is_available(), reason="blocksparse requires gpu")
104
+ @pytest.mark.skip(reason="not part of latest xformers")
105
+ @pytest.mark.parametrize("device", ["cuda"])
106
+ @pytest.mark.parametrize("add_zero_attn", [False])
107
+ @pytest.mark.parametrize("batch_size", [20])
108
+ @pytest.mark.parametrize("embedding", [64])
109
+ @pytest.mark.parametrize("seq_len", [64])
110
+ @pytest.mark.parametrize("num_heads", [4])
111
+ def test_xformers_blocksparse_parity(
112
+ device,
113
+ add_zero_attn,
114
+ batch_size,
115
+ embedding,
116
+ seq_len,
117
+ num_heads,
118
+ ):
119
+
120
+ xformers_att_config = '{"name": "scaled_dot_product"}'
121
+ xformers_blocksparse_blocksize = 16
122
+ xformers_blocksparse_layout = torch.ones(
123
+ seq_len // xformers_blocksparse_blocksize,
124
+ seq_len // xformers_blocksparse_blocksize,
125
+ dtype=torch.int32,
126
+ )
127
+
128
+ q = torch.rand(seq_len, batch_size, embedding).to(device).half()
129
+ q.requires_grad = True
130
+ k = torch.rand(seq_len, batch_size, embedding).to(device).half()
131
+ k.requires_grad = True
132
+ v = torch.rand(seq_len, batch_size, embedding).to(device).half()
133
+ v.requires_grad = True
134
+
135
+ q_ = q.detach().clone().half()
136
+ q_.requires_grad = True
137
+ k_ = k.detach().clone().half()
138
+ k_.requires_grad = True
139
+ v_ = v.detach().clone().half()
140
+ v_.requires_grad = True
141
+
142
+ _reset_seeds()
143
+ xf_blocksparse_mha = (
144
+ MultiheadAttention(
145
+ embedding,
146
+ num_heads,
147
+ dropout=0.0,
148
+ add_zero_attn=add_zero_attn,
149
+ xformers_att_config=xformers_att_config,
150
+ xformers_blocksparse_layout=xformers_blocksparse_layout,
151
+ xformers_blocksparse_blocksize=xformers_blocksparse_blocksize,
152
+ )
153
+ .to(device)
154
+ .half()
155
+ )
156
+
157
+ xf_blocksparse_output, _ = xf_blocksparse_mha(
158
+ q,
159
+ k,
160
+ v,
161
+ )
162
+
163
+ _reset_seeds()
164
+ xformers_mha = (
165
+ MultiheadAttention(
166
+ embedding,
167
+ num_heads,
168
+ dropout=0.0,
169
+ add_zero_attn=add_zero_attn,
170
+ xformers_att_config=xformers_att_config,
171
+ xformers_blocksparse_layout=None,
172
+ )
173
+ .to(device)
174
+ .half()
175
+ )
176
+
177
+ xformers_output, _ = xformers_mha(
178
+ q_,
179
+ k_,
180
+ v_,
181
+ )
182
+
183
+ # # account for when nan != nan
184
+ rand = random.uniform(0, 1)
185
+ xformers_output = xformers_output.masked_fill(xformers_output.isnan(), rand)
186
+ xf_blocksparse_output = xf_blocksparse_output.masked_fill(
187
+ xf_blocksparse_output.isnan(), rand
188
+ )
189
+
190
+ assert_almost_equal(xformers_output, xf_blocksparse_output)
191
+
192
+ loss_blocksparse = torch.norm(xformers_output)
193
+ loss_original = torch.norm(xf_blocksparse_output)
194
+ loss_blocksparse.backward()
195
+ loss_original.backward()
196
+
197
+ q.masked_fill(q.isnan(), rand)
198
+ q_.masked_fill(q_.isnan(), rand)
199
+ k.masked_fill(k.isnan(), rand)
200
+ k_.masked_fill(k_.isnan(), rand)
201
+ v.masked_fill(v.isnan(), rand)
202
+ v_.masked_fill(v_.isnan(), rand)
203
+
204
+ assert_almost_equal(q.grad, q_.grad)
205
+ assert_almost_equal(k.grad, k_.grad)
206
+ assert_almost_equal(v.grad, v_.grad)
207
+
208
+
209
+ @pytest.mark.parametrize("device", DEVICE)
210
+ @pytest.mark.parametrize("attn_dtype", ATTN_MASK_DTYPE)
211
+ @pytest.mark.parametrize("key_padding_dtype", KEY_PADDING_MASK_DTYPE)
212
+ @pytest.mark.parametrize("add_bias_kv", [True, False])
213
+ @pytest.mark.parametrize("add_zero_attn", [True, False])
214
+ # TODO: test with static_kv True
215
+ @pytest.mark.parametrize("static_kv", [False])
216
+ @pytest.mark.parametrize("batch_size", BATCH)
217
+ @pytest.mark.parametrize("embedding", EMB)
218
+ @pytest.mark.parametrize("seq_len", SEQ)
219
+ @pytest.mark.parametrize("num_heads", HEADS)
220
+ def test_xformers_single_forward_parity(
221
+ device,
222
+ attn_dtype,
223
+ key_padding_dtype,
224
+ add_bias_kv,
225
+ add_zero_attn,
226
+ static_kv,
227
+ batch_size,
228
+ embedding,
229
+ seq_len,
230
+ num_heads,
231
+ ):
232
+
233
+ xformers_att_config = '{"name": "scaled_dot_product"}'
234
+
235
+ attn_mask = (
236
+ None
237
+ if attn_dtype is None
238
+ else _get_mask(to_dtype=attn_dtype, dim0=seq_len, dim1=seq_len).to(device)
239
+ )
240
+ key_padding_mask = (
241
+ None
242
+ if key_padding_dtype is None
243
+ else _get_mask(to_dtype=key_padding_dtype, dim0=batch_size, dim1=seq_len).to(
244
+ device
245
+ )
246
+ )
247
+
248
+ q = torch.rand(seq_len, batch_size, embedding).to(device)
249
+ q.requires_grad = True
250
+ k = torch.rand(seq_len, batch_size, embedding).to(device)
251
+ k.requires_grad = True
252
+ v = torch.rand(seq_len, batch_size, embedding).to(device)
253
+ v.requires_grad = True
254
+
255
+ q_ = q.detach().clone()
256
+ q_.requires_grad = True
257
+ k_ = k.detach().clone()
258
+ k_.requires_grad = True
259
+ v_ = v.detach().clone()
260
+ v_.requires_grad = True
261
+
262
+ # TODO: dropouts in the two implementations lead to different entries dropped.
263
+ _reset_seeds()
264
+ xformers_mha = MultiheadAttention(
265
+ embedding,
266
+ num_heads,
267
+ dropout=0.0,
268
+ xformers_att_config=xformers_att_config,
269
+ add_bias_kv=add_bias_kv,
270
+ add_zero_attn=add_zero_attn,
271
+ ).to(device)
272
+ xformers_output, _ = xformers_mha(
273
+ q,
274
+ k,
275
+ v,
276
+ key_padding_mask=key_padding_mask,
277
+ attn_mask=attn_mask,
278
+ static_kv=static_kv,
279
+ )
280
+
281
+ _reset_seeds()
282
+ original_mha = MultiheadAttention(
283
+ embedding,
284
+ num_heads,
285
+ dropout=0.0,
286
+ xformers_att_config=None,
287
+ add_bias_kv=add_bias_kv,
288
+ add_zero_attn=add_zero_attn,
289
+ ).to(device)
290
+ original_output, _ = original_mha(
291
+ q_,
292
+ k_,
293
+ v_,
294
+ key_padding_mask=key_padding_mask,
295
+ attn_mask=attn_mask,
296
+ static_kv=static_kv,
297
+ )
298
+
299
+ # account for when nan != nan
300
+ if xformers_output.isnan().any() or original_output.isnan().any():
301
+ rand = random.uniform(0, 1)
302
+ xformers_output = xformers_output.masked_fill(xformers_output.isnan(), rand)
303
+ original_output = original_output.masked_fill(original_output.isnan(), rand)
304
+
305
+ # torch.equal works for cpu, on cuda allclose is needed.
306
+ assert torch.allclose(
307
+ xformers_output, original_output, atol=1e-06
308
+ ), f"max diff is {torch.max(torch.abs(xformers_output - original_output))}"
309
+
310
+ loss_xformers = torch.norm(xformers_output)
311
+ loss_original = torch.norm(original_output)
312
+ loss_xformers.backward()
313
+ loss_original.backward()
314
+
315
+ # torch.equal works for cpu, on cuda allclose is needed.
316
+ assert torch.allclose(
317
+ q.grad, q_.grad
318
+ ), f"max diff is {torch.max(torch.abs(q.grad - q_.grad))}"
319
+ assert torch.allclose(
320
+ k.grad, k_.grad
321
+ ), f"max diff is {torch.max(torch.abs(k.grad - k_.grad))}"
322
+ assert torch.allclose(
323
+ v.grad, v_.grad
324
+ ), f"max diff is {torch.max(torch.abs(v.grad - v_.grad))}"
325
+
326
+
327
+ def test_mask_padding_parity():
328
+ def old_padding_code(key_padding_mask, attn_mask):
329
+ if attn_mask is not None:
330
+ attn_mask = torch.cat(
331
+ [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1
332
+ )
333
+ if key_padding_mask is not None:
334
+ key_padding_mask = torch.cat(
335
+ [
336
+ key_padding_mask,
337
+ torch.zeros(key_padding_mask.size(0), 1).type_as(key_padding_mask),
338
+ ],
339
+ dim=1,
340
+ )
341
+ return key_padding_mask, attn_mask
342
+
343
+ # values don't matter for this test.
344
+ mha = MultiheadAttention(
345
+ embed_dim=8,
346
+ num_heads=2,
347
+ dropout=0.0,
348
+ add_bias_kv=True,
349
+ add_zero_attn=True,
350
+ )
351
+
352
+ key_padding_mask = torch.rand((8, 64))
353
+ attn_mask = torch.rand((64, 64))
354
+
355
+ kp_mask_orig, a_mask_orig = old_padding_code(key_padding_mask, attn_mask)
356
+ kp_mask_new, a_mask_new = mha._pad_masks(key_padding_mask, attn_mask)
357
+
358
+ assert kp_mask_orig.size() == kp_mask_new.size()
359
+ assert a_mask_orig.size() == a_mask_new.size()
360
+ assert torch.equal(kp_mask_orig, kp_mask_new)
361
+ assert torch.equal(a_mask_orig, a_mask_new)
362
+
363
+
364
+ def test_add_bias_parity():
365
+ # values don't matter for this test.
366
+ mha = MultiheadAttention(
367
+ embed_dim=8,
368
+ num_heads=2,
369
+ dropout=0.0,
370
+ add_bias_kv=True,
371
+ add_zero_attn=True,
372
+ )
373
+
374
+ def old_bias_code(k, v, key_padding_mask, attn_mask, bsz):
375
+ k = torch.cat([k, mha.bias_k.repeat(1, bsz, 1)])
376
+ v = torch.cat([v, mha.bias_v.repeat(1, bsz, 1)])
377
+ if attn_mask is not None:
378
+ attn_mask = torch.cat(
379
+ [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1
380
+ )
381
+ if key_padding_mask is not None:
382
+ key_padding_mask = torch.cat(
383
+ [
384
+ key_padding_mask,
385
+ key_padding_mask.new_zeros(key_padding_mask.size(0), 1),
386
+ ],
387
+ dim=1,
388
+ )
389
+ return k, v, key_padding_mask, attn_mask
390
+
391
+ seq_len = 64
392
+ bsz = 8
393
+ embedding = 8
394
+ key_padding_mask = torch.rand((bsz, seq_len))
395
+ attn_mask = torch.rand((seq_len, seq_len))
396
+ k = torch.rand((seq_len, bsz, embedding))
397
+ v = torch.rand((seq_len, bsz, embedding))
398
+
399
+ k_orig, v_orig, kp_mask_orig, a_mask_orig = old_bias_code(
400
+ k, v, key_padding_mask, attn_mask, bsz
401
+ )
402
+ k_new, v_new, kp_mask_new, a_mask_new = mha._add_bias(
403
+ k, v, key_padding_mask, attn_mask, bsz
404
+ )
405
+
406
+ assert torch.equal(k_orig, k_new)
407
+ assert torch.equal(v_orig, v_new)
408
+ assert torch.equal(kp_mask_orig, kp_mask_new)
409
+ assert torch.equal(a_mask_orig, a_mask_new)
410
+
411
+
412
+ class TestMultiheadAttention(unittest.TestCase):
413
+ def test_append_prev_key_padding_mask(self):
414
+ bsz = 1
415
+ src_len = 4
416
+
417
+ cases = [
418
+ # no padding mask
419
+ (None, None, None),
420
+ # current padding mask only
421
+ (
422
+ torch.tensor([[1]]).bool(),
423
+ None,
424
+ torch.tensor([[0, 0, 0, 1]]).bool(),
425
+ ),
426
+ # previous padding mask only
427
+ (
428
+ None,
429
+ torch.tensor([[0, 1, 0]]).bool(),
430
+ torch.tensor([[0, 1, 0, 0]]).bool(),
431
+ ),
432
+ # both padding masks
433
+ (
434
+ torch.tensor([[1]]).bool(),
435
+ torch.tensor([[0, 1, 0]]).bool(),
436
+ torch.tensor([[0, 1, 0, 1]]).bool(),
437
+ ),
438
+ # prev_key_padding_mask already full
439
+ (
440
+ torch.tensor([[0, 1, 0, 1]]).bool(),
441
+ None,
442
+ torch.tensor([[0, 1, 0, 1]]).bool(),
443
+ ),
444
+ # key_padding_mask already full
445
+ (
446
+ None,
447
+ torch.tensor([[0, 1, 0, 1]]).bool(),
448
+ torch.tensor([[0, 1, 0, 1]]).bool(),
449
+ ),
450
+ ]
451
+ for c in cases:
452
+ key_padding_mask = MultiheadAttention._append_prev_key_padding_mask(
453
+ c[0],
454
+ c[1],
455
+ batch_size=bsz,
456
+ src_len=src_len,
457
+ static_kv=False,
458
+ )
459
+
460
+ if key_padding_mask is not None:
461
+ self.assertTrue(
462
+ torch.all(torch.eq(key_padding_mask, c[2])),
463
+ f"Unexpected resultant key padding mask: {key_padding_mask}"
464
+ f" given current: {c[0]} and previous: {c[1]}",
465
+ )
466
+ self.assertEqual(key_padding_mask.size(0), bsz)
467
+ self.assertEqual(key_padding_mask.size(1), src_len)
468
+ else:
469
+ self.assertIsNone(c[2])
470
+
471
+ def test_pruning_heads(self):
472
+ embed_dim = 768
473
+ num_heads = 12
474
+ num_heads_to_keep = 8
475
+ dummy_input = torch.randn(32, 2, embed_dim)
476
+ mha = MultiheadAttention(embed_dim=embed_dim, num_heads=num_heads)
477
+ reserve_head_index = mha._get_reserve_head_index(
478
+ num_heads_to_keep=num_heads_to_keep
479
+ )
480
+ mha._adaptive_prune_heads(reserve_head_index=reserve_head_index)
481
+ mha._set_skip_embed_dim_check()
482
+ mha(query=dummy_input, key=dummy_input, value=dummy_input)
483
+ self.assertEqual(mha.head_dim, embed_dim / num_heads)
484
+ self.assertEqual(mha.num_heads, num_heads_to_keep)
485
+
486
+
487
+ if __name__ == "__main__":
488
+ unittest.main()
data/fairseq/tests/test_positional_encoding.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+
3
+ import torch
4
+ from fairseq.modules import RelPositionalEncoding
5
+ import numpy as np
6
+
7
+
8
+ class TestRelPositionalEncoding(unittest.TestCase):
9
+ def setUp(self) -> None:
10
+ self.T = 3
11
+ self.B = 1
12
+ self.C = 2
13
+ torch.manual_seed(0)
14
+ self.sample = torch.randn(self.T, self.B, self.C) # TBC
15
+ self.rel_pos_enc = RelPositionalEncoding(max_len=4, d_model=self.C)
16
+
17
+ def test_extend_pe(self):
18
+ inp = self.sample.transpose(0, 1)
19
+ self.rel_pos_enc.extend_pe(inp)
20
+ expected_pe = torch.tensor(
21
+ [
22
+ [
23
+ [0.1411, -0.9900],
24
+ [0.9093, -0.4161],
25
+ [0.8415, 0.5403],
26
+ [0.0000, 1.0000],
27
+ [-0.8415, 0.5403],
28
+ [-0.9093, -0.4161],
29
+ [-0.1411, -0.9900],
30
+ ]
31
+ ]
32
+ )
33
+
34
+ self.assertTrue(
35
+ np.allclose(
36
+ expected_pe.cpu().detach().numpy(),
37
+ self.rel_pos_enc.pe.cpu().detach().numpy(),
38
+ atol=1e-4,
39
+ )
40
+ )
41
+
42
+ def test_forward(self):
43
+ pos_enc = self.rel_pos_enc(self.sample)
44
+ expected_pos_enc = torch.tensor(
45
+ [
46
+ [[0.9093, -0.4161]],
47
+ [[0.8415, 0.5403]],
48
+ [[0.0000, 1.0000]],
49
+ [[-0.8415, 0.5403]],
50
+ [[-0.9093, -0.4161]],
51
+ ]
52
+ )
53
+ self.assertTrue(
54
+ np.allclose(
55
+ pos_enc.cpu().detach().numpy(),
56
+ expected_pos_enc.cpu().detach().numpy(),
57
+ atol=1e-4,
58
+ )
59
+ )
60
+
61
+
62
+ if __name__ == "__main__":
63
+ unittest.main()
data/fairseq/tests/test_reproducibility.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import json
7
+ import os
8
+ import tempfile
9
+ import unittest
10
+
11
+ import torch
12
+
13
+ from . import test_binaries
14
+
15
+
16
+ class TestReproducibility(unittest.TestCase):
17
+ def _test_reproducibility(
18
+ self,
19
+ name,
20
+ extra_flags=None,
21
+ delta=0.0001,
22
+ resume_checkpoint="checkpoint1.pt",
23
+ max_epoch=3,
24
+ ):
25
+ def get_last_log_stats_containing_string(log_records, search_string):
26
+ for log_record in logs.records[::-1]:
27
+ if isinstance(log_record.msg, str) and search_string in log_record.msg:
28
+ return json.loads(log_record.msg)
29
+
30
+ if extra_flags is None:
31
+ extra_flags = []
32
+
33
+ with tempfile.TemporaryDirectory(name) as data_dir:
34
+ with self.assertLogs() as logs:
35
+ test_binaries.create_dummy_data(data_dir)
36
+ test_binaries.preprocess_translation_data(data_dir)
37
+
38
+ # train epochs 1 and 2 together
39
+ with self.assertLogs() as logs:
40
+ test_binaries.train_translation_model(
41
+ data_dir,
42
+ "fconv_iwslt_de_en",
43
+ [
44
+ "--dropout",
45
+ "0.0",
46
+ "--log-format",
47
+ "json",
48
+ "--log-interval",
49
+ "1",
50
+ "--max-epoch",
51
+ str(max_epoch),
52
+ ]
53
+ + extra_flags,
54
+ )
55
+ train_log = get_last_log_stats_containing_string(logs.records, "train_loss")
56
+ valid_log = get_last_log_stats_containing_string(logs.records, "valid_loss")
57
+
58
+ # train epoch 2, resuming from previous checkpoint 1
59
+ os.rename(
60
+ os.path.join(data_dir, resume_checkpoint),
61
+ os.path.join(data_dir, "checkpoint_last.pt"),
62
+ )
63
+ with self.assertLogs() as logs:
64
+ test_binaries.train_translation_model(
65
+ data_dir,
66
+ "fconv_iwslt_de_en",
67
+ [
68
+ "--dropout",
69
+ "0.0",
70
+ "--log-format",
71
+ "json",
72
+ "--log-interval",
73
+ "1",
74
+ "--max-epoch",
75
+ str(max_epoch),
76
+ ]
77
+ + extra_flags,
78
+ )
79
+ train_res_log = get_last_log_stats_containing_string(
80
+ logs.records, "train_loss"
81
+ )
82
+ valid_res_log = get_last_log_stats_containing_string(
83
+ logs.records, "valid_loss"
84
+ )
85
+
86
+ for k in ["train_loss", "train_ppl", "train_num_updates", "train_gnorm"]:
87
+ self.assertAlmostEqual(
88
+ float(train_log[k]), float(train_res_log[k]), delta=delta
89
+ )
90
+ for k in [
91
+ "valid_loss",
92
+ "valid_ppl",
93
+ "valid_num_updates",
94
+ "valid_best_loss",
95
+ ]:
96
+ self.assertAlmostEqual(
97
+ float(valid_log[k]), float(valid_res_log[k]), delta=delta
98
+ )
99
+
100
+ def test_reproducibility(self):
101
+ self._test_reproducibility("test_reproducibility")
102
+
103
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
104
+ def test_reproducibility_fp16(self):
105
+ self._test_reproducibility(
106
+ "test_reproducibility_fp16",
107
+ [
108
+ "--fp16",
109
+ "--fp16-init-scale",
110
+ "4096",
111
+ ],
112
+ delta=0.011,
113
+ )
114
+
115
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
116
+ def test_reproducibility_memory_efficient_fp16(self):
117
+ self._test_reproducibility(
118
+ "test_reproducibility_memory_efficient_fp16",
119
+ [
120
+ "--memory-efficient-fp16",
121
+ "--fp16-init-scale",
122
+ "4096",
123
+ ],
124
+ )
125
+
126
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
127
+ def test_reproducibility_amp(self):
128
+ self._test_reproducibility(
129
+ "test_reproducibility_amp",
130
+ [
131
+ "--amp",
132
+ "--fp16-init-scale",
133
+ "4096",
134
+ ],
135
+ delta=0.011,
136
+ )
137
+
138
+ def test_mid_epoch_reproducibility(self):
139
+ self._test_reproducibility(
140
+ "test_mid_epoch_reproducibility",
141
+ ["--save-interval-updates", "3"],
142
+ resume_checkpoint="checkpoint_1_3.pt",
143
+ max_epoch=1,
144
+ )
145
+
146
+
147
+ if __name__ == "__main__":
148
+ unittest.main()
data/fairseq/tests/test_resampling_dataset.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import collections
7
+ import unittest
8
+
9
+ import numpy as np
10
+ from fairseq.data import ListDataset, ResamplingDataset
11
+
12
+
13
+ class TestResamplingDataset(unittest.TestCase):
14
+ def setUp(self):
15
+ self.strings = ["ab", "c", "def", "ghij"]
16
+ self.weights = [4.0, 2.0, 7.0, 1.5]
17
+ self.size_ratio = 2
18
+ self.dataset = ListDataset(
19
+ self.strings, np.array([len(s) for s in self.strings])
20
+ )
21
+
22
+ def _test_common(self, resampling_dataset, iters):
23
+ assert len(self.dataset) == len(self.strings) == len(self.weights)
24
+ assert len(resampling_dataset) == self.size_ratio * len(self.strings)
25
+
26
+ results = {"ordered_by_size": True, "max_distribution_diff": 0.0}
27
+
28
+ totalfreqs = 0
29
+ freqs = collections.defaultdict(int)
30
+
31
+ for epoch_num in range(iters):
32
+ resampling_dataset.set_epoch(epoch_num)
33
+
34
+ indices = resampling_dataset.ordered_indices()
35
+ assert len(indices) == len(resampling_dataset)
36
+
37
+ prev_size = -1
38
+
39
+ for i in indices:
40
+ cur_size = resampling_dataset.size(i)
41
+ # Make sure indices map to same sequences within an epoch
42
+ assert resampling_dataset[i] == resampling_dataset[i]
43
+
44
+ # Make sure length of sequence is correct
45
+ assert cur_size == len(resampling_dataset[i])
46
+
47
+ freqs[resampling_dataset[i]] += 1
48
+ totalfreqs += 1
49
+
50
+ if prev_size > cur_size:
51
+ results["ordered_by_size"] = False
52
+
53
+ prev_size = cur_size
54
+
55
+ assert set(freqs.keys()) == set(self.strings)
56
+ for s, weight in zip(self.strings, self.weights):
57
+ freq = freqs[s] / totalfreqs
58
+ expected_freq = weight / sum(self.weights)
59
+ results["max_distribution_diff"] = max(
60
+ results["max_distribution_diff"], abs(expected_freq - freq)
61
+ )
62
+
63
+ return results
64
+
65
+ def test_resampling_dataset_batch_by_size_false(self):
66
+ resampling_dataset = ResamplingDataset(
67
+ self.dataset,
68
+ self.weights,
69
+ size_ratio=self.size_ratio,
70
+ batch_by_size=False,
71
+ seed=0,
72
+ )
73
+
74
+ results = self._test_common(resampling_dataset, iters=1000)
75
+
76
+ # For batch_by_size = False, the batches should be returned in
77
+ # arbitrary order of size.
78
+ assert not results["ordered_by_size"]
79
+
80
+ # Allow tolerance in distribution error of 2%.
81
+ assert results["max_distribution_diff"] < 0.02
82
+
83
+ def test_resampling_dataset_batch_by_size_true(self):
84
+ resampling_dataset = ResamplingDataset(
85
+ self.dataset,
86
+ self.weights,
87
+ size_ratio=self.size_ratio,
88
+ batch_by_size=True,
89
+ seed=0,
90
+ )
91
+
92
+ results = self._test_common(resampling_dataset, iters=1000)
93
+
94
+ # For batch_by_size = True, the batches should be returned in
95
+ # increasing order of size.
96
+ assert results["ordered_by_size"]
97
+
98
+ # Allow tolerance in distribution error of 2%.
99
+ assert results["max_distribution_diff"] < 0.02
100
+
101
+
102
+ if __name__ == "__main__":
103
+ unittest.main()
data/fairseq/tests/test_rotary_positional_embedding.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import unittest
4
+ from fairseq.modules.rotary_positional_embedding import apply_rotary_pos_emb
5
+ from fairseq.modules import RotaryPositionalEmbedding
6
+
7
+
8
+ class TestRotaryPositionalEmbedding(unittest.TestCase):
9
+ def setUp(self) -> None:
10
+ self.T = 3
11
+ self.B = 1
12
+ self.C = 2
13
+ torch.manual_seed(0)
14
+ self.sample = torch.randn(self.T, self.B, self.C) # TBC
15
+ self.rope_pos_emd = RotaryPositionalEmbedding(dim=self.C)
16
+
17
+ def test_forward(self):
18
+ expected_cos = torch.tensor(
19
+ [[[[1.0000, 1.0000]]], [[[0.5403, 0.5403]]], [[[-0.4161, -0.4161]]]]
20
+ )
21
+ expected_sin = torch.tensor(
22
+ [[[[0.0000, 0.0000]]], [[[0.8415, 0.8415]]], [[[0.9093, 0.9093]]]]
23
+ )
24
+ cos, sin = self.rope_pos_emd(self.sample, self.T)
25
+ self.assertTrue(
26
+ np.allclose(
27
+ expected_cos.cpu().detach().numpy(),
28
+ cos.cpu().detach().numpy(),
29
+ atol=1e-4,
30
+ )
31
+ )
32
+ self.assertTrue(
33
+ np.allclose(
34
+ expected_sin.cpu().detach().numpy(),
35
+ sin.cpu().detach().numpy(),
36
+ atol=1e-4,
37
+ )
38
+ )
39
+
40
+ def test_apply_rotary_pos_emb(self):
41
+ cos, sin = self.rope_pos_emd(self.sample, self.T)
42
+ query = self.sample.view(self.T, self.B, 1, self.C)
43
+ expected_query = torch.tensor(
44
+ [[[[1.5410, -0.2934]]], [[[-1.6555, -1.5263]]], [[[1.7231, -0.4041]]]]
45
+ )
46
+ new_query, new_key = apply_rotary_pos_emb(query, query, cos, sin)
47
+ self.assertTrue(
48
+ np.allclose(
49
+ expected_query.cpu().detach().numpy(),
50
+ new_query.cpu().detach().numpy(),
51
+ atol=1e-4,
52
+ )
53
+ )
54
+ self.assertTrue(
55
+ np.allclose(
56
+ expected_query.cpu().detach().numpy(),
57
+ new_key.cpu().detach().numpy(),
58
+ atol=1e-4,
59
+ )
60
+ )
61
+
62
+ def test_jit_compile_rope_module(self):
63
+ module_scripted = torch.jit.script(self.rope_pos_emd)
64
+ apply_rotary_scripted = torch.jit.script(apply_rotary_pos_emb)
65
+ # Test several different lengths
66
+ for T in [3, 5, 10]:
67
+ sample = torch.randn(T, self.B, self.C)
68
+ # Run forward pass with the original module
69
+ cos_original, sin_original = self.rope_pos_emd(sample, T)
70
+ query = sample.view(T, self.B, 1, self.C)
71
+ new_query, new_key = apply_rotary_pos_emb(query, query, cos_original, sin_original)
72
+
73
+ # Run forward pass with the scripted module
74
+ cos_scripted, sin_scripted = module_scripted(sample, T)
75
+ new_query_scripted, new_key_scripted = apply_rotary_scripted(query, query, cos_scripted, sin_scripted)
76
+
77
+ # Ensure the outputs are the same
78
+ self.assertTrue(torch.allclose(cos_original, cos_scripted))
79
+ self.assertTrue(torch.allclose(sin_original, sin_scripted))
80
+ self.assertTrue(torch.allclose(new_query, new_query_scripted))
81
+ self.assertTrue(torch.allclose(new_key, new_key_scripted))
82
+
83
+
84
+ if __name__ == "__main__":
85
+ unittest.main()
data/fairseq/tests/test_sequence_generator.py ADDED
@@ -0,0 +1,744 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ import math
8
+ import tempfile
9
+ import unittest
10
+
11
+ import numpy as np
12
+ import torch
13
+
14
+ import tests.utils as test_utils
15
+ from fairseq import search
16
+ from fairseq.data.dictionary import Dictionary
17
+ from fairseq.models.transformer import TransformerModel
18
+ from fairseq.ngram_repeat_block import NGramRepeatBlock
19
+ from fairseq.sequence_generator import EnsembleModel, SequenceGenerator
20
+ from fairseq.tasks.fairseq_task import LegacyFairseqTask
21
+
22
+ DEFAULT_TEST_VOCAB_SIZE = 100
23
+
24
+
25
+ class DummyTask(LegacyFairseqTask):
26
+ def __init__(self, args):
27
+ super().__init__(args)
28
+ self.dictionary = get_dummy_dictionary()
29
+ if getattr(self.args, "ctc", False):
30
+ self.dictionary.add_symbol("<ctc_blank>")
31
+ self.src_dict = self.dictionary
32
+ self.tgt_dict = self.dictionary
33
+
34
+ @property
35
+ def source_dictionary(self):
36
+ return self.src_dict
37
+
38
+ @property
39
+ def target_dictionary(self):
40
+ return self.dictionary
41
+
42
+
43
+ def get_dummy_dictionary(vocab_size=DEFAULT_TEST_VOCAB_SIZE):
44
+ dummy_dict = Dictionary()
45
+ # add dummy symbol to satisfy vocab size
46
+ for id, _ in enumerate(range(vocab_size)):
47
+ dummy_dict.add_symbol("{}".format(id), n=1000)
48
+ return dummy_dict
49
+
50
+
51
+ def get_dummy_task_and_parser():
52
+ """
53
+ to build a fariseq model, we need some dummy parse and task. This function
54
+ is used to create dummy task and parser to faciliate model/criterion test
55
+
56
+ Note: we use FbSpeechRecognitionTask as the dummy task. You may want
57
+ to use other task by providing another function
58
+ """
59
+ parser = argparse.ArgumentParser(
60
+ description="test_dummy_s2s_task", argument_default=argparse.SUPPRESS
61
+ )
62
+ DummyTask.add_args(parser)
63
+ args = parser.parse_args([])
64
+ task = DummyTask.setup_task(args)
65
+ return task, parser
66
+
67
+
68
+ class TestJitSequenceGeneratorBase(unittest.TestCase):
69
+ def setUp(self):
70
+ self.task, self.parser = get_dummy_task_and_parser()
71
+ eos = self.task.tgt_dict.eos()
72
+ src_tokens = torch.randint(3, 50, (2, 10)).long()
73
+ src_tokens = torch.cat((src_tokens, torch.LongTensor([[eos], [eos]])), -1)
74
+ src_lengths = torch.LongTensor([2, 10])
75
+ self.sample = {
76
+ "net_input": {"src_tokens": src_tokens, "src_lengths": src_lengths}
77
+ }
78
+ TransformerModel.add_args(self.parser)
79
+ args = self.parser.parse_args([])
80
+ args.encoder_layers = 2
81
+ args.decoder_layers = 1
82
+ self.transformer_model = TransformerModel.build_model(args, self.task)
83
+
84
+ def assertOutputEqual(self, hypo, pos_probs):
85
+ pos_scores = torch.FloatTensor(pos_probs).log()
86
+ self.assertTensorSizeEqual(hypo["positional_scores"], pos_scores)
87
+ self.assertTensorSizeEqual(pos_scores.numel(), hypo["tokens"].numel())
88
+
89
+ def assertTensorSizeEqual(self, t1, t2):
90
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
91
+
92
+ def assertAlmostEqual(self, t1, t2):
93
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
94
+ self.assertLess((t1 - t2).abs().max(), 1e-4)
95
+
96
+ def assertTensorEqual(self, t1, t2):
97
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
98
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
99
+
100
+ def assertHypoEqual(self, h1, h2):
101
+ "Check two hypos are equal"
102
+ self.assertTensorEqual(h1["tokens"], h2["tokens"])
103
+ self.assertAlmostEqual(h1["positional_scores"], h2["positional_scores"])
104
+ self.assertLess(abs(h1["score"] - h2["score"]), 1e-6)
105
+ self.assertAlmostEqual(h1["attention"], h2["attention"])
106
+
107
+ def _test_save_and_load(self, scripted_module):
108
+ with tempfile.NamedTemporaryFile() as f:
109
+ scripted_module.save(f.name)
110
+ torch.jit.load(f.name)
111
+
112
+
113
+ JIT_MSG = "Targeting OSS scriptability for the 1.6 release"
114
+
115
+
116
+ @unittest.skipIf(torch.__version__ < "1.6.0", JIT_MSG)
117
+ class TestJitSequenceGenerator(TestJitSequenceGeneratorBase):
118
+ def test_export_transformer(self):
119
+ model = self.transformer_model
120
+ torch.jit.script(model)
121
+
122
+ def test_ensemble_sequence_generator(self):
123
+ model = self.transformer_model
124
+ generator = SequenceGenerator(
125
+ [model],
126
+ self.task.tgt_dict,
127
+ beam_size=2,
128
+ no_repeat_ngram_size=2,
129
+ max_len_b=10,
130
+ )
131
+ scripted_model = torch.jit.script(generator)
132
+ self._test_save_and_load(scripted_model)
133
+
134
+ def test_export_ensemble_model(self):
135
+ model = self.transformer_model
136
+ ensemble_models = EnsembleModel([model])
137
+ torch.jit.script(ensemble_models)
138
+
139
+
140
+ class TestExportSearch(unittest.TestCase):
141
+ def setUp(self):
142
+ task, _ = get_dummy_task_and_parser()
143
+ self.tgt_dict = task.tgt_dict
144
+ self.min_top1_prob = 0.4
145
+
146
+ def test_export_diverse_bs(self):
147
+ search_strategy = search.DiverseBeamSearch(
148
+ self.tgt_dict, num_groups=2, diversity_strength=0.0
149
+ )
150
+ torch.jit.script(search_strategy)
151
+
152
+ def test_export_sampling(self):
153
+ low_sampling_topp = self.min_top1_prob / 2.0
154
+ search_strategy = search.Sampling(
155
+ self.tgt_dict, sampling_topp=low_sampling_topp
156
+ )
157
+ torch.jit.script(search_strategy)
158
+
159
+ def test_export_diverse_siblings_search(self):
160
+ search_strategy = search.DiverseSiblingsSearch(
161
+ self.tgt_dict, diversity_rate=0.5
162
+ )
163
+ torch.jit.script(search_strategy)
164
+
165
+
166
+ class TestSequenceGeneratorBase(unittest.TestCase):
167
+ def assertHypoTokens(self, hypo, tokens):
168
+ self.assertTensorEqual(hypo["tokens"], torch.LongTensor(tokens))
169
+
170
+ def assertHypoScore(self, hypo, pos_probs, normalized=True, lenpen=1.0):
171
+ pos_scores = torch.FloatTensor(pos_probs).log()
172
+ self.assertAlmostEqual(hypo["positional_scores"], pos_scores)
173
+ self.assertEqual(pos_scores.numel(), hypo["tokens"].numel())
174
+ score = pos_scores.sum()
175
+ if normalized:
176
+ score /= pos_scores.numel() ** lenpen
177
+ self.assertLess(abs(score - hypo["score"]), 1e-6)
178
+
179
+ def assertAlmostEqual(self, t1, t2):
180
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
181
+ self.assertLess((t1 - t2).abs().max(), 1e-4)
182
+
183
+ def assertTensorEqual(self, t1, t2):
184
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
185
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
186
+
187
+
188
+ class TestSequenceGenerator(TestSequenceGeneratorBase):
189
+ def setUp(self):
190
+ (
191
+ self.tgt_dict,
192
+ self.w1,
193
+ self.w2,
194
+ src_tokens,
195
+ src_lengths,
196
+ self.model,
197
+ ) = test_utils.sequence_generator_setup()
198
+ self.sample = {
199
+ "net_input": {"src_tokens": src_tokens, "src_lengths": src_lengths}
200
+ }
201
+
202
+ def test_with_normalization(self):
203
+ generator = SequenceGenerator([self.model], self.tgt_dict, beam_size=2)
204
+ hypos = generator.forward(self.sample)
205
+ eos, w1, w2 = self.tgt_dict.eos(), self.w1, self.w2
206
+ # sentence 1, beam 1
207
+ self.assertHypoTokens(hypos[0][0], [w1, eos])
208
+ self.assertHypoScore(hypos[0][0], [0.9, 1.0])
209
+ # sentence 1, beam 2
210
+ self.assertHypoTokens(hypos[0][1], [w2, w1, w2, eos])
211
+ self.assertHypoScore(hypos[0][1], [0.1, 0.9, 0.9, 1.0])
212
+ # sentence 2, beam 1
213
+ self.assertHypoTokens(hypos[1][0], [w1, w2, w1, eos])
214
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.4, 1.0])
215
+ # sentence 2, beam 2
216
+ self.assertHypoTokens(hypos[1][1], [w1, w2, eos])
217
+ self.assertHypoScore(hypos[1][1], [0.7, 0.4, 0.6])
218
+
219
+ def test_without_normalization(self):
220
+ # Sentence 1: unchanged from the normalized case
221
+ # Sentence 2: beams swap order
222
+ generator = SequenceGenerator(
223
+ [self.model], self.tgt_dict, beam_size=2, normalize_scores=False
224
+ )
225
+ hypos = generator.forward(self.sample)
226
+ eos, w1, w2 = self.tgt_dict.eos(), self.w1, self.w2
227
+ # sentence 1, beam 1
228
+ self.assertHypoTokens(hypos[0][0], [w1, eos])
229
+ self.assertHypoScore(hypos[0][0], [0.9, 1.0], normalized=False)
230
+ # sentence 1, beam 2
231
+ self.assertHypoTokens(hypos[0][1], [w2, w1, w2, eos])
232
+ self.assertHypoScore(hypos[0][1], [0.1, 0.9, 0.9, 1.0], normalized=False)
233
+ # sentence 2, beam 1
234
+ self.assertHypoTokens(hypos[1][0], [w1, w2, eos])
235
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.6], normalized=False)
236
+ # sentence 2, beam 2
237
+ self.assertHypoTokens(hypos[1][1], [w1, w2, w1, eos])
238
+ self.assertHypoScore(hypos[1][1], [0.7, 0.4, 0.4, 1.0], normalized=False)
239
+
240
+ def test_with_lenpen_favoring_short_hypos(self):
241
+ lenpen = 0.6
242
+ generator = SequenceGenerator(
243
+ [self.model], self.tgt_dict, beam_size=2, len_penalty=lenpen
244
+ )
245
+ hypos = generator.forward(self.sample)
246
+ eos, w1, w2 = self.tgt_dict.eos(), self.w1, self.w2
247
+ # sentence 1, beam 1
248
+ self.assertHypoTokens(hypos[0][0], [w1, eos])
249
+ self.assertHypoScore(hypos[0][0], [0.9, 1.0], lenpen=lenpen)
250
+ # sentence 1, beam 2
251
+ self.assertHypoTokens(hypos[0][1], [w2, w1, w2, eos])
252
+ self.assertHypoScore(hypos[0][1], [0.1, 0.9, 0.9, 1.0], lenpen=lenpen)
253
+ # sentence 2, beam 1
254
+ self.assertHypoTokens(hypos[1][0], [w1, w2, eos])
255
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.6], lenpen=lenpen)
256
+ # sentence 2, beam 2
257
+ self.assertHypoTokens(hypos[1][1], [w1, w2, w1, eos])
258
+ self.assertHypoScore(hypos[1][1], [0.7, 0.4, 0.4, 1.0], lenpen=lenpen)
259
+
260
+ def test_with_lenpen_favoring_long_hypos(self):
261
+ lenpen = 5.0
262
+ generator = SequenceGenerator(
263
+ [self.model], self.tgt_dict, beam_size=2, len_penalty=lenpen
264
+ )
265
+ hypos = generator.forward(self.sample)
266
+ eos, w1, w2 = self.tgt_dict.eos(), self.w1, self.w2
267
+ # sentence 1, beam 1
268
+ self.assertHypoTokens(hypos[0][0], [w2, w1, w2, eos])
269
+ self.assertHypoScore(hypos[0][0], [0.1, 0.9, 0.9, 1.0], lenpen=lenpen)
270
+ # sentence 1, beam 2
271
+ self.assertHypoTokens(hypos[0][1], [w1, eos])
272
+ self.assertHypoScore(hypos[0][1], [0.9, 1.0], lenpen=lenpen)
273
+ # sentence 2, beam 1
274
+ self.assertHypoTokens(hypos[1][0], [w1, w2, w1, eos])
275
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.4, 1.0], lenpen=lenpen)
276
+ # sentence 2, beam 2
277
+ self.assertHypoTokens(hypos[1][1], [w1, w2, eos])
278
+ self.assertHypoScore(hypos[1][1], [0.7, 0.4, 0.6], lenpen=lenpen)
279
+
280
+ def test_maxlen(self):
281
+ generator = SequenceGenerator(
282
+ [self.model], self.tgt_dict, beam_size=2, max_len_b=2
283
+ )
284
+ hypos = generator.forward(self.sample)
285
+ eos, w1, w2 = self.tgt_dict.eos(), self.w1, self.w2
286
+ # sentence 1, beam 1
287
+ self.assertHypoTokens(hypos[0][0], [w1, eos])
288
+ self.assertHypoScore(hypos[0][0], [0.9, 1.0])
289
+ # sentence 1, beam 2
290
+ self.assertHypoTokens(hypos[0][1], [w2, w2, eos])
291
+ self.assertHypoScore(hypos[0][1], [0.1, 0.1, 0.6])
292
+ # sentence 2, beam 1
293
+ self.assertHypoTokens(hypos[1][0], [w1, w2, eos])
294
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.6])
295
+ # sentence 2, beam 2
296
+ self.assertHypoTokens(hypos[1][1], [w2, w2, eos])
297
+ self.assertHypoScore(hypos[1][1], [0.3, 0.9, 0.01])
298
+
299
+ def test_encoder_with_different_output_len(self):
300
+ args = self.model.encoder.args
301
+ task = test_utils.TestTranslationTask.setup_task(
302
+ args, self.tgt_dict, self.tgt_dict
303
+ )
304
+ reshaping_model = test_utils.TestReshapingModel.build_model(args, task)
305
+ generator = SequenceGenerator(
306
+ [reshaping_model], self.tgt_dict, beam_size=2, max_len_b=2
307
+ )
308
+ hypos = generator.forward(self.sample)
309
+ for sent in [0, 1]:
310
+ for beam in [0, 1]:
311
+ assert hypos[sent][beam]["attention"] is not None
312
+
313
+ def test_generation_with_additional_input(self):
314
+ args = self.model.encoder.args
315
+ task = test_utils.TestTranslationTask.setup_task(
316
+ args, self.tgt_dict, self.tgt_dict
317
+ )
318
+ add_input_model = test_utils.TestAdditionalInputModel.build_model(args, task)
319
+ generator = SequenceGenerator([add_input_model], self.tgt_dict, beam_size=2)
320
+ sample = self.sample.copy()
321
+ sample["net_input"]["fancy_other_input"] = sample["net_input"]["src_tokens"]
322
+ hypos = generator.forward(self.sample)
323
+ eos, w1 = self.tgt_dict.eos(), self.w1
324
+ # sentence 1, beam 1
325
+ self.assertHypoTokens(hypos[0][0], [w1, eos])
326
+ self.assertHypoScore(hypos[0][0], [0.9, 1.0])
327
+
328
+
329
+ @unittest.skipUnless(torch.cuda.is_available(), "")
330
+ class TestRepeatNgramBlocking(TestSequenceGeneratorBase):
331
+ @classmethod
332
+ def setUpClass(cls):
333
+ (
334
+ cls.tgt_dict,
335
+ cls.w1,
336
+ cls.w2,
337
+ src_tokens,
338
+ src_lengths,
339
+ cls.model,
340
+ ) = test_utils.sequence_generator_setup()
341
+ return cls
342
+
343
+ def test_finds_repetitive_tokens(self):
344
+ bsz, vocab_size, beam_size, step = 2, 4, 1, 3
345
+ generated_tok = torch.tensor(
346
+ [[2, 2, 2, 2], [3, 3, 3, 3]], dtype=torch.long, device="cuda"
347
+ )
348
+ lprobs = torch.zeros((beam_size * bsz, vocab_size), device="cuda")
349
+ desired_result = lprobs.new_tensor(
350
+ [[0.0, 0.0, -math.inf, 0.0], [0.0, 0.0, 0.0, -math.inf]]
351
+ )
352
+
353
+ cuda_ext_result, baseline_result = self._compare_cuda_ext_to_default_implem(
354
+ bsz, beam_size, generated_tok, lprobs, step, 2
355
+ )
356
+ self.assertTensorEqual(cuda_ext_result, desired_result)
357
+ self.assertTensorEqual(baseline_result, desired_result)
358
+
359
+ @unittest.skipIf(torch.__version__ < "1.6.0", JIT_MSG)
360
+ def test_jit_no_extension(self):
361
+ bsz, vocab_size, beam_size, step = 2, 4, 1, 3
362
+ generated_tok = torch.tensor(
363
+ [[2, 2, 2, 2], [3, 3, 3, 3]], dtype=torch.long, device="cuda"
364
+ )
365
+ lprobs = torch.zeros((beam_size * bsz, vocab_size), device="cuda")
366
+ blocker = NGramRepeatBlock(2, use_extension=False)
367
+ base_result = blocker(generated_tok, lprobs.clone(), bsz, beam_size, step)
368
+ scripted_blocker = torch.jit.script(blocker)
369
+ jit_result = scripted_blocker(
370
+ generated_tok, lprobs.clone(), bsz, beam_size, step
371
+ )
372
+ self.assertTensorEqual(base_result, jit_result)
373
+
374
+ def test_ngram_blocking_same_as_default_implem(self):
375
+ """Test that cuda extension returns same things as default impl in many settings."""
376
+ vocab_size = 4
377
+ step = 6
378
+ for _ in range(2):
379
+ block_param = np.random.choice([1, 2, 3, 4])
380
+ batch_size = np.random.randint(1, 8)
381
+ beam_size = np.random.choice([1, 2, 4, 8])
382
+ lprobs = torch.zeros((beam_size * batch_size, vocab_size), device="cuda")
383
+
384
+ generated_tok = torch.tensor(
385
+ np.random.randint(
386
+ 0, vocab_size, size=(batch_size * beam_size, step + 1)
387
+ ),
388
+ device="cuda",
389
+ dtype=torch.long,
390
+ )
391
+ self._compare_cuda_ext_to_default_implem(
392
+ batch_size,
393
+ beam_size,
394
+ generated_tok,
395
+ lprobs,
396
+ step,
397
+ block_param,
398
+ )
399
+
400
+ def _compare_cuda_ext_to_default_implem(
401
+ self, bsz, beam_size, generated_tok, lprobs, step, block_param
402
+ ):
403
+ """Assert that cuda extension and default implem return the same thing."""
404
+ blocker = NGramRepeatBlock(block_param)
405
+ assert blocker.use_extension, "Extension not compiled"
406
+ cuda_ext_result = blocker(
407
+ generated_tok,
408
+ lprobs.clone(),
409
+ bsz,
410
+ beam_size,
411
+ step,
412
+ )
413
+ blocker.use_extension = False
414
+ baseline_result = blocker(
415
+ generated_tok,
416
+ lprobs.clone(),
417
+ bsz,
418
+ beam_size,
419
+ step,
420
+ )
421
+ self.assertTensorEqual(cuda_ext_result, baseline_result)
422
+ blocker.use_extension = True
423
+ return cuda_ext_result, baseline_result
424
+
425
+
426
+ class TestDiverseBeamSearch(TestSequenceGeneratorBase):
427
+ def setUp(self):
428
+ # construct dummy dictionary
429
+ d = test_utils.dummy_dictionary(vocab_size=2)
430
+ self.assertEqual(d.pad(), 1)
431
+ self.assertEqual(d.eos(), 2)
432
+ self.assertEqual(d.unk(), 3)
433
+ self.eos = d.eos()
434
+ self.w1 = 4
435
+ self.w2 = 5
436
+
437
+ # construct source data
438
+ self.src_tokens = torch.LongTensor(
439
+ [
440
+ [self.w1, self.w2, self.eos],
441
+ [self.w1, self.w2, self.eos],
442
+ ]
443
+ )
444
+ self.src_lengths = torch.LongTensor([2, 2])
445
+
446
+ args = argparse.Namespace()
447
+ unk = 0.0
448
+ args.beam_probs = [
449
+ # step 0:
450
+ torch.FloatTensor(
451
+ [
452
+ # eos w1 w2
453
+ # sentence 1:
454
+ [0.0, unk, 0.9, 0.1], # beam 1
455
+ [0.0, unk, 0.9, 0.1], # beam 2
456
+ # sentence 2:
457
+ [0.0, unk, 0.7, 0.3],
458
+ [0.0, unk, 0.7, 0.3],
459
+ ]
460
+ ),
461
+ # step 1:
462
+ torch.FloatTensor(
463
+ [
464
+ # eos w1 w2
465
+ # sentence 1:
466
+ [0.0, unk, 0.6, 0.4],
467
+ [0.0, unk, 0.6, 0.4],
468
+ # sentence 2:
469
+ [0.25, unk, 0.35, 0.4],
470
+ [0.25, unk, 0.35, 0.4],
471
+ ]
472
+ ),
473
+ # step 2:
474
+ torch.FloatTensor(
475
+ [
476
+ # eos w1 w2
477
+ # sentence 1:
478
+ [1.0, unk, 0.0, 0.0],
479
+ [1.0, unk, 0.0, 0.0],
480
+ # sentence 2:
481
+ [0.9, unk, 0.1, 0.0],
482
+ [0.9, unk, 0.1, 0.0],
483
+ ]
484
+ ),
485
+ ]
486
+
487
+ task = test_utils.TestTranslationTask.setup_task(args, d, d)
488
+ self.model = task.build_model(args)
489
+ self.tgt_dict = task.target_dictionary
490
+
491
+ def test_diverse_beam_search(self):
492
+ search_strategy = search.DiverseBeamSearch(
493
+ self.tgt_dict, num_groups=2, diversity_strength=0.0
494
+ )
495
+ generator = SequenceGenerator(
496
+ [self.model],
497
+ self.tgt_dict,
498
+ beam_size=2,
499
+ search_strategy=search_strategy,
500
+ )
501
+ sample = {
502
+ "net_input": {
503
+ "src_tokens": self.src_tokens,
504
+ "src_lengths": self.src_lengths,
505
+ }
506
+ }
507
+ hypos = generator.forward(sample)
508
+ eos, w1, w2 = self.eos, self.w1, self.w2
509
+ # sentence 1, beam 1
510
+ self.assertHypoTokens(hypos[0][0], [w1, w1, eos])
511
+ self.assertHypoScore(hypos[0][0], [0.9, 0.6, 1.0])
512
+ # sentence 1, beam 2
513
+ self.assertHypoTokens(hypos[0][1], [w1, w1, eos])
514
+ self.assertHypoScore(hypos[0][1], [0.9, 0.6, 1.0])
515
+ # sentence 2, beam 1
516
+ self.assertHypoTokens(hypos[1][0], [w1, w2, eos])
517
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.9])
518
+ # sentence 2, beam 2
519
+ self.assertHypoTokens(hypos[1][1], [w1, w2, eos])
520
+ self.assertHypoScore(hypos[1][1], [0.7, 0.4, 0.9])
521
+
522
+
523
+ class TestDiverseSiblingsSearch(TestDiverseBeamSearch):
524
+ def assertHypoScore(
525
+ self, hypo, pos_probs, sibling_rank, diversity_rate, normalized=True, lenpen=1.0
526
+ ):
527
+ pos_scores = torch.FloatTensor(pos_probs).log()
528
+ pos_scores.sub_(torch.Tensor(sibling_rank) * diversity_rate)
529
+ self.assertAlmostEqual(hypo["positional_scores"], pos_scores)
530
+ self.assertEqual(pos_scores.numel(), hypo["tokens"].numel())
531
+ score = pos_scores.sum()
532
+ if normalized:
533
+ score /= pos_scores.numel() ** lenpen
534
+ self.assertLess(abs(score - hypo["score"]), 1e-6)
535
+
536
+ def test_diverse_beam_search(self):
537
+ search_strategy = search.DiverseSiblingsSearch(
538
+ self.tgt_dict, diversity_rate=0.5
539
+ )
540
+ generator = SequenceGenerator(
541
+ [self.model], self.tgt_dict, beam_size=2, search_strategy=search_strategy
542
+ )
543
+ sample = {
544
+ "net_input": {
545
+ "src_tokens": self.src_tokens,
546
+ "src_lengths": self.src_lengths,
547
+ }
548
+ }
549
+ hypos = generator.forward(sample)
550
+ eos, w1, w2 = self.eos, self.w1, self.w2
551
+ # sentence 1, beam 1
552
+ self.assertHypoTokens(hypos[0][0], [w1, w1, eos])
553
+ self.assertHypoScore(hypos[0][0], [0.9, 0.6, 1.0], [0, 1, 1], 0.5)
554
+ # sentence 1, beam 2
555
+ self.assertHypoTokens(hypos[0][1], [w1, w2, eos])
556
+ self.assertHypoScore(hypos[0][1], [0.9, 0.4, 1.0], [0, 2, 1], 0.5)
557
+ # sentence 2, beam 1
558
+ self.assertHypoTokens(hypos[1][0], [w1, w2, eos])
559
+ self.assertHypoScore(hypos[1][0], [0.7, 0.4, 0.9], [0, 1, 1], 0.5)
560
+ # sentence 2, beam 2
561
+ self.assertHypoTokens(hypos[1][1], [w1, w1, eos])
562
+ self.assertHypoScore(hypos[1][1], [0.7, 0.35, 0.9], [0, 2, 1], 0.5)
563
+
564
+
565
+ class TestTopPSamplingSearch(TestSequenceGeneratorBase):
566
+ def setUp(self):
567
+ # construct dummy dictionary
568
+ d = test_utils.dummy_dictionary(vocab_size=2)
569
+ self.assertEqual(d.pad(), 1)
570
+ self.assertEqual(d.eos(), 2)
571
+ self.assertEqual(d.unk(), 3)
572
+ self.eos = d.eos()
573
+ self.w1 = 4
574
+ self.w2 = 5
575
+
576
+ # construct source data
577
+ self.src_tokens = torch.LongTensor(
578
+ [
579
+ [self.w1, self.w2, self.eos],
580
+ [self.w1, self.w2, self.eos],
581
+ ]
582
+ )
583
+ self.src_lengths = torch.LongTensor([2, 2])
584
+
585
+ args = argparse.Namespace()
586
+ unk = 0.0
587
+ # The minimal probability of top 2 tokens.
588
+ self.min_top2_prob = 0.75
589
+ # The minimal probability of the top 1 token.
590
+ self.min_top1_prob = 0.4
591
+
592
+ w1_prob = self.min_top1_prob
593
+ w2_prob = self.min_top2_prob - self.min_top1_prob
594
+ eos_prob = 1 - self.min_top2_prob
595
+
596
+ args.beam_probs = [
597
+ # step 0:
598
+ torch.FloatTensor(
599
+ [
600
+ # eos w1 w2
601
+ [0.0, unk, 1.0, 0.0],
602
+ [0.0, unk, 1.0, 0.0],
603
+ [0.0, unk, 1.0, 0.0],
604
+ [0.0, unk, 1.0, 0.0],
605
+ ]
606
+ ),
607
+ # step 1:
608
+ torch.FloatTensor(
609
+ [
610
+ # eos w1 w2
611
+ [eos_prob, unk, w1_prob, w2_prob],
612
+ [eos_prob, unk, w1_prob, w2_prob],
613
+ [eos_prob, unk, w1_prob, w2_prob],
614
+ [eos_prob, unk, w1_prob, w2_prob],
615
+ ]
616
+ ),
617
+ # step 2:
618
+ torch.FloatTensor(
619
+ [
620
+ # eos w1 w2
621
+ [1.0, unk, 0.0, 0.0],
622
+ [1.0, unk, 0.0, 0.0],
623
+ [1.0, unk, 0.0, 0.0],
624
+ [1.0, unk, 0.0, 0.0],
625
+ ]
626
+ ),
627
+ ]
628
+
629
+ task = test_utils.TestTranslationTask.setup_task(args, d, d)
630
+ self.model = task.build_model(args)
631
+ self.tgt_dict = task.target_dictionary
632
+
633
+ def test_topp_sampling_search_low_prob(self):
634
+ # Given a prob low enough to top-P sampling, we expect only the top
635
+ # 1 token to be sampled, which always results in the same output.
636
+ low_sampling_topp = self.min_top1_prob / 2.0
637
+ search_strategy = search.Sampling(
638
+ self.tgt_dict, sampling_topp=low_sampling_topp
639
+ )
640
+ generator = SequenceGenerator(
641
+ [self.model], self.tgt_dict, beam_size=2, search_strategy=search_strategy
642
+ )
643
+ sample = {
644
+ "net_input": {
645
+ "src_tokens": self.src_tokens,
646
+ "src_lengths": self.src_lengths,
647
+ }
648
+ }
649
+ hypos = generator.forward(sample)
650
+ eos, w1 = self.eos, self.w1
651
+ # sentence 1, beam 1
652
+ self.assertHypoTokens(hypos[0][0], [w1, w1, eos])
653
+ self.assertHypoScore(hypos[0][0], [1.0, 0.4, 1.0])
654
+ # sentence 1, beam 2
655
+ self.assertHypoTokens(hypos[0][1], [w1, w1, eos])
656
+ self.assertHypoScore(hypos[0][1], [1.0, 0.4, 1.0])
657
+ # sentence 2, beam 1
658
+ self.assertHypoTokens(hypos[1][0], [w1, w1, eos])
659
+ self.assertHypoScore(hypos[1][0], [1.0, 0.4, 1.0])
660
+ # sentence 2, beam 2
661
+ self.assertHypoTokens(hypos[1][1], [w1, w1, eos])
662
+ self.assertHypoScore(hypos[1][1], [1.0, 0.4, 1.0])
663
+
664
+ def test_topp_sampling_search_high_prob(self):
665
+ # Given a prob high enough to top-P sampling, any of the top 2
666
+ # tokens could be sampled. This can cause different outputs.
667
+ high_sampling_topp = (self.min_top1_prob + self.min_top2_prob) / 2.0
668
+ search_strategy = search.Sampling(
669
+ self.tgt_dict, sampling_topp=high_sampling_topp
670
+ )
671
+ generator = SequenceGenerator(
672
+ [self.model], self.tgt_dict, beam_size=2, search_strategy=search_strategy
673
+ )
674
+ sample = {
675
+ "net_input": {
676
+ "src_tokens": self.src_tokens,
677
+ "src_lengths": self.src_lengths,
678
+ }
679
+ }
680
+ hypos = generator.forward(sample)
681
+ eos, w1, w2 = self.eos, self.w1, self.w2
682
+ # sentence 1, beam 1
683
+ self.assertTrue(
684
+ self.hypoTokens(hypos[0][0], [w1, w1, eos])
685
+ or self.hypoTokens(hypos[0][0], [w1, w2, eos])
686
+ )
687
+ self.assertTrue(
688
+ self.hypoScore(hypos[0][0], [1.0, 0.4, 1.0])
689
+ or self.hypoScore(hypos[0][0], [1.0, 0.35, 1.0])
690
+ )
691
+
692
+ # sentence 1, beam 2
693
+ self.assertTrue(
694
+ self.hypoTokens(hypos[0][1], [w1, w1, eos])
695
+ or self.hypoTokens(hypos[0][1], [w1, w2, eos])
696
+ )
697
+ self.assertTrue(
698
+ self.hypoScore(hypos[0][1], [1.0, 0.4, 1.0])
699
+ or self.hypoScore(hypos[0][1], [1.0, 0.35, 1.0])
700
+ )
701
+
702
+ # sentence 2, beam 1
703
+ self.assertTrue(
704
+ self.hypoTokens(hypos[1][0], [w1, w1, eos])
705
+ or self.hypoTokens(hypos[1][0], [w1, w2, eos])
706
+ )
707
+ self.assertTrue(
708
+ self.hypoScore(hypos[1][0], [1.0, 0.4, 1.0])
709
+ or self.hypoScore(hypos[1][0], [1.0, 0.35, 1.0])
710
+ )
711
+
712
+ # sentence 2, beam 2
713
+ self.assertTrue(
714
+ self.hypoTokens(hypos[1][1], [w1, w1, eos])
715
+ or self.hypoTokens(hypos[1][1], [w1, w2, eos])
716
+ )
717
+ self.assertTrue(
718
+ self.hypoScore(hypos[1][1], [1.0, 0.4, 1.0])
719
+ or self.hypoScore(hypos[1][1], [1.0, 0.35, 1.0])
720
+ )
721
+
722
+ def hypoTokens(self, hypo, tokens):
723
+ return self.tensorEqual(hypo["tokens"], torch.LongTensor(tokens))
724
+
725
+ def hypoScore(self, hypo, pos_probs, normalized=True, lenpen=1.0):
726
+ pos_scores = torch.FloatTensor(pos_probs).log()
727
+ if not self.almostEqual(hypo["positional_scores"], pos_scores):
728
+ return False
729
+ if pos_scores.numel() != hypo["tokens"].numel():
730
+ return False
731
+ score = pos_scores.sum()
732
+ if normalized:
733
+ score /= pos_scores.numel() ** lenpen
734
+ return abs(score - hypo["score"]) < 1e-6
735
+
736
+ def almostEqual(self, t1, t2):
737
+ return t1.size() == t2.size() and (t1 - t2).abs().max() < 1e-4
738
+
739
+ def tensorEqual(self, t1, t2):
740
+ return t1.size() == t2.size() and t1.ne(t2).long().sum() == 0
741
+
742
+
743
+ if __name__ == "__main__":
744
+ unittest.main()
data/fairseq/tests/test_sequence_scorer.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ import unittest
8
+
9
+ import tests.utils as test_utils
10
+ import torch
11
+ from fairseq.sequence_scorer import SequenceScorer
12
+
13
+
14
+ class TestSequenceScorer(unittest.TestCase):
15
+ def test_sequence_scorer(self):
16
+ # construct dummy dictionary
17
+ d = test_utils.dummy_dictionary(vocab_size=2)
18
+ self.assertEqual(d.pad(), 1)
19
+ self.assertEqual(d.eos(), 2)
20
+ self.assertEqual(d.unk(), 3)
21
+ eos = d.eos()
22
+ w1 = 4
23
+ w2 = 5
24
+
25
+ # construct dataloader
26
+ data = [
27
+ {
28
+ "source": torch.LongTensor([w1, w2, eos]),
29
+ "target": torch.LongTensor([w1, w2, w1, eos]),
30
+ },
31
+ {
32
+ "source": torch.LongTensor([w2, eos]),
33
+ "target": torch.LongTensor([w2, w1, eos]),
34
+ },
35
+ {
36
+ "source": torch.LongTensor([w2, eos]),
37
+ "target": torch.LongTensor([w2, eos]),
38
+ },
39
+ ]
40
+ data_itr = test_utils.dummy_dataloader(data)
41
+
42
+ # specify expected output probabilities
43
+ args = argparse.Namespace()
44
+ unk = 0.0
45
+ args.beam_probs = [
46
+ # step 0:
47
+ torch.FloatTensor(
48
+ [
49
+ # eos w1 w2
50
+ [0.0, unk, 0.6, 0.4], # sentence 1
51
+ [0.0, unk, 0.4, 0.6], # sentence 2
52
+ [0.0, unk, 0.7, 0.3], # sentence 3
53
+ ]
54
+ ),
55
+ # step 1:
56
+ torch.FloatTensor(
57
+ [
58
+ # eos w1 w2
59
+ [0.0, unk, 0.2, 0.7], # sentence 1
60
+ [0.0, unk, 0.8, 0.2], # sentence 2
61
+ [0.7, unk, 0.1, 0.2], # sentence 3
62
+ ]
63
+ ),
64
+ # step 2:
65
+ torch.FloatTensor(
66
+ [
67
+ # eos w1 w2
68
+ [0.10, unk, 0.50, 0.4], # sentence 1
69
+ [0.15, unk, 0.15, 0.7], # sentence 2
70
+ [0.00, unk, 0.00, 0.0], # sentence 3
71
+ ]
72
+ ),
73
+ # step 3:
74
+ torch.FloatTensor(
75
+ [
76
+ # eos w1 w2
77
+ [0.9, unk, 0.05, 0.05], # sentence 1
78
+ [0.0, unk, 0.00, 0.0], # sentence 2
79
+ [0.0, unk, 0.00, 0.0], # sentence 3
80
+ ]
81
+ ),
82
+ ]
83
+ expected_scores = [
84
+ [0.6, 0.7, 0.5, 0.9], # sentence 1
85
+ [0.6, 0.8, 0.15], # sentence 2
86
+ [0.3, 0.7], # sentence 3
87
+ ]
88
+
89
+ task = test_utils.TestTranslationTask.setup_task(args, d, d)
90
+ model = task.build_model(args)
91
+ scorer = SequenceScorer(task.target_dictionary)
92
+ for sample in data_itr:
93
+ hypos = task.inference_step(scorer, [model], sample)
94
+ for id, hypos_id in zip(sample["id"].tolist(), hypos):
95
+ self.assertHypoTokens(hypos_id[0], data[id]["target"])
96
+ self.assertHypoScore(hypos_id[0], expected_scores[id])
97
+
98
+ def assertHypoTokens(self, hypo, tokens):
99
+ self.assertTensorEqual(hypo["tokens"], torch.LongTensor(tokens))
100
+
101
+ def assertHypoScore(self, hypo, pos_probs, normalized=True, lenpen=1.0):
102
+ pos_scores = torch.FloatTensor(pos_probs).log()
103
+ self.assertAlmostEqual(hypo["positional_scores"], pos_scores)
104
+ self.assertEqual(pos_scores.numel(), hypo["tokens"].numel())
105
+ score = pos_scores.sum()
106
+ if normalized:
107
+ score /= pos_scores.numel() ** lenpen
108
+ self.assertLess(abs(score - hypo["score"]), 1e-6)
109
+
110
+ def assertAlmostEqual(self, t1, t2):
111
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
112
+ self.assertLess((t1 - t2).abs().max(), 1e-4)
113
+
114
+ def assertTensorEqual(self, t1, t2):
115
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
116
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
117
+
118
+
119
+ if __name__ == "__main__":
120
+ unittest.main()
data/fairseq/tests/test_sparse_multihead_attention.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import torch
9
+ from fairseq.modules.sparse_multihead_attention import SparseMultiheadAttention
10
+
11
+
12
+ class TestSparseMultiheadAttention(unittest.TestCase):
13
+ def test_sparse_multihead_attention(self):
14
+ attn_weights = torch.randn(1, 8, 8)
15
+ bidirectional_sparse_mask = torch.tensor(
16
+ [
17
+ [0, 0, 0, 0, 0, float("-inf"), float("-inf"), 0],
18
+ [0, 0, 0, 0, 0, float("-inf"), float("-inf"), 0],
19
+ [0, 0, 0, 0, 0, float("-inf"), float("-inf"), 0],
20
+ [0, 0, 0, 0, 0, float("-inf"), float("-inf"), 0],
21
+ [float("-inf"), float("-inf"), float("-inf"), 0, 0, 0, 0, 0],
22
+ [float("-inf"), float("-inf"), float("-inf"), 0, 0, 0, 0, 0],
23
+ [float("-inf"), float("-inf"), float("-inf"), 0, 0, 0, 0, 0],
24
+ [float("-inf"), float("-inf"), float("-inf"), 0, 0, 0, 0, 0],
25
+ ]
26
+ )
27
+
28
+ bidirectional_attention = SparseMultiheadAttention(
29
+ 16, 1, stride=4, expressivity=1, is_bidirectional=True
30
+ )
31
+ bidirectional_attention_sparse_mask = (
32
+ bidirectional_attention.buffered_sparse_mask(attn_weights, 8, 8)
33
+ )
34
+ torch.all(
35
+ torch.eq(bidirectional_attention_sparse_mask, bidirectional_sparse_mask)
36
+ )
37
+
38
+ sparse_mask = torch.tensor(
39
+ [
40
+ [
41
+ 0,
42
+ float("-inf"),
43
+ float("-inf"),
44
+ float("-inf"),
45
+ float("-inf"),
46
+ float("-inf"),
47
+ float("-inf"),
48
+ float("-inf"),
49
+ ],
50
+ [
51
+ 0,
52
+ 0,
53
+ float("-inf"),
54
+ float("-inf"),
55
+ float("-inf"),
56
+ float("-inf"),
57
+ float("-inf"),
58
+ float("-inf"),
59
+ ],
60
+ [
61
+ 0,
62
+ 0,
63
+ 0,
64
+ float("-inf"),
65
+ float("-inf"),
66
+ float("-inf"),
67
+ float("-inf"),
68
+ float("-inf"),
69
+ ],
70
+ [
71
+ 0,
72
+ 0,
73
+ 0,
74
+ 0,
75
+ float("-inf"),
76
+ float("-inf"),
77
+ float("-inf"),
78
+ float("-inf"),
79
+ ],
80
+ [0, 0, 0, 0, 0, float("-inf"), float("-inf"), float("-inf")],
81
+ [
82
+ float("-inf"),
83
+ float("-inf"),
84
+ float("-inf"),
85
+ 0,
86
+ 0,
87
+ 0,
88
+ float("-inf"),
89
+ float("-inf"),
90
+ ],
91
+ [
92
+ float("-inf"),
93
+ float("-inf"),
94
+ float("-inf"),
95
+ 0,
96
+ 0,
97
+ 0,
98
+ 0,
99
+ float("-inf"),
100
+ ],
101
+ [float("-inf"), float("-inf"), float("-inf"), 0, 0, 0, 0, 0],
102
+ ]
103
+ )
104
+
105
+ attention = SparseMultiheadAttention(
106
+ 16, 1, stride=4, expressivity=1, is_bidirectional=False
107
+ )
108
+ attention_sparse_mask = attention.buffered_sparse_mask(attn_weights, 8, 8)
109
+
110
+ torch.all(torch.eq(attention_sparse_mask, sparse_mask))
111
+
112
+
113
+ if __name__ == "__main__":
114
+ unittest.main()
data/fairseq/tests/test_token_block_dataset.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+
8
+ import tests.utils as test_utils
9
+ import torch
10
+ from fairseq.data import TokenBlockDataset
11
+
12
+
13
+ class TestTokenBlockDataset(unittest.TestCase):
14
+ def _build_dataset(self, data, **kwargs):
15
+ sizes = [len(x) for x in data]
16
+ underlying_ds = test_utils.TestDataset(data)
17
+ return TokenBlockDataset(underlying_ds, sizes, **kwargs)
18
+
19
+ def test_eos_break_mode(self):
20
+ data = [
21
+ torch.tensor([5, 4, 3, 2, 1], dtype=torch.long),
22
+ torch.tensor([1], dtype=torch.long),
23
+ torch.tensor([8, 7, 6, 1], dtype=torch.long),
24
+ ]
25
+ ds = self._build_dataset(data, block_size=None, pad=0, eos=1, break_mode="eos")
26
+ self.assertEqual(ds[0].tolist(), [5, 4, 3, 2, 1])
27
+ self.assertEqual(ds[1].tolist(), [1])
28
+ self.assertEqual(ds[2].tolist(), [8, 7, 6, 1])
29
+
30
+ data = [
31
+ torch.tensor([5, 4, 3, 2, 1], dtype=torch.long),
32
+ torch.tensor([8, 7, 6, 1], dtype=torch.long),
33
+ torch.tensor([1], dtype=torch.long),
34
+ ]
35
+ ds = self._build_dataset(data, block_size=None, pad=0, eos=1, break_mode="eos")
36
+ self.assertEqual(ds[0].tolist(), [5, 4, 3, 2, 1])
37
+ self.assertEqual(ds[1].tolist(), [8, 7, 6, 1])
38
+ self.assertEqual(ds[2].tolist(), [1])
39
+
40
+ def test_block_break_mode(self):
41
+ data = [
42
+ torch.tensor([5, 4, 3, 2, 1], dtype=torch.long),
43
+ torch.tensor([8, 7, 6, 1], dtype=torch.long),
44
+ torch.tensor([9, 1], dtype=torch.long),
45
+ ]
46
+ ds = self._build_dataset(data, block_size=3, pad=0, eos=1, break_mode="none")
47
+ self.assertEqual(ds[0].tolist(), [5, 4, 3])
48
+ self.assertEqual(ds[1].tolist(), [2, 1, 8])
49
+ self.assertEqual(ds[2].tolist(), [7, 6, 1])
50
+ self.assertEqual(ds[3].tolist(), [9, 1])
51
+
52
+ def test_complete_break_mode(self):
53
+ data = [
54
+ torch.tensor([5, 4, 3, 2, 1], dtype=torch.long),
55
+ torch.tensor([8, 7, 6, 1], dtype=torch.long),
56
+ torch.tensor([9, 1], dtype=torch.long),
57
+ ]
58
+ ds = self._build_dataset(
59
+ data, block_size=6, pad=0, eos=1, break_mode="complete"
60
+ )
61
+ self.assertEqual(ds[0].tolist(), [5, 4, 3, 2, 1])
62
+ self.assertEqual(ds[1].tolist(), [8, 7, 6, 1, 9, 1])
63
+
64
+ data = [
65
+ torch.tensor([4, 3, 2, 1], dtype=torch.long),
66
+ torch.tensor([5, 1], dtype=torch.long),
67
+ torch.tensor([1], dtype=torch.long),
68
+ torch.tensor([6, 1], dtype=torch.long),
69
+ ]
70
+ ds = self._build_dataset(
71
+ data, block_size=3, pad=0, eos=1, break_mode="complete"
72
+ )
73
+ self.assertEqual(ds[0].tolist(), [4, 3, 2, 1])
74
+ self.assertEqual(ds[1].tolist(), [5, 1, 1])
75
+ self.assertEqual(ds[2].tolist(), [6, 1])
76
+
77
+ def test_4billion_tokens(self):
78
+ """Regression test for numpy type promotion issue https://github.com/numpy/numpy/issues/5745"""
79
+ data = [torch.tensor(list(range(10000)), dtype=torch.long)] * 430000
80
+ ds = self._build_dataset(
81
+ data, block_size=6, pad=0, eos=1, break_mode="complete"
82
+ )
83
+ ds[-1] # __getitem__ works
84
+ start, end = ds.slice_indices[-1]
85
+ assert end > 4294967295 # data must be sufficiently large to overflow uint32
86
+ assert not isinstance(
87
+ end + 1, float
88
+ ) # this would also raise, since np.uint64(1) + 1 => 2.0
89
+
90
+
91
+ if __name__ == "__main__":
92
+ unittest.main()
data/fairseq/tests/test_transformer.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import unittest
3
+ from typing import Any, Dict, Sequence
4
+
5
+ import torch
6
+ from fairseq.models import transformer
7
+
8
+ from tests.test_roberta import FakeTask
9
+
10
+
11
+ def mk_sample(tok: Sequence[int] = None, batch_size: int = 2) -> Dict[str, Any]:
12
+ if not tok:
13
+ tok = [10, 11, 12, 13, 14, 15, 2]
14
+
15
+ batch = torch.stack([torch.tensor(tok, dtype=torch.long)] * batch_size)
16
+ sample = {
17
+ "net_input": {
18
+ "src_tokens": batch,
19
+ "prev_output_tokens": batch,
20
+ "src_lengths": torch.tensor(
21
+ [len(tok)] * batch_size, dtype=torch.long, device=batch.device
22
+ ),
23
+ },
24
+ "target": batch[:, 1:],
25
+ }
26
+ return sample
27
+
28
+
29
+ def mk_transformer(**extra_args: Any):
30
+ overrides = {
31
+ # Use characteristics dimensions
32
+ "encoder_embed_dim": 12,
33
+ "encoder_ffn_embed_dim": 14,
34
+ "decoder_embed_dim": 12,
35
+ "decoder_ffn_embed_dim": 14,
36
+ # Disable dropout so we have comparable tests.
37
+ "dropout": 0,
38
+ "attention_dropout": 0,
39
+ "activation_dropout": 0,
40
+ "encoder_layerdrop": 0,
41
+ }
42
+ overrides.update(extra_args)
43
+ # Overrides the defaults from the parser
44
+ args = argparse.Namespace(**overrides)
45
+ transformer.tiny_architecture(args)
46
+
47
+ torch.manual_seed(0)
48
+ task = FakeTask(args)
49
+ return transformer.TransformerModel.build_model(args, task)
50
+
51
+
52
+ class TransformerTestCase(unittest.TestCase):
53
+ def test_forward_backward(self):
54
+ model = mk_transformer(encoder_embed_dim=12, decoder_embed_dim=12)
55
+ sample = mk_sample()
56
+ o, _ = model.forward(**sample["net_input"])
57
+ loss = o.sum()
58
+ loss.backward()
59
+
60
+ def test_different_encoder_decoder_embed_dim(self):
61
+ model = mk_transformer(encoder_embed_dim=12, decoder_embed_dim=16)
62
+ sample = mk_sample()
63
+ o, _ = model.forward(**sample["net_input"])
64
+ loss = o.sum()
65
+ loss.backward()