NMCxyz commited on
Commit
97b0a3c
·
verified ·
1 Parent(s): 7a55374

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. omni_speech/infer/fairseq/tests/__init__.py +0 -0
  2. omni_speech/infer/fairseq/tests/distributed/__init__.py +0 -0
  3. omni_speech/infer/fairseq/tests/distributed/test_bmuf.py +204 -0
  4. omni_speech/infer/fairseq/tests/distributed/test_distributed_timeout_wrapper.py +52 -0
  5. omni_speech/infer/fairseq/tests/distributed/test_module_proxy_wrapper.py +74 -0
  6. omni_speech/infer/fairseq/tests/distributed/test_utils.py +124 -0
  7. omni_speech/infer/fairseq/tests/distributed/utils.py +65 -0
  8. omni_speech/infer/fairseq/tests/gpu/__init__.py +0 -0
  9. omni_speech/infer/fairseq/tests/gpu/test_binaries_gpu.py +590 -0
  10. omni_speech/infer/fairseq/tests/gpu/test_ema_gpu.py +215 -0
  11. omni_speech/infer/fairseq/tests/gpu/transformer_quantization_config.yaml +28 -0
  12. omni_speech/infer/fairseq/tests/speech/__init__.py +210 -0
  13. omni_speech/infer/fairseq/tests/speech/test_convtransformer_simul_trans.py +33 -0
  14. omni_speech/infer/fairseq/tests/speech/test_dual_input_wav_transformer.py +76 -0
  15. omni_speech/infer/fairseq/tests/speech/test_dualinput_s2t_transformer.py +110 -0
  16. omni_speech/infer/fairseq/tests/speech/test_fastspeech2.py +53 -0
  17. omni_speech/infer/fairseq/tests/speech/test_s2s_transformer.py +51 -0
  18. omni_speech/infer/fairseq/tests/speech/test_s2t_conformer.py +23 -0
  19. omni_speech/infer/fairseq/tests/speech/test_s2t_transformer.py +23 -0
  20. omni_speech/infer/fairseq/tests/speech/test_tts_transformer.py +53 -0
  21. omni_speech/infer/fairseq/tests/speech/test_wav2vec2.py +90 -0
  22. omni_speech/infer/fairseq/tests/speech/test_xm_transformer.py +29 -0
  23. omni_speech/infer/fairseq/tests/speech_recognition/__init__.py +0 -0
  24. omni_speech/infer/fairseq/tests/speech_recognition/asr_test_base.py +557 -0
  25. omni_speech/infer/fairseq/tests/speech_recognition/test_collaters.py +58 -0
  26. omni_speech/infer/fairseq/tests/speech_recognition/test_cross_entropy.py +37 -0
  27. omni_speech/infer/fairseq/tests/speech_recognition/test_data_utils.py +62 -0
  28. omni_speech/infer/fairseq/tests/speech_recognition/test_vggtransformer.py +135 -0
  29. omni_speech/infer/fairseq/tests/tasks/test_denoising.py +96 -0
  30. omni_speech/infer/fairseq/tests/tasks/test_masked_lm.py +78 -0
  31. omni_speech/infer/fairseq/tests/tasks/test_multilingual_denoising.py +98 -0
  32. omni_speech/infer/fairseq/tests/tasks/test_span_masked_lm.py +106 -0
  33. omni_speech/infer/fairseq/tests/test_convtbc.py +54 -0
  34. omni_speech/infer/fairseq/tests/test_data_utils.py +136 -0
  35. omni_speech/infer/fairseq/tests/test_dataclass_utils.py +87 -0
  36. omni_speech/infer/fairseq/tests/test_dataset.py +66 -0
  37. omni_speech/infer/fairseq/tests/test_dictionary.py +145 -0
  38. omni_speech/infer/fairseq/tests/test_ema.py +275 -0
  39. omni_speech/infer/fairseq/tests/test_espnet_multihead_attention.py +176 -0
  40. omni_speech/infer/fairseq/tests/test_export.py +120 -0
  41. omni_speech/infer/fairseq/tests/test_file_chunker_utils.py +63 -0
  42. omni_speech/infer/fairseq/tests/test_file_io.py +59 -0
  43. omni_speech/infer/fairseq/tests/test_fp16_optimizer.py +111 -0
  44. omni_speech/infer/fairseq/tests/test_hf_hub.py +29 -0
  45. omni_speech/infer/fairseq/tests/test_huffman.py +179 -0
  46. omni_speech/infer/fairseq/tests/test_inference_dropout.py +70 -0
  47. omni_speech/infer/fairseq/tests/test_iopath.py +28 -0
  48. omni_speech/infer/fairseq/tests/test_iterators.py +194 -0
  49. omni_speech/infer/fairseq/tests/test_label_smoothing.py +123 -0
  50. omni_speech/infer/fairseq/tests/test_lm_context_window.py +54 -0
