Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- omni_speech/infer/fairseq/tests/__init__.py +0 -0
- omni_speech/infer/fairseq/tests/distributed/__init__.py +0 -0
- omni_speech/infer/fairseq/tests/distributed/test_bmuf.py +204 -0
- omni_speech/infer/fairseq/tests/distributed/test_distributed_timeout_wrapper.py +52 -0
- omni_speech/infer/fairseq/tests/distributed/test_module_proxy_wrapper.py +74 -0
- omni_speech/infer/fairseq/tests/distributed/test_utils.py +124 -0
- omni_speech/infer/fairseq/tests/distributed/utils.py +65 -0
- omni_speech/infer/fairseq/tests/gpu/__init__.py +0 -0
- omni_speech/infer/fairseq/tests/gpu/test_binaries_gpu.py +590 -0
- omni_speech/infer/fairseq/tests/gpu/test_ema_gpu.py +215 -0
- omni_speech/infer/fairseq/tests/gpu/transformer_quantization_config.yaml +28 -0
- omni_speech/infer/fairseq/tests/speech/__init__.py +210 -0
- omni_speech/infer/fairseq/tests/speech/test_convtransformer_simul_trans.py +33 -0
- omni_speech/infer/fairseq/tests/speech/test_dual_input_wav_transformer.py +76 -0
- omni_speech/infer/fairseq/tests/speech/test_dualinput_s2t_transformer.py +110 -0
- omni_speech/infer/fairseq/tests/speech/test_fastspeech2.py +53 -0
- omni_speech/infer/fairseq/tests/speech/test_s2s_transformer.py +51 -0
- omni_speech/infer/fairseq/tests/speech/test_s2t_conformer.py +23 -0
- omni_speech/infer/fairseq/tests/speech/test_s2t_transformer.py +23 -0
- omni_speech/infer/fairseq/tests/speech/test_tts_transformer.py +53 -0
- omni_speech/infer/fairseq/tests/speech/test_wav2vec2.py +90 -0
- omni_speech/infer/fairseq/tests/speech/test_xm_transformer.py +29 -0
- omni_speech/infer/fairseq/tests/speech_recognition/__init__.py +0 -0
- omni_speech/infer/fairseq/tests/speech_recognition/asr_test_base.py +557 -0
- omni_speech/infer/fairseq/tests/speech_recognition/test_collaters.py +58 -0
- omni_speech/infer/fairseq/tests/speech_recognition/test_cross_entropy.py +37 -0
- omni_speech/infer/fairseq/tests/speech_recognition/test_data_utils.py +62 -0
- omni_speech/infer/fairseq/tests/speech_recognition/test_vggtransformer.py +135 -0
- omni_speech/infer/fairseq/tests/tasks/test_denoising.py +96 -0
- omni_speech/infer/fairseq/tests/tasks/test_masked_lm.py +78 -0
- omni_speech/infer/fairseq/tests/tasks/test_multilingual_denoising.py +98 -0
- omni_speech/infer/fairseq/tests/tasks/test_span_masked_lm.py +106 -0
- omni_speech/infer/fairseq/tests/test_convtbc.py +54 -0
- omni_speech/infer/fairseq/tests/test_data_utils.py +136 -0
- omni_speech/infer/fairseq/tests/test_dataclass_utils.py +87 -0
- omni_speech/infer/fairseq/tests/test_dataset.py +66 -0
- omni_speech/infer/fairseq/tests/test_dictionary.py +145 -0
- omni_speech/infer/fairseq/tests/test_ema.py +275 -0
- omni_speech/infer/fairseq/tests/test_espnet_multihead_attention.py +176 -0
- omni_speech/infer/fairseq/tests/test_export.py +120 -0
- omni_speech/infer/fairseq/tests/test_file_chunker_utils.py +63 -0
- omni_speech/infer/fairseq/tests/test_file_io.py +59 -0
- omni_speech/infer/fairseq/tests/test_fp16_optimizer.py +111 -0
- omni_speech/infer/fairseq/tests/test_hf_hub.py +29 -0
- omni_speech/infer/fairseq/tests/test_huffman.py +179 -0
- omni_speech/infer/fairseq/tests/test_inference_dropout.py +70 -0
- omni_speech/infer/fairseq/tests/test_iopath.py +28 -0
- omni_speech/infer/fairseq/tests/test_iterators.py +194 -0
- omni_speech/infer/fairseq/tests/test_label_smoothing.py +123 -0
- 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()
|