omni_speech/infer/fairseq/tests/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/tests/distributed/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/tests/distributed/test_bmuf.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 functools
8
+ import random
9
+ import unittest
10
+ from multiprocessing import Manager
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ from omegaconf import OmegaConf
15
+
16
+ from fairseq import optim
17
+ from fairseq.distributed import utils as distributed_utils
18
+
19
+
20
+ class Model(nn.Module):
21
+ def __init__(self, input_size, output_size):
22
+ super(Model, self).__init__()
23
+ self.fc = nn.Linear(input_size, output_size)
24
+
25
+ def forward(self, input):
26
+ output = self.fc(input)
27
+ return output
28
+
29
+
30
+ def setup_model_loss_criterion(cfg, args, rank, is_cuda):
31
+ """
32
+ setup model, criterion and optimizer based on input args
33
+ """
34
+ args.distributed_rank = rank
35
+ cfg.distributed_training.distributed_rank = args.distributed_rank
36
+ if cfg.distributed_training.distributed_world_size > 1:
37
+ distributed_utils.distributed_init(cfg)
38
+ torch.manual_seed(1)
39
+ model = Model(args.input_size, args.nb_classes)
40
+ loss_fn = nn.CrossEntropyLoss()
41
+ if is_cuda:
42
+ model = model.cuda()
43
+ loss_fn = loss_fn.cuda()
44
+
45
+ optimizer = optim.sgd.SGD(args, model.parameters())
46
+ optimizer = optim.FairseqBMUF(cfg=cfg.bmuf, optimizer=optimizer)
47
+
48
+ return model, loss_fn, optimizer
49
+
50
+
51
+ def train_step(input, target, model, loss_fn, optimizer, **unused):
52
+ """Do forward, backward and parameter update."""
53
+ model.train()
54
+ output = model(input)
55
+ loss = loss_fn(output, target)
56
+ optimizer.backward(loss)
57
+ optimizer.step()
58
+
59
+
60
+ def single_gpu_training(cfg, args, rank, iterations, shared_results):
61
+
62
+ is_cuda = torch.cuda.is_available()
63
+ if is_cuda:
64
+ torch.cuda.set_device(rank)
65
+
66
+ model, loss_fn, optimizer = setup_model_loss_criterion(cfg, args, rank, is_cuda)
67
+
68
+ for _ in range(iterations):
69
+ input = torch.randn(1, args.input_size)
70
+ target = torch.empty(args.batch_size, dtype=torch.long).random_(args.nb_classes)
71
+
72
+ if is_cuda:
73
+ input = input.cuda()
74
+ target = target.cuda()
75
+ train_step(input, target, model, loss_fn, optimizer)
76
+
77
+ results = []
78
+ for param in model.parameters():
79
+ if len(results) == 0:
80
+ results = param.flatten().cpu().data
81
+ else:
82
+ results = torch.cat((results, param.flatten().cpu().data), 0)
83
+
84
+ shared_results[rank] = results
85
+
86
+
87
+ def setup_args():
88
+ args = argparse.Namespace()
89
+ args.global_sync_iter = 20
90
+ args.block_momentum = 0.875
91
+ args.block_lr = 0.5
92
+ args.input_size = 5
93
+ args.nb_classes = 2
94
+ args.batch_size = 1
95
+ args.lr = [1e-3]
96
+ args.momentum = 0
97
+ args.weight_decay = 0
98
+ args.warmup_iterations = 0
99
+ args.use_nbm = True
100
+ args.average_sync = True
101
+ args.global_sync_iter = 1
102
+ args.model_parallel_size = 1
103
+ args.distributed_backend = "gloo"
104
+
105
+ args.distributed_world_size = 2
106
+ port = random.randint(10000, 20000)
107
+ args.distributed_init_method = "tcp://localhost:{port}".format(port=port)
108
+ args.distributed_init_host = "localhost"
109
+ args.distributed_port = port + 1
110
+ args.local_world_size = args.distributed_world_size
111
+
112
+ cfg = OmegaConf.create()
113
+ cfg.optimization = OmegaConf.create()
114
+ cfg.common = OmegaConf.create()
115
+ cfg.distributed_training = OmegaConf.create()
116
+ cfg.dataset = OmegaConf.create()
117
+ cfg.bmuf = OmegaConf.create()
118
+ cfg.optimizer = OmegaConf.create()
119
+
120
+ cfg.bmuf.global_sync_iter = args.global_sync_iter
121
+ cfg.bmuf.block_momentum = args.block_momentum
122
+ cfg.bmuf.block_lr = args.block_lr
123
+ cfg.dataset.batch_size = args.batch_size
124
+ cfg.optimization.lr = args.lr
125
+ cfg.optimizer.momentum = args.momentum
126
+ cfg.optimizer.weight_decay = args.weight_decay
127
+ cfg.bmuf.warmup_iterations = args.warmup_iterations
128
+ cfg.bmuf.use_nbm = args.use_nbm
129
+ cfg.bmuf.average_sync = args.average_sync
130
+ cfg.common.model_parallel_size = args.model_parallel_size
131
+ cfg.distributed_training.distributed_backend = args.distributed_backend
132
+ cfg.distributed_training.distributed_world_size = args.distributed_world_size
133
+ cfg.bmuf.distributed_world_size = args.distributed_world_size
134
+ cfg.distributed_training.distributed_init_method = args.distributed_init_method
135
+ cfg.distributed_training.distributed_port = args.distributed_port
136
+
137
+ return cfg, args
138
+
139
+
140
+ @unittest.skipIf(torch.cuda.device_count() < 2, "test requires 2 GPUs")
141
+ class TestBMUF(unittest.TestCase):
142
+ def bmuf_process(self, cfg, args, iterations):
143
+ results = Manager().dict()
144
+ torch.multiprocessing.spawn(
145
+ fn=functools.partial(single_gpu_training, cfg, args),
146
+ args=(iterations, results),
147
+ nprocs=args.distributed_world_size,
148
+ join=True,
149
+ )
150
+ return results
151
+
152
+ def test_bmuf_sync(self):
153
+ # Train model for 1 iteration and do bmuf sync without doing warmup
154
+ cfg, args = setup_args()
155
+ iterations = 1
156
+ results = self.bmuf_process(cfg, args, iterations)
157
+ # Make sure params in both machines are same
158
+ assert len(results) == 2
159
+ self.assertAlmostEqual(results[0], results[1])
160
+
161
+ def test_warmup_sync(self):
162
+ # Train model for 20 iteration and do warmup sync without doing bmuf sync
163
+ cfg, args = setup_args()
164
+ args.warmup_iterations = 20
165
+ cfg.bmuf.warmup_iterations = args.warmup_iterations
166
+ iterations = 20
167
+ results = self.bmuf_process(cfg, args, iterations)
168
+ # Make sure params in both machines are same
169
+ assert len(results) == 2
170
+ self.assertAlmostEqual(results[0], results[1])
171
+
172
+ def test_warmup_sync_bmuf_sync(self):
173
+ # Train model for 25 iteration and do warmup sync after 20 iteration
174
+ # and bmuf sync after 25 iteration
175
+ cfg, args = setup_args()
176
+ args.warmup_iterations = 20
177
+ args.global_sync_iter = 5
178
+ cfg.bmuf.warmup_iterations = args.warmup_iterations
179
+ cfg.bmuf.global_sync_iter = args.global_sync_iter
180
+ iterations = 25
181
+ results = self.bmuf_process(cfg, args, iterations)
182
+ # Make sure params in both machines are same
183
+ assert len(results) == 2
184
+ self.assertAlmostEqual(results[0], results[1])
185
+
186
+ def test_single_gpu_bmuf(self):
187
+ # Train model for 5 iterations and use GPU 1
188
+ cfg, args = setup_args()
189
+ args.distributed_world_size = 1
190
+ args.warmup_iterations = 5
191
+ cfg.distributed_training.distributed_world_size = args.distributed_world_size
192
+ cfg.bmuf.distributed_world_size = args.distributed_world_size
193
+ cfg.bmuf.warmup_iterations = args.warmup_iterations
194
+ iterations = 20
195
+ results = self.bmuf_process(cfg, args, iterations)
196
+ assert len(results) == 1
197
+
198
+ def assertAlmostEqual(self, t1, t2):
199
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
200
+ self.assertLess((t1 - t2).abs().max(), 1e-4)
201
+
202
+
203
+ if __name__ == "__main__":
204
+ unittest.main()
omni_speech/infer/fairseq/tests/distributed/test_distributed_timeout_wrapper.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 signal
8
+ import time
9
+ import unittest
10
+
11
+ import torch
12
+ from torch import nn
13
+
14
+ from fairseq.distributed import DistributedTimeoutWrapper
15
+
16
+
17
+ class ModuleWithDelay(nn.Module):
18
+ def __init__(self, delay):
19
+ super().__init__()
20
+ self.delay = delay
21
+
22
+ def forward(self, x):
23
+ time.sleep(self.delay)
24
+ return x
25
+
26
+
27
+ class TestDistributedTimeoutWrapper(unittest.TestCase):
28
+ def setUp(self):
29
+ logging.disable(logging.CRITICAL)
30
+
31
+ def tearDown(self):
32
+ logging.disable(logging.NOTSET)
33
+
34
+ def test_no_timeout(self):
35
+ module = DistributedTimeoutWrapper(ModuleWithDelay(1), 0, signal.SIGINT)
36
+ module(torch.rand(5))
37
+ module.stop_timeout()
38
+
39
+ def test_timeout_safe(self):
40
+ module = DistributedTimeoutWrapper(ModuleWithDelay(1), 10, signal.SIGINT)
41
+ module(torch.rand(5))
42
+ module.stop_timeout()
43
+
44
+ def test_timeout_killed(self):
45
+ with self.assertRaises(KeyboardInterrupt):
46
+ module = DistributedTimeoutWrapper(ModuleWithDelay(5), 1, signal.SIGINT)
47
+ module(torch.rand(5))
48
+ module.stop_timeout()
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
omni_speech/infer/fairseq/tests/distributed/test_module_proxy_wrapper.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch import nn
10
+
11
+ from fairseq.distributed import ModuleProxyWrapper
12
+
13
+ from .utils import objects_are_equal
14
+
15
+
16
+ class MockDDPWrapper(nn.Module):
17
+ """A simple wrapper with an interface similar to DistributedDataParallel."""
18
+
19
+ def __init__(self, module):
20
+ super().__init__()
21
+ self.module = module
22
+
23
+ def forward(self, x):
24
+ return self.module(x)
25
+
26
+
27
+ class Model(nn.Module):
28
+ def __init__(self):
29
+ super().__init__()
30
+ self.linear = nn.Linear(5, 10)
31
+ self.xyz = "hello"
32
+
33
+ def forward(self, x):
34
+ return self.linear(x)
35
+
36
+ def get_xyz(self):
37
+ return self.xyz
38
+
39
+
40
+ class TestModuleProxyWrapper(unittest.TestCase):
41
+ def _get_module(self):
42
+ module = Model()
43
+ wrapped_module = MockDDPWrapper(module)
44
+ wrapped_module = ModuleProxyWrapper(wrapped_module)
45
+ return wrapped_module, module
46
+
47
+ def test_getattr_forwarding(self):
48
+ wrapped_module, module = self._get_module()
49
+ assert module.xyz == "hello"
50
+ assert module.get_xyz() == "hello"
51
+ assert wrapped_module.xyz == "hello"
52
+
53
+ wrapped_module.xyz = "world"
54
+ assert wrapped_module.xyz == "world"
55
+ assert module.get_xyz() == "hello"
56
+
57
+ def test_state_dict(self):
58
+ wrapped_module, module = self._get_module()
59
+ assert objects_are_equal(wrapped_module.state_dict(), module.state_dict())
60
+
61
+ def test_load_state_dict(self):
62
+ wrapped_module, module = self._get_module()
63
+ wrapped_module.load_state_dict(module.state_dict())
64
+ input = torch.rand(4, 5)
65
+ torch.testing.assert_allclose(wrapped_module(input), module(input))
66
+
67
+ def test_forward(self):
68
+ wrapped_module, module = self._get_module()
69
+ input = torch.rand(4, 5)
70
+ torch.testing.assert_allclose(wrapped_module(input), module(input))
71
+
72
+
73
+ if __name__ == "__main__":
74
+ unittest.main()
omni_speech/infer/fairseq/tests/distributed/test_utils.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 functools
7
+ import sys
8
+ import unittest
9
+
10
+ import torch
11
+
12
+ from fairseq.distributed import utils as dist_utils
13
+
14
+ from .utils import objects_are_equal, spawn_and_init
15
+
16
+
17
+ class DistributedTest(unittest.TestCase):
18
+ def setUp(self):
19
+ if not torch.cuda.is_available():
20
+ raise unittest.SkipTest("CUDA not available, skipping test")
21
+ if sys.platform == "win32":
22
+ raise unittest.SkipTest("NCCL doesn't support Windows, skipping test")
23
+ if torch.cuda.device_count() < 2:
24
+ raise unittest.SkipTest("distributed tests require 2+ GPUs, skipping")
25
+
26
+
27
+ class TestBroadcastObject(DistributedTest):
28
+ def test_str(self):
29
+ spawn_and_init(
30
+ functools.partial(
31
+ TestBroadcastObject._test_broadcast_object, "hello world"
32
+ ),
33
+ world_size=2,
34
+ )
35
+
36
+ def test_tensor(self):
37
+ spawn_and_init(
38
+ functools.partial(
39
+ TestBroadcastObject._test_broadcast_object,
40
+ torch.rand(5),
41
+ ),
42
+ world_size=2,
43
+ )
44
+
45
+ def test_complex(self):
46
+ spawn_and_init(
47
+ functools.partial(
48
+ TestBroadcastObject._test_broadcast_object,
49
+ {
50
+ "a": "1",
51
+ "b": [2, torch.rand(2, 3), 3],
52
+ "c": (torch.rand(2, 3), 4),
53
+ "d": {5, torch.rand(5)},
54
+ "e": torch.rand(5),
55
+ "f": torch.rand(5).int().cuda(),
56
+ },
57
+ ),
58
+ world_size=2,
59
+ )
60
+
61
+ @staticmethod
62
+ def _test_broadcast_object(ref_obj, rank, group):
63
+ obj = dist_utils.broadcast_object(
64
+ ref_obj if rank == 0 else None, src_rank=0, group=group
65
+ )
66
+ assert objects_are_equal(ref_obj, obj)
67
+
68
+
69
+ class TestAllGatherList(DistributedTest):
70
+ def test_str_equality(self):
71
+ spawn_and_init(
72
+ functools.partial(
73
+ TestAllGatherList._test_all_gather_list_equality,
74
+ "hello world",
75
+ ),
76
+ world_size=2,
77
+ )
78
+
79
+ def test_tensor_equality(self):
80
+ spawn_and_init(
81
+ functools.partial(
82
+ TestAllGatherList._test_all_gather_list_equality,
83
+ torch.rand(5),
84
+ ),
85
+ world_size=2,
86
+ )
87
+
88
+ def test_complex_equality(self):
89
+ spawn_and_init(
90
+ functools.partial(
91
+ TestAllGatherList._test_all_gather_list_equality,
92
+ {
93
+ "a": "1",
94
+ "b": [2, torch.rand(2, 3), 3],
95
+ "c": (torch.rand(2, 3), 4),
96
+ "d": {5, torch.rand(5)},
97
+ "e": torch.rand(5),
98
+ "f": torch.rand(5).int(),
99
+ },
100
+ ),
101
+ world_size=2,
102
+ )
103
+
104
+ @staticmethod
105
+ def _test_all_gather_list_equality(ref_obj, rank, group):
106
+ objs = dist_utils.all_gather_list(ref_obj, group)
107
+ for obj in objs:
108
+ assert objects_are_equal(ref_obj, obj)
109
+
110
+ def test_rank_tensor(self):
111
+ spawn_and_init(
112
+ TestAllGatherList._test_all_gather_list_rank_tensor, world_size=2
113
+ )
114
+
115
+ @staticmethod
116
+ def _test_all_gather_list_rank_tensor(rank, group):
117
+ obj = torch.tensor([rank])
118
+ objs = dist_utils.all_gather_list(obj, group)
119
+ for i, obj in enumerate(objs):
120
+ assert obj.item() == i
121
+
122
+
123
+ if __name__ == "__main__":
124
+ unittest.main()
omni_speech/infer/fairseq/tests/distributed/utils.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 functools
7
+ import tempfile
8
+
9
+ import torch
10
+
11
+
12
+ def spawn_and_init(fn, world_size, args=None):
13
+ if args is None:
14
+ args = ()
15
+ with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
16
+ torch.multiprocessing.spawn(
17
+ fn=functools.partial(init_and_run, fn, args),
18
+ args=(
19
+ world_size,
20
+ tmp_file.name,
21
+ ),
22
+ nprocs=world_size,
23
+ join=True,
24
+ )
25
+
26
+
27
+ def distributed_init(rank, world_size, tmp_file):
28
+ torch.distributed.init_process_group(
29
+ backend="nccl",
30
+ init_method="file://{}".format(tmp_file),
31
+ world_size=world_size,
32
+ rank=rank,
33
+ )
34
+ torch.cuda.set_device(rank)
35
+
36
+
37
+ def init_and_run(fn, args, rank, world_size, tmp_file):
38
+ distributed_init(rank, world_size, tmp_file)
39
+ group = torch.distributed.new_group()
40
+ fn(rank, group, *args)
41
+
42
+
43
+ def objects_are_equal(a, b) -> bool:
44
+ if type(a) is not type(b):
45
+ return False
46
+ if isinstance(a, dict):
47
+ if set(a.keys()) != set(b.keys()):
48
+ return False
49
+ for k in a.keys():
50
+ if not objects_are_equal(a[k], b[k]):
51
+ return False
52
+ return True
53
+ elif isinstance(a, (list, tuple, set)):
54
+ if len(a) != len(b):
55
+ return False
56
+ return all(objects_are_equal(x, y) for x, y in zip(a, b))
57
+ elif torch.is_tensor(a):
58
+ return (
59
+ a.size() == b.size()
60
+ and a.dtype == b.dtype
61
+ and a.device == b.device
62
+ and torch.all(a == b)
63
+ )
64
+ else:
65
+ return a == b
omni_speech/infer/fairseq/tests/gpu/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/tests/gpu/test_binaries_gpu.py ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tempfile
11
+ import unittest
12
+ from io import StringIO
13
+
14
+ import torch
15
+
16
+ from fairseq import options
17
+ from fairseq_cli import train
18
+ from tests.utils import (
19
+ create_dummy_data,
20
+ generate_main,
21
+ preprocess_lm_data,
22
+ preprocess_translation_data,
23
+ train_language_model,
24
+ train_translation_model,
25
+ )
26
+
27
+
28
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
29
+ class TestMultiGPU(unittest.TestCase):
30
+ @staticmethod
31
+ def parse_logs(logfile):
32
+ logs = []
33
+ for ln in open(logfile, "r").readlines():
34
+ try:
35
+ logs.append(json.loads(ln))
36
+ except json.JSONDecodeError:
37
+ continue
38
+ return logs
39
+
40
+ @property
41
+ def world_size(self):
42
+ return torch.cuda.device_count()
43
+
44
+ def train_flags(self, mu):
45
+ return [
46
+ "--memory-efficient-fp16",
47
+ "--update-freq",
48
+ "1",
49
+ "--seed",
50
+ "1",
51
+ "--log-format",
52
+ "json",
53
+ "--max-update",
54
+ str(mu),
55
+ "--tokens-per-sample",
56
+ "20",
57
+ "--batch-size",
58
+ "2",
59
+ "--share-decoder-input-output-embed",
60
+ "--optimizer",
61
+ "adam",
62
+ "--max-valid-steps",
63
+ "1",
64
+ "--pad-to-fixed-length",
65
+ "--sample-break-mode",
66
+ "none",
67
+ ]
68
+
69
+ def _test_resume_multilingual_training(
70
+ self, extra_clargs, arch="transformer_lm_gpt2_tiny"
71
+ ):
72
+ languages = ["en_XX", "fr_XX", "zh_CN"]
73
+ save_interval = 5
74
+ mu = 10
75
+ flags = (
76
+ self.train_flags(mu)
77
+ + ["--save-interval-updates", str(save_interval), "--log-interval", "1"]
78
+ + extra_clargs
79
+ )
80
+ with contextlib.redirect_stdout(StringIO()):
81
+ with tempfile.TemporaryDirectory("test_fp16") as data_dir:
82
+ log = os.path.join(data_dir, "train.log")
83
+ create_dummy_data(
84
+ data_dir,
85
+ num_examples=int(
86
+ mu * 20 * self.world_size * 1.5
87
+ ), # make sure enough data for max updates
88
+ languages=languages,
89
+ )
90
+ preprocess_lm_data(data_dir, languages)
91
+ train_language_model(
92
+ data_dir,
93
+ arch,
94
+ flags + ["--log-file", log],
95
+ task="multilingual_language_modeling",
96
+ world_size=self.world_size,
97
+ )
98
+ log2 = os.path.join(data_dir, "resume.log")
99
+ ckpt_name = f"checkpoint_1_{save_interval}.pt"
100
+ restore_file = os.path.join(data_dir, ckpt_name)
101
+ train_language_model(
102
+ data_dir,
103
+ arch,
104
+ flags
105
+ + ["--log-file", log2, "--restore-file", restore_file, "--no-save"],
106
+ task="multilingual_language_modeling",
107
+ world_size=self.world_size,
108
+ )
109
+
110
+ l1 = self.parse_logs(log)
111
+ assert (
112
+ int(l1[-1]["train_num_updates"]) == mu
113
+ ), f"The first run did not complete {mu} updates. Add more data"
114
+ l2 = self.parse_logs(log2)
115
+
116
+ if int(l2[0]["num_updates"]) != save_interval + 1:
117
+ all_ckpt_files = [
118
+ x for x in os.listdir(data_dir) if x.endswith(".pt")
119
+ ]
120
+ import shutil
121
+
122
+ shutil.move(data_dir, "last_failed_resume")
123
+ raise AssertionError(
124
+ f"Likely failed to load {ckpt_name}. {all_ckpt_files} \n LOGS: {l1} \n\n {l2}. "
125
+ )
126
+ for k in [
127
+ "train_loss",
128
+ "train_num_updates",
129
+ "train_ppl",
130
+ "train_gnorm",
131
+ ]:
132
+ from_scratch, resumed = float(l1[-1][k]), float(l2[-1][k])
133
+ # This fails without rounding!
134
+ assert (
135
+ from_scratch == resumed
136
+ ), f"difference at {k} {from_scratch} != {resumed}"
137
+
138
+
139
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
140
+ class TestTranslationGPU(unittest.TestCase):
141
+ def setUp(self):
142
+ logging.disable(logging.CRITICAL)
143
+
144
+ def tearDown(self):
145
+ logging.disable(logging.NOTSET)
146
+
147
+ def test_fp16_multigpu(self):
148
+ self._test_multigpu("test_fp16", ["--fp16"])
149
+
150
+ def test_slowmo_multigpu(self):
151
+ self._test_multigpu(
152
+ "test_slowmo", ["--ddp-backend", "slowmo", "--nprocs-per-node", "1"]
153
+ )
154
+
155
+ def test_slowmo_single_node_multigpu(self):
156
+ self._test_multigpu(
157
+ "test_slowmo_single_node",
158
+ ["--ddp-backend", "slowmo", "--nprocs-per-node", "2"],
159
+ )
160
+
161
+ def _test_multigpu(self, test_name, test_args):
162
+ with contextlib.redirect_stdout(StringIO()):
163
+ with tempfile.TemporaryDirectory(test_name) as data_dir:
164
+ log = os.path.join(data_dir, "train.log")
165
+ create_dummy_data(data_dir)
166
+ preprocess_translation_data(data_dir)
167
+ train_translation_model(
168
+ data_dir,
169
+ "fconv_iwslt_de_en",
170
+ test_args + ["--log-file", log],
171
+ world_size=min(torch.cuda.device_count(), 2),
172
+ )
173
+ generate_main(data_dir)
174
+ assert os.path.exists(log)
175
+
176
+ @staticmethod
177
+ def parse_logs(logfile):
178
+ logs = []
179
+ for ln in open(logfile, "r").readlines():
180
+ try:
181
+ logs.append(json.loads(ln))
182
+ except json.JSONDecodeError:
183
+ continue
184
+ return logs
185
+
186
+ def test_resume_training_fsdp(self):
187
+ self._test_resume_training(["--ddp-backend", "fully_sharded"])
188
+
189
+ def test_resume_training_fsdp_sharded_state(self):
190
+ self._test_resume_training(
191
+ ["--ddp-backend", "fully_sharded", "--use-sharded-state"]
192
+ )
193
+
194
+ def test_resume_training_noc10d(self):
195
+ self._test_resume_training([])
196
+
197
+ def _test_resume_training(self, extra_clargs, arch="fconv_iwslt_de_en"):
198
+ flags = [
199
+ "--fp16",
200
+ "--log-format",
201
+ "json",
202
+ "--max-update",
203
+ "10",
204
+ "--save-interval-updates",
205
+ "2",
206
+ "--log-interval",
207
+ "1",
208
+ ] + extra_clargs
209
+ world_size = min(torch.cuda.device_count(), 2)
210
+ with contextlib.redirect_stdout(StringIO()):
211
+ with tempfile.TemporaryDirectory("test_fp16") as data_dir:
212
+ log = os.path.join(data_dir, "train.log")
213
+ create_dummy_data(data_dir)
214
+ preprocess_translation_data(data_dir)
215
+ train_translation_model(
216
+ data_dir,
217
+ arch,
218
+ flags + ["--log-file", log],
219
+ world_size=world_size,
220
+ )
221
+ log2 = os.path.join(data_dir, "resume.log")
222
+ restore_file = os.path.join(data_dir, "checkpoint_1_2.pt")
223
+ train_translation_model(
224
+ data_dir,
225
+ arch,
226
+ flags + ["--log-file", log2, "--restore-file", restore_file],
227
+ world_size=world_size,
228
+ )
229
+
230
+ l1 = self.parse_logs(log)
231
+ l2 = self.parse_logs(log2)
232
+ assert int(l2[0]["num_updates"]) == 3, f"{l1}\n\n {l2}"
233
+ for k in [
234
+ "train_loss",
235
+ "train_num_updates",
236
+ "train_ppl",
237
+ "train_gnorm",
238
+ ]:
239
+ from_scratch, resumed = l1[-1][k], l2[-1][k]
240
+ assert (
241
+ from_scratch == resumed
242
+ ), f"difference at {k} {from_scratch} != {resumed}"
243
+
244
+ def test_memory_efficient_fp16(self):
245
+ with contextlib.redirect_stdout(StringIO()):
246
+ with tempfile.TemporaryDirectory("test_memory_efficient_fp16") as data_dir:
247
+ create_dummy_data(data_dir)
248
+ preprocess_translation_data(data_dir)
249
+ train_translation_model(
250
+ data_dir, "fconv_iwslt_de_en", ["--memory-efficient-fp16"]
251
+ )
252
+ generate_main(data_dir)
253
+
254
+ def test_transformer_fp16(self):
255
+ with contextlib.redirect_stdout(StringIO()):
256
+ with tempfile.TemporaryDirectory("test_transformer") as data_dir:
257
+ create_dummy_data(data_dir)
258
+ preprocess_translation_data(data_dir)
259
+ train_translation_model(
260
+ data_dir,
261
+ "transformer_iwslt_de_en",
262
+ [
263
+ "--encoder-layers",
264
+ "2",
265
+ "--decoder-layers",
266
+ "2",
267
+ "--encoder-embed-dim",
268
+ "64",
269
+ "--decoder-embed-dim",
270
+ "64",
271
+ "--fp16",
272
+ ],
273
+ run_validation=True,
274
+ )
275
+ generate_main(data_dir)
276
+
277
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
278
+ def test_amp(self):
279
+ with contextlib.redirect_stdout(StringIO()):
280
+ with tempfile.TemporaryDirectory("test_amp") as data_dir:
281
+ create_dummy_data(data_dir)
282
+ preprocess_translation_data(data_dir)
283
+ train_translation_model(data_dir, "fconv_iwslt_de_en", ["--amp"])
284
+ generate_main(data_dir)
285
+
286
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
287
+ def test_transformer_amp(self):
288
+ with contextlib.redirect_stdout(StringIO()):
289
+ with tempfile.TemporaryDirectory("test_transformer") as data_dir:
290
+ create_dummy_data(data_dir)
291
+ preprocess_translation_data(data_dir)
292
+ train_translation_model(
293
+ data_dir,
294
+ "transformer_iwslt_de_en",
295
+ [
296
+ "--encoder-layers",
297
+ "2",
298
+ "--decoder-layers",
299
+ "2",
300
+ "--encoder-embed-dim",
301
+ "64",
302
+ "--decoder-embed-dim",
303
+ "64",
304
+ "--amp",
305
+ ],
306
+ run_validation=True,
307
+ )
308
+ generate_main(data_dir)
309
+
310
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
311
+ def test_levenshtein_transformer(self):
312
+ with contextlib.redirect_stdout(StringIO()):
313
+ with tempfile.TemporaryDirectory(
314
+ "test_levenshtein_transformer"
315
+ ) as data_dir:
316
+ create_dummy_data(data_dir)
317
+ preprocess_translation_data(data_dir, ["--joined-dictionary"])
318
+ train_translation_model(
319
+ data_dir,
320
+ "levenshtein_transformer",
321
+ [
322
+ "--apply-bert-init",
323
+ "--early-exit",
324
+ "6,6,6",
325
+ "--criterion",
326
+ "nat_loss",
327
+ ],
328
+ task="translation_lev",
329
+ )
330
+ gen_config = [
331
+ "--task",
332
+ "translation_lev",
333
+ "--iter-decode-max-iter",
334
+ "9",
335
+ "--iter-decode-eos-penalty",
336
+ "0",
337
+ "--print-step",
338
+ ]
339
+ # non-ensemble generation
340
+ generate_main(data_dir, gen_config)
341
+ # ensemble generation
342
+ generate_main(
343
+ data_dir,
344
+ gen_config,
345
+ path=os.pathsep.join(
346
+ [
347
+ os.path.join(data_dir, "checkpoint_last.pt"),
348
+ os.path.join(data_dir, "checkpoint_last.pt"),
349
+ ]
350
+ ),
351
+ )
352
+
353
+ def test_fsdp_checkpoint_generate(self):
354
+ with contextlib.redirect_stdout(StringIO()):
355
+ with tempfile.TemporaryDirectory("test_fsdp_sharded") as data_dir:
356
+ log = os.path.join(data_dir, "train.log")
357
+ create_dummy_data(data_dir)
358
+ preprocess_translation_data(data_dir)
359
+ world_size = min(torch.cuda.device_count(), 2)
360
+ train_translation_model(
361
+ data_dir,
362
+ "fconv_iwslt_de_en",
363
+ ["--log-file", log, "--ddp-backend", "fully_sharded"],
364
+ world_size=world_size,
365
+ )
366
+ generate_main(data_dir)
367
+ assert os.path.exists(log)
368
+
369
+ def test_fsdp_sharded_checkpoint_generate(self):
370
+ with contextlib.redirect_stdout(StringIO()):
371
+ with tempfile.TemporaryDirectory("test_fsdp_sharded") as data_dir:
372
+ log = os.path.join(data_dir, "train.log")
373
+ create_dummy_data(data_dir)
374
+ preprocess_translation_data(data_dir)
375
+ world_size = min(torch.cuda.device_count(), 2)
376
+ train_translation_model(
377
+ data_dir,
378
+ "fconv_iwslt_de_en",
379
+ [
380
+ "--log-file",
381
+ log,
382
+ "--ddp-backend",
383
+ "fully_sharded",
384
+ "--use-sharded-state",
385
+ ],
386
+ world_size=world_size,
387
+ )
388
+ generate_main(data_dir, ["--checkpoint-shard-count", str(world_size)])
389
+ assert os.path.exists(log)
390
+
391
+
392
+ def _quantize_language_model(data_dir, arch, extra_flags=None, run_validation=False):
393
+ train_parser = options.get_training_parser()
394
+ train_args = options.parse_args_and_arch(
395
+ train_parser,
396
+ [
397
+ "--task",
398
+ "language_modeling",
399
+ data_dir,
400
+ "--arch",
401
+ arch,
402
+ "--optimizer",
403
+ "adam",
404
+ "--lr",
405
+ "0.0001",
406
+ "--criterion",
407
+ "adaptive_loss",
408
+ "--adaptive-softmax-cutoff",
409
+ "5,10,15",
410
+ "--max-tokens",
411
+ "500",
412
+ "--tokens-per-sample",
413
+ "500",
414
+ "--save-dir",
415
+ data_dir,
416
+ "--max-epoch",
417
+ "1",
418
+ "--no-progress-bar",
419
+ "--distributed-world-size",
420
+ "1",
421
+ "--ddp-backend",
422
+ "no_c10d",
423
+ "--num-workers",
424
+ "0",
425
+ ]
426
+ + (extra_flags or []),
427
+ )
428
+ train.main(train_args)
429
+
430
+ # try scalar quantization
431
+ scalar_quant_train_parser = options.get_training_parser()
432
+ scalar_quant_train_args = options.parse_args_and_arch(
433
+ scalar_quant_train_parser,
434
+ [
435
+ "--task",
436
+ "language_modeling",
437
+ data_dir,
438
+ "--arch",
439
+ arch,
440
+ "--optimizer",
441
+ "adam",
442
+ "--lr",
443
+ "0.0001",
444
+ "--criterion",
445
+ "adaptive_loss",
446
+ "--adaptive-softmax-cutoff",
447
+ "5,10,15",
448
+ "--max-tokens",
449
+ "500",
450
+ "--tokens-per-sample",
451
+ "500",
452
+ "--save-dir",
453
+ data_dir,
454
+ "--max-update",
455
+ "3",
456
+ "--no-progress-bar",
457
+ "--distributed-world-size",
458
+ "1",
459
+ "--ddp-backend",
460
+ "no_c10d",
461
+ "--num-workers",
462
+ "0",
463
+ "--quant-noise-scalar",
464
+ "0.5",
465
+ ]
466
+ + (extra_flags or []),
467
+ )
468
+ train.main(scalar_quant_train_args)
469
+
470
+ # try iterative PQ quantization
471
+ quantize_parser = options.get_training_parser()
472
+ quantize_args = options.parse_args_and_arch(
473
+ quantize_parser,
474
+ [
475
+ "--task",
476
+ "language_modeling",
477
+ data_dir,
478
+ "--arch",
479
+ arch,
480
+ "--optimizer",
481
+ "adam",
482
+ "--lr",
483
+ "0.0001",
484
+ "--criterion",
485
+ "adaptive_loss",
486
+ "--adaptive-softmax-cutoff",
487
+ "5,10,15",
488
+ "--max-tokens",
489
+ "50",
490
+ "--tokens-per-sample",
491
+ "50",
492
+ "--max-update",
493
+ "6",
494
+ "--no-progress-bar",
495
+ "--distributed-world-size",
496
+ "1",
497
+ "--ddp-backend",
498
+ "no_c10d",
499
+ "--num-workers",
500
+ "0",
501
+ "--restore-file",
502
+ os.path.join(data_dir, "checkpoint_last.pt"),
503
+ "--reset-optimizer",
504
+ "--quantization-config-path",
505
+ os.path.join(
506
+ os.path.dirname(__file__), "transformer_quantization_config.yaml"
507
+ ),
508
+ ]
509
+ + (extra_flags or []),
510
+ )
511
+ train.main(quantize_args)
512
+
513
+
514
+ @unittest.skipIf(
515
+ int(torch.__version__[2]) < 10, reason="quantized kernels are only supported on CPU"
516
+ )
517
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
518
+ class TestQuantization(unittest.TestCase):
519
+ def setUp(self):
520
+ logging.disable(logging.CRITICAL)
521
+
522
+ def tearDown(self):
523
+ logging.disable(logging.NOTSET)
524
+
525
+ def test_quantization(self):
526
+ with contextlib.redirect_stdout(StringIO()):
527
+ with tempfile.TemporaryDirectory("test_quantization") as data_dir:
528
+ create_dummy_data(data_dir)
529
+ preprocess_lm_data(data_dir)
530
+ # tests both scalar and iterative PQ quantization
531
+ _quantize_language_model(data_dir, "transformer_lm")
532
+
533
+
534
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
535
+ class TestOptimizersGPU(unittest.TestCase):
536
+ def setUp(self):
537
+ logging.disable(logging.CRITICAL)
538
+
539
+ def tearDown(self):
540
+ logging.disable(logging.NOTSET)
541
+
542
+ def test_flat_grads(self):
543
+ with contextlib.redirect_stdout(StringIO()):
544
+ with tempfile.TemporaryDirectory("test_flat_grads") as data_dir:
545
+ # Use just a bit of data and tiny model to keep this test runtime reasonable
546
+ create_dummy_data(data_dir, num_examples=10, maxlen=5)
547
+ preprocess_translation_data(data_dir)
548
+ with self.assertRaises(RuntimeError):
549
+ # adafactor isn't compatible with flat grads, which
550
+ # are used by default with --fp16
551
+ train_translation_model(
552
+ data_dir,
553
+ "lstm",
554
+ [
555
+ "--required-batch-size-multiple",
556
+ "1",
557
+ "--encoder-layers",
558
+ "1",
559
+ "--encoder-hidden-size",
560
+ "32",
561
+ "--decoder-layers",
562
+ "1",
563
+ "--optimizer",
564
+ "adafactor",
565
+ "--fp16",
566
+ ],
567
+ )
568
+ # but it should pass once we set --fp16-no-flatten-grads
569
+ train_translation_model(
570
+ data_dir,
571
+ "lstm",
572
+ [
573
+ "--required-batch-size-multiple",
574
+ "1",
575
+ "--encoder-layers",
576
+ "1",
577
+ "--encoder-hidden-size",
578
+ "32",
579
+ "--decoder-layers",
580
+ "1",
581
+ "--optimizer",
582
+ "adafactor",
583
+ "--fp16",
584
+ "--fp16-no-flatten-grads",
585
+ ],
586
+ )
587
+
588
+
589
+ if __name__ == "__main__":
590
+ unittest.main()
omni_speech/infer/fairseq/tests/gpu/test_ema_gpu.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from typing import Optional
10
+
11
+ import torch
12
+
13
+ from fairseq.models.ema import EMA
14
+
15
+
16
+ class DummyModule(torch.nn.Module):
17
+ def __init__(self) -> None:
18
+ """LightningModule for testing purposes
19
+
20
+ Args:
21
+ epoch_min_loss_override (int, optional): Pass in an epoch that will be set to the minimum
22
+ validation loss for testing purposes (zero based). If None this is ignored. Defaults to None.
23
+ """
24
+ super().__init__()
25
+ self.layer = torch.nn.Linear(in_features=32, out_features=2)
26
+ self.another_layer = torch.nn.Linear(in_features=2, out_features=2)
27
+
28
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
29
+ x = self.layer(x)
30
+ return self.another_layer(x)
31
+
32
+
33
+ @dataclass
34
+ class EMAConfig(object):
35
+ ema_decay: float = 0.99
36
+ ema_start_update: int = 0
37
+ ema_fp32: bool = False
38
+ ema_seed_model: Optional[str] = None
39
+ ema_update_freq: int = 1
40
+
41
+
42
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
43
+ class TestEMAGPU(unittest.TestCase):
44
+ def assertTorchAllClose(self, x, y, atol=1e-8, rtol=1e-5, msg=None):
45
+ diff = x.float() - y.float()
46
+ diff_norm = torch.norm(diff)
47
+ other_norm = torch.norm(y.float())
48
+
49
+ if msg is None:
50
+ msg = "|input - other| > {} + {} * |other|".format(atol, rtol)
51
+
52
+ self.assertLessEqual(
53
+ diff_norm,
54
+ atol + rtol * other_norm,
55
+ msg=msg,
56
+ )
57
+
58
+ def test_ema(self):
59
+ model = DummyModule().cuda()
60
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
61
+ state = deepcopy(model.state_dict())
62
+ config = EMAConfig()
63
+ ema = EMA(model, config)
64
+
65
+ # set decay
66
+ ema._set_decay(config.ema_decay)
67
+ self.assertEqual(ema.get_decay(), config.ema_decay)
68
+
69
+ # get model
70
+ self.assertEqual(ema.get_model(), ema.model)
71
+
72
+ # Since fp32 params is not used, it should be of size 0
73
+ self.assertEqual(len(ema.fp32_params), 0)
74
+
75
+ # EMA step
76
+ x = torch.randn(32).cuda()
77
+ y = model(x)
78
+ loss = y.sum()
79
+ loss.backward()
80
+ optimizer.step()
81
+
82
+ ema.step(model)
83
+
84
+ ema_state_dict = ema.get_model().state_dict()
85
+
86
+ for key, param in model.state_dict().items():
87
+ prev_param = state[key]
88
+ ema_param = ema_state_dict[key]
89
+
90
+ if "version" in key:
91
+ # Do not decay a model.version pytorch param
92
+ continue
93
+ self.assertTorchAllClose(
94
+ ema_param,
95
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param,
96
+ )
97
+
98
+ # Since fp32 params is not used, it should be of size 0
99
+ self.assertEqual(len(ema.fp32_params), 0)
100
+
101
+ # Load EMA into model
102
+ model2 = DummyModule().cuda()
103
+ ema.reverse(model2)
104
+
105
+ for key, param in model2.state_dict().items():
106
+ ema_param = ema_state_dict[key]
107
+ self.assertTrue(torch.allclose(ema_param, param))
108
+
109
+ def test_ema_fp32(self):
110
+ model = DummyModule().cuda().half()
111
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
112
+ state = deepcopy(model.state_dict())
113
+ config = EMAConfig(ema_fp32=True)
114
+ ema = EMA(model, config)
115
+
116
+ x = torch.randn(32).cuda()
117
+ y = model(x.half())
118
+ loss = y.sum()
119
+ loss.backward()
120
+ optimizer.step()
121
+
122
+ ema.step(model)
123
+
124
+ for key, param in model.state_dict().items():
125
+ prev_param = state[key]
126
+ ema_param = ema.get_model().state_dict()[key]
127
+
128
+ if "version" in key:
129
+ # Do not decay a model.version pytorch param
130
+ continue
131
+ self.assertIn(key, ema.fp32_params)
132
+
133
+ # EMA update is done in fp32, and hence the EMA param must be
134
+ # closer to the EMA update done in fp32 than in fp16.
135
+ self.assertLessEqual(
136
+ torch.norm(
137
+ ema_param.float()
138
+ - (
139
+ config.ema_decay * prev_param.float()
140
+ + (1 - config.ema_decay) * param.float()
141
+ )
142
+ .half()
143
+ .float()
144
+ ),
145
+ torch.norm(
146
+ ema_param.float()
147
+ - (
148
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param
149
+ ).float()
150
+ ),
151
+ )
152
+ self.assertTorchAllClose(
153
+ ema_param,
154
+ (
155
+ config.ema_decay * prev_param.float()
156
+ + (1 - config.ema_decay) * param.float()
157
+ ).half(),
158
+ )
159
+
160
+ def test_ema_fp16(self):
161
+ model = DummyModule().cuda().half()
162
+ optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
163
+ state = deepcopy(model.state_dict())
164
+ config = EMAConfig(ema_fp32=False)
165
+ ema = EMA(model, config)
166
+
167
+ # Since fp32 params is not used, it should be of size 0
168
+ self.assertEqual(len(ema.fp32_params), 0)
169
+
170
+ x = torch.randn(32).cuda()
171
+ y = model(x.half())
172
+ loss = y.sum()
173
+ loss.backward()
174
+ optimizer.step()
175
+
176
+ ema.step(model)
177
+
178
+ for key, param in model.state_dict().items():
179
+ prev_param = state[key]
180
+ ema_param = ema.get_model().state_dict()[key]
181
+
182
+ if "version" in key:
183
+ # Do not decay a model.version pytorch param
184
+ continue
185
+
186
+ # EMA update is done in fp16, and hence the EMA param must be
187
+ # closer to the EMA update done in fp16 than in fp32.
188
+ self.assertLessEqual(
189
+ torch.norm(
190
+ ema_param.float()
191
+ - (
192
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param
193
+ ).float()
194
+ ),
195
+ torch.norm(
196
+ ema_param.float()
197
+ - (
198
+ config.ema_decay * prev_param.float()
199
+ + (1 - config.ema_decay) * param.float()
200
+ )
201
+ .half()
202
+ .float()
203
+ ),
204
+ )
205
+ self.assertTorchAllClose(
206
+ ema_param,
207
+ config.ema_decay * prev_param + (1 - config.ema_decay) * param,
208
+ )
209
+
210
+ # Since fp32 params is not used, it should be of size 0
211
+ self.assertEqual(len(ema.fp32_params), 0)
212
+
213
+
214
+ if __name__ == "__main__":
215
+ unittest.main()
omni_speech/infer/fairseq/tests/gpu/transformer_quantization_config.yaml 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
+ # This file defines example configuration arguments for quantizing
7
+ # a transformer model with product quantization
8
+
9
+ n_centroids:
10
+ Linear:
11
+ key: in_features
12
+ value: {"*": 8}
13
+ Embedding:
14
+ key: embedding_dim
15
+ value: {"*": 8}
16
+
17
+ block_sizes:
18
+ Linear:
19
+ key: fuzzy_name
20
+ value: {fc: 8, attn: 4, emb: 4}
21
+ Embedding:
22
+ key: fuzzy_name
23
+ value: {emb: 8}
24
+
25
+ layers_to_quantize:
26
+ - decoder\\.layers\\.\d+\\.fc[12]
27
+ - decoder\\.embed_tokens\\.embeddings\\.[012]\\.[01]
28
+ - decoder\\.layers\\.\d+\\.self_attn\\.(k_proj|v_proj|q_proj|out_proj)
omni_speech/infer/fairseq/tests/speech/__init__.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from argparse import Namespace
7
+ import os
8
+ import re
9
+ import unittest
10
+ from pathlib import Path
11
+ from tqdm import tqdm
12
+ from typing import List, Dict, Optional
13
+ import torch
14
+ from fairseq.checkpoint_utils import load_model_ensemble_and_task
15
+ from fairseq.scoring.wer import WerScorer
16
+ from fairseq.scoring.bleu import SacrebleuScorer
17
+ from fairseq import utils
18
+ import zipfile
19
+
20
+ S3_BASE_URL = "https://dl.fbaipublicfiles.com/fairseq"
21
+
22
+
23
+ class TestFairseqSpeech(unittest.TestCase):
24
+ @classmethod
25
+ def download(cls, base_url: str, out_root: Path, filename: str):
26
+ url = f"{base_url}/{filename}"
27
+ path = out_root / filename
28
+ if not path.exists():
29
+ torch.hub.download_url_to_file(url, path.as_posix(), progress=True)
30
+ return path
31
+
32
+ def _set_up(self, dataset_id: str, s3_dir: str, data_filenames: List[str]):
33
+ self.use_cuda = torch.cuda.is_available()
34
+ self.root = Path.home() / ".cache" / "fairseq" / dataset_id
35
+ self.root.mkdir(exist_ok=True, parents=True)
36
+ os.chdir(self.root)
37
+ self.base_url = (
38
+ s3_dir if re.search("^https:", s3_dir) else f"{S3_BASE_URL}/{s3_dir}"
39
+ )
40
+ for filename in data_filenames:
41
+ self.download(self.base_url, self.root, filename)
42
+
43
+ def set_up_librispeech(self):
44
+ self._set_up(
45
+ "librispeech",
46
+ "s2t/librispeech",
47
+ [
48
+ "cfg_librispeech.yaml",
49
+ "spm_librispeech_unigram10000.model",
50
+ "spm_librispeech_unigram10000.txt",
51
+ "librispeech_test-other.tsv",
52
+ "librispeech_test-other.zip",
53
+ ],
54
+ )
55
+
56
+ def set_up_ljspeech(self):
57
+ self._set_up(
58
+ "ljspeech",
59
+ "s2/ljspeech",
60
+ [
61
+ "cfg_ljspeech_g2p.yaml",
62
+ "ljspeech_g2p_gcmvn_stats.npz",
63
+ "ljspeech_g2p.txt",
64
+ "ljspeech_test.tsv",
65
+ "ljspeech_test.zip",
66
+ ],
67
+ )
68
+
69
+ def set_up_sotasty_es_en(self):
70
+ self._set_up(
71
+ "sotasty_es_en",
72
+ "s2t/big/es-en",
73
+ [
74
+ "cfg_es_en.yaml",
75
+ "spm_bpe32768_es_en.model",
76
+ "spm_bpe32768_es_en.txt",
77
+ "sotasty_es_en_test_ted.tsv",
78
+ "sotasty_es_en_test_ted.zip",
79
+ ],
80
+ )
81
+
82
+ def set_up_mustc_de_fbank(self):
83
+ self._set_up(
84
+ "mustc_de_fbank",
85
+ "https://dl.fbaipublicfiles.com/joint_speech_text_4_s2t/must_c/en_de",
86
+ [
87
+ "config.yaml",
88
+ "spm.model",
89
+ "dict.txt",
90
+ "src_dict.txt",
91
+ "tgt_dict.txt",
92
+ "tst-COMMON.tsv",
93
+ "tst-COMMON.zip",
94
+ ],
95
+ )
96
+
97
+ def download_and_load_checkpoint(
98
+ self,
99
+ checkpoint_filename: str,
100
+ arg_overrides: Optional[Dict[str, str]] = None,
101
+ strict: bool = True,
102
+ ):
103
+ path = self.download(self.base_url, self.root, checkpoint_filename)
104
+ _arg_overrides = arg_overrides or {}
105
+ _arg_overrides["data"] = self.root.as_posix()
106
+ models, cfg, task = load_model_ensemble_and_task(
107
+ [path.as_posix()], arg_overrides=_arg_overrides, strict=strict
108
+ )
109
+ if self.use_cuda:
110
+ for model in models:
111
+ model.cuda()
112
+
113
+ return models, cfg, task, self.build_generator(task, models, cfg)
114
+
115
+ def build_generator(
116
+ self,
117
+ task,
118
+ models,
119
+ cfg,
120
+ ):
121
+ return task.build_generator(models, cfg)
122
+
123
+ @classmethod
124
+ def get_batch_iterator(cls, task, test_split, max_tokens, max_positions):
125
+ task.load_dataset(test_split)
126
+ return task.get_batch_iterator(
127
+ dataset=task.dataset(test_split),
128
+ max_tokens=max_tokens,
129
+ max_positions=max_positions,
130
+ num_workers=1,
131
+ ).next_epoch_itr(shuffle=False)
132
+
133
+ @classmethod
134
+ def get_wer_scorer(
135
+ cls, tokenizer="none", lowercase=False, remove_punct=False, char_level=False
136
+ ):
137
+ scorer_args = {
138
+ "wer_tokenizer": tokenizer,
139
+ "wer_lowercase": lowercase,
140
+ "wer_remove_punct": remove_punct,
141
+ "wer_char_level": char_level,
142
+ }
143
+ return WerScorer(Namespace(**scorer_args))
144
+
145
+ @classmethod
146
+ def get_bleu_scorer(cls, tokenizer="13a", lowercase=False, char_level=False):
147
+ scorer_args = {
148
+ "sacrebleu_tokenizer": tokenizer,
149
+ "sacrebleu_lowercase": lowercase,
150
+ "sacrebleu_char_level": char_level,
151
+ }
152
+ return SacrebleuScorer(Namespace(**scorer_args))
153
+
154
+ @torch.no_grad()
155
+ def base_test(
156
+ self,
157
+ ckpt_name,
158
+ reference_score,
159
+ score_delta=0.3,
160
+ dataset="librispeech_test-other",
161
+ max_tokens=65_536,
162
+ max_positions=(4_096, 1_024),
163
+ arg_overrides=None,
164
+ strict=True,
165
+ score_type="wer",
166
+ ):
167
+ models, _, task, generator = self.download_and_load_checkpoint(
168
+ ckpt_name, arg_overrides=arg_overrides, strict=strict
169
+ )
170
+ if not self.use_cuda:
171
+ return
172
+
173
+ batch_iterator = self.get_batch_iterator(
174
+ task, dataset, max_tokens, max_positions
175
+ )
176
+ if score_type == "bleu":
177
+ scorer = self.get_bleu_scorer()
178
+ elif score_type == "wer":
179
+ scorer = self.get_wer_scorer()
180
+ else:
181
+ raise Exception(f"Unsupported score type {score_type}")
182
+
183
+ progress = tqdm(enumerate(batch_iterator), total=len(batch_iterator))
184
+ for batch_idx, sample in progress:
185
+ sample = utils.move_to_cuda(sample) if self.use_cuda else sample
186
+ hypo = task.inference_step(generator, models, sample)
187
+ for i, sample_id in enumerate(sample["id"].tolist()):
188
+ tgt_str, hypo_str = self.postprocess_tokens(
189
+ task,
190
+ sample["target"][i, :],
191
+ hypo[i][0]["tokens"].int().cpu(),
192
+ )
193
+ if batch_idx == 0 and i < 3:
194
+ print(f"T-{sample_id} {tgt_str}")
195
+ print(f"H-{sample_id} {hypo_str}")
196
+ scorer.add_string(tgt_str, hypo_str)
197
+
198
+ print(scorer.result_string() + f" (reference: {reference_score})")
199
+ self.assertAlmostEqual(scorer.score(), reference_score, delta=score_delta)
200
+
201
+ def postprocess_tokens(self, task, target, hypo_tokens):
202
+ tgt_tokens = utils.strip_pad(target, task.tgt_dict.pad()).int().cpu()
203
+ tgt_str = task.tgt_dict.string(tgt_tokens, "sentencepiece")
204
+ hypo_str = task.tgt_dict.string(hypo_tokens, "sentencepiece")
205
+ return tgt_str, hypo_str
206
+
207
+ def unzip_files(self, zip_file_name):
208
+ zip_file_path = self.root / zip_file_name
209
+ with zipfile.ZipFile(zip_file_path, "r") as zip_ref:
210
+ zip_ref.extractall(self.root / zip_file_name.strip(".zip"))
omni_speech/infer/fairseq/tests/speech/test_convtransformer_simul_trans.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tests.speech import TestFairseqSpeech
8
+
9
+ S3_BASE_URL = "https://dl.fbaipublicfiles.com/fairseq/"
10
+
11
+
12
+ class TestConvtransformerSimulTrans(TestFairseqSpeech):
13
+ def setUp(self):
14
+ self._set_up(
15
+ "simul",
16
+ "speech_tests/simul",
17
+ ["config_gcmvn_specaug.yaml", "dict.txt", "dev.tsv"],
18
+ )
19
+
20
+ def test_waitk_checkpoint(self):
21
+ """Only test model loading since fairseq currently doesn't support inference of simultaneous models"""
22
+ _, _, _, _ = self.download_and_load_checkpoint(
23
+ "checkpoint_best.pt",
24
+ arg_overrides={
25
+ "config_yaml": "config_gcmvn_specaug.yaml",
26
+ "load_pretrained_encoder_from": None,
27
+ },
28
+ )
29
+ return
30
+
31
+
32
+ if __name__ == "__main__":
33
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_dual_input_wav_transformer.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 namedtuple
8
+ from pathlib import Path
9
+
10
+ import torch
11
+ from tqdm import tqdm
12
+
13
+ import fairseq
14
+ from fairseq import utils
15
+ from fairseq.checkpoint_utils import load_model_ensemble_and_task
16
+ from fairseq.scoring.bleu import SacrebleuScorer
17
+ from fairseq.tasks import import_tasks
18
+ from tests.speech import S3_BASE_URL, TestFairseqSpeech
19
+
20
+
21
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
22
+ class TestLibrispeechDualInputWavTransformer(TestFairseqSpeech):
23
+ def setUp(self):
24
+ dataset_id = "librispeech_wvtrasnformer"
25
+ base_url = "https://dl.fbaipublicfiles.com/joint_speech_text_4_s2t/acl2022/librispeech/finetuned"
26
+ data_filenames = [
27
+ "checkpoint_ave_10.pt",
28
+ "spm.model",
29
+ "src_dict.txt",
30
+ "tgt_dict.txt",
31
+ "config.yaml",
32
+ ]
33
+ self._set_up(
34
+ dataset_id,
35
+ "s2t",
36
+ [
37
+ "librispeech_flac_test-other.tsv",
38
+ "librispeech_flac_test-other.zip",
39
+ ],
40
+ )
41
+ for filename in data_filenames:
42
+ self.download(base_url, self.root, filename)
43
+
44
+ def import_user_module(self):
45
+ user_dir = (
46
+ Path(fairseq.__file__).parent.parent / "examples/speech_text_joint_to_text"
47
+ )
48
+ Arg = namedtuple("Arg", ["user_dir"])
49
+ arg = Arg(user_dir.__str__())
50
+ utils.import_user_module(arg)
51
+
52
+ @torch.no_grad()
53
+ def test_librispeech_dualinput_wav_transformer_checkpoint(self):
54
+ self.import_user_module()
55
+ checkpoint_filename = "checkpoint_ave_10.pt"
56
+ arg_overrides = {
57
+ "config_yaml": "config.yaml",
58
+ "load_pretrained_speech_text_encoder": "",
59
+ "load_pretrained_speech_text_decoder": "",
60
+ "beam": 10,
61
+ "nbest": 1,
62
+ "lenpen": 1.0,
63
+ "load_speech_only": True,
64
+ }
65
+ self.base_test(
66
+ checkpoint_filename,
67
+ 4.6,
68
+ dataset="librispeech_flac_test-other",
69
+ max_tokens=800000,
70
+ max_positions=(800000, 1024),
71
+ arg_overrides=arg_overrides,
72
+ )
73
+
74
+
75
+ if __name__ == "__main__":
76
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_dualinput_s2t_transformer.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 argparse import Namespace
8
+ from collections import namedtuple
9
+ from pathlib import Path
10
+
11
+ import torch
12
+ from tqdm import tqdm
13
+
14
+ import fairseq
15
+ from fairseq import utils
16
+ from fairseq.checkpoint_utils import load_model_ensemble_and_task
17
+ from fairseq.scoring.bleu import SacrebleuScorer
18
+ from fairseq.tasks import import_tasks
19
+ from tests.speech import TestFairseqSpeech
20
+
21
+
22
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
23
+ class TestDualInputS2TTransformer(TestFairseqSpeech):
24
+ def setUp(self):
25
+ self.set_up_mustc_de_fbank()
26
+
27
+ def import_user_module(self):
28
+ user_dir = (
29
+ Path(fairseq.__file__).parent.parent / "examples/speech_text_joint_to_text"
30
+ )
31
+ Arg = namedtuple("Arg", ["user_dir"])
32
+ arg = Arg(user_dir.__str__())
33
+ utils.import_user_module(arg)
34
+
35
+ @torch.no_grad()
36
+ def test_mustc_de_fbank_dualinput_s2t_transformer_checkpoint(self):
37
+ self.import_user_module()
38
+ checkpoint_filename = "checkpoint_ave_10.pt"
39
+ path = self.download(self.base_url, self.root, checkpoint_filename)
40
+ models, cfg, task = load_model_ensemble_and_task(
41
+ [path.as_posix()],
42
+ arg_overrides={
43
+ "data": self.root.as_posix(),
44
+ "config_yaml": "config.yaml",
45
+ "load_pretrain_speech_encoder": "",
46
+ "load_pretrain_text_encoder_last": "",
47
+ "load_pretrain_decoder": "",
48
+ "beam": 10,
49
+ "nbest": 1,
50
+ "lenpen": 1.0,
51
+ "load_speech_only": True,
52
+ },
53
+ )
54
+ if self.use_cuda:
55
+ for model in models:
56
+ model.cuda()
57
+ generator = task.build_generator(models, cfg)
58
+ test_split = "tst-COMMON"
59
+ task.load_dataset(test_split)
60
+ batch_iterator = task.get_batch_iterator(
61
+ dataset=task.dataset(test_split),
62
+ max_tokens=250_000,
63
+ max_positions=(10_000, 1_024),
64
+ num_workers=1,
65
+ ).next_epoch_itr(shuffle=False)
66
+
67
+ tokenizer = task.build_tokenizer(cfg.tokenizer)
68
+ bpe = task.build_bpe(cfg.bpe)
69
+
70
+ def decode_fn(x):
71
+ if bpe is not None:
72
+ x = bpe.decode(x)
73
+ if tokenizer is not None:
74
+ x = tokenizer.decode(x)
75
+ return x
76
+
77
+ scorer_args = {
78
+ "sacrebleu_tokenizer": "13a",
79
+ "sacrebleu_lowercase": False,
80
+ "sacrebleu_char_level": False,
81
+ }
82
+ scorer = SacrebleuScorer(Namespace(**scorer_args))
83
+ progress = tqdm(enumerate(batch_iterator), total=len(batch_iterator))
84
+ for batch_idx, sample in progress:
85
+ sample = utils.move_to_cuda(sample) if self.use_cuda else sample
86
+ hypo = task.inference_step(generator, models, sample)
87
+ for i, sample_id in enumerate(sample["id"].tolist()):
88
+ tgt_tokens = (
89
+ utils.strip_pad(sample["target"][i, :], task.tgt_dict.pad())
90
+ .int()
91
+ .cpu()
92
+ )
93
+
94
+ tgt_str = task.tgt_dict.string(tgt_tokens, "sentencepiece")
95
+ hypo_str = task.tgt_dict.string(
96
+ hypo[i][0]["tokens"].int().cpu(), "sentencepiece"
97
+ )
98
+ if batch_idx == 0 and i < 3:
99
+ print(f"T-{sample_id} {tgt_str}")
100
+ print(f"D-{sample_id} {hypo_str}")
101
+ scorer.add_string(tgt_str, hypo_str)
102
+ reference_bleu = 27.3
103
+ result = scorer.result_string()
104
+ print(result + f" (reference: {reference_bleu})")
105
+ res_bleu = float(result.split()[2])
106
+ self.assertAlmostEqual(res_bleu, reference_bleu, delta=0.3)
107
+
108
+
109
+ if __name__ == "__main__":
110
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_fastspeech2.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tqdm import tqdm
10
+
11
+ from fairseq import utils
12
+ from fairseq.tasks.text_to_speech import batch_mel_cepstral_distortion
13
+ from tests.speech import TestFairseqSpeech
14
+
15
+
16
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
17
+ class TestFastSpeech2(TestFairseqSpeech):
18
+ def setUp(self):
19
+ self.set_up_ljspeech()
20
+
21
+ @torch.no_grad()
22
+ def test_ljspeech_fastspeech2_checkpoint(self):
23
+ models, cfg, task, generator = self.download_and_load_checkpoint(
24
+ "ljspeech_fastspeech2_g2p.pt",
25
+ arg_overrides={
26
+ "config_yaml": "cfg_ljspeech_g2p.yaml",
27
+ "vocoder": "griffin_lim",
28
+ "fp16": False,
29
+ },
30
+ )
31
+
32
+ batch_iterator = self.get_batch_iterator(task, "ljspeech_test", 65_536, 4_096)
33
+ progress = tqdm(batch_iterator, total=len(batch_iterator))
34
+ mcd, n_samples = 0.0, 0
35
+ for sample in progress:
36
+ sample = utils.move_to_cuda(sample) if self.use_cuda else sample
37
+ hypos = generator.generate(models[0], sample, has_targ=True)
38
+ rets = batch_mel_cepstral_distortion(
39
+ [hypo["targ_waveform"] for hypo in hypos],
40
+ [hypo["waveform"] for hypo in hypos],
41
+ sr=task.sr,
42
+ )
43
+ mcd += sum(d.item() for d, _ in rets)
44
+ n_samples += len(sample["id"].tolist())
45
+
46
+ mcd = round(mcd / n_samples, 1)
47
+ reference_mcd = 3.2
48
+ print(f"MCD: {mcd} (reference: {reference_mcd})")
49
+ self.assertAlmostEqual(mcd, reference_mcd, delta=0.1)
50
+
51
+
52
+ if __name__ == "__main__":
53
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_s2s_transformer.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tests.speech import TestFairseqSpeech
8
+ from fairseq import utils
9
+
10
+ S3_BASE_URL = "https://dl.fbaipublicfiles.com/fairseq/"
11
+
12
+
13
+ class TestS2STransformer(TestFairseqSpeech):
14
+ def setUp(self):
15
+ self._set_up(
16
+ "s2s",
17
+ "speech_tests/s2s",
18
+ [
19
+ "dev_shuf200.tsv",
20
+ "src_feat.zip",
21
+ "config_specaug_lb.yaml",
22
+ "vocoder",
23
+ "vocoder_config.json",
24
+ ],
25
+ )
26
+
27
+ def test_s2s_transformer_checkpoint(self):
28
+ self.base_test(
29
+ ckpt_name="s2u_transformer_reduced_fisher.pt",
30
+ reference_score=38.3,
31
+ dataset="dev_shuf200",
32
+ arg_overrides={
33
+ "config_yaml": "config_specaug_lb.yaml",
34
+ "multitask_config_yaml": None,
35
+ "target_is_code": True,
36
+ "target_code_size": 100,
37
+ "eval_inference": False,
38
+ },
39
+ score_type="bleu",
40
+ strict=False,
41
+ )
42
+
43
+ def postprocess_tokens(self, task, target, hypo_tokens):
44
+ tgt_tokens = utils.strip_pad(target, task.tgt_dict.pad()).int().cpu()
45
+ tgt_str = task.tgt_dict.string(tgt_tokens)
46
+ hypo_str = task.tgt_dict.string(hypo_tokens)
47
+ return tgt_str, hypo_str
48
+
49
+
50
+ if __name__ == "__main__":
51
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_s2t_conformer.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tests.speech import TestFairseqSpeech
8
+
9
+
10
+ class TestS2TConformer(TestFairseqSpeech):
11
+ def setUp(self):
12
+ self.set_up_librispeech()
13
+
14
+ def test_librispeech_s2t_conformer_s_checkpoint(self):
15
+ self.base_test(
16
+ ckpt_name="librispeech_conformer_rel_pos_s.pt",
17
+ reference_score=12,
18
+ arg_overrides={"config_yaml": "cfg_librispeech.yaml"},
19
+ )
20
+
21
+
22
+ if __name__ == "__main__":
23
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_s2t_transformer.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tests.speech import TestFairseqSpeech
8
+
9
+
10
+ class TestS2TTransformer(TestFairseqSpeech):
11
+ def setUp(self):
12
+ self.set_up_librispeech()
13
+
14
+ def test_librispeech_s2t_transformer_s_checkpoint(self):
15
+ self.base_test(
16
+ ckpt_name="librispeech_transformer_s.pt",
17
+ reference_score=9,
18
+ arg_overrides={"config_yaml": "cfg_librispeech.yaml"},
19
+ )
20
+
21
+
22
+ if __name__ == "__main__":
23
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_tts_transformer.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tqdm import tqdm
10
+
11
+ from fairseq import utils
12
+ from fairseq.tasks.text_to_speech import batch_mel_cepstral_distortion
13
+ from tests.speech import TestFairseqSpeech
14
+
15
+
16
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
17
+ class TestTTSTransformer(TestFairseqSpeech):
18
+ def setUp(self):
19
+ self.set_up_ljspeech()
20
+
21
+ @torch.no_grad()
22
+ def test_ljspeech_tts_transformer_checkpoint(self):
23
+ models, cfg, task, generator = self.download_and_load_checkpoint(
24
+ "ljspeech_transformer_g2p.pt",
25
+ arg_overrides={
26
+ "config_yaml": "cfg_ljspeech_g2p.yaml",
27
+ "vocoder": "griffin_lim",
28
+ "fp16": False,
29
+ },
30
+ )
31
+
32
+ batch_iterator = self.get_batch_iterator(task, "ljspeech_test", 65_536, 1024)
33
+ progress = tqdm(batch_iterator, total=len(batch_iterator))
34
+ mcd, n_samples = 0.0, 0
35
+ for sample in progress:
36
+ sample = utils.move_to_cuda(sample) if self.use_cuda else sample
37
+ hypos = generator.generate(models[0], sample, has_targ=True)
38
+ rets = batch_mel_cepstral_distortion(
39
+ [hypo["targ_waveform"] for hypo in hypos],
40
+ [hypo["waveform"] for hypo in hypos],
41
+ sr=task.sr,
42
+ )
43
+ mcd += sum(d.item() for d, _ in rets)
44
+ n_samples += len(sample["id"].tolist())
45
+
46
+ mcd = round(mcd / n_samples, 1)
47
+ reference_mcd = 3.3
48
+ print(f"MCD: {mcd} (reference: {reference_mcd})")
49
+ self.assertAlmostEqual(mcd, reference_mcd, delta=0.1)
50
+
51
+
52
+ if __name__ == "__main__":
53
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_wav2vec2.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import torch
8
+ from tests.speech import TestFairseqSpeech
9
+ from fairseq.data.data_utils import post_process
10
+ from fairseq import utils
11
+ from omegaconf import open_dict
12
+
13
+ S3_BASE_URL = "https://dl.fbaipublicfiles.com/fairseq"
14
+
15
+
16
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
17
+ class TestWav2Vec2(TestFairseqSpeech):
18
+ def setUp(self):
19
+ self._set_up(
20
+ "librispeech_w2v2",
21
+ "conformer/wav2vec2/librispeech",
22
+ [
23
+ "test_librispeech-other.ltr",
24
+ "test_librispeech-other.tsv",
25
+ "test_librispeech-other_small.ltr_100",
26
+ "test_librispeech-other_small.tsv",
27
+ "test-other.zip",
28
+ "dict.ltr.txt",
29
+ "dict.ltr_100.txt",
30
+ ],
31
+ )
32
+ self.unzip_files(
33
+ "test-other.zip",
34
+ )
35
+
36
+ def test_transformer_w2v2(self):
37
+ self.base_test(
38
+ ckpt_name="transformer_oss_small_100h.pt",
39
+ reference_score=38,
40
+ score_delta=1,
41
+ dataset="test_librispeech-other",
42
+ max_tokens=1000000,
43
+ max_positions=(700000, 1000),
44
+ arg_overrides={
45
+ "task": "audio_finetuning",
46
+ "labels": "ltr",
47
+ "nbest": 1,
48
+ "tpu": False,
49
+ },
50
+ strict=False,
51
+ )
52
+
53
+ def test_conformer_w2v2(self):
54
+ self.base_test(
55
+ ckpt_name="conformer_LS_PT_LS_FT_rope.pt",
56
+ reference_score=4.5,
57
+ score_delta=1,
58
+ dataset="test_librispeech-other_small",
59
+ max_tokens=1000000,
60
+ max_positions=(700000, 1000),
61
+ arg_overrides={
62
+ "task": "audio_finetuning",
63
+ "labels": "ltr_100",
64
+ "nbest": 1,
65
+ "tpu": False,
66
+ },
67
+ strict=True,
68
+ )
69
+
70
+ def build_generator(self, task, models, cfg):
71
+ try:
72
+ from examples.speech_recognition.w2l_decoder import W2lViterbiDecoder
73
+ except Exception:
74
+ raise Exception("Cannot run this test without flashlight dependency")
75
+ with open_dict(cfg):
76
+ cfg.nbest = 1
77
+ return W2lViterbiDecoder(cfg, task.target_dictionary)
78
+
79
+ def postprocess_tokens(self, task, target, hypo_tokens):
80
+ tgt_tokens = utils.strip_pad(target, task.target_dictionary.pad()).int().cpu()
81
+ tgt_str = task.target_dictionary.string(tgt_tokens)
82
+ tgt_str = post_process(tgt_str, "letter")
83
+
84
+ hypo_pieces = task.target_dictionary.string(hypo_tokens)
85
+ hypo_str = post_process(hypo_pieces, "letter")
86
+ return tgt_str, hypo_str
87
+
88
+
89
+ if __name__ == "__main__":
90
+ unittest.main()
omni_speech/infer/fairseq/tests/speech/test_xm_transformer.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 tests.speech import TestFairseqSpeech
8
+
9
+
10
+ class TestXMTransformer(TestFairseqSpeech):
11
+ def setUp(self):
12
+ self.set_up_sotasty_es_en()
13
+
14
+ # TODO: investigate increases BLEU score (30.42 -> 31.74)
15
+ def test_sotasty_es_en_600m_checkpoint(self):
16
+ self.base_test(
17
+ ckpt_name="xm_transformer_600m_es_en_md.pt",
18
+ reference_score=31.74,
19
+ score_delta=0.2,
20
+ max_tokens=3_000_000,
21
+ max_positions=(1_000_000, 1_024),
22
+ dataset="sotasty_es_en_test_ted",
23
+ arg_overrides={"config_yaml": "cfg_es_en.yaml"},
24
+ score_type="bleu",
25
+ )
26
+
27
+
28
+ if __name__ == "__main__":
29
+ unittest.main()
omni_speech/infer/fairseq/tests/speech_recognition/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/tests/speech_recognition/asr_test_base.py ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import os
5
+ import unittest
6
+ from inspect import currentframe, getframeinfo
7
+
8
+ import numpy as np
9
+ import torch
10
+ from examples.speech_recognition.data.data_utils import lengths_to_encoder_padding_mask
11
+ from fairseq.data import data_utils as fairseq_data_utils
12
+ from fairseq.data.dictionary import Dictionary
13
+ from fairseq.models import (
14
+ BaseFairseqModel,
15
+ FairseqDecoder,
16
+ FairseqEncoder,
17
+ FairseqEncoderDecoderModel,
18
+ FairseqEncoderModel,
19
+ FairseqModel,
20
+ )
21
+ from fairseq.tasks.fairseq_task import LegacyFairseqTask
22
+
23
+
24
+ DEFAULT_TEST_VOCAB_SIZE = 100
25
+
26
+
27
+ # ///////////////////////////////////////////////////////////////////////////
28
+ # utility function to setup dummy dict/task/input
29
+ # ///////////////////////////////////////////////////////////////////////////
30
+
31
+
32
+ def get_dummy_dictionary(vocab_size=DEFAULT_TEST_VOCAB_SIZE):
33
+ dummy_dict = Dictionary()
34
+ # add dummy symbol to satisfy vocab size
35
+ for id, _ in enumerate(range(vocab_size)):
36
+ dummy_dict.add_symbol("{}".format(id), 1000)
37
+ return dummy_dict
38
+
39
+
40
+ class DummyTask(LegacyFairseqTask):
41
+ def __init__(self, args):
42
+ super().__init__(args)
43
+ self.dictionary = get_dummy_dictionary()
44
+ if getattr(self.args, "ctc", False):
45
+ self.dictionary.add_symbol("<ctc_blank>")
46
+ self.tgt_dict = self.dictionary
47
+
48
+ @property
49
+ def target_dictionary(self):
50
+ return self.dictionary
51
+
52
+
53
+ def get_dummy_task_and_parser():
54
+ """
55
+ to build a fariseq model, we need some dummy parse and task. This function
56
+ is used to create dummy task and parser to faciliate model/criterion test
57
+
58
+ Note: we use FbSpeechRecognitionTask as the dummy task. You may want
59
+ to use other task by providing another function
60
+ """
61
+ parser = argparse.ArgumentParser(
62
+ description="test_dummy_s2s_task", argument_default=argparse.SUPPRESS
63
+ )
64
+ DummyTask.add_args(parser)
65
+ args = parser.parse_args([])
66
+ task = DummyTask.setup_task(args)
67
+ return task, parser
68
+
69
+
70
+ def get_dummy_input(T=100, D=80, B=5, K=100):
71
+ forward_input = {}
72
+ # T max sequence length
73
+ # D feature vector dimension
74
+ # B batch size
75
+ # K target dimension size
76
+ feature = torch.randn(B, T, D)
77
+ # this (B, T, D) layout is just a convention, you can override it by
78
+ # write your own _prepare_forward_input function
79
+ src_lengths = torch.from_numpy(
80
+ np.random.randint(low=1, high=T, size=B, dtype=np.int64)
81
+ )
82
+ src_lengths[0] = T # make sure the maximum length matches
83
+ prev_output_tokens = []
84
+ for b in range(B):
85
+ token_length = np.random.randint(low=1, high=src_lengths[b].item() + 1)
86
+ tokens = np.random.randint(low=0, high=K, size=token_length, dtype=np.int64)
87
+ prev_output_tokens.append(torch.from_numpy(tokens))
88
+
89
+ prev_output_tokens = fairseq_data_utils.collate_tokens(
90
+ prev_output_tokens,
91
+ pad_idx=1,
92
+ eos_idx=2,
93
+ left_pad=False,
94
+ move_eos_to_beginning=False,
95
+ )
96
+ src_lengths, sorted_order = src_lengths.sort(descending=True)
97
+ forward_input["src_tokens"] = feature.index_select(0, sorted_order)
98
+ forward_input["src_lengths"] = src_lengths
99
+ forward_input["prev_output_tokens"] = prev_output_tokens
100
+
101
+ return forward_input
102
+
103
+
104
+ def get_dummy_encoder_output(encoder_out_shape=(100, 80, 5)):
105
+ """
106
+ This only provides an example to generate dummy encoder output
107
+ """
108
+ (T, B, D) = encoder_out_shape
109
+ encoder_out = {}
110
+
111
+ encoder_out["encoder_out"] = torch.from_numpy(
112
+ np.random.randn(*encoder_out_shape).astype(np.float32)
113
+ )
114
+ seq_lengths = torch.from_numpy(np.random.randint(low=1, high=T, size=B))
115
+ # some dummy mask
116
+ encoder_out["encoder_padding_mask"] = torch.arange(T).view(1, T).expand(
117
+ B, -1
118
+ ) >= seq_lengths.view(B, 1).expand(-1, T)
119
+ encoder_out["encoder_padding_mask"].t_()
120
+
121
+ # encoer_padding_mask is (T, B) tensor, with (t, b)-th element indicate
122
+ # whether encoder_out[t, b] is valid (=0) or not (=1)
123
+ return encoder_out
124
+
125
+
126
+ def _current_postion_info():
127
+ cf = currentframe()
128
+ frameinfo = " (at {}:{})".format(
129
+ os.path.basename(getframeinfo(cf).filename), cf.f_back.f_lineno
130
+ )
131
+ return frameinfo
132
+
133
+
134
+ def check_encoder_output(encoder_output, batch_size=None):
135
+ """we expect encoder_output to be a dict with the following
136
+ key/value pairs:
137
+ - encoder_out: a Torch.Tensor
138
+ - encoder_padding_mask: a binary Torch.Tensor
139
+ """
140
+ if not isinstance(encoder_output, dict):
141
+ msg = (
142
+ "FairseqEncoderModel.forward(...) must be a dict" + _current_postion_info()
143
+ )
144
+ return False, msg
145
+
146
+ if "encoder_out" not in encoder_output:
147
+ msg = (
148
+ "FairseqEncoderModel.forward(...) must contain encoder_out"
149
+ + _current_postion_info()
150
+ )
151
+ return False, msg
152
+
153
+ if "encoder_padding_mask" not in encoder_output:
154
+ msg = (
155
+ "FairseqEncoderModel.forward(...) must contain encoder_padding_mask"
156
+ + _current_postion_info()
157
+ )
158
+ return False, msg
159
+
160
+ if not isinstance(encoder_output["encoder_out"], torch.Tensor):
161
+ msg = "encoder_out must be a torch.Tensor" + _current_postion_info()
162
+ return False, msg
163
+
164
+ if encoder_output["encoder_out"].dtype != torch.float32:
165
+ msg = "encoder_out must have float32 dtype" + _current_postion_info()
166
+ return False, msg
167
+
168
+ mask = encoder_output["encoder_padding_mask"]
169
+ if mask is not None:
170
+ if not isinstance(mask, torch.Tensor):
171
+ msg = (
172
+ "encoder_padding_mask must be a torch.Tensor" + _current_postion_info()
173
+ )
174
+ return False, msg
175
+ if mask.dtype != torch.uint8 and (
176
+ not hasattr(torch, "bool") or mask.dtype != torch.bool
177
+ ):
178
+ msg = (
179
+ "encoder_padding_mask must have dtype of uint8"
180
+ + _current_postion_info()
181
+ )
182
+ return False, msg
183
+
184
+ if mask.dim() != 2:
185
+ msg = (
186
+ "we expect encoder_padding_mask to be a 2-d tensor, in shape (T, B)"
187
+ + _current_postion_info()
188
+ )
189
+ return False, msg
190
+
191
+ if batch_size is not None and mask.size(1) != batch_size:
192
+ msg = (
193
+ "we expect encoder_padding_mask to be a 2-d tensor, with size(1)"
194
+ + " being the batch size"
195
+ + _current_postion_info()
196
+ )
197
+ return False, msg
198
+ return True, None
199
+
200
+
201
+ def check_decoder_output(decoder_output):
202
+ """we expect output from a decoder is a tuple with the following constraint:
203
+ - the first element is a torch.Tensor
204
+ - the second element can be anything (reserved for future use)
205
+ """
206
+ if not isinstance(decoder_output, tuple):
207
+ msg = "FariseqDecoder output must be a tuple" + _current_postion_info()
208
+ return False, msg
209
+
210
+ if len(decoder_output) != 2:
211
+ msg = "FairseqDecoder output must be 2-elem tuple" + _current_postion_info()
212
+ return False, msg
213
+
214
+ if not isinstance(decoder_output[0], torch.Tensor):
215
+ msg = (
216
+ "FariseqDecoder output[0] must be a torch.Tensor" + _current_postion_info()
217
+ )
218
+ return False, msg
219
+
220
+ return True, None
221
+
222
+
223
+ # ///////////////////////////////////////////////////////////////////////////
224
+ # Base Test class
225
+ # ///////////////////////////////////////////////////////////////////////////
226
+
227
+
228
+ class TestBaseFairseqModelBase(unittest.TestCase):
229
+ """
230
+ This class is used to facilitate writing unittest for any class derived from
231
+ `BaseFairseqModel`.
232
+ """
233
+
234
+ @classmethod
235
+ def setUpClass(cls):
236
+ if cls is TestBaseFairseqModelBase:
237
+ raise unittest.SkipTest("Skipping test case in base")
238
+ super().setUpClass()
239
+
240
+ def setUpModel(self, model):
241
+ self.assertTrue(isinstance(model, BaseFairseqModel))
242
+ self.model = model
243
+
244
+ def setupInput(self):
245
+ pass
246
+
247
+ def setUp(self):
248
+ self.model = None
249
+ self.forward_input = None
250
+ pass
251
+
252
+
253
+ class TestFairseqEncoderDecoderModelBase(TestBaseFairseqModelBase):
254
+ """
255
+ base code to test FairseqEncoderDecoderModel (formally known as
256
+ `FairseqModel`) must be derived from this base class
257
+ """
258
+
259
+ @classmethod
260
+ def setUpClass(cls):
261
+ if cls is TestFairseqEncoderDecoderModelBase:
262
+ raise unittest.SkipTest("Skipping test case in base")
263
+ super().setUpClass()
264
+
265
+ def setUpModel(self, model_cls, extra_args_setters=None):
266
+ self.assertTrue(
267
+ issubclass(model_cls, (FairseqEncoderDecoderModel, FairseqModel)),
268
+ msg="This class only tests for FairseqModel subclasses",
269
+ )
270
+
271
+ task, parser = get_dummy_task_and_parser()
272
+ model_cls.add_args(parser)
273
+
274
+ args = parser.parse_args([])
275
+
276
+ if extra_args_setters is not None:
277
+ for args_setter in extra_args_setters:
278
+ args_setter(args)
279
+ model = model_cls.build_model(args, task)
280
+ self.model = model
281
+
282
+ def setUpInput(self, input=None):
283
+ self.forward_input = get_dummy_input() if input is None else input
284
+
285
+ def setUp(self):
286
+ super().setUp()
287
+
288
+ def test_forward(self):
289
+ if self.model and self.forward_input:
290
+ forward_output = self.model.forward(**self.forward_input)
291
+ # for FairseqEncoderDecoderModel, forward returns a tuple of two
292
+ # elements, the first one is a Torch.Tensor
293
+ succ, msg = check_decoder_output(forward_output)
294
+ if not succ:
295
+ self.assertTrue(succ, msg=msg)
296
+ self.forward_output = forward_output
297
+
298
+ def test_get_normalized_probs(self):
299
+ if self.model and self.forward_input:
300
+ forward_output = self.model.forward(**self.forward_input)
301
+ logprob = self.model.get_normalized_probs(forward_output, log_probs=True)
302
+ prob = self.model.get_normalized_probs(forward_output, log_probs=False)
303
+
304
+ # in order for different models/criterion to play with each other
305
+ # we need to know whether the logprob or prob output is batch_first
306
+ # or not. We assume an additional attribute will be attached to logprob
307
+ # or prob. If you find your code failed here, simply override
308
+ # FairseqModel.get_normalized_probs, see example at
309
+ # https://fburl.com/batch_first_example
310
+ self.assertTrue(hasattr(logprob, "batch_first"))
311
+ self.assertTrue(hasattr(prob, "batch_first"))
312
+
313
+ self.assertTrue(torch.is_tensor(logprob))
314
+ self.assertTrue(torch.is_tensor(prob))
315
+
316
+
317
+ class TestFairseqEncoderModelBase(TestBaseFairseqModelBase):
318
+ """
319
+ base class to test FairseqEncoderModel
320
+ """
321
+
322
+ @classmethod
323
+ def setUpClass(cls):
324
+ if cls is TestFairseqEncoderModelBase:
325
+ raise unittest.SkipTest("Skipping test case in base")
326
+ super().setUpClass()
327
+
328
+ def setUpModel(self, model_cls, extra_args_setters=None):
329
+ self.assertTrue(
330
+ issubclass(model_cls, FairseqEncoderModel),
331
+ msg="This class is only used for testing FairseqEncoderModel",
332
+ )
333
+ task, parser = get_dummy_task_and_parser()
334
+ model_cls.add_args(parser)
335
+ args = parser.parse_args([])
336
+ if extra_args_setters is not None:
337
+ for args_setter in extra_args_setters:
338
+ args_setter(args)
339
+
340
+ model = model_cls.build_model(args, task)
341
+ self.model = model
342
+
343
+ def setUpInput(self, input=None):
344
+ self.forward_input = get_dummy_input() if input is None else input
345
+ # get_dummy_input() is originally for s2s, here we delete extra dict
346
+ # items, so it can be used for EncoderModel / Encoder as well
347
+ self.forward_input.pop("prev_output_tokens", None)
348
+
349
+ def setUp(self):
350
+ super().setUp()
351
+
352
+ def test_forward(self):
353
+ if self.forward_input and self.model:
354
+ bsz = self.forward_input["src_tokens"].size(0)
355
+ forward_output = self.model.forward(**self.forward_input)
356
+
357
+ # we expect forward_output to be a dict with the following
358
+ # key/value pairs:
359
+ # - encoder_out: a Torch.Tensor
360
+ # - encoder_padding_mask: a binary Torch.Tensor
361
+ succ, msg = check_encoder_output(forward_output, batch_size=bsz)
362
+ if not succ:
363
+ self.assertTrue(succ, msg=msg)
364
+ self.forward_output = forward_output
365
+
366
+ def test_get_normalized_probs(self):
367
+ if self.model and self.forward_input:
368
+ forward_output = self.model.forward(**self.forward_input)
369
+ logprob = self.model.get_normalized_probs(forward_output, log_probs=True)
370
+ prob = self.model.get_normalized_probs(forward_output, log_probs=False)
371
+
372
+ # in order for different models/criterion to play with each other
373
+ # we need to know whether the logprob or prob output is batch_first
374
+ # or not. We assume an additional attribute will be attached to logprob
375
+ # or prob. If you find your code failed here, simply override
376
+ # FairseqModel.get_normalized_probs, see example at
377
+ # https://fburl.com/batch_first_example
378
+ self.assertTrue(hasattr(logprob, "batch_first"))
379
+ self.assertTrue(hasattr(prob, "batch_first"))
380
+
381
+ self.assertTrue(torch.is_tensor(logprob))
382
+ self.assertTrue(torch.is_tensor(prob))
383
+
384
+
385
+ class TestFairseqEncoderBase(unittest.TestCase):
386
+ """
387
+ base class to test FairseqEncoder
388
+ """
389
+
390
+ @classmethod
391
+ def setUpClass(cls):
392
+ if cls is TestFairseqEncoderBase:
393
+ raise unittest.SkipTest("Skipping test case in base")
394
+ super().setUpClass()
395
+
396
+ def setUpEncoder(self, encoder):
397
+ self.assertTrue(
398
+ isinstance(encoder, FairseqEncoder),
399
+ msg="This class is only used for test FairseqEncoder",
400
+ )
401
+ self.encoder = encoder
402
+
403
+ def setUpInput(self, input=None):
404
+ self.forward_input = get_dummy_input() if input is None else input
405
+ # get_dummy_input() is originally for s2s, here we delete extra dict
406
+ # items, so it can be used for EncoderModel / Encoder as well
407
+ self.forward_input.pop("prev_output_tokens", None)
408
+
409
+ def setUp(self):
410
+ self.encoder = None
411
+ self.forward_input = None
412
+
413
+ def test_forward(self):
414
+ if self.encoder and self.forward_input:
415
+ bsz = self.forward_input["src_tokens"].size(0)
416
+
417
+ forward_output = self.encoder.forward(**self.forward_input)
418
+ succ, msg = check_encoder_output(forward_output, batch_size=bsz)
419
+ if not succ:
420
+ self.assertTrue(succ, msg=msg)
421
+ self.forward_output = forward_output
422
+
423
+
424
+ class TestFairseqDecoderBase(unittest.TestCase):
425
+ """
426
+ base class to test FairseqDecoder
427
+ """
428
+
429
+ @classmethod
430
+ def setUpClass(cls):
431
+ if cls is TestFairseqDecoderBase:
432
+ raise unittest.SkipTest("Skipping test case in base")
433
+ super().setUpClass()
434
+
435
+ def setUpDecoder(self, decoder):
436
+ self.assertTrue(
437
+ isinstance(decoder, FairseqDecoder),
438
+ msg="This class is only used for test FairseqDecoder",
439
+ )
440
+ self.decoder = decoder
441
+
442
+ def setUpInput(self, input=None):
443
+ self.forward_input = get_dummy_encoder_output() if input is None else input
444
+
445
+ def setUpPrevOutputTokens(self, tokens=None):
446
+ if tokens is None:
447
+ self.encoder_input = get_dummy_input()
448
+ self.prev_output_tokens = self.encoder_input["prev_output_tokens"]
449
+ else:
450
+ self.prev_output_tokens = tokens
451
+
452
+ def setUp(self):
453
+ self.decoder = None
454
+ self.forward_input = None
455
+ self.prev_output_tokens = None
456
+
457
+ def test_forward(self):
458
+ if (
459
+ self.decoder is not None
460
+ and self.forward_input is not None
461
+ and self.prev_output_tokens is not None
462
+ ):
463
+ forward_output = self.decoder.forward(
464
+ prev_output_tokens=self.prev_output_tokens,
465
+ encoder_out=self.forward_input,
466
+ )
467
+ succ, msg = check_decoder_output(forward_output)
468
+ if not succ:
469
+ self.assertTrue(succ, msg=msg)
470
+ self.forward_input = forward_output
471
+
472
+
473
+ class DummyEncoderModel(FairseqEncoderModel):
474
+ def __init__(self, encoder):
475
+ super().__init__(encoder)
476
+
477
+ @classmethod
478
+ def build_model(cls, args, task):
479
+ return cls(DummyEncoder())
480
+
481
+ def get_logits(self, net_output):
482
+ # Inverse of sigmoid to use with BinaryCrossEntropyWithLogitsCriterion as
483
+ # F.binary_cross_entropy_with_logits combines sigmoid and CE
484
+ return torch.log(
485
+ torch.div(net_output["encoder_out"], 1 - net_output["encoder_out"])
486
+ )
487
+
488
+ def get_normalized_probs(self, net_output, log_probs, sample=None):
489
+ lprobs = super().get_normalized_probs(net_output, log_probs, sample=sample)
490
+ lprobs.batch_first = True
491
+ return lprobs
492
+
493
+
494
+ class DummyEncoder(FairseqEncoder):
495
+ def __init__(self):
496
+ super().__init__(None)
497
+
498
+ def forward(self, src_tokens, src_lengths):
499
+ mask, max_len = lengths_to_encoder_padding_mask(src_lengths)
500
+ return {"encoder_out": src_tokens, "encoder_padding_mask": mask}
501
+
502
+
503
+ class CrossEntropyCriterionTestBase(unittest.TestCase):
504
+ @classmethod
505
+ def setUpClass(cls):
506
+ if cls is CrossEntropyCriterionTestBase:
507
+ raise unittest.SkipTest("Skipping base class test case")
508
+ super().setUpClass()
509
+
510
+ def setUpArgs(self):
511
+ args = argparse.Namespace()
512
+ args.sentence_avg = False
513
+ args.threshold = 0.1 # to use with BinaryCrossEntropyWithLogitsCriterion
514
+ return args
515
+
516
+ def setUp(self):
517
+ args = self.setUpArgs()
518
+ self.model = DummyEncoderModel(encoder=DummyEncoder())
519
+ self.criterion = self.criterion_cls.build_criterion(args, task=DummyTask(args))
520
+
521
+ def get_src_tokens(self, correct_prediction, aggregate):
522
+ """
523
+ correct_prediction: True if the net_output (src_tokens) should
524
+ predict the correct target
525
+ aggregate: True if the criterion expects net_output (src_tokens)
526
+ aggregated across time axis
527
+ """
528
+ predicted_idx = 0 if correct_prediction else 1
529
+ if aggregate:
530
+ src_tokens = torch.zeros((2, 2), dtype=torch.float)
531
+ for b in range(2):
532
+ src_tokens[b][predicted_idx] = 1.0
533
+ else:
534
+ src_tokens = torch.zeros((2, 10, 2), dtype=torch.float)
535
+ for b in range(2):
536
+ for t in range(10):
537
+ src_tokens[b][t][predicted_idx] = 1.0
538
+ return src_tokens
539
+
540
+ def get_target(self, soft_target):
541
+ if soft_target:
542
+ target = torch.zeros((2, 2), dtype=torch.float)
543
+ for b in range(2):
544
+ target[b][0] = 1.0
545
+ else:
546
+ target = torch.zeros((2, 10), dtype=torch.long)
547
+ return target
548
+
549
+ def get_test_sample(self, correct, soft_target, aggregate):
550
+ src_tokens = self.get_src_tokens(correct, aggregate)
551
+ target = self.get_target(soft_target)
552
+ L = src_tokens.size(1)
553
+ return {
554
+ "net_input": {"src_tokens": src_tokens, "src_lengths": torch.tensor([L])},
555
+ "target": target,
556
+ "ntokens": src_tokens.size(0) * src_tokens.size(1),
557
+ }
omni_speech/infer/fairseq/tests/speech_recognition/test_collaters.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 numpy as np
10
+ import torch
11
+ from examples.speech_recognition.data.collaters import Seq2SeqCollater
12
+
13
+
14
+ class TestSeq2SeqCollator(unittest.TestCase):
15
+ def test_collate(self):
16
+
17
+ eos_idx = 1
18
+ pad_idx = 0
19
+ collater = Seq2SeqCollater(
20
+ feature_index=0, label_index=1, pad_index=pad_idx, eos_index=eos_idx
21
+ )
22
+
23
+ # 2 frames in the first sample and 3 frames in the second one
24
+ frames1 = np.array([[7, 8], [9, 10]])
25
+ frames2 = np.array([[1, 2], [3, 4], [5, 6]])
26
+ target1 = np.array([4, 2, 3, eos_idx])
27
+ target2 = np.array([3, 2, eos_idx])
28
+ sample1 = {"id": 0, "data": [frames1, target1]}
29
+ sample2 = {"id": 1, "data": [frames2, target2]}
30
+ batch = collater.collate([sample1, sample2])
31
+
32
+ # collate sort inputs by frame's length before creating the batch
33
+ self.assertTensorEqual(batch["id"], torch.tensor([1, 0]))
34
+ self.assertEqual(batch["ntokens"], 7)
35
+ self.assertTensorEqual(
36
+ batch["net_input"]["src_tokens"],
37
+ torch.tensor(
38
+ [[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [pad_idx, pad_idx]]]
39
+ ),
40
+ )
41
+ self.assertTensorEqual(
42
+ batch["net_input"]["prev_output_tokens"],
43
+ torch.tensor([[eos_idx, 3, 2, pad_idx], [eos_idx, 4, 2, 3]]),
44
+ )
45
+ self.assertTensorEqual(batch["net_input"]["src_lengths"], torch.tensor([3, 2]))
46
+ self.assertTensorEqual(
47
+ batch["target"],
48
+ torch.tensor([[3, 2, eos_idx, pad_idx], [4, 2, 3, eos_idx]]),
49
+ )
50
+ self.assertEqual(batch["nsentences"], 2)
51
+
52
+ def assertTensorEqual(self, t1, t2):
53
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
54
+ self.assertEqual(t1.ne(t2).long().sum(), 0)
55
+
56
+
57
+ if __name__ == "__main__":
58
+ unittest.main()
omni_speech/infer/fairseq/tests/speech_recognition/test_cross_entropy.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from examples.speech_recognition.criterions.cross_entropy_acc import (
8
+ CrossEntropyWithAccCriterion,
9
+ )
10
+
11
+ from .asr_test_base import CrossEntropyCriterionTestBase
12
+
13
+
14
+ class CrossEntropyWithAccCriterionTest(CrossEntropyCriterionTestBase):
15
+ def setUp(self):
16
+ self.criterion_cls = CrossEntropyWithAccCriterion
17
+ super().setUp()
18
+
19
+ def test_cross_entropy_all_correct(self):
20
+ sample = self.get_test_sample(correct=True, soft_target=False, aggregate=False)
21
+ loss, sample_size, logging_output = self.criterion(
22
+ self.model, sample, "sum", log_probs=True
23
+ )
24
+ assert logging_output["correct"] == 20
25
+ assert logging_output["total"] == 20
26
+ assert logging_output["sample_size"] == 20
27
+ assert logging_output["ntokens"] == 20
28
+
29
+ def test_cross_entropy_all_wrong(self):
30
+ sample = self.get_test_sample(correct=False, soft_target=False, aggregate=False)
31
+ loss, sample_size, logging_output = self.criterion(
32
+ self.model, sample, "sum", log_probs=True
33
+ )
34
+ assert logging_output["correct"] == 0
35
+ assert logging_output["total"] == 20
36
+ assert logging_output["sample_size"] == 20
37
+ assert logging_output["ntokens"] == 20
omni_speech/infer/fairseq/tests/speech_recognition/test_data_utils.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import unittest
7
+
8
+ import torch
9
+ from examples.speech_recognition.data import data_utils
10
+
11
+
12
+ class DataUtilsTest(unittest.TestCase):
13
+ def test_normalization(self):
14
+ sample_len1 = torch.tensor(
15
+ [
16
+ [
17
+ -0.7661,
18
+ -1.3889,
19
+ -2.0972,
20
+ -0.9134,
21
+ -0.7071,
22
+ -0.9765,
23
+ -0.8700,
24
+ -0.8283,
25
+ 0.7512,
26
+ 1.3211,
27
+ 2.1532,
28
+ 2.1174,
29
+ 1.2800,
30
+ 1.2633,
31
+ 1.6147,
32
+ 1.6322,
33
+ 2.0723,
34
+ 3.1522,
35
+ 3.2852,
36
+ 2.2309,
37
+ 2.5569,
38
+ 2.2183,
39
+ 2.2862,
40
+ 1.5886,
41
+ 0.8773,
42
+ 0.8725,
43
+ 1.2662,
44
+ 0.9899,
45
+ 1.1069,
46
+ 1.3926,
47
+ 1.2795,
48
+ 1.1199,
49
+ 1.1477,
50
+ 1.2687,
51
+ 1.3843,
52
+ 1.1903,
53
+ 0.8355,
54
+ 1.1367,
55
+ 1.2639,
56
+ 1.4707,
57
+ ]
58
+ ]
59
+ )
60
+ out = data_utils.apply_mv_norm(sample_len1)
61
+ assert not torch.isnan(out).any()
62
+ assert (out == sample_len1).all()
omni_speech/infer/fairseq/tests/speech_recognition/test_vggtransformer.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ # import models/encoder/decoder to be tested
4
+ from examples.speech_recognition.models.vggtransformer import (
5
+ TransformerDecoder,
6
+ VGGTransformerEncoder,
7
+ VGGTransformerModel,
8
+ vggtransformer_1,
9
+ vggtransformer_2,
10
+ vggtransformer_base,
11
+ )
12
+
13
+ # import base test class
14
+ from .asr_test_base import (
15
+ DEFAULT_TEST_VOCAB_SIZE,
16
+ TestFairseqDecoderBase,
17
+ TestFairseqEncoderBase,
18
+ TestFairseqEncoderDecoderModelBase,
19
+ get_dummy_dictionary,
20
+ get_dummy_encoder_output,
21
+ get_dummy_input,
22
+ )
23
+
24
+
25
+ class VGGTransformerModelTest_mid(TestFairseqEncoderDecoderModelBase):
26
+ def setUp(self):
27
+ def override_config(args):
28
+ """
29
+ vggtrasformer_1 use 14 layers of transformer,
30
+ for testing purpose, it is too expensive. For fast turn-around
31
+ test, reduce the number of layers to 3.
32
+ """
33
+ args.transformer_enc_config = (
34
+ "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 3"
35
+ )
36
+
37
+ super().setUp()
38
+ extra_args_setter = [vggtransformer_1, override_config]
39
+
40
+ self.setUpModel(VGGTransformerModel, extra_args_setter)
41
+ self.setUpInput(get_dummy_input(T=50, D=80, B=5, K=DEFAULT_TEST_VOCAB_SIZE))
42
+
43
+
44
+ class VGGTransformerModelTest_big(TestFairseqEncoderDecoderModelBase):
45
+ def setUp(self):
46
+ def override_config(args):
47
+ """
48
+ vggtrasformer_2 use 16 layers of transformer,
49
+ for testing purpose, it is too expensive. For fast turn-around
50
+ test, reduce the number of layers to 3.
51
+ """
52
+ args.transformer_enc_config = (
53
+ "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 3"
54
+ )
55
+
56
+ super().setUp()
57
+ extra_args_setter = [vggtransformer_2, override_config]
58
+
59
+ self.setUpModel(VGGTransformerModel, extra_args_setter)
60
+ self.setUpInput(get_dummy_input(T=50, D=80, B=5, K=DEFAULT_TEST_VOCAB_SIZE))
61
+
62
+
63
+ class VGGTransformerModelTest_base(TestFairseqEncoderDecoderModelBase):
64
+ def setUp(self):
65
+ def override_config(args):
66
+ """
67
+ vggtrasformer_base use 12 layers of transformer,
68
+ for testing purpose, it is too expensive. For fast turn-around
69
+ test, reduce the number of layers to 3.
70
+ """
71
+ args.transformer_enc_config = (
72
+ "((512, 8, 2048, True, 0.15, 0.15, 0.15),) * 3"
73
+ )
74
+
75
+ super().setUp()
76
+ extra_args_setter = [vggtransformer_base, override_config]
77
+
78
+ self.setUpModel(VGGTransformerModel, extra_args_setter)
79
+ self.setUpInput(get_dummy_input(T=50, D=80, B=5, K=DEFAULT_TEST_VOCAB_SIZE))
80
+
81
+
82
+ class VGGTransformerEncoderTest(TestFairseqEncoderBase):
83
+ def setUp(self):
84
+ super().setUp()
85
+
86
+ self.setUpInput(get_dummy_input(T=50, D=80, B=5))
87
+
88
+ def test_forward(self):
89
+ print("1. test standard vggtransformer")
90
+ self.setUpEncoder(VGGTransformerEncoder(input_feat_per_channel=80))
91
+ super().test_forward()
92
+ print("2. test vggtransformer with limited right context")
93
+ self.setUpEncoder(
94
+ VGGTransformerEncoder(
95
+ input_feat_per_channel=80, transformer_context=(-1, 5)
96
+ )
97
+ )
98
+ super().test_forward()
99
+ print("3. test vggtransformer with limited left context")
100
+ self.setUpEncoder(
101
+ VGGTransformerEncoder(
102
+ input_feat_per_channel=80, transformer_context=(5, -1)
103
+ )
104
+ )
105
+ super().test_forward()
106
+ print("4. test vggtransformer with limited right context and sampling")
107
+ self.setUpEncoder(
108
+ VGGTransformerEncoder(
109
+ input_feat_per_channel=80,
110
+ transformer_context=(-1, 12),
111
+ transformer_sampling=(2, 2),
112
+ )
113
+ )
114
+ super().test_forward()
115
+ print("5. test vggtransformer with windowed context and sampling")
116
+ self.setUpEncoder(
117
+ VGGTransformerEncoder(
118
+ input_feat_per_channel=80,
119
+ transformer_context=(12, 12),
120
+ transformer_sampling=(2, 2),
121
+ )
122
+ )
123
+
124
+
125
+ class TransformerDecoderTest(TestFairseqDecoderBase):
126
+ def setUp(self):
127
+ super().setUp()
128
+
129
+ dict = get_dummy_dictionary(vocab_size=DEFAULT_TEST_VOCAB_SIZE)
130
+ decoder = TransformerDecoder(dict)
131
+ dummy_encoder_output = get_dummy_encoder_output(encoder_out_shape=(50, 5, 256))
132
+
133
+ self.setUpDecoder(decoder)
134
+ self.setUpInput(dummy_encoder_output)
135
+ self.setUpPrevOutputTokens()
omni_speech/infer/fairseq/tests/tasks/test_denoising.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import os
7
+ import unittest
8
+ from tempfile import TemporaryDirectory
9
+
10
+ from fairseq import options
11
+ from fairseq.binarizer import FileBinarizer, VocabularyDatasetBinarizer
12
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
13
+ from fairseq.tasks.denoising import DenoisingTask
14
+ from tests.utils import build_vocab, make_data
15
+
16
+
17
+ class TestDenoising(unittest.TestCase):
18
+ def test_denoising(self):
19
+ with TemporaryDirectory() as dirname:
20
+
21
+ # prep input file
22
+ raw_file = os.path.join(dirname, "raw")
23
+ data = make_data(out_file=raw_file)
24
+ vocab = build_vocab(data)
25
+
26
+ # binarize
27
+ binarizer = VocabularyDatasetBinarizer(vocab, append_eos=False)
28
+ split = "train"
29
+ bin_file = os.path.join(dirname, split)
30
+ dataset_impl = "mmap"
31
+ FileBinarizer.multiprocess_dataset(
32
+ input_file=raw_file,
33
+ binarizer=binarizer,
34
+ dataset_impl=dataset_impl,
35
+ vocab_size=len(vocab),
36
+ output_prefix=bin_file,
37
+ )
38
+
39
+ # setup task
40
+ train_args = options.parse_args_and_arch(
41
+ options.get_training_parser(),
42
+ [
43
+ "--task",
44
+ "denoising",
45
+ "--arch",
46
+ "bart_base",
47
+ "--seed",
48
+ "42",
49
+ "--mask-length",
50
+ "word",
51
+ "--permute-sentences",
52
+ "1",
53
+ "--rotate",
54
+ "0",
55
+ "--replace-length",
56
+ "-1",
57
+ "--mask",
58
+ "0.2",
59
+ dirname,
60
+ ],
61
+ )
62
+ cfg = convert_namespace_to_omegaconf(train_args)
63
+ task = DenoisingTask(cfg.task, binarizer.dict)
64
+
65
+ # load datasets
66
+ original_dataset = task._load_dataset_split(bin_file, 1, False)
67
+ task.load_dataset(split)
68
+ masked_dataset = task.dataset(split)
69
+
70
+ iterator = task.get_batch_iterator(
71
+ dataset=masked_dataset,
72
+ max_tokens=65_536,
73
+ max_positions=4_096,
74
+ ).next_epoch_itr(shuffle=False)
75
+ mask_index = task.source_dictionary.index("<mask>")
76
+ for batch in iterator:
77
+ for sample in range(len(batch)):
78
+ net_input = batch["net_input"]
79
+ masked_src_tokens = net_input["src_tokens"][sample]
80
+ masked_src_length = net_input["src_lengths"][sample]
81
+ masked_tgt_tokens = batch["target"][sample]
82
+
83
+ sample_id = batch["id"][sample]
84
+ original_tokens = original_dataset[sample_id]
85
+ original_tokens = original_tokens.masked_select(
86
+ masked_src_tokens[:masked_src_length] == mask_index
87
+ )
88
+ masked_tokens = masked_tgt_tokens.masked_select(
89
+ masked_src_tokens == mask_index
90
+ )
91
+
92
+ assert masked_tokens.equal(original_tokens)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ unittest.main()
omni_speech/infer/fairseq/tests/tasks/test_masked_lm.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import os
7
+ import unittest
8
+ from tempfile import TemporaryDirectory
9
+
10
+ from fairseq.binarizer import FileBinarizer, VocabularyDatasetBinarizer
11
+ from fairseq.tasks.masked_lm import MaskedLMConfig, MaskedLMTask
12
+ from tests.utils import build_vocab, make_data
13
+
14
+
15
+ class TestMaskedLM(unittest.TestCase):
16
+ def test_masks_tokens(self):
17
+ with TemporaryDirectory() as dirname:
18
+
19
+ # prep input file
20
+ raw_file = os.path.join(dirname, "raw")
21
+ data = make_data(out_file=raw_file)
22
+ vocab = build_vocab(data)
23
+
24
+ # binarize
25
+ binarizer = VocabularyDatasetBinarizer(vocab, append_eos=False)
26
+ split = "train"
27
+ bin_file = os.path.join(dirname, split)
28
+ FileBinarizer.multiprocess_dataset(
29
+ input_file=raw_file,
30
+ binarizer=binarizer,
31
+ dataset_impl="mmap",
32
+ vocab_size=len(vocab),
33
+ output_prefix=bin_file,
34
+ )
35
+
36
+ # setup task
37
+ cfg = MaskedLMConfig(
38
+ data=dirname,
39
+ seed=42,
40
+ mask_prob=0.5, # increasing the odds of masking
41
+ random_token_prob=0, # avoiding random tokens for exact match
42
+ leave_unmasked_prob=0, # always masking for exact match
43
+ )
44
+ task = MaskedLMTask(cfg, binarizer.dict)
45
+
46
+ original_dataset = task._load_dataset_split(bin_file, 1, False)
47
+
48
+ # load datasets
49
+ task.load_dataset(split)
50
+ masked_dataset = task.dataset(split)
51
+
52
+ mask_index = task.source_dictionary.index("<mask>")
53
+ iterator = task.get_batch_iterator(
54
+ dataset=masked_dataset,
55
+ max_tokens=65_536,
56
+ max_positions=4_096,
57
+ ).next_epoch_itr(shuffle=False)
58
+ for batch in iterator:
59
+ for sample in range(len(batch)):
60
+ net_input = batch["net_input"]
61
+ masked_src_tokens = net_input["src_tokens"][sample]
62
+ masked_src_length = net_input["src_lengths"][sample]
63
+ masked_tgt_tokens = batch["target"][sample]
64
+
65
+ sample_id = batch["id"][sample]
66
+ original_tokens = original_dataset[sample_id]
67
+ original_tokens = original_tokens.masked_select(
68
+ masked_src_tokens[:masked_src_length] == mask_index
69
+ )
70
+ masked_tokens = masked_tgt_tokens.masked_select(
71
+ masked_tgt_tokens != task.source_dictionary.pad()
72
+ )
73
+
74
+ assert masked_tokens.equal(original_tokens)
75
+
76
+
77
+ if __name__ == "__main__":
78
+ unittest.main()
omni_speech/infer/fairseq/tests/tasks/test_multilingual_denoising.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 os
7
+ import unittest
8
+ from tempfile import TemporaryDirectory
9
+
10
+ from fairseq import options
11
+ from fairseq.binarizer import FileBinarizer, VocabularyDatasetBinarizer
12
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
13
+ from fairseq.tasks.multilingual_denoising import MultilingualDenoisingTask
14
+ from tests.utils import build_vocab, make_data
15
+
16
+
17
+ class TestMultilingualDenoising(unittest.TestCase):
18
+ def test_multilingual_denoising(self):
19
+ with TemporaryDirectory() as dirname:
20
+
21
+ # prep input file
22
+ lang_dir = os.path.join(dirname, "en")
23
+ os.mkdir(lang_dir)
24
+ raw_file = os.path.join(lang_dir, "raw")
25
+ data = make_data(out_file=raw_file)
26
+ vocab = build_vocab(data)
27
+
28
+ # binarize
29
+ binarizer = VocabularyDatasetBinarizer(vocab, append_eos=False)
30
+ split = "train"
31
+ bin_file = os.path.join(lang_dir, split)
32
+ dataset_impl = "mmap"
33
+ FileBinarizer.multiprocess_dataset(
34
+ input_file=raw_file,
35
+ binarizer=binarizer,
36
+ dataset_impl=dataset_impl,
37
+ vocab_size=len(vocab),
38
+ output_prefix=bin_file,
39
+ )
40
+
41
+ # setup task
42
+ train_args = options.parse_args_and_arch(
43
+ options.get_training_parser(),
44
+ [
45
+ "--task",
46
+ "multilingual_denoising",
47
+ "--arch",
48
+ "bart_base",
49
+ "--seed",
50
+ "42",
51
+ "--mask-length",
52
+ "word",
53
+ "--permute-sentences",
54
+ "1",
55
+ "--rotate",
56
+ "0",
57
+ "--replace-length",
58
+ "-1",
59
+ "--mask",
60
+ "0.2",
61
+ dirname,
62
+ ],
63
+ )
64
+ cfg = convert_namespace_to_omegaconf(train_args)
65
+ task = MultilingualDenoisingTask(cfg.task, binarizer.dict)
66
+
67
+ # load datasets
68
+ original_dataset = task._load_dataset_split(bin_file, 1, False)
69
+ task.load_dataset(split)
70
+ masked_dataset = task.dataset(split)
71
+
72
+ iterator = task.get_batch_iterator(
73
+ dataset=masked_dataset,
74
+ max_tokens=65_536,
75
+ max_positions=4_096,
76
+ ).next_epoch_itr(shuffle=False)
77
+ mask_index = task.source_dictionary.index("<mask>")
78
+ for batch in iterator:
79
+ for sample in range(len(batch)):
80
+ net_input = batch["net_input"]
81
+ masked_src_tokens = net_input["src_tokens"][sample]
82
+ masked_src_length = net_input["src_lengths"][sample]
83
+ masked_tgt_tokens = batch["target"][sample]
84
+
85
+ sample_id = batch["id"][sample]
86
+ original_tokens = original_dataset[sample_id]
87
+ original_tokens = original_tokens.masked_select(
88
+ masked_src_tokens[:masked_src_length] == mask_index
89
+ )
90
+ masked_tokens = masked_tgt_tokens.masked_select(
91
+ masked_src_tokens == mask_index
92
+ )
93
+
94
+ assert masked_tokens.equal(original_tokens)
95
+
96
+
97
+ if __name__ == "__main__":
98
+ unittest.main()
omni_speech/infer/fairseq/tests/tasks/test_span_masked_lm.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 os
7
+ import unittest
8
+ from tempfile import TemporaryDirectory
9
+
10
+ from fairseq import options
11
+ from fairseq.binarizer import FileBinarizer, VocabularyDatasetBinarizer
12
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
13
+ from fairseq.tasks.span_masked_lm import SpanMaskedLMTask
14
+ from tests.utils import build_vocab, make_data
15
+
16
+
17
+ class TestSpanMaskedLM(unittest.TestCase):
18
+ def test_masks_token_spans(self):
19
+ with TemporaryDirectory() as dirname:
20
+
21
+ # prep input file
22
+ raw_file = os.path.join(dirname, "raw")
23
+ data = make_data(out_file=raw_file)
24
+ vocab = build_vocab(data)
25
+
26
+ # binarize
27
+ binarizer = VocabularyDatasetBinarizer(vocab, append_eos=False)
28
+ split = "train"
29
+ bin_file = os.path.join(dirname, split)
30
+ dataset_impl = "mmap"
31
+
32
+ FileBinarizer.multiprocess_dataset(
33
+ input_file=raw_file,
34
+ binarizer=binarizer,
35
+ dataset_impl=dataset_impl,
36
+ vocab_size=len(vocab),
37
+ output_prefix=bin_file,
38
+ )
39
+
40
+ # adding sentinel tokens
41
+ for i in range(100):
42
+ vocab.add_symbol(f"<extra_id_{i}>")
43
+
44
+ # setup task
45
+ train_args = options.parse_args_and_arch(
46
+ options.get_training_parser(),
47
+ [
48
+ "--task",
49
+ "span_masked_lm",
50
+ "--arch",
51
+ "bart_base",
52
+ "--seed",
53
+ "42",
54
+ dirname,
55
+ ],
56
+ )
57
+ cfg = convert_namespace_to_omegaconf(train_args)
58
+ task = SpanMaskedLMTask(cfg.task, binarizer.dict)
59
+
60
+ # load datasets
61
+ original_dataset = task._load_dataset_split(bin_file, 1, False)
62
+ task.load_dataset(split)
63
+ masked_dataset = task.dataset(split)
64
+
65
+ iterator = task.get_batch_iterator(
66
+ dataset=masked_dataset,
67
+ max_tokens=65_536,
68
+ max_positions=4_096,
69
+ ).next_epoch_itr(shuffle=False)
70
+ num_tokens = len(vocab)
71
+ for batch in iterator:
72
+ for sample in range(len(batch)):
73
+ sample_id = batch["id"][sample]
74
+ original_tokens = original_dataset[sample_id]
75
+ masked_src_tokens = batch["net_input"]["src_tokens"][sample]
76
+ masked_src_length = batch["net_input"]["src_lengths"][sample]
77
+ masked_tgt_tokens = batch["target"][sample]
78
+
79
+ original_offset = 0
80
+ masked_tgt_offset = 0
81
+ extra_id_token = len(vocab) - 1
82
+ for masked_src_token in masked_src_tokens[:masked_src_length]:
83
+ if masked_src_token == extra_id_token:
84
+ assert (
85
+ masked_src_token == masked_tgt_tokens[masked_tgt_offset]
86
+ )
87
+ extra_id_token -= 1
88
+ masked_tgt_offset += 1
89
+ while (
90
+ original_offset < len(original_tokens)
91
+ and masked_tgt_tokens[masked_tgt_offset]
92
+ != extra_id_token
93
+ ):
94
+ assert (
95
+ original_tokens[original_offset]
96
+ == masked_tgt_tokens[masked_tgt_offset]
97
+ )
98
+ original_offset += 1
99
+ masked_tgt_offset += 1
100
+ else:
101
+ assert original_tokens[original_offset] == masked_src_token
102
+ original_offset += 1
103
+
104
+
105
+ if __name__ == "__main__":
106
+ unittest.main()
omni_speech/infer/fairseq/tests/test_convtbc.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 import ConvTBC
11
+
12
+
13
+ class TestConvTBC(unittest.TestCase):
14
+ def test_convtbc(self):
15
+ # ksz, in_channels, out_channels
16
+ conv_tbc = ConvTBC(4, 5, kernel_size=3, padding=1)
17
+ # out_channels, in_channels, ksz
18
+ conv1d = nn.Conv1d(4, 5, kernel_size=3, padding=1)
19
+
20
+ conv_tbc.weight.data.copy_(conv1d.weight.data.transpose(0, 2))
21
+ conv_tbc.bias.data.copy_(conv1d.bias.data)
22
+
23
+ input_tbc = torch.randn(7, 2, 4, requires_grad=True)
24
+ input1d = input_tbc.data.transpose(0, 1).transpose(1, 2)
25
+ input1d.requires_grad = True
26
+
27
+ output_tbc = conv_tbc(input_tbc)
28
+ output1d = conv1d(input1d)
29
+
30
+ self.assertAlmostEqual(
31
+ output_tbc.data.transpose(0, 1).transpose(1, 2), output1d.data
32
+ )
33
+
34
+ grad_tbc = torch.randn(output_tbc.size())
35
+ grad1d = grad_tbc.transpose(0, 1).transpose(1, 2).contiguous()
36
+
37
+ output_tbc.backward(grad_tbc)
38
+ output1d.backward(grad1d)
39
+
40
+ self.assertAlmostEqual(
41
+ conv_tbc.weight.grad.data.transpose(0, 2), conv1d.weight.grad.data
42
+ )
43
+ self.assertAlmostEqual(conv_tbc.bias.grad.data, conv1d.bias.grad.data)
44
+ self.assertAlmostEqual(
45
+ input_tbc.grad.data.transpose(0, 1).transpose(1, 2), input1d.grad.data
46
+ )
47
+
48
+ def assertAlmostEqual(self, t1, t2):
49
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
50
+ self.assertLess((t1 - t2).abs().max(), 1e-4)
51
+
52
+
53
+ if __name__ == "__main__":
54
+ unittest.main()
omni_speech/infer/fairseq/tests/test_data_utils.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 numpy as np
9
+
10
+ from fairseq.data.data_utils_fast import batch_by_size_fn, batch_by_size_vec
11
+
12
+
13
+ class TestBatchBySize(unittest.TestCase):
14
+ @classmethod
15
+ def batch_by_size_baseline(
16
+ cls,
17
+ indices,
18
+ num_tokens_vec,
19
+ max_tokens,
20
+ max_sentences,
21
+ bsz_mult,
22
+ ):
23
+ """Simple, reliable and slow implementation of batch by size"""
24
+ batches = []
25
+ start = 0
26
+ while start < len(indices):
27
+ for end in range(start + 1, len(indices) + 1):
28
+ max_val = max(num_tokens_vec[pos] for pos in range(start, end))
29
+ sent_count = end - start
30
+ num_tokens = max_val * sent_count
31
+ overflow = num_tokens > max_tokens > 0 or sent_count > max_sentences > 0
32
+ terminate = overflow or end == len(indices)
33
+ if overflow:
34
+ sent_count -= 1
35
+ if terminate:
36
+ if sent_count > bsz_mult:
37
+ sent_count = sent_count - sent_count % bsz_mult
38
+ batches.append(indices[start : start + sent_count])
39
+ start = start + sent_count
40
+ break
41
+ return batches
42
+
43
+ @classmethod
44
+ def _get_error_message(
45
+ cls, max_sentences, max_tokens, bsz_mult, num_tokens_vec, validation, results
46
+ ):
47
+ return f"""Reference batch_by_size implementation should produce
48
+ same output as the baseline method.
49
+ Params:
50
+ max_sentences={max_sentences},
51
+ max_tokens={max_tokens},
52
+ bsz_mult={bsz_mult},
53
+ num_tokens_vec={num_tokens_vec},
54
+ expected_batches={validation},
55
+ returned_batches={results}"""
56
+
57
+ def _compare_results(
58
+ self,
59
+ indices_len,
60
+ batch_by_size_impl,
61
+ max_sentences,
62
+ max_tokens,
63
+ bsz_mult,
64
+ num_tokens_vec,
65
+ ):
66
+ indices = np.array(list(range(indices_len)))
67
+ validation = self.batch_by_size_baseline(
68
+ indices,
69
+ num_tokens_vec,
70
+ max_tokens=max_tokens,
71
+ max_sentences=max_sentences,
72
+ bsz_mult=bsz_mult,
73
+ )
74
+ results = batch_by_size_impl(
75
+ indices,
76
+ num_tokens_vec,
77
+ max_tokens=max_tokens,
78
+ max_sentences=max_sentences,
79
+ bsz_mult=bsz_mult,
80
+ )
81
+ error_msg = self._get_error_message(
82
+ max_sentences, max_tokens, bsz_mult, num_tokens_vec, validation, results
83
+ )
84
+ self.assertEqual(len(validation), len(results), error_msg)
85
+ for first, second in zip(validation, results):
86
+ self.assertTrue(np.array_equal(first, second), error_msg)
87
+
88
+ def _run_compare_with_baseline_sweep(self, batch_by_size_impl):
89
+ """Compare reference batch_by_size implementation with batch_by_size_baseline
90
+ across a dense grid of hyperparam values"""
91
+ MAX_MAX_TOKENS = 10
92
+ NUM_TOKENS_VECS_COUNT = 5
93
+ for indices_len in [10, 11]: # try odd and even len of indices
94
+ for max_sentences in range(0, indices_len + 2):
95
+ for max_tokens in range(0, MAX_MAX_TOKENS):
96
+ for bsz_mult in range(1, max(MAX_MAX_TOKENS, indices_len) + 2):
97
+ for _ in range(NUM_TOKENS_VECS_COUNT):
98
+ num_tokens_vec = np.random.randint(
99
+ 0, max_tokens + 1, size=indices_len
100
+ )
101
+ self._compare_results(
102
+ indices_len,
103
+ batch_by_size_impl,
104
+ max_sentences,
105
+ max_tokens,
106
+ bsz_mult,
107
+ num_tokens_vec,
108
+ )
109
+
110
+
111
+ class TestBatchBySizeVec(TestBatchBySize):
112
+ def test_compare_with_baseline(self):
113
+ self._run_compare_with_baseline_sweep(batch_by_size_vec)
114
+
115
+
116
+ class TestBatchBySizeFn(TestBatchBySize):
117
+ def test_compare_with_baseline(self):
118
+ def batch_by_size_fn_wrapper(
119
+ indices,
120
+ num_tokens_vec,
121
+ max_tokens,
122
+ max_sentences,
123
+ bsz_mult,
124
+ ):
125
+ def num_tokens_fn(idx):
126
+ return num_tokens_vec[idx]
127
+
128
+ return batch_by_size_fn(
129
+ indices, num_tokens_fn, max_tokens, max_sentences, bsz_mult
130
+ )
131
+
132
+ self._run_compare_with_baseline_sweep(batch_by_size_fn_wrapper)
133
+
134
+
135
+ if __name__ == "__main__":
136
+ unittest.main()
omni_speech/infer/fairseq/tests/test_dataclass_utils.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import unittest
7
+ from argparse import ArgumentParser
8
+ from dataclasses import dataclass, field
9
+
10
+ from fairseq.dataclass import FairseqDataclass
11
+ from fairseq.dataclass.utils import gen_parser_from_dataclass
12
+
13
+
14
+ @dataclass
15
+ class A(FairseqDataclass):
16
+ data: str = field(default="test", metadata={"help": "the data input"})
17
+ num_layers: int = field(default=200, metadata={"help": "more layers is better?"})
18
+
19
+
20
+ @dataclass
21
+ class B(FairseqDataclass):
22
+ bar: A = field(default=A())
23
+ foo: int = field(default=0, metadata={"help": "not a bar"})
24
+
25
+
26
+ @dataclass
27
+ class D(FairseqDataclass):
28
+ arch: A = field(default=A())
29
+ foo: int = field(default=0, metadata={"help": "not a bar"})
30
+
31
+
32
+ @dataclass
33
+ class C(FairseqDataclass):
34
+ data: str = field(default="test", metadata={"help": "root level data input"})
35
+ encoder: D = field(default=D())
36
+ decoder: A = field(default=A())
37
+ lr: int = field(default=0, metadata={"help": "learning rate"})
38
+
39
+
40
+ class TestDataclassUtils(unittest.TestCase):
41
+ def test_argparse_convert_basic(self):
42
+ parser = ArgumentParser()
43
+ gen_parser_from_dataclass(parser, A(), True)
44
+ args = parser.parse_args(["--num-layers", "10", "the/data/path"])
45
+ self.assertEqual(args.num_layers, 10)
46
+ self.assertEqual(args.data, "the/data/path")
47
+
48
+ def test_argparse_recursive(self):
49
+ parser = ArgumentParser()
50
+ gen_parser_from_dataclass(parser, B(), True)
51
+ args = parser.parse_args(["--num-layers", "10", "--foo", "10", "the/data/path"])
52
+ self.assertEqual(args.num_layers, 10)
53
+ self.assertEqual(args.foo, 10)
54
+ self.assertEqual(args.data, "the/data/path")
55
+
56
+ def test_argparse_recursive_prefixing(self):
57
+ self.maxDiff = None
58
+ parser = ArgumentParser()
59
+ gen_parser_from_dataclass(parser, C(), True, "")
60
+ args = parser.parse_args(
61
+ [
62
+ "--encoder-arch-data",
63
+ "ENCODER_ARCH_DATA",
64
+ "--encoder-arch-num-layers",
65
+ "10",
66
+ "--encoder-foo",
67
+ "10",
68
+ "--decoder-data",
69
+ "DECODER_DATA",
70
+ "--decoder-num-layers",
71
+ "10",
72
+ "--lr",
73
+ "10",
74
+ "the/data/path",
75
+ ]
76
+ )
77
+ self.assertEqual(args.encoder_arch_data, "ENCODER_ARCH_DATA")
78
+ self.assertEqual(args.encoder_arch_num_layers, 10)
79
+ self.assertEqual(args.encoder_foo, 10)
80
+ self.assertEqual(args.decoder_data, "DECODER_DATA")
81
+ self.assertEqual(args.decoder_num_layers, 10)
82
+ self.assertEqual(args.lr, 10)
83
+ self.assertEqual(args.data, "the/data/path")
84
+
85
+
86
+ if __name__ == "__main__":
87
+ unittest.main()
omni_speech/infer/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)})
omni_speech/infer/fairseq/tests/test_dictionary.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import io
7
+ import os
8
+ import string
9
+ import tempfile
10
+ import unittest
11
+
12
+ import torch
13
+ from fairseq import tokenizer
14
+ from fairseq.data import Dictionary
15
+
16
+
17
+ class TestDictionary(unittest.TestCase):
18
+ def test_finalize(self):
19
+ txt = [
20
+ "A B C D",
21
+ "B C D",
22
+ "C D",
23
+ "D",
24
+ ]
25
+ ref_ids1 = list(
26
+ map(
27
+ torch.IntTensor,
28
+ [
29
+ [4, 5, 6, 7, 2],
30
+ [5, 6, 7, 2],
31
+ [6, 7, 2],
32
+ [7, 2],
33
+ ],
34
+ )
35
+ )
36
+ ref_ids2 = list(
37
+ map(
38
+ torch.IntTensor,
39
+ [
40
+ [7, 6, 5, 4, 2],
41
+ [6, 5, 4, 2],
42
+ [5, 4, 2],
43
+ [4, 2],
44
+ ],
45
+ )
46
+ )
47
+
48
+ # build dictionary
49
+ d = Dictionary()
50
+ for line in txt:
51
+ d.encode_line(line, add_if_not_exist=True)
52
+
53
+ def get_ids(dictionary):
54
+ ids = []
55
+ for line in txt:
56
+ ids.append(dictionary.encode_line(line, add_if_not_exist=False))
57
+ return ids
58
+
59
+ def assertMatch(ids, ref_ids):
60
+ for toks, ref_toks in zip(ids, ref_ids):
61
+ self.assertEqual(toks.size(), ref_toks.size())
62
+ self.assertEqual(0, (toks != ref_toks).sum().item())
63
+
64
+ ids = get_ids(d)
65
+ assertMatch(ids, ref_ids1)
66
+
67
+ # check finalized dictionary
68
+ d.finalize()
69
+ finalized_ids = get_ids(d)
70
+ assertMatch(finalized_ids, ref_ids2)
71
+
72
+ # write to disk and reload
73
+ with tempfile.NamedTemporaryFile(mode="w") as tmp_dict:
74
+ d.save(tmp_dict.name)
75
+ d = Dictionary.load(tmp_dict.name)
76
+ reload_ids = get_ids(d)
77
+ assertMatch(reload_ids, ref_ids2)
78
+ assertMatch(finalized_ids, reload_ids)
79
+
80
+ def test_overwrite(self):
81
+ # for example, Camembert overwrites <unk>, <s> and </s>
82
+ dict_file = io.StringIO(
83
+ "<unk> 999 #fairseq:overwrite\n"
84
+ "<s> 999 #fairseq:overwrite\n"
85
+ "</s> 999 #fairseq:overwrite\n"
86
+ ", 999\n"
87
+ "▁de 999\n"
88
+ )
89
+ d = Dictionary()
90
+ d.add_from_file(dict_file)
91
+ self.assertEqual(d.index("<pad>"), 1)
92
+ self.assertEqual(d.index("foo"), 3)
93
+ self.assertEqual(d.index("<unk>"), 4)
94
+ self.assertEqual(d.index("<s>"), 5)
95
+ self.assertEqual(d.index("</s>"), 6)
96
+ self.assertEqual(d.index(","), 7)
97
+ self.assertEqual(d.index("▁de"), 8)
98
+
99
+ def test_no_overwrite(self):
100
+ # for example, Camembert overwrites <unk>, <s> and </s>
101
+ dict_file = io.StringIO(
102
+ "<unk> 999\n" "<s> 999\n" "</s> 999\n" ", 999\n" "▁de 999\n"
103
+ )
104
+ d = Dictionary()
105
+ with self.assertRaisesRegex(RuntimeError, "Duplicate"):
106
+ d.add_from_file(dict_file)
107
+
108
+ def test_space(self):
109
+ # for example, character models treat space as a symbol
110
+ dict_file = io.StringIO(" 999\n" "a 999\n" "b 999\n")
111
+ d = Dictionary()
112
+ d.add_from_file(dict_file)
113
+ self.assertEqual(d.index(" "), 4)
114
+ self.assertEqual(d.index("a"), 5)
115
+ self.assertEqual(d.index("b"), 6)
116
+
117
+ def test_add_file_to_dict(self):
118
+ counts = {}
119
+ num_lines = 100
120
+ per_line = 10
121
+ with tempfile.TemporaryDirectory("test_sampling") as data_dir:
122
+ filename = os.path.join(data_dir, "dummy.txt")
123
+ with open(filename, "w", encoding="utf-8") as data:
124
+ for c in string.ascii_letters:
125
+ line = f"{c} " * per_line
126
+ for _ in range(num_lines):
127
+ data.write(f"{line}\n")
128
+ counts[c] = per_line * num_lines
129
+ per_line += 5
130
+
131
+ dict = Dictionary()
132
+ Dictionary.add_file_to_dictionary(
133
+ filename, dict, tokenizer.tokenize_line, 10
134
+ )
135
+ dict.finalize(threshold=0, nwords=-1, padding_factor=8)
136
+
137
+ for c in string.ascii_letters:
138
+ count = dict.get_count(dict.index(c))
139
+ self.assertEqual(
140
+ counts[c], count, f"{c} count is {count} but should be {counts[c]}"
141
+ )
142
+
143
+
144
+ if __name__ == "__main__":
145
+ unittest.main()
omni_speech/infer/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()
omni_speech/infer/fairseq/tests/test_espnet_multihead_attention.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import unittest
4
+ from fairseq.modules import (
5
+ ESPNETMultiHeadedAttention,
6
+ RelPositionMultiHeadedAttention,
7
+ RotaryPositionMultiHeadedAttention,
8
+ )
9
+
10
+ torch.use_deterministic_algorithms(True)
11
+
12
+
13
+ class TestESPNETMultiHeadedAttention(unittest.TestCase):
14
+ def setUp(self) -> None:
15
+ self.T = 3
16
+ self.B = 1
17
+ self.C = 2
18
+ torch.manual_seed(0)
19
+ self.sample = torch.randn(self.T, self.B, self.C) # TBC
20
+ self.sample_scores = torch.randn(self.B, 1, self.T, self.T)
21
+ self.MHA = ESPNETMultiHeadedAttention(self.C, 1, dropout=0)
22
+
23
+ def test_forward(self):
24
+ expected_scores = torch.tensor(
25
+ [[[0.1713, -0.3776]], [[0.2263, -0.4486]], [[0.2243, -0.4538]]]
26
+ )
27
+ scores, _ = self.MHA(self.sample, self.sample, self.sample)
28
+ self.assertTrue(
29
+ np.allclose(
30
+ expected_scores.cpu().detach().numpy(),
31
+ scores.cpu().detach().numpy(),
32
+ atol=1e-4,
33
+ )
34
+ )
35
+
36
+ def test_forward_qkv(self):
37
+ expected_query = torch.tensor(
38
+ [[[[-1.0235, 0.0409], [0.4008, 1.3077], [0.5396, 2.0698]]]]
39
+ )
40
+ expected_key = torch.tensor(
41
+ [[[[0.5053, -0.4965], [-0.3730, -0.9473], [-0.7019, -0.1935]]]]
42
+ )
43
+ expected_val = torch.tensor(
44
+ [[[[-0.9940, 0.5403], [0.5924, -0.7619], [0.7504, -1.0892]]]]
45
+ )
46
+ sample_t = self.sample.transpose(0, 1)
47
+ query, key, val = self.MHA.forward_qkv(sample_t, sample_t, sample_t)
48
+ self.assertTrue(
49
+ np.allclose(
50
+ expected_query.cpu().detach().numpy(),
51
+ query.cpu().detach().numpy(),
52
+ atol=1e-4,
53
+ )
54
+ )
55
+ self.assertTrue(
56
+ np.allclose(
57
+ expected_key.cpu().detach().numpy(),
58
+ key.cpu().detach().numpy(),
59
+ atol=1e-4,
60
+ )
61
+ )
62
+ self.assertTrue(
63
+ np.allclose(
64
+ expected_val.cpu().detach().numpy(),
65
+ val.cpu().detach().numpy(),
66
+ atol=1e-4,
67
+ )
68
+ )
69
+
70
+ def test_forward_attention(self):
71
+ expected_scores = torch.tensor(
72
+ [[[0.1627, -0.6249], [-0.2547, -0.6487], [-0.0711, -0.8545]]]
73
+ )
74
+ scores = self.MHA.forward_attention(
75
+ self.sample.transpose(0, 1).view(self.B, 1, self.T, self.C),
76
+ self.sample_scores,
77
+ mask=None,
78
+ )
79
+ self.assertTrue(
80
+ np.allclose(
81
+ expected_scores.cpu().detach().numpy(),
82
+ scores.cpu().detach().numpy(),
83
+ atol=1e-4,
84
+ )
85
+ )
86
+
87
+
88
+ class TestRelPositionMultiHeadedAttention(unittest.TestCase):
89
+ def setUp(self) -> None:
90
+ self.T = 3
91
+ self.B = 1
92
+ self.C = 2
93
+ torch.manual_seed(0)
94
+ self.sample = torch.randn(self.T, self.B, self.C) # TBC
95
+ self.sample_x = torch.randn(self.B, 1, self.T, self.T * 2 - 1)
96
+ self.sample_pos = torch.randn(self.B, self.T * 2 - 1, self.C)
97
+ self.MHA = RelPositionMultiHeadedAttention(self.C, 1, dropout=0)
98
+
99
+ def test_rel_shift(self):
100
+ expected_x = torch.tensor(
101
+ [
102
+ [
103
+ [
104
+ [-0.7193, -0.4033, -0.5966],
105
+ [-0.8567, 1.1006, -1.0712],
106
+ [-0.5663, 0.3731, -0.8920],
107
+ ]
108
+ ]
109
+ ]
110
+ )
111
+ x = self.MHA.rel_shift(self.sample_x)
112
+ self.assertTrue(
113
+ np.allclose(
114
+ expected_x.cpu().detach().numpy(),
115
+ x.cpu().detach().numpy(),
116
+ atol=1e-4,
117
+ )
118
+ )
119
+
120
+ def test_forward(self):
121
+ expected_scores = torch.tensor(
122
+ [
123
+ [[-0.9609, -0.5020]],
124
+ [[-0.9308, -0.4890]],
125
+ [[-0.9473, -0.4948]],
126
+ [[-0.9609, -0.5020]],
127
+ [[-0.9308, -0.4890]],
128
+ [[-0.9473, -0.4948]],
129
+ [[-0.9609, -0.5020]],
130
+ [[-0.9308, -0.4890]],
131
+ [[-0.9473, -0.4948]],
132
+ [[-0.9609, -0.5020]],
133
+ [[-0.9308, -0.4890]],
134
+ [[-0.9473, -0.4948]],
135
+ [[-0.9609, -0.5020]],
136
+ [[-0.9308, -0.4890]],
137
+ [[-0.9473, -0.4948]],
138
+ ]
139
+ )
140
+ scores, _ = self.MHA(self.sample, self.sample, self.sample, self.sample_pos)
141
+ self.assertTrue(
142
+ np.allclose(
143
+ expected_scores.cpu().detach().numpy(),
144
+ scores.cpu().detach().numpy(),
145
+ atol=1e-4,
146
+ )
147
+ )
148
+
149
+
150
+ class TestRotaryPositionMultiHeadedAttention(unittest.TestCase):
151
+ def setUp(self) -> None:
152
+ self.T = 3
153
+ self.B = 1
154
+ self.C = 2
155
+ torch.manual_seed(0)
156
+ self.sample = torch.randn(self.T, self.B, self.C) # TBC
157
+ self.MHA = RotaryPositionMultiHeadedAttention(
158
+ self.C, 1, dropout=0, precision=None
159
+ )
160
+
161
+ def test_forward(self):
162
+ expected_scores = torch.tensor(
163
+ [[[-0.3220, -0.4726]], [[-1.2813, -0.0979]], [[-0.3138, -0.4758]]]
164
+ )
165
+ scores, _ = self.MHA(self.sample, self.sample, self.sample)
166
+ self.assertTrue(
167
+ np.allclose(
168
+ expected_scores.cpu().detach().numpy(),
169
+ scores.cpu().detach().numpy(),
170
+ atol=1e-4,
171
+ )
172
+ )
173
+
174
+
175
+ if __name__ == "__main__":
176
+ unittest.main()
omni_speech/infer/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()
omni_speech/infer/fairseq/tests/test_file_chunker_utils.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This source code is licensed under the MIT license found in the
2
+ # LICENSE file in the root directory of this source tree.
3
+
4
+ import os
5
+ import shutil
6
+ import tempfile
7
+ import unittest
8
+ from typing import Optional
9
+
10
+
11
+ class TestFileChunker(unittest.TestCase):
12
+ _tmpdir: Optional[str] = None
13
+ _tmpfile: Optional[str] = None
14
+ _line_content = "Hello, World\n"
15
+ _num_bytes = None
16
+ _num_lines = 200
17
+ _num_splits = 20
18
+
19
+ @classmethod
20
+ def setUpClass(cls) -> None:
21
+ cls._num_bytes = len(cls._line_content.encode("utf-8"))
22
+ cls._tmpdir = tempfile.mkdtemp()
23
+ with open(os.path.join(cls._tmpdir, "test.txt"), "w") as f:
24
+ cls._tmpfile = f.name
25
+ for _i in range(cls._num_lines):
26
+ f.write(cls._line_content)
27
+ f.flush()
28
+
29
+ @classmethod
30
+ def tearDownClass(cls) -> None:
31
+ # Cleanup temp working dir.
32
+ if cls._tmpdir is not None:
33
+ shutil.rmtree(cls._tmpdir) # type: ignore
34
+
35
+ def test_find_offsets(self):
36
+ from fairseq.file_chunker_utils import find_offsets
37
+
38
+ offsets = find_offsets(self._tmpfile, self._num_splits)
39
+ self.assertEqual(len(offsets), self._num_splits + 1)
40
+ (zero, *real_offsets, last) = offsets
41
+ self.assertEqual(zero, 0)
42
+ for i, o in enumerate(real_offsets):
43
+ self.assertEqual(
44
+ o,
45
+ self._num_bytes
46
+ + ((i + 1) * self._num_bytes * self._num_lines / self._num_splits),
47
+ )
48
+ self.assertEqual(last, self._num_bytes * self._num_lines)
49
+
50
+ def test_readchunks(self):
51
+ from fairseq.file_chunker_utils import Chunker, find_offsets
52
+
53
+ offsets = find_offsets(self._tmpfile, self._num_splits)
54
+ for start, end in zip(offsets, offsets[1:]):
55
+ with Chunker(self._tmpfile, start, end) as lines:
56
+ all_lines = list(lines)
57
+ num_lines = self._num_lines / self._num_splits
58
+ self.assertAlmostEqual(
59
+ len(all_lines), num_lines, delta=1
60
+ ) # because we split on the bites, we might end up with one more/less line in a chunk
61
+ self.assertListEqual(
62
+ all_lines, [self._line_content for _ in range(len(all_lines))]
63
+ )
omni_speech/infer/fairseq/tests/test_file_io.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This source code is licensed under the MIT license found in the
2
+ # LICENSE file in the root directory of this source tree.
3
+
4
+ import os
5
+ import shutil
6
+ import sys
7
+ import tempfile
8
+ import unittest
9
+ from typing import Optional
10
+ from unittest.mock import MagicMock
11
+
12
+
13
+ class TestFileIO(unittest.TestCase):
14
+
15
+ _tmpdir: Optional[str] = None
16
+ _tmpfile: Optional[str] = None
17
+ _tmpfile_contents = "Hello, World"
18
+
19
+ @classmethod
20
+ def setUpClass(cls) -> None:
21
+ cls._tmpdir = tempfile.mkdtemp()
22
+ with open(os.path.join(cls._tmpdir, "test.txt"), "w") as f:
23
+ cls._tmpfile = f.name
24
+ f.write(cls._tmpfile_contents)
25
+ f.flush()
26
+
27
+ @classmethod
28
+ def tearDownClass(cls) -> None:
29
+ # Cleanup temp working dir.
30
+ if cls._tmpdir is not None:
31
+ shutil.rmtree(cls._tmpdir) # type: ignore
32
+
33
+ def test_file_io(self):
34
+ from fairseq.file_io import PathManager
35
+
36
+ with PathManager.open(os.path.join(self._tmpdir, "test.txt"), "r") as f:
37
+ s = f.read()
38
+ self.assertEqual(s, self._tmpfile_contents)
39
+
40
+ def test_file_io_oss(self):
41
+ # Mock iopath to simulate oss environment.
42
+ sys.modules["iopath"] = MagicMock()
43
+ from fairseq.file_io import PathManager
44
+
45
+ with PathManager.open(os.path.join(self._tmpdir, "test.txt"), "r") as f:
46
+ s = f.read()
47
+ self.assertEqual(s, self._tmpfile_contents)
48
+
49
+ def test_file_io_async(self):
50
+ # ioPath `PathManager` is initialized after the first `opena` call.
51
+ try:
52
+ from fairseq.file_io import PathManager
53
+
54
+ _asyncfile = os.path.join(self._tmpdir, "async.txt")
55
+ f = PathManager.opena(_asyncfile, "wb")
56
+ f.close()
57
+
58
+ finally:
59
+ self.assertTrue(PathManager.async_close())
omni_speech/infer/fairseq/tests/test_fp16_optimizer.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import copy
7
+ import logging
8
+ import unittest
9
+
10
+ import torch
11
+ from fairseq.optim.fp16_optimizer import FP16Optimizer, MemoryEfficientFP16Optimizer
12
+ from omegaconf import OmegaConf
13
+
14
+
15
+ @unittest.skipIf(not torch.cuda.is_available(), "test requires a GPU")
16
+ class TestGradientScaling(unittest.TestCase):
17
+ def setUp(self):
18
+ self.x = torch.tensor([2.0]).cuda().half()
19
+ weight = 3.0
20
+ bias = 5.0
21
+ self.error = 1.0
22
+ self.target = torch.tensor([self.x * weight + bias + self.error]).cuda().half()
23
+ self.loss_fn = torch.nn.L1Loss()
24
+
25
+ self.model = torch.nn.Linear(1, 1)
26
+ self.model.weight.data = torch.tensor([[weight]])
27
+ self.model.bias.data = torch.tensor([bias])
28
+ self.model.cuda().half()
29
+ self.params = list(self.model.parameters())
30
+
31
+ self.cfg_dls = OmegaConf.create(
32
+ {
33
+ "optimization": {
34
+ "lr": [0.1],
35
+ },
36
+ "optimizer": {
37
+ "_name": "adam",
38
+ "lr": [0.1],
39
+ "adam_betas": "(0.9, 0.999)",
40
+ "adam_eps": 1e-8,
41
+ "weight_decay": 0.0,
42
+ },
43
+ "common": {
44
+ "fp16_init_scale": 1,
45
+ "fp16_scale_window": 1,
46
+ "fp16_scale_tolerance": 1,
47
+ "threshold_loss_scale": 1,
48
+ "min_loss_scale": 1e-4,
49
+ "tpu": False,
50
+ },
51
+ }
52
+ )
53
+ logging.disable(logging.CRITICAL)
54
+
55
+ def tearDown(self):
56
+ logging.disable(logging.NOTSET)
57
+
58
+ def run_iter(self, model, params, optimizer):
59
+ optimizer.zero_grad()
60
+ y = model(self.x)
61
+ loss = self.loss_fn(y, self.target)
62
+ optimizer.backward(loss)
63
+ self.assertEqual(loss, torch.tensor(1.0, device="cuda:0", dtype=torch.float16))
64
+
65
+ grad_norm = optimizer.clip_grad_norm(0)
66
+ self.assertAlmostEqual(grad_norm.item(), 2.2361, 4)
67
+
68
+ optimizer.step()
69
+ self.assertEqual(
70
+ model.weight,
71
+ torch.tensor(
72
+ [[3.0996]], device="cuda:0", dtype=torch.float16, requires_grad=True
73
+ ),
74
+ )
75
+ self.assertEqual(
76
+ model.bias,
77
+ torch.tensor(
78
+ [5.1016], device="cuda:0", dtype=torch.float16, requires_grad=True
79
+ ),
80
+ )
81
+ self.assertEqual(optimizer.scaler.loss_scale, 2.0)
82
+
83
+ def test_mixed_precision(self):
84
+ model = copy.deepcopy(self.model)
85
+ params = list(model.parameters())
86
+ optimizer = FP16Optimizer.build_optimizer(self.cfg_dls, params)
87
+
88
+ self.run_iter(model, params, optimizer)
89
+ self.assertTrue(
90
+ all(
91
+ torch.all(
92
+ fp32_params.eq(
93
+ torch.tensor(
94
+ [3.1000, 5.1000], device="cuda:0", requires_grad=True
95
+ )
96
+ )
97
+ )
98
+ for fp32_params in optimizer.fp32_params.values()
99
+ )
100
+ )
101
+
102
+ def test_memory_efficient(self):
103
+ model = copy.deepcopy(self.model)
104
+ params = list(model.parameters())
105
+ optimizer = MemoryEfficientFP16Optimizer.build_optimizer(self.cfg_dls, params)
106
+
107
+ self.run_iter(model, params, optimizer)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ unittest.main()
omni_speech/infer/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()
omni_speech/infer/fairseq/tests/test_huffman.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 os
7
+ import typing as tp
8
+ import unittest
9
+ from collections import Counter
10
+ from tempfile import NamedTemporaryFile, TemporaryDirectory
11
+
12
+ from fairseq.data import Dictionary, indexed_dataset
13
+ from fairseq.data.huffman import (
14
+ HuffmanCodeBuilder,
15
+ HuffmanCoder,
16
+ HuffmanMMapIndexedDataset,
17
+ HuffmanMMapIndexedDatasetBuilder,
18
+ )
19
+ from tests.utils import POPULATION, make_data, sizes
20
+
21
+
22
+ def make_counts(data: tp.List[tp.List[str]]) -> Counter:
23
+ return Counter([symbol for sentence in data for symbol in sentence])
24
+
25
+
26
+ def make_code_builder(data: tp.List[tp.List[str]]) -> HuffmanCodeBuilder:
27
+ builder = HuffmanCodeBuilder()
28
+ for sentence in data:
29
+ builder.add_symbols(*sentence)
30
+ return builder
31
+
32
+
33
+ class TestCodeBuilder(unittest.TestCase):
34
+ def test_code_builder_can_count(self):
35
+ data = make_data()
36
+ counts = make_counts(data)
37
+ builder = make_code_builder(data)
38
+
39
+ self.assertEqual(builder.symbols, counts)
40
+
41
+ def test_code_builder_can_add(self):
42
+ data = make_data()
43
+ counts = make_counts(data)
44
+ builder = make_code_builder(data)
45
+
46
+ new_builder = builder + builder
47
+
48
+ self.assertEqual(new_builder.symbols, counts + counts)
49
+
50
+ def test_code_builder_can_io(self):
51
+ data = make_data()
52
+ builder = make_code_builder(data)
53
+
54
+ with NamedTemporaryFile() as tmp_fp:
55
+ builder.to_file(tmp_fp.name)
56
+ other_builder = HuffmanCodeBuilder.from_file(tmp_fp.name)
57
+
58
+ self.assertEqual(builder.symbols, other_builder.symbols)
59
+
60
+
61
+ class TestCoder(unittest.TestCase):
62
+ def test_coder_can_io(self):
63
+ data = make_data()
64
+ builder = make_code_builder(data)
65
+ coder = builder.build_code()
66
+
67
+ with NamedTemporaryFile() as tmp_fp:
68
+ coder.to_file(tmp_fp.name)
69
+ other_coder = HuffmanCoder.from_file(tmp_fp.name)
70
+
71
+ self.assertEqual(coder, other_coder)
72
+
73
+ def test_coder_can_encode_decode(self):
74
+ data = make_data()
75
+ builder = make_code_builder(data)
76
+ coder = builder.build_code()
77
+
78
+ encoded = [coder.encode(sentence) for sentence in data]
79
+ decoded = [[n.symbol for n in coder.decode(enc)] for enc in encoded]
80
+
81
+ self.assertEqual(decoded, data)
82
+
83
+ unseen_data = make_data()
84
+ unseen_encoded = [coder.encode(sentence) for sentence in unseen_data]
85
+ unseen_decoded = [
86
+ [n.symbol for n in coder.decode(enc)] for enc in unseen_encoded
87
+ ]
88
+ self.assertEqual(unseen_decoded, unseen_data)
89
+
90
+
91
+ def build_dataset(prefix, data, coder):
92
+ with HuffmanMMapIndexedDatasetBuilder(prefix, coder) as builder:
93
+ for sentence in data:
94
+ builder.add_item(sentence)
95
+
96
+
97
+ class TestHuffmanDataset(unittest.TestCase):
98
+ def test_huffman_can_encode_decode(self):
99
+ data = make_data()
100
+ builder = make_code_builder(data)
101
+ coder = builder.build_code()
102
+
103
+ with TemporaryDirectory() as dirname:
104
+ prefix = os.path.join(dirname, "test1")
105
+ build_dataset(prefix, data, coder)
106
+ dataset = HuffmanMMapIndexedDataset(prefix)
107
+
108
+ self.assertEqual(len(dataset), len(data))
109
+ decoded = [list(dataset.get_symbols(i)) for i in range(0, len(dataset))]
110
+
111
+ self.assertEqual(decoded, data)
112
+ data_sizes = [i.item() for i in dataset.sizes]
113
+ self.assertEqual(data_sizes, sizes(data))
114
+
115
+ def test_huffman_compresses(self):
116
+ data = make_data()
117
+ builder = make_code_builder(data)
118
+ coder = builder.build_code()
119
+
120
+ with TemporaryDirectory() as dirname:
121
+ prefix = os.path.join(dirname, "huffman")
122
+ build_dataset(prefix, data, coder)
123
+
124
+ prefix_mmap = os.path.join(dirname, "mmap")
125
+ mmap_builder = indexed_dataset.make_builder(
126
+ indexed_dataset.data_file_path(prefix_mmap),
127
+ "mmap",
128
+ vocab_size=len(POPULATION),
129
+ )
130
+ dictionary = Dictionary()
131
+ for c in POPULATION:
132
+ dictionary.add_symbol(c)
133
+ dictionary.finalize()
134
+ for sentence in data:
135
+ mmap_builder.add_item(dictionary.encode_line(" ".join(sentence)))
136
+ mmap_builder.finalize(indexed_dataset.index_file_path(prefix_mmap))
137
+
138
+ huff_size = os.stat(indexed_dataset.data_file_path(prefix)).st_size
139
+ mmap_size = os.stat(indexed_dataset.data_file_path(prefix_mmap)).st_size
140
+ self.assertLess(huff_size, mmap_size)
141
+
142
+ def test_huffman_can_append(self):
143
+ data1 = make_data()
144
+ builder = make_code_builder(data1)
145
+ coder = builder.build_code()
146
+
147
+ with TemporaryDirectory() as dirname:
148
+ prefix1 = os.path.join(dirname, "test1")
149
+ build_dataset(prefix1, data1, coder)
150
+
151
+ data2 = make_data()
152
+ prefix2 = os.path.join(dirname, "test2")
153
+ build_dataset(prefix2, data2, coder)
154
+
155
+ prefix3 = os.path.join(dirname, "test3")
156
+
157
+ with HuffmanMMapIndexedDatasetBuilder(prefix3, coder) as builder:
158
+ builder.append(prefix1)
159
+ builder.append(prefix2)
160
+
161
+ dataset = HuffmanMMapIndexedDataset(prefix3)
162
+
163
+ self.assertEqual(len(dataset), len(data1) + len(data2))
164
+
165
+ decoded1 = [list(dataset.get_symbols(i)) for i in range(0, len(data1))]
166
+ self.assertEqual(decoded1, data1)
167
+
168
+ decoded2 = [
169
+ list(dataset.get_symbols(i)) for i in range(len(data1), len(dataset))
170
+ ]
171
+ self.assertEqual(decoded2, data2)
172
+
173
+ data_sizes = [i.item() for i in dataset.sizes]
174
+ self.assertEqual(data_sizes[: len(data1)], sizes(data1))
175
+ self.assertEqual(data_sizes[len(data1) : len(dataset)], sizes(data2))
176
+
177
+
178
+ if __name__ == "__main__":
179
+ unittest.main()
omni_speech/infer/fairseq/tests/test_inference_dropout.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
9
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
10
+ from fairseq.models.transformer import TransformerModel
11
+ from tests.test_sequence_generator import get_dummy_task_and_parser
12
+
13
+
14
+ class TestInferenceDropout(unittest.TestCase):
15
+ def setUp(self):
16
+ self.task, self.parser = get_dummy_task_and_parser()
17
+ TransformerModel.add_args(self.parser)
18
+ self.args = self.parser.parse_args([])
19
+ self.args.encoder_layers = 2
20
+ self.args.decoder_layers = 1
21
+ logging.disable(logging.CRITICAL)
22
+
23
+ def tearDown(self):
24
+ logging.disable(logging.NOTSET)
25
+
26
+ def test_sets_inference_dropout_to_true(self):
27
+ self.args.retain_dropout = True
28
+ self.transformer_model = TransformerModel.build_model(self.args, self.task)
29
+ cfg = convert_namespace_to_omegaconf(self.args)
30
+ self.transformer_model.prepare_for_inference_(cfg)
31
+ assert self.transformer_model.encoder.dropout_module.apply_during_inference
32
+ assert self.transformer_model.decoder.dropout_module.apply_during_inference
33
+ for layer in self.transformer_model.encoder.layers:
34
+ assert layer.dropout_module.apply_during_inference
35
+
36
+ def test_inference_dropout_false_by_default(self):
37
+ self.transformer_model = TransformerModel.build_model(self.args, self.task)
38
+ cfg = convert_namespace_to_omegaconf(self.args)
39
+ self.transformer_model.prepare_for_inference_(cfg)
40
+ assert not self.transformer_model.encoder.dropout_module.apply_during_inference
41
+ assert not self.transformer_model.decoder.dropout_module.apply_during_inference
42
+ for layer in self.transformer_model.encoder.layers:
43
+ assert not layer.dropout_module.apply_during_inference
44
+ for layer in self.transformer_model.decoder.layers:
45
+ assert not layer.dropout_module.apply_during_inference
46
+
47
+ def test_applies_training_mode(self):
48
+ self.transformer_model = TransformerModel.build_model(self.args, self.task)
49
+ assert self.transformer_model.encoder.dropout_module.training
50
+ for layer in self.transformer_model.encoder.layers:
51
+ assert layer.dropout_module.training
52
+
53
+ self.transformer_model.eval()
54
+ assert not self.transformer_model.decoder.dropout_module.training
55
+ for layer in self.transformer_model.encoder.layers:
56
+ assert not layer.dropout_module.training
57
+
58
+ def test_retain_modules(self):
59
+ self.args.retain_dropout = True
60
+ self.args.retain_dropout_modules = [
61
+ "TransformerEncoder",
62
+ "TransformerEncoderLayer",
63
+ ]
64
+ self.transformer_model = TransformerModel.build_model(self.args, self.task)
65
+ cfg = convert_namespace_to_omegaconf(self.args)
66
+ self.transformer_model.prepare_for_inference_(cfg)
67
+ assert self.transformer_model.encoder.dropout_module.apply_during_inference
68
+ assert not self.transformer_model.decoder.dropout_module.apply_during_inference
69
+ for layer in self.transformer_model.decoder.layers:
70
+ assert not layer.dropout_module.apply_during_inference
omni_speech/infer/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()
omni_speech/infer/fairseq/tests/test_iterators.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ from fairseq.data import iterators, ListDataset
9
+
10
+
11
+ class TestIterators(unittest.TestCase):
12
+ def test_counting_iterator_index(self, ref=None, itr=None):
13
+ # Test the indexing functionality of CountingIterator
14
+ if ref is None:
15
+ assert itr is None
16
+ ref = list(range(10))
17
+ itr = iterators.CountingIterator(ref)
18
+ else:
19
+ assert len(ref) == 10
20
+ assert itr is not None
21
+
22
+ self.assertTrue(itr.has_next())
23
+ self.assertEqual(itr.n, 0)
24
+ self.assertEqual(next(itr), ref[0])
25
+ self.assertEqual(itr.n, 1)
26
+ self.assertEqual(next(itr), ref[1])
27
+ self.assertEqual(itr.n, 2)
28
+ itr.skip(3)
29
+ self.assertEqual(itr.n, 5)
30
+ self.assertEqual(next(itr), ref[5])
31
+ itr.skip(2)
32
+ self.assertEqual(itr.n, 8)
33
+ self.assertEqual(list(itr), [ref[8], ref[9]])
34
+ self.assertFalse(itr.has_next())
35
+
36
+ def test_counting_iterator_length_mismatch(self):
37
+ ref = list(range(10))
38
+ # When the underlying iterable is longer than the CountingIterator,
39
+ # the remaining items in the iterable should be ignored
40
+ itr = iterators.CountingIterator(ref, total=8)
41
+ self.assertEqual(list(itr), ref[:8])
42
+ # When the underlying iterable is shorter than the CountingIterator,
43
+ # raise an IndexError when the underlying iterable is exhausted
44
+ itr = iterators.CountingIterator(ref, total=12)
45
+ self.assertRaises(IndexError, list, itr)
46
+
47
+ def test_counting_iterator_take(self):
48
+ # Test the "take" method of CountingIterator
49
+ ref = list(range(10))
50
+ itr = iterators.CountingIterator(ref)
51
+ itr.take(5)
52
+ self.assertEqual(len(itr), len(list(iter(itr))))
53
+ self.assertEqual(len(itr), 5)
54
+
55
+ itr = iterators.CountingIterator(ref)
56
+ itr.take(5)
57
+ self.assertEqual(next(itr), ref[0])
58
+ self.assertEqual(next(itr), ref[1])
59
+ itr.skip(2)
60
+ self.assertEqual(next(itr), ref[4])
61
+ self.assertFalse(itr.has_next())
62
+
63
+ def test_grouped_iterator(self):
64
+ # test correctness
65
+ x = list(range(10))
66
+ itr = iterators.GroupedIterator(x, 1)
67
+ self.assertEqual(list(itr), [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]])
68
+ itr = iterators.GroupedIterator(x, 4)
69
+ self.assertEqual(list(itr), [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]])
70
+ itr = iterators.GroupedIterator(x, 5)
71
+ self.assertEqual(list(itr), [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
72
+
73
+ # test the GroupIterator also works correctly as a CountingIterator
74
+ x = list(range(30))
75
+ ref = list(iterators.GroupedIterator(x, 3))
76
+ itr = iterators.GroupedIterator(x, 3)
77
+ self.test_counting_iterator_index(ref, itr)
78
+
79
+ def test_sharded_iterator(self):
80
+ # test correctness
81
+ x = list(range(10))
82
+ itr = iterators.ShardedIterator(x, num_shards=1, shard_id=0)
83
+ self.assertEqual(list(itr), x)
84
+ itr = iterators.ShardedIterator(x, num_shards=2, shard_id=0)
85
+ self.assertEqual(list(itr), [0, 2, 4, 6, 8])
86
+ itr = iterators.ShardedIterator(x, num_shards=2, shard_id=1)
87
+ self.assertEqual(list(itr), [1, 3, 5, 7, 9])
88
+ itr = iterators.ShardedIterator(x, num_shards=3, shard_id=0)
89
+ self.assertEqual(list(itr), [0, 3, 6, 9])
90
+ itr = iterators.ShardedIterator(x, num_shards=3, shard_id=1)
91
+ self.assertEqual(list(itr), [1, 4, 7, None])
92
+ itr = iterators.ShardedIterator(x, num_shards=3, shard_id=2)
93
+ self.assertEqual(list(itr), [2, 5, 8, None])
94
+
95
+ # test CountingIterator functionality
96
+ x = list(range(30))
97
+ ref = list(iterators.ShardedIterator(x, num_shards=3, shard_id=0))
98
+ itr = iterators.ShardedIterator(x, num_shards=3, shard_id=0)
99
+ self.test_counting_iterator_index(ref, itr)
100
+
101
+ def test_counting_iterator_buffered_iterator_take(self):
102
+ ref = list(range(10))
103
+ buffered_itr = iterators.BufferedIterator(2, ref)
104
+ itr = iterators.CountingIterator(buffered_itr)
105
+ itr.take(5)
106
+ self.assertEqual(len(itr), len(list(iter(itr))))
107
+ self.assertEqual(len(itr), 5)
108
+
109
+ buffered_itr = iterators.BufferedIterator(2, ref)
110
+ itr = iterators.CountingIterator(buffered_itr)
111
+ itr.take(5)
112
+ self.assertEqual(len(buffered_itr), 5)
113
+ self.assertEqual(len(list(iter(buffered_itr))), 5)
114
+
115
+ buffered_itr = iterators.BufferedIterator(2, ref)
116
+ itr = iterators.CountingIterator(buffered_itr)
117
+ itr.take(5)
118
+ self.assertEqual(next(itr), ref[0])
119
+ self.assertEqual(next(itr), ref[1])
120
+ itr.skip(2)
121
+ self.assertEqual(next(itr), ref[4])
122
+ self.assertFalse(itr.has_next())
123
+ self.assertRaises(StopIteration, next, buffered_itr)
124
+
125
+ ref = list(range(4, 10))
126
+ buffered_itr = iterators.BufferedIterator(2, ref)
127
+ itr = iterators.CountingIterator(buffered_itr, start=4)
128
+ itr.take(5)
129
+ self.assertEqual(len(itr), 5)
130
+ self.assertEqual(len(buffered_itr), 1)
131
+ self.assertEqual(next(itr), ref[0])
132
+ self.assertFalse(itr.has_next())
133
+ self.assertRaises(StopIteration, next, buffered_itr)
134
+
135
+ def test_epoch_batch_iterator_skip_remainder_batch(self):
136
+ reference = [1, 2, 3]
137
+ itr1 = _get_epoch_batch_itr(reference, 2, True)
138
+ self.assertEqual(len(itr1), 1)
139
+ itr2 = _get_epoch_batch_itr(reference, 2, False)
140
+ self.assertEqual(len(itr2), 2)
141
+ itr3 = _get_epoch_batch_itr(reference, 1, True)
142
+ self.assertEqual(len(itr3), 2)
143
+ itr4 = _get_epoch_batch_itr(reference, 1, False)
144
+ self.assertEqual(len(itr4), 3)
145
+ itr5 = _get_epoch_batch_itr(reference, 4, True)
146
+ self.assertEqual(len(itr5), 0)
147
+ self.assertFalse(itr5.has_next())
148
+ itr6 = _get_epoch_batch_itr(reference, 4, False)
149
+ self.assertEqual(len(itr6), 1)
150
+
151
+ def test_grouped_iterator_skip_remainder_batch(self):
152
+ reference = [1, 2, 3, 4, 5, 6, 7, 8, 9]
153
+ itr1 = _get_epoch_batch_itr(reference, 3, False)
154
+ grouped_itr1 = iterators.GroupedIterator(itr1, 2, True)
155
+ self.assertEqual(len(grouped_itr1), 1)
156
+
157
+ itr2 = _get_epoch_batch_itr(reference, 3, False)
158
+ grouped_itr2 = iterators.GroupedIterator(itr2, 2, False)
159
+ self.assertEqual(len(grouped_itr2), 2)
160
+
161
+ itr3 = _get_epoch_batch_itr(reference, 3, True)
162
+ grouped_itr3 = iterators.GroupedIterator(itr3, 2, True)
163
+ self.assertEqual(len(grouped_itr3), 1)
164
+
165
+ itr4 = _get_epoch_batch_itr(reference, 3, True)
166
+ grouped_itr4 = iterators.GroupedIterator(itr4, 2, False)
167
+ self.assertEqual(len(grouped_itr4), 1)
168
+
169
+ itr5 = _get_epoch_batch_itr(reference, 5, True)
170
+ grouped_itr5 = iterators.GroupedIterator(itr5, 2, True)
171
+ self.assertEqual(len(grouped_itr5), 0)
172
+
173
+ itr6 = _get_epoch_batch_itr(reference, 5, True)
174
+ grouped_itr6 = iterators.GroupedIterator(itr6, 2, False)
175
+ self.assertEqual(len(grouped_itr6), 1)
176
+
177
+
178
+ def _get_epoch_batch_itr(ref, bsz, skip_remainder_batch):
179
+ dsz = len(ref)
180
+ indices = range(dsz)
181
+ starts = indices[::bsz]
182
+ batch_sampler = [indices[s : s + bsz] for s in starts]
183
+ dataset = ListDataset(ref)
184
+ itr = iterators.EpochBatchIterator(
185
+ dataset=dataset,
186
+ collate_fn=dataset.collater,
187
+ batch_sampler=batch_sampler,
188
+ skip_remainder_batch=skip_remainder_batch,
189
+ )
190
+ return itr.next_epoch_itr()
191
+
192
+
193
+ if __name__ == "__main__":
194
+ unittest.main()
omni_speech/infer/fairseq/tests/test_label_smoothing.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 argparse
7
+ import copy
8
+ import unittest
9
+
10
+ import tests.utils as test_utils
11
+ import torch
12
+ from fairseq.criterions.cross_entropy import CrossEntropyCriterion
13
+ from fairseq.criterions.label_smoothed_cross_entropy import (
14
+ LabelSmoothedCrossEntropyCriterion,
15
+ )
16
+
17
+
18
+ class TestLabelSmoothing(unittest.TestCase):
19
+ def setUp(self):
20
+ # build dictionary
21
+ self.d = test_utils.dummy_dictionary(3)
22
+ vocab = len(self.d)
23
+ self.assertEqual(vocab, 4 + 3) # 4 special + 3 tokens
24
+ self.assertEqual(self.d.pad(), 1)
25
+ self.assertEqual(self.d.eos(), 2)
26
+ self.assertEqual(self.d.unk(), 3)
27
+ pad, eos, unk, w1, w2, w3 = 1, 2, 3, 4, 5, 6 # noqa: F841
28
+
29
+ # build dataset
30
+ self.data = [
31
+ # the first batch item has padding
32
+ {
33
+ "source": torch.LongTensor([w1, eos]),
34
+ "target": torch.LongTensor([w1, eos]),
35
+ },
36
+ {
37
+ "source": torch.LongTensor([w1, eos]),
38
+ "target": torch.LongTensor([w1, w1, eos]),
39
+ },
40
+ ]
41
+ self.sample = next(test_utils.dummy_dataloader(self.data))
42
+
43
+ # build model
44
+ self.args = argparse.Namespace()
45
+ self.args.sentence_avg = False
46
+ self.args.report_accuracy = False
47
+ self.args.probs = (
48
+ torch.FloatTensor(
49
+ [
50
+ # pad eos unk w1 w2 w3
51
+ [0.05, 0.05, 0.1, 0.05, 0.3, 0.4, 0.05],
52
+ [0.05, 0.10, 0.2, 0.05, 0.2, 0.3, 0.10],
53
+ [0.05, 0.15, 0.3, 0.05, 0.1, 0.2, 0.15],
54
+ ]
55
+ )
56
+ .unsqueeze(0)
57
+ .expand(2, 3, 7)
58
+ ) # add batch dimension
59
+ self.task = test_utils.TestTranslationTask.setup_task(self.args, self.d, self.d)
60
+ self.model = self.task.build_model(self.args)
61
+
62
+ def test_nll_loss(self):
63
+ self.args.label_smoothing = 0.1
64
+ nll_crit = CrossEntropyCriterion.build_criterion(self.args, self.task)
65
+ smooth_crit = LabelSmoothedCrossEntropyCriterion.build_criterion(
66
+ self.args, self.task
67
+ )
68
+ nll_loss, nll_sample_size, nll_logging_output = nll_crit(
69
+ self.model, self.sample
70
+ )
71
+ smooth_loss, smooth_sample_size, smooth_logging_output = smooth_crit(
72
+ self.model, self.sample
73
+ )
74
+ self.assertLess(abs(nll_loss - nll_logging_output["loss"]), 1e-6)
75
+ self.assertLess(abs(nll_loss - smooth_logging_output["nll_loss"]), 1e-6)
76
+
77
+ def test_padding(self):
78
+ self.args.label_smoothing = 0.1
79
+ crit = LabelSmoothedCrossEntropyCriterion.build_criterion(self.args, self.task)
80
+ loss, _, logging_output = crit(self.model, self.sample)
81
+
82
+ def get_one_no_padding(idx):
83
+ # create a new sample with just a single batch item so that there's
84
+ # no padding
85
+ sample1 = next(test_utils.dummy_dataloader([self.data[idx]]))
86
+ args1 = copy.copy(self.args)
87
+ args1.probs = args1.probs[idx, :, :].unsqueeze(0)
88
+ model1 = self.task.build_model(args1)
89
+ loss1, _, _ = crit(model1, sample1)
90
+ return loss1
91
+
92
+ loss1 = get_one_no_padding(0)
93
+ loss2 = get_one_no_padding(1)
94
+ self.assertAlmostEqual(loss, loss1 + loss2)
95
+
96
+ def test_reduction(self):
97
+ self.args.label_smoothing = 0.1
98
+ crit = LabelSmoothedCrossEntropyCriterion.build_criterion(self.args, self.task)
99
+ loss, _, logging_output = crit(self.model, self.sample, reduce=True)
100
+ unreduced_loss, _, _ = crit(self.model, self.sample, reduce=False)
101
+ self.assertAlmostEqual(loss, unreduced_loss.sum())
102
+
103
+ def test_zero_eps(self):
104
+ self.args.label_smoothing = 0.0
105
+ nll_crit = CrossEntropyCriterion.build_criterion(self.args, self.task)
106
+ smooth_crit = LabelSmoothedCrossEntropyCriterion.build_criterion(
107
+ self.args, self.task
108
+ )
109
+ nll_loss, nll_sample_size, nll_logging_output = nll_crit(
110
+ self.model, self.sample
111
+ )
112
+ smooth_loss, smooth_sample_size, smooth_logging_output = smooth_crit(
113
+ self.model, self.sample
114
+ )
115
+ self.assertAlmostEqual(nll_loss, smooth_loss)
116
+
117
+ def assertAlmostEqual(self, t1, t2):
118
+ self.assertEqual(t1.size(), t2.size(), "size mismatch")
119
+ self.assertLess((t1 - t2).abs().max(), 1e-6)
120
+
121
+
122
+ if __name__ == "__main__":
123
+ unittest.main()
omni_speech/infer/fairseq/tests/test_lm_context_window.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
10
+ from fairseq.data import MonolingualDataset
11
+ from fairseq.tasks.language_modeling import LanguageModelingConfig, LanguageModelingTask
12
+ from tests import utils as test_utils
13
+
14
+
15
+ class TestLMContextWindow(unittest.TestCase):
16
+ def test_eval_dataloader(self):
17
+ dictionary = test_utils.dummy_dictionary(10)
18
+ assert len(dictionary) == 14 # 4 extra special symbols
19
+ assert dictionary.pad() == 1
20
+
21
+ dataset = test_utils.TestDataset(
22
+ [
23
+ torch.tensor([4, 5, 6, 7], dtype=torch.long),
24
+ torch.tensor([8, 9, 10, 11], dtype=torch.long),
25
+ torch.tensor([12, 13], dtype=torch.long),
26
+ ]
27
+ )
28
+ dataset = MonolingualDataset(dataset, sizes=[4, 4, 2], src_vocab=dictionary)
29
+
30
+ config = LanguageModelingConfig(tokens_per_sample=4)
31
+ task = LanguageModelingTask(config, dictionary)
32
+
33
+ eval_dataloader = task.eval_lm_dataloader(
34
+ dataset=dataset,
35
+ batch_size=1,
36
+ context_window=2,
37
+ num_workers=0,
38
+ )
39
+
40
+ batch = next(eval_dataloader)
41
+ assert batch["net_input"]["src_tokens"][0].tolist() == [4, 5, 6, 7, 1, 1]
42
+ assert batch["target"][0].tolist() == [4, 5, 6, 7, 1, 1]
43
+
44
+ batch = next(eval_dataloader)
45
+ assert batch["net_input"]["src_tokens"][0].tolist() == [6, 7, 8, 9, 10, 11]
46
+ assert batch["target"][0].tolist() == [1, 1, 8, 9, 10, 11]
47
+
48
+ batch = next(eval_dataloader)
49
+ assert batch["net_input"]["src_tokens"][0].tolist() == [10, 11, 12, 13]
50
+ assert batch["target"][0].tolist() == [1, 1, 12, 13]
51
+
52
+
53
+ if __name__ == "__main__":
54
+ unittest.main()