NMCxyz commited on
Commit
c0476af
·
verified ·
1 Parent(s): 42b4306

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/__init__.py +0 -0
  2. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/model.py +669 -0
  3. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/numbers.py +71 -0
  4. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/stft.py +141 -0
  5. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/symbols.py +18 -0
  6. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/text.py +107 -0
  7. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/utils.py +171 -0
  8. omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/waveglow_denoiser.py +40 -0
  9. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/README.md +318 -0
  10. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/data_utils.py +107 -0
  11. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/eval/__init__.py +4 -0
  12. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/eval/cont_metrics.py +730 -0
  13. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/generate_waveform.py +120 -0
  14. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/inference_dataset.py +103 -0
  15. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/naive_decoder.py +40 -0
  16. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/prepare_dataset.py +143 -0
  17. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/preprocess_f0.py +65 -0
  18. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/quantize_f0.py +94 -0
  19. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/sample/__init__.py +4 -0
  20. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/sample/sample.py +612 -0
  21. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/join_units_manifest.py +48 -0
  22. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/prepare_data.sh +57 -0
  23. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/prepare_f0_quantization.sh +27 -0
  24. omni_speech/infer/fairseq/examples/textless_nlp/pgslm/truncated_laplace.py +29 -0
  25. omni_speech/infer/fairseq/examples/textless_nlp/speech-resynth/README.md +28 -0
  26. omni_speech/infer/fairseq/examples/translation/README.md +301 -0
  27. omni_speech/infer/fairseq/examples/translation/prepare-iwslt14.sh +115 -0
  28. omni_speech/infer/fairseq/examples/translation/prepare-iwslt17-multilingual.sh +133 -0
  29. omni_speech/infer/fairseq/examples/translation/prepare-wmt14en2de.sh +142 -0
  30. omni_speech/infer/fairseq/examples/translation/prepare-wmt14en2fr.sh +136 -0
  31. omni_speech/infer/fairseq/examples/translation_moe/README.md +89 -0
  32. omni_speech/infer/fairseq/examples/translation_moe/score.py +197 -0
  33. omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/__init__.py +6 -0
  34. omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/logsumexp_moe.py +26 -0
  35. omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/mean_pool_gating_network.py +50 -0
  36. omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/translation_moe.py +259 -0
  37. omni_speech/infer/fairseq/examples/truncated_bptt/README.md +70 -0
  38. omni_speech/infer/fairseq/examples/truncated_bptt/__init__.py +6 -0
  39. omni_speech/infer/fairseq/examples/truncated_bptt/transformer_xl_model.py +143 -0
  40. omni_speech/infer/fairseq/examples/truncated_bptt/truncated_bptt_lm_task.py +285 -0
  41. omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/README.md +126 -0
  42. omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/aggregate_scores.py +41 -0
  43. omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/meteor.py +109 -0
  44. omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/repeat_lines.py +28 -0
  45. omni_speech/infer/fairseq/examples/wav2vec/README.md +426 -0
  46. omni_speech/infer/fairseq/examples/wav2vec/__init__.py +0 -0
  47. omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_100h.yaml +58 -0
  48. omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_10h.yaml +63 -0
  49. omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_10m.yaml +63 -0
  50. omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_1h.yaml +63 -0
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/model.py ADDED
@@ -0,0 +1,669 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from math import sqrt
2
+ import torch
3
+ import torch.distributions as distr
4
+ from torch.autograd import Variable
5
+ from torch import nn
6
+ from torch.nn import functional as F
7
+ from .layers import ConvNorm, LinearNorm, GlobalAvgPool
8
+ from .utils import to_gpu, get_mask_from_lengths
9
+
10
+
11
+ class LocationLayer(nn.Module):
12
+ def __init__(self, attention_n_filters, attention_kernel_size,
13
+ attention_dim):
14
+ super(LocationLayer, self).__init__()
15
+ padding = int((attention_kernel_size - 1) / 2)
16
+ self.location_conv = ConvNorm(2, attention_n_filters,
17
+ kernel_size=attention_kernel_size,
18
+ padding=padding, bias=False, stride=1,
19
+ dilation=1)
20
+ self.location_dense = LinearNorm(attention_n_filters, attention_dim,
21
+ bias=False, w_init_gain='tanh')
22
+
23
+ def forward(self, attention_weights_cat):
24
+ processed_attention = self.location_conv(attention_weights_cat)
25
+ processed_attention = processed_attention.transpose(1, 2)
26
+ processed_attention = self.location_dense(processed_attention)
27
+ return processed_attention
28
+
29
+
30
+ class Attention(nn.Module):
31
+ def __init__(self, attention_rnn_dim, embedding_dim, attention_dim,
32
+ attention_location_n_filters, attention_location_kernel_size):
33
+ super(Attention, self).__init__()
34
+ self.query_layer = LinearNorm(attention_rnn_dim, attention_dim,
35
+ bias=False, w_init_gain='tanh')
36
+ self.memory_layer = LinearNorm(embedding_dim, attention_dim, bias=False,
37
+ w_init_gain='tanh')
38
+ self.v = LinearNorm(attention_dim, 1, bias=False)
39
+ self.location_layer = LocationLayer(attention_location_n_filters,
40
+ attention_location_kernel_size,
41
+ attention_dim)
42
+ self.score_mask_value = -float("inf")
43
+
44
+ def get_alignment_energies(self, query, processed_memory,
45
+ attention_weights_cat):
46
+ """
47
+ PARAMS
48
+ ------
49
+ query: decoder output (batch, n_mel_channels * n_frames_per_step)
50
+ processed_memory: processed encoder outputs (B, T_in, attention_dim)
51
+ attention_weights_cat: cumulative and prev. att weights (B, 2, max_time)
52
+
53
+ RETURNS
54
+ -------
55
+ alignment (batch, max_time)
56
+ """
57
+
58
+ processed_query = self.query_layer(query.unsqueeze(1))
59
+ processed_attention_weights = self.location_layer(attention_weights_cat)
60
+ energies = self.v(torch.tanh(
61
+ processed_query + processed_attention_weights + processed_memory))
62
+
63
+ energies = energies.squeeze(-1)
64
+ return energies
65
+
66
+ def forward(self, attention_hidden_state, memory, processed_memory,
67
+ attention_weights_cat, mask):
68
+ """
69
+ PARAMS
70
+ ------
71
+ attention_hidden_state: attention rnn last output
72
+ memory: encoder outputs
73
+ processed_memory: processed encoder outputs
74
+ attention_weights_cat: previous and cummulative attention weights
75
+ mask: binary mask for padded data
76
+ """
77
+ alignment = self.get_alignment_energies(
78
+ attention_hidden_state, processed_memory, attention_weights_cat)
79
+
80
+ if mask is not None:
81
+ alignment.data.masked_fill_(mask, self.score_mask_value)
82
+
83
+ attention_weights = F.softmax(alignment, dim=1)
84
+ attention_context = torch.bmm(attention_weights.unsqueeze(1), memory)
85
+ attention_context = attention_context.squeeze(1)
86
+
87
+ return attention_context, attention_weights
88
+
89
+
90
+ class Prenet(nn.Module):
91
+ def __init__(self, in_dim, sizes):
92
+ super(Prenet, self).__init__()
93
+ in_sizes = [in_dim] + sizes[:-1]
94
+ self.layers = nn.ModuleList(
95
+ [LinearNorm(in_size, out_size, bias=False)
96
+ for (in_size, out_size) in zip(in_sizes, sizes)])
97
+
98
+ def forward(self, x):
99
+ for linear in self.layers:
100
+ x = F.dropout(F.relu(linear(x)), p=0.5, training=True)
101
+ return x
102
+
103
+
104
+ class Postnet(nn.Module):
105
+ """Postnet
106
+ - Five 1-d convolution with 512 channels and kernel size 5
107
+ """
108
+
109
+ def __init__(self, hparams):
110
+ super(Postnet, self).__init__()
111
+ self.convolutions = nn.ModuleList()
112
+
113
+ self.convolutions.append(
114
+ nn.Sequential(
115
+ ConvNorm(hparams.n_mel_channels, hparams.postnet_embedding_dim,
116
+ kernel_size=hparams.postnet_kernel_size, stride=1,
117
+ padding=int((hparams.postnet_kernel_size - 1) / 2),
118
+ dilation=1, w_init_gain='tanh'),
119
+ nn.BatchNorm1d(hparams.postnet_embedding_dim))
120
+ )
121
+
122
+ for i in range(1, hparams.postnet_n_convolutions - 1):
123
+ self.convolutions.append(
124
+ nn.Sequential(
125
+ ConvNorm(hparams.postnet_embedding_dim,
126
+ hparams.postnet_embedding_dim,
127
+ kernel_size=hparams.postnet_kernel_size, stride=1,
128
+ padding=int((hparams.postnet_kernel_size - 1) / 2),
129
+ dilation=1, w_init_gain='tanh'),
130
+ nn.BatchNorm1d(hparams.postnet_embedding_dim))
131
+ )
132
+
133
+ self.convolutions.append(
134
+ nn.Sequential(
135
+ ConvNorm(hparams.postnet_embedding_dim, hparams.n_mel_channels,
136
+ kernel_size=hparams.postnet_kernel_size, stride=1,
137
+ padding=int((hparams.postnet_kernel_size - 1) / 2),
138
+ dilation=1, w_init_gain='linear'),
139
+ nn.BatchNorm1d(hparams.n_mel_channels))
140
+ )
141
+
142
+ def forward(self, x):
143
+ for i in range(len(self.convolutions) - 1):
144
+ x = F.dropout(torch.tanh(self.convolutions[i](x)), 0.5, self.training)
145
+ x = F.dropout(self.convolutions[-1](x), 0.5, self.training)
146
+
147
+ return x
148
+
149
+
150
+ class Encoder(nn.Module):
151
+ """Encoder module:
152
+ - Three 1-d convolution banks
153
+ - Bidirectional LSTM
154
+ """
155
+ def __init__(self, hparams):
156
+ super(Encoder, self).__init__()
157
+
158
+ convolutions = []
159
+ for _ in range(hparams.encoder_n_convolutions):
160
+ conv_layer = nn.Sequential(
161
+ ConvNorm(hparams.encoder_embedding_dim,
162
+ hparams.encoder_embedding_dim,
163
+ kernel_size=hparams.encoder_kernel_size, stride=1,
164
+ padding=int((hparams.encoder_kernel_size - 1) / 2),
165
+ dilation=1, w_init_gain='relu'),
166
+ nn.BatchNorm1d(hparams.encoder_embedding_dim))
167
+ convolutions.append(conv_layer)
168
+ self.convolutions = nn.ModuleList(convolutions)
169
+
170
+ self.lstm = nn.LSTM(hparams.encoder_embedding_dim,
171
+ int(hparams.encoder_embedding_dim / 2), 1,
172
+ batch_first=True, bidirectional=True)
173
+
174
+ def forward(self, x, input_lengths):
175
+ for conv in self.convolutions:
176
+ x = F.dropout(F.relu(conv(x)), 0.5, self.training)
177
+
178
+ x = x.transpose(1, 2)
179
+
180
+ # pytorch tensor are not reversible, hence the conversion
181
+ input_lengths = input_lengths.cpu().numpy()
182
+ x = nn.utils.rnn.pack_padded_sequence(
183
+ x, input_lengths, batch_first=True)
184
+
185
+ self.lstm.flatten_parameters()
186
+ outputs, _ = self.lstm(x)
187
+
188
+ outputs, _ = nn.utils.rnn.pad_packed_sequence(
189
+ outputs, batch_first=True)
190
+
191
+ return outputs
192
+
193
+ def inference(self, x):
194
+ for conv in self.convolutions:
195
+ x = F.dropout(F.relu(conv(x)), 0.5, self.training)
196
+
197
+ x = x.transpose(1, 2)
198
+
199
+ self.lstm.flatten_parameters()
200
+ outputs, _ = self.lstm(x)
201
+
202
+ return outputs
203
+
204
+
205
+ class AudioEncoder(nn.Module):
206
+ def __init__(self, hparams):
207
+ super(AudioEncoder, self).__init__()
208
+
209
+ assert hparams.lat_dim > 0
210
+
211
+ convolutions = []
212
+ inp_dim = hparams.n_mel_channels
213
+ for _ in range(hparams.lat_n_convolutions):
214
+ conv_layer = nn.Sequential(
215
+ ConvNorm(inp_dim, hparams.lat_n_filters,
216
+ kernel_size=hparams.lat_kernel_size, stride=1,
217
+ padding=int((hparams.lat_kernel_size - 1) / 2),
218
+ dilation=1, w_init_gain='tanh'),
219
+ nn.BatchNorm1d(hparams.lat_n_filters))
220
+ inp_dim = hparams.lat_n_filters
221
+ convolutions.append(conv_layer)
222
+ self.convolutions = nn.ModuleList(convolutions)
223
+
224
+ self.lstm = nn.LSTM(hparams.lat_n_filters,
225
+ int(hparams.lat_n_filters / 2),
226
+ hparams.lat_n_blstms, batch_first=True,
227
+ bidirectional=True)
228
+ self.pool = GlobalAvgPool()
229
+
230
+ self.mu_proj = LinearNorm(hparams.lat_n_filters, hparams.lat_dim)
231
+ self.logvar_proj = LinearNorm(hparams.lat_n_filters, hparams.lat_dim)
232
+ self.lat_dim = hparams.lat_dim
233
+
234
+ def forward(self, x, lengths):
235
+ """
236
+ Args:
237
+ x (torch.Tensor): (B, F, T)
238
+ """
239
+
240
+ for conv in self.convolutions:
241
+ x = F.dropout(F.tanh(conv(x)), 0.5, self.training)
242
+
243
+ x = x.transpose(1, 2) # (B, T, D)
244
+
245
+ # x may not be sorted by length. Sort->process->unsort
246
+ max_len = x.size(1)
247
+ assert max_len == torch.max(lengths).item()
248
+
249
+ lengths, perm_idx = lengths.sort(0, descending=True)
250
+ x = x[perm_idx]
251
+ x = nn.utils.rnn.pack_padded_sequence(x, lengths, batch_first=True)
252
+
253
+ self.lstm.flatten_parameters()
254
+ outputs, _ = self.lstm(x)
255
+ outputs, _ = nn.utils.rnn.pad_packed_sequence(outputs, batch_first=True)
256
+
257
+ _, unperm_idx = perm_idx.sort(0)
258
+ outputs = outputs[unperm_idx] # (B, T, D)
259
+ lengths = lengths[unperm_idx] # (B, T, D)
260
+
261
+ outputs = self.pool(outputs, lengths) # (B, D)
262
+
263
+ mu = self.mu_proj(outputs)
264
+ logvar = self.logvar_proj(outputs)
265
+ z = distr.Normal(mu, logvar).rsample()
266
+ return z, mu, logvar
267
+
268
+
269
+ class Decoder(nn.Module):
270
+ def __init__(self, hparams):
271
+ super(Decoder, self).__init__()
272
+ self.n_mel_channels = hparams.n_mel_channels
273
+ self.n_frames_per_step = hparams.n_frames_per_step
274
+ self.encoder_embedding_dim = hparams.encoder_embedding_dim
275
+ self.obs_dim = hparams.obs_dim
276
+ self.lat_dim = hparams.lat_dim
277
+ self.attention_rnn_dim = hparams.attention_rnn_dim
278
+ self.decoder_rnn_dim = hparams.decoder_rnn_dim
279
+ self.prenet_dim = hparams.prenet_dim
280
+ self.max_decoder_steps = hparams.max_decoder_steps
281
+ self.gate_threshold = hparams.gate_threshold
282
+ self.p_attention_dropout = hparams.p_attention_dropout
283
+ self.p_decoder_dropout = hparams.p_decoder_dropout
284
+
285
+ self.prenet = Prenet(
286
+ hparams.n_mel_channels * hparams.n_frames_per_step,
287
+ [hparams.prenet_dim, hparams.prenet_dim])
288
+
289
+ self.attention_rnn = nn.LSTMCell(
290
+ hparams.prenet_dim + hparams.encoder_embedding_dim,
291
+ hparams.attention_rnn_dim)
292
+
293
+ self.attention_layer = Attention(
294
+ hparams.attention_rnn_dim, hparams.encoder_embedding_dim,
295
+ hparams.attention_dim, hparams.attention_location_n_filters,
296
+ hparams.attention_location_kernel_size)
297
+
298
+ encoder_tot_dim = (hparams.encoder_embedding_dim + \
299
+ hparams.lat_dim + hparams.obs_dim)
300
+ self.decoder_rnn = nn.LSTMCell(
301
+ hparams.attention_rnn_dim + encoder_tot_dim,
302
+ hparams.decoder_rnn_dim, 1)
303
+
304
+ self.linear_projection = LinearNorm(
305
+ hparams.decoder_rnn_dim + encoder_tot_dim,
306
+ hparams.n_mel_channels * hparams.n_frames_per_step)
307
+
308
+ self.gate_layer = LinearNorm(
309
+ hparams.decoder_rnn_dim + encoder_tot_dim, 1,
310
+ bias=True, w_init_gain='sigmoid')
311
+
312
+ def get_go_frame(self, memory):
313
+ """ Gets all zeros frames to use as first decoder input
314
+ PARAMS
315
+ ------
316
+ memory: decoder outputs
317
+
318
+ RETURNS
319
+ -------
320
+ decoder_input: all zeros frames
321
+ """
322
+ B = memory.size(0)
323
+ decoder_input = Variable(memory.data.new(
324
+ B, self.n_mel_channels * self.n_frames_per_step).zero_())
325
+ return decoder_input
326
+
327
+ def initialize_decoder_states(self, memory, obs_and_lat, mask):
328
+ """ Initializes attention rnn states, decoder rnn states, attention
329
+ weights, attention cumulative weights, attention context, stores memory
330
+ and stores processed memory
331
+ PARAMS
332
+ ------
333
+ memory: Encoder outputs
334
+ obs_and_lat: Observed and latent attribute embeddings
335
+ mask: Mask for padded data if training, expects None for inference
336
+ """
337
+ B = memory.size(0)
338
+ MAX_TIME = memory.size(1)
339
+
340
+ self.attention_hidden = Variable(memory.data.new(
341
+ B, self.attention_rnn_dim).zero_())
342
+ self.attention_cell = Variable(memory.data.new(
343
+ B, self.attention_rnn_dim).zero_())
344
+
345
+ self.decoder_hidden = Variable(memory.data.new(
346
+ B, self.decoder_rnn_dim).zero_())
347
+ self.decoder_cell = Variable(memory.data.new(
348
+ B, self.decoder_rnn_dim).zero_())
349
+
350
+ self.attention_weights = Variable(memory.data.new(
351
+ B, MAX_TIME).zero_())
352
+ self.attention_weights_cum = Variable(memory.data.new(
353
+ B, MAX_TIME).zero_())
354
+ self.attention_context = Variable(memory.data.new(
355
+ B, self.encoder_embedding_dim).zero_())
356
+
357
+ self.memory = memory
358
+ self.processed_memory = self.attention_layer.memory_layer(memory)
359
+ self.obs_and_lat = obs_and_lat
360
+ self.mask = mask
361
+
362
+ def parse_decoder_inputs(self, decoder_inputs):
363
+ """ Prepares decoder inputs, i.e. mel outputs
364
+ PARAMS
365
+ ------
366
+ decoder_inputs: inputs used for teacher-forced training, i.e. mel-specs
367
+
368
+ RETURNS
369
+ -------
370
+ inputs: processed decoder inputs
371
+
372
+ """
373
+ # (B, n_mel_channels, T_out) -> (B, T_out, n_mel_channels)
374
+ decoder_inputs = decoder_inputs.transpose(1, 2)
375
+ decoder_inputs = decoder_inputs.view(
376
+ decoder_inputs.size(0),
377
+ int(decoder_inputs.size(1)/self.n_frames_per_step), -1)
378
+ # (B, T_out, n_mel_channels) -> (T_out, B, n_mel_channels)
379
+ decoder_inputs = decoder_inputs.transpose(0, 1)
380
+ return decoder_inputs
381
+
382
+ def parse_decoder_outputs(self, mel_outputs, gate_outputs, alignments):
383
+ """ Prepares decoder outputs for output
384
+ PARAMS
385
+ ------
386
+ mel_outputs:
387
+ gate_outputs: gate output energies
388
+ alignments:
389
+
390
+ RETURNS
391
+ -------
392
+ mel_outputs:
393
+ gate_outpust: gate output energies
394
+ alignments:
395
+ """
396
+ # (T_out, B) -> (B, T_out)
397
+ alignments = torch.stack(alignments).transpose(0, 1)
398
+ # (T_out, B) -> (B, T_out)
399
+ gate_outputs = torch.stack(gate_outputs).transpose(0, 1)
400
+ gate_outputs = gate_outputs.contiguous()
401
+ # (T_out, B, n_mel_channels) -> (B, T_out, n_mel_channels)
402
+ mel_outputs = torch.stack(mel_outputs).transpose(0, 1).contiguous()
403
+ # decouple frames per step
404
+ mel_outputs = mel_outputs.view(
405
+ mel_outputs.size(0), -1, self.n_mel_channels)
406
+ # (B, T_out, n_mel_channels) -> (B, n_mel_channels, T_out)
407
+ mel_outputs = mel_outputs.transpose(1, 2)
408
+
409
+ return mel_outputs, gate_outputs, alignments
410
+
411
+ def decode(self, decoder_input):
412
+ """ Decoder step using stored states, attention and memory
413
+ PARAMS
414
+ ------
415
+ decoder_input: previous mel output
416
+
417
+ RETURNS
418
+ -------
419
+ mel_output:
420
+ gate_output: gate output energies
421
+ attention_weights:
422
+ """
423
+ cell_input = torch.cat((decoder_input, self.attention_context), -1)
424
+ self.attention_hidden, self.attention_cell = self.attention_rnn(
425
+ cell_input, (self.attention_hidden, self.attention_cell))
426
+ self.attention_hidden = F.dropout(
427
+ self.attention_hidden, self.p_attention_dropout, self.training)
428
+
429
+ attention_weights_cat = torch.cat(
430
+ (self.attention_weights.unsqueeze(1),
431
+ self.attention_weights_cum.unsqueeze(1)), dim=1)
432
+ self.attention_context, self.attention_weights = self.attention_layer(
433
+ self.attention_hidden, self.memory, self.processed_memory,
434
+ attention_weights_cat, self.mask)
435
+
436
+ self.attention_weights_cum += self.attention_weights
437
+ decoder_input = torch.cat(
438
+ (self.attention_hidden, self.attention_context), -1)
439
+ if self.obs_and_lat is not None:
440
+ decoder_input = torch.cat((decoder_input, self.obs_and_lat), -1)
441
+ self.decoder_hidden, self.decoder_cell = self.decoder_rnn(
442
+ decoder_input, (self.decoder_hidden, self.decoder_cell))
443
+ self.decoder_hidden = F.dropout(
444
+ self.decoder_hidden, self.p_decoder_dropout, self.training)
445
+
446
+ decoder_hidden_attention_context = torch.cat(
447
+ (self.decoder_hidden, self.attention_context), dim=1)
448
+ if self.obs_and_lat is not None:
449
+ decoder_hidden_attention_context = torch.cat(
450
+ (decoder_hidden_attention_context, self.obs_and_lat), dim=1)
451
+ decoder_output = self.linear_projection(
452
+ decoder_hidden_attention_context)
453
+
454
+ gate_prediction = self.gate_layer(decoder_hidden_attention_context)
455
+ return decoder_output, gate_prediction, self.attention_weights
456
+
457
+ def forward(self, memory, obs_and_lat, decoder_inputs, memory_lengths):
458
+ """ Decoder forward pass for training
459
+ PARAMS
460
+ ------
461
+ memory: Encoder outputs
462
+ obs_and_lat: Observed and latent attribute embeddings
463
+ decoder_inputs: Decoder inputs for teacher forcing. i.e. mel-specs
464
+ memory_lengths: Encoder output lengths for attention masking.
465
+
466
+ RETURNS
467
+ -------
468
+ mel_outputs: mel outputs from the decoder
469
+ gate_outputs: gate outputs from the decoder
470
+ alignments: sequence of attention weights from the decoder
471
+ """
472
+
473
+ decoder_input = self.get_go_frame(memory).unsqueeze(0)
474
+ decoder_inputs = self.parse_decoder_inputs(decoder_inputs)
475
+ decoder_inputs = torch.cat((decoder_input, decoder_inputs), dim=0)
476
+ decoder_inputs = self.prenet(decoder_inputs)
477
+
478
+ self.initialize_decoder_states(
479
+ memory, obs_and_lat, mask=~get_mask_from_lengths(memory_lengths))
480
+
481
+ mel_outputs, gate_outputs, alignments = [], [], []
482
+ while len(mel_outputs) < decoder_inputs.size(0) - 1:
483
+ decoder_input = decoder_inputs[len(mel_outputs)]
484
+ mel_output, gate_output, attention_weights = self.decode(
485
+ decoder_input)
486
+ mel_outputs += [mel_output.squeeze(1)]
487
+ gate_outputs += [gate_output.squeeze()]
488
+ alignments += [attention_weights]
489
+
490
+ mel_outputs, gate_outputs, alignments = self.parse_decoder_outputs(
491
+ mel_outputs, gate_outputs, alignments)
492
+
493
+ return mel_outputs, gate_outputs, alignments
494
+
495
+ def inference(self, memory, obs_and_lat, ret_has_eos=False):
496
+ """ Decoder inference
497
+ PARAMS
498
+ ------
499
+ memory: Encoder outputs
500
+ obs_and_lat: Observed and latent attribute embeddings
501
+
502
+ RETURNS
503
+ -------
504
+ mel_outputs: mel outputs from the decoder
505
+ gate_outputs: gate outputs from the decoder
506
+ alignments: sequence of attention weights from the decoder
507
+ """
508
+ decoder_input = self.get_go_frame(memory)
509
+
510
+ self.initialize_decoder_states(memory, obs_and_lat, mask=None)
511
+
512
+ mel_outputs, gate_outputs, alignments = [], [], []
513
+ has_eos = False
514
+ while True:
515
+ decoder_input = self.prenet(decoder_input)
516
+ mel_output, gate_output, alignment = self.decode(decoder_input)
517
+
518
+ mel_outputs += [mel_output.squeeze(1)]
519
+ gate_outputs += [gate_output]
520
+ alignments += [alignment]
521
+
522
+ if torch.sigmoid(gate_output.data) > self.gate_threshold:
523
+ has_eos = True
524
+ break
525
+ elif len(mel_outputs) == self.max_decoder_steps:
526
+ # print("Warning! Reached max decoder steps")
527
+ break
528
+
529
+ decoder_input = mel_output
530
+
531
+ mel_outputs, gate_outputs, alignments = self.parse_decoder_outputs(
532
+ mel_outputs, gate_outputs, alignments)
533
+
534
+ if ret_has_eos:
535
+ return mel_outputs, gate_outputs, alignments, has_eos
536
+ else:
537
+ return mel_outputs, gate_outputs, alignments
538
+
539
+
540
+ class Tacotron2(nn.Module):
541
+ def __init__(self, hparams):
542
+ super(Tacotron2, self).__init__()
543
+ self.mask_padding = hparams.mask_padding
544
+ self.fp16_run = hparams.fp16_run
545
+ self.n_mel_channels = hparams.n_mel_channels
546
+ self.n_frames_per_step = hparams.n_frames_per_step
547
+
548
+ # initialize text encoder embedding
549
+ self.embedding = nn.Embedding(
550
+ hparams.n_symbols, hparams.symbols_embedding_dim)
551
+ std = sqrt(2.0 / (hparams.n_symbols + hparams.symbols_embedding_dim))
552
+ val = sqrt(3.0) * std # uniform bounds for std
553
+ self.embedding.weight.data.uniform_(-val, val)
554
+
555
+ # initialize observed attribute embedding
556
+ self.obs_embedding = None
557
+ if hparams.obs_dim > 0:
558
+ self.obs_embedding = nn.Embedding(
559
+ hparams.obs_n_class, hparams.obs_dim)
560
+ std = sqrt(2.0 / (hparams.obs_n_class + hparams.obs_dim))
561
+ val = sqrt(3.0) * std # uniform bounds for std
562
+ self.obs_embedding.weight.data.uniform_(-val, val)
563
+
564
+ self.encoder = Encoder(hparams)
565
+ self.decoder = Decoder(hparams)
566
+ self.postnet = Postnet(hparams)
567
+
568
+ self.lat_encoder = None
569
+ if hparams.lat_dim > 0:
570
+ self.lat_encoder = AudioEncoder(hparams)
571
+
572
+ def parse_batch(self, batch):
573
+ (text_padded, input_lengths, obs_labels,
574
+ mel_padded, gate_padded, output_lengths) = batch
575
+ text_padded = to_gpu(text_padded).long()
576
+ input_lengths = to_gpu(input_lengths).long()
577
+ obs_labels = to_gpu(obs_labels).long()
578
+ max_len = torch.max(input_lengths.data).item()
579
+ mel_padded = to_gpu(mel_padded).float()
580
+ gate_padded = to_gpu(gate_padded).float()
581
+ output_lengths = to_gpu(output_lengths).long()
582
+
583
+ return (
584
+ (text_padded, input_lengths, obs_labels,
585
+ mel_padded, max_len, output_lengths),
586
+ (mel_padded, gate_padded))
587
+
588
+ def parse_output(self, outputs, output_lengths=None):
589
+ if self.mask_padding and output_lengths is not None:
590
+ mask = ~get_mask_from_lengths(output_lengths)
591
+ mask = mask.expand(self.n_mel_channels, mask.size(0), mask.size(1))
592
+ mask = mask.permute(1, 0, 2)
593
+
594
+ outputs[0].data.masked_fill_(mask, 0.0)
595
+ outputs[1].data.masked_fill_(mask, 0.0)
596
+ outputs[2].data.masked_fill_(mask[:, 0, :], 1e3) # gate energies
597
+
598
+ return outputs
599
+
600
+ def forward(self, inputs):
601
+ (text_inputs, text_lengths, obs_labels,
602
+ mels, max_len, output_lengths) = inputs
603
+ text_lengths, output_lengths = text_lengths.data, output_lengths.data
604
+
605
+ embedded_inputs = self.embedding(text_inputs).transpose(1, 2)
606
+
607
+ encoder_outputs = self.encoder(embedded_inputs, text_lengths)
608
+
609
+ obs = None
610
+ if self.obs_embedding is not None:
611
+ obs = self.obs_embedding(obs_labels)
612
+
613
+ lat, lat_mu, lat_logvar = None, None, None
614
+ if self.lat_encoder is not None:
615
+ (lat, lat_mu, lat_logvar) = self.lat_encoder(mels, output_lengths)
616
+
617
+ obs_and_lat = [x for x in [obs, lat] if x is not None]
618
+ if bool(obs_and_lat):
619
+ obs_and_lat = torch.cat(obs_and_lat, dim=-1)
620
+ else:
621
+ obs_and_lat = None
622
+
623
+ mel_outputs, gate_outputs, alignments = self.decoder(
624
+ encoder_outputs, obs_and_lat, mels, memory_lengths=text_lengths)
625
+
626
+ mel_outputs_postnet = self.postnet(mel_outputs)
627
+ mel_outputs_postnet = mel_outputs + mel_outputs_postnet
628
+
629
+ return self.parse_output(
630
+ [mel_outputs, mel_outputs_postnet, gate_outputs, alignments,
631
+ lat_mu, lat_logvar],
632
+ output_lengths)
633
+
634
+ def inference(self, inputs, obs_labels=None, lat=None, ret_has_eos=False):
635
+ embedded_inputs = self.embedding(inputs).transpose(1, 2)
636
+ encoder_outputs = self.encoder.inference(embedded_inputs)
637
+
638
+ if obs_labels is None:
639
+ obs_labels = torch.LongTensor(len(inputs))
640
+ obs_labels = obs_labels.to(inputs.device).zero_()
641
+
642
+ obs = None
643
+ if self.obs_embedding is not None:
644
+ obs = self.obs_embedding(obs_labels)
645
+
646
+ if self.lat_encoder is not None:
647
+ if lat is None:
648
+ lat = torch.FloatTensor(len(inputs), self.lat_encoder.lat_dim)
649
+ lat = lat.to(inputs.device).zero_().type(encoder_outputs.type())
650
+
651
+ obs_and_lat = [x for x in [obs, lat] if x is not None]
652
+ if bool(obs_and_lat):
653
+ obs_and_lat = torch.cat(obs_and_lat, dim=-1)
654
+ else:
655
+ obs_and_lat = None
656
+
657
+ mel_outputs, gate_outputs, alignments, has_eos = self.decoder.inference(
658
+ encoder_outputs, obs_and_lat, ret_has_eos=True)
659
+
660
+ mel_outputs_postnet = self.postnet(mel_outputs)
661
+ mel_outputs_postnet = mel_outputs + mel_outputs_postnet
662
+
663
+ outputs = self.parse_output(
664
+ [mel_outputs, mel_outputs_postnet, gate_outputs, alignments])
665
+
666
+ if ret_has_eos:
667
+ return outputs + [has_eos]
668
+ else:
669
+ return outputs
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/numbers.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ from https://github.com/keithito/tacotron """
2
+
3
+ import inflect
4
+ import re
5
+
6
+
7
+ _inflect = inflect.engine()
8
+ _comma_number_re = re.compile(r'([0-9][0-9\,]+[0-9])')
9
+ _decimal_number_re = re.compile(r'([0-9]+\.[0-9]+)')
10
+ _pounds_re = re.compile(r'£([0-9\,]*[0-9]+)')
11
+ _dollars_re = re.compile(r'\$([0-9\.\,]*[0-9]+)')
12
+ _ordinal_re = re.compile(r'[0-9]+(st|nd|rd|th)')
13
+ _number_re = re.compile(r'[0-9]+')
14
+
15
+
16
+ def _remove_commas(m):
17
+ return m.group(1).replace(',', '')
18
+
19
+
20
+ def _expand_decimal_point(m):
21
+ return m.group(1).replace('.', ' point ')
22
+
23
+
24
+ def _expand_dollars(m):
25
+ match = m.group(1)
26
+ parts = match.split('.')
27
+ if len(parts) > 2:
28
+ return match + ' dollars' # Unexpected format
29
+ dollars = int(parts[0]) if parts[0] else 0
30
+ cents = int(parts[1]) if len(parts) > 1 and parts[1] else 0
31
+ if dollars and cents:
32
+ dollar_unit = 'dollar' if dollars == 1 else 'dollars'
33
+ cent_unit = 'cent' if cents == 1 else 'cents'
34
+ return '%s %s, %s %s' % (dollars, dollar_unit, cents, cent_unit)
35
+ elif dollars:
36
+ dollar_unit = 'dollar' if dollars == 1 else 'dollars'
37
+ return '%s %s' % (dollars, dollar_unit)
38
+ elif cents:
39
+ cent_unit = 'cent' if cents == 1 else 'cents'
40
+ return '%s %s' % (cents, cent_unit)
41
+ else:
42
+ return 'zero dollars'
43
+
44
+
45
+ def _expand_ordinal(m):
46
+ return _inflect.number_to_words(m.group(0))
47
+
48
+
49
+ def _expand_number(m):
50
+ num = int(m.group(0))
51
+ if num > 1000 and num < 3000:
52
+ if num == 2000:
53
+ return 'two thousand'
54
+ elif num > 2000 and num < 2010:
55
+ return 'two thousand ' + _inflect.number_to_words(num % 100)
56
+ elif num % 100 == 0:
57
+ return _inflect.number_to_words(num // 100) + ' hundred'
58
+ else:
59
+ return _inflect.number_to_words(num, andword='', zero='oh', group=2).replace(', ', ' ')
60
+ else:
61
+ return _inflect.number_to_words(num, andword='')
62
+
63
+
64
+ def normalize_numbers(text):
65
+ text = re.sub(_comma_number_re, _remove_commas, text)
66
+ text = re.sub(_pounds_re, r'\1 pounds', text)
67
+ text = re.sub(_dollars_re, _expand_dollars, text)
68
+ text = re.sub(_decimal_number_re, _expand_decimal_point, text)
69
+ text = re.sub(_ordinal_re, _expand_ordinal, text)
70
+ text = re.sub(_number_re, _expand_number, text)
71
+ return text
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/stft.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ BSD 3-Clause License
3
+
4
+ Copyright (c) 2017, Prem Seetharaman
5
+ All rights reserved.
6
+
7
+ * Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice,
11
+ this list of conditions and the following disclaimer.
12
+
13
+ * Redistributions in binary form must reproduce the above copyright notice, this
14
+ list of conditions and the following disclaimer in the
15
+ documentation and/or other materials provided with the distribution.
16
+
17
+ * Neither the name of the copyright holder nor the names of its
18
+ contributors may be used to endorse or promote products derived from this
19
+ software without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ """
32
+
33
+ import torch
34
+ import numpy as np
35
+ import torch.nn.functional as F
36
+ from torch.autograd import Variable
37
+ from scipy.signal import get_window
38
+ from librosa.util import pad_center, tiny
39
+ from .audio_processing import window_sumsquare
40
+
41
+
42
+ class STFT(torch.nn.Module):
43
+ """adapted from Prem Seetharaman's https://github.com/pseeth/pytorch-stft"""
44
+ def __init__(self, filter_length=800, hop_length=200, win_length=800,
45
+ window='hann'):
46
+ super(STFT, self).__init__()
47
+ self.filter_length = filter_length
48
+ self.hop_length = hop_length
49
+ self.win_length = win_length
50
+ self.window = window
51
+ self.forward_transform = None
52
+ scale = self.filter_length / self.hop_length
53
+ fourier_basis = np.fft.fft(np.eye(self.filter_length))
54
+
55
+ cutoff = int((self.filter_length / 2 + 1))
56
+ fourier_basis = np.vstack([np.real(fourier_basis[:cutoff, :]),
57
+ np.imag(fourier_basis[:cutoff, :])])
58
+
59
+ forward_basis = torch.FloatTensor(fourier_basis[:, None, :])
60
+ inverse_basis = torch.FloatTensor(
61
+ np.linalg.pinv(scale * fourier_basis).T[:, None, :])
62
+
63
+ if window is not None:
64
+ assert(filter_length >= win_length)
65
+ # get window and zero center pad it to filter_length
66
+ fft_window = get_window(window, win_length, fftbins=True)
67
+ fft_window = pad_center(fft_window, filter_length)
68
+ fft_window = torch.from_numpy(fft_window).float()
69
+
70
+ # window the bases
71
+ forward_basis *= fft_window
72
+ inverse_basis *= fft_window
73
+
74
+ self.register_buffer('forward_basis', forward_basis.float())
75
+ self.register_buffer('inverse_basis', inverse_basis.float())
76
+
77
+ def transform(self, input_data):
78
+ num_batches = input_data.size(0)
79
+ num_samples = input_data.size(1)
80
+
81
+ self.num_samples = num_samples
82
+
83
+ # similar to librosa, reflect-pad the input
84
+ input_data = input_data.view(num_batches, 1, num_samples)
85
+ input_data = F.pad(
86
+ input_data.unsqueeze(1),
87
+ (int(self.filter_length / 2), int(self.filter_length / 2), 0, 0),
88
+ mode='reflect')
89
+ input_data = input_data.squeeze(1)
90
+
91
+ forward_transform = F.conv1d(
92
+ input_data,
93
+ Variable(self.forward_basis, requires_grad=False),
94
+ stride=self.hop_length,
95
+ padding=0)
96
+
97
+ cutoff = int((self.filter_length / 2) + 1)
98
+ real_part = forward_transform[:, :cutoff, :]
99
+ imag_part = forward_transform[:, cutoff:, :]
100
+
101
+ magnitude = torch.sqrt(real_part**2 + imag_part**2)
102
+ phase = torch.autograd.Variable(
103
+ torch.atan2(imag_part.data, real_part.data))
104
+
105
+ return magnitude, phase
106
+
107
+ def inverse(self, magnitude, phase):
108
+ recombine_magnitude_phase = torch.cat(
109
+ [magnitude*torch.cos(phase), magnitude*torch.sin(phase)], dim=1)
110
+
111
+ inverse_transform = F.conv_transpose1d(
112
+ recombine_magnitude_phase,
113
+ Variable(self.inverse_basis, requires_grad=False),
114
+ stride=self.hop_length,
115
+ padding=0)
116
+
117
+ if self.window is not None:
118
+ window_sum = window_sumsquare(
119
+ self.window, magnitude.size(-1), hop_length=self.hop_length,
120
+ win_length=self.win_length, n_fft=self.filter_length,
121
+ dtype=np.float32)
122
+ # remove modulation effects
123
+ approx_nonzero_indices = torch.from_numpy(
124
+ np.where(window_sum > tiny(window_sum))[0])
125
+ window_sum = torch.autograd.Variable(
126
+ torch.from_numpy(window_sum), requires_grad=False)
127
+ window_sum = window_sum.cuda() if magnitude.is_cuda else window_sum
128
+ inverse_transform[:, :, approx_nonzero_indices] /= window_sum[approx_nonzero_indices]
129
+
130
+ # scale by hop ratio
131
+ inverse_transform *= float(self.filter_length) / self.hop_length
132
+
133
+ inverse_transform = inverse_transform[:, :, int(self.filter_length/2):]
134
+ inverse_transform = inverse_transform[:, :, :-int(self.filter_length/2):]
135
+
136
+ return inverse_transform
137
+
138
+ def forward(self, input_data):
139
+ self.magnitude, self.phase = self.transform(input_data)
140
+ reconstruction = self.inverse(self.magnitude, self.phase)
141
+ return reconstruction
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/symbols.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ from https://github.com/keithito/tacotron """
2
+
3
+ '''
4
+ Defines the set of symbols used in text input to the model.
5
+
6
+ The default is a set of ASCII characters that works well for English or text that has been run through Unidecode. For other data, you can modify _characters. See TRAINING_DATA.md for details. '''
7
+ from . import cmudict
8
+
9
+ _pad = '_'
10
+ _punctuation = '!\'(),.:;? '
11
+ _special = '-'
12
+ _letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
13
+
14
+ # Prepend "@" to ARPAbet symbols to ensure uniqueness (some are the same as uppercase letters):
15
+ _arpabet = ['@' + s for s in cmudict.valid_symbols]
16
+
17
+ # Export all symbols:
18
+ symbols = [_pad] + list(_special) + list(_punctuation) + list(_letters) + _arpabet
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/text.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ from https://github.com/keithito/tacotron """
2
+ import numpy as np
3
+ import re
4
+ from . import cleaners
5
+ from .symbols import symbols
6
+
7
+
8
+ # Mappings from symbol to numeric ID and vice versa:
9
+ _symbol_to_id = {s: i for i, s in enumerate(symbols)}
10
+ _id_to_symbol = {i: s for i, s in enumerate(symbols)}
11
+
12
+ # Regular expression matching text enclosed in curly braces:
13
+ _curly_re = re.compile(r'(.*?)\{(.+?)\}(.*)')
14
+
15
+ # Special symbols
16
+ SOS_TOK = '<s>'
17
+ EOS_TOK = '</s>'
18
+
19
+ def text_to_sequence(text, cleaner_names):
20
+ '''Converts a string of text to a sequence of IDs corresponding to the symbols in the text.
21
+
22
+ The text can optionally have ARPAbet sequences enclosed in curly braces embedded
23
+ in it. For example, "Turn left on {HH AW1 S S T AH0 N} Street."
24
+
25
+ Args:
26
+ text: string to convert to a sequence
27
+ cleaner_names: names of the cleaner functions to run the text through
28
+
29
+ Returns:
30
+ List of integers corresponding to the symbols in the text
31
+ '''
32
+ sequence = []
33
+
34
+ # Check for curly braces and treat their contents as ARPAbet:
35
+ while len(text):
36
+ m = _curly_re.match(text)
37
+ if not m:
38
+ sequence += _symbols_to_sequence(_clean_text(text, cleaner_names))
39
+ break
40
+ sequence += _symbols_to_sequence(_clean_text(m.group(1), cleaner_names))
41
+ sequence += _arpabet_to_sequence(m.group(2))
42
+ text = m.group(3)
43
+
44
+ return sequence
45
+
46
+
47
+ def sample_code_chunk(code, size):
48
+ assert(size > 0 and size <= len(code))
49
+ start = np.random.randint(len(code) - size + 1)
50
+ end = start + size
51
+ return code[start:end], start, end
52
+
53
+
54
+ def code_to_sequence(code, code_dict, collapse_code):
55
+ if collapse_code:
56
+ prev_c = None
57
+ sequence = []
58
+ for c in code:
59
+ if c in code_dict and c != prev_c:
60
+ sequence.append(code_dict[c])
61
+ prev_c = c
62
+ else:
63
+ sequence = [code_dict[c] for c in code if c in code_dict]
64
+ if len(sequence) < 0.95 * len(code):
65
+ print('WARNING : over 5%% codes are OOV')
66
+
67
+ return sequence
68
+
69
+
70
+ def sequence_to_text(sequence):
71
+ '''Converts a sequence of IDs back to a string'''
72
+ result = ''
73
+ for symbol_id in sequence:
74
+ if symbol_id in _id_to_symbol:
75
+ s = _id_to_symbol[symbol_id]
76
+ # Enclose ARPAbet back in curly braces:
77
+ if len(s) > 1 and s[0] == '@':
78
+ s = '{%s}' % s[1:]
79
+ result += s
80
+ return result.replace('}{', ' ')
81
+
82
+
83
+ def sequence_to_code(sequence, code_dict):
84
+ '''Analogous to sequence_to_text'''
85
+ id_to_code = {i: c for c, i in code_dict.items()}
86
+ return ' '.join([id_to_code[i] for i in sequence])
87
+
88
+
89
+ def _clean_text(text, cleaner_names):
90
+ for name in cleaner_names:
91
+ cleaner = getattr(cleaners, name)
92
+ if not cleaner:
93
+ raise Exception('Unknown cleaner: %s' % name)
94
+ text = cleaner(text)
95
+ return text
96
+
97
+
98
+ def _symbols_to_sequence(symbols):
99
+ return [_symbol_to_id[s] for s in symbols if _should_keep_symbol(s)]
100
+
101
+
102
+ def _arpabet_to_sequence(text):
103
+ return _symbols_to_sequence(['@' + s for s in text.split()])
104
+
105
+
106
+ def _should_keep_symbol(s):
107
+ return s in _symbol_to_id and s != '_' and s != '~'
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/utils.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import collections
7
+ import io
8
+ import json
9
+ import librosa
10
+ import numpy as np
11
+ import soundfile as sf
12
+ import time
13
+ import torch
14
+ from scipy.io.wavfile import read
15
+ from .text import SOS_TOK, EOS_TOK
16
+
17
+
18
+ def get_mask_from_lengths(lengths):
19
+ max_len = torch.max(lengths).item()
20
+ ids = torch.arange(0, max_len, out=torch.cuda.LongTensor(max_len))
21
+ mask = (ids < lengths.unsqueeze(1))
22
+ return mask
23
+
24
+
25
+ def load_wav_to_torch(full_path, sr=None):
26
+ data, sr = librosa.load(full_path, sr=sr)
27
+ data = np.clip(data, -1, 1) # potentially out of [-1, 1] due to resampling
28
+ data = data * 32768.0 # match values loaded by scipy
29
+ return torch.FloatTensor(data.astype(np.float32)), sr
30
+
31
+
32
+ def read_binary_audio(bin_data, tar_sr=None):
33
+ """
34
+ read binary audio (`bytes` or `uint8` `numpy.ndarray`) to `float32`
35
+ `numpy.ndarray`
36
+
37
+ RETURNS:
38
+ data (np.ndarray) : audio of shape (n,) or (2, n)
39
+ tar_sr (int) : sample rate
40
+ """
41
+ data, ori_sr = sf.read(io.BytesIO(bin_data), dtype='float32')
42
+ data = data.T
43
+ if (tar_sr is not None) and (ori_sr != tar_sr):
44
+ data = librosa.resample(data, ori_sr, tar_sr)
45
+ else:
46
+ tar_sr = ori_sr
47
+ data = np.clip(data, -1, 1)
48
+ data = data * 32768.0
49
+ return torch.FloatTensor(data.astype(np.float32)), tar_sr
50
+
51
+
52
+ def load_filepaths_and_text(filename):
53
+ with open(filename, encoding='utf-8') as f:
54
+ data = [json.loads(line.rstrip()) for line in f]
55
+ return data
56
+
57
+
58
+ def to_gpu(x):
59
+ x = x.contiguous()
60
+
61
+ if torch.cuda.is_available():
62
+ x = x.cuda(non_blocking=True)
63
+ return torch.autograd.Variable(x)
64
+
65
+
66
+ def load_code_dict(path, add_sos=False, add_eos=False):
67
+ if not path:
68
+ return {}
69
+
70
+ with open(path, 'r') as f:
71
+ codes = ['_'] + [line.rstrip() for line in f] # '_' for pad
72
+ code_dict = {c: i for i, c in enumerate(codes)}
73
+
74
+ if add_sos:
75
+ code_dict[SOS_TOK] = len(code_dict)
76
+ if add_eos:
77
+ code_dict[EOS_TOK] = len(code_dict)
78
+ assert(set(code_dict.values()) == set(range(len(code_dict))))
79
+
80
+ return code_dict
81
+
82
+
83
+ def load_obs_label_dict(path):
84
+ if not path:
85
+ return {}
86
+ with open(path, 'r') as f:
87
+ obs_labels = [line.rstrip() for line in f]
88
+ return {c: i for i, c in enumerate(obs_labels)}
89
+
90
+
91
+ # A simple timer class inspired from `tnt.TimeMeter`
92
+ class CudaTimer:
93
+ def __init__(self, keys):
94
+ self.keys = keys
95
+ self.reset()
96
+
97
+ def start(self, key):
98
+ s = torch.cuda.Event(enable_timing=True)
99
+ s.record()
100
+ self.start_events[key].append(s)
101
+ return self
102
+
103
+ def stop(self, key):
104
+ e = torch.cuda.Event(enable_timing=True)
105
+ e.record()
106
+ self.end_events[key].append(e)
107
+ return self
108
+
109
+ def reset(self):
110
+ self.start_events = collections.defaultdict(list)
111
+ self.end_events = collections.defaultdict(list)
112
+ self.running_times = collections.defaultdict(float)
113
+ self.n = collections.defaultdict(int)
114
+ return self
115
+
116
+ def value(self):
117
+ self._synchronize()
118
+ return {k: self.running_times[k] / self.n[k] for k in self.keys}
119
+
120
+ def _synchronize(self):
121
+ torch.cuda.synchronize()
122
+ for k in self.keys:
123
+ starts = self.start_events[k]
124
+ ends = self.end_events[k]
125
+ if len(starts) == 0:
126
+ raise ValueError("Trying to divide by zero in TimeMeter")
127
+ if len(ends) != len(starts):
128
+ raise ValueError("Call stop before checking value!")
129
+ time = 0
130
+ for start, end in zip(starts, ends):
131
+ time += start.elapsed_time(end)
132
+ self.running_times[k] += time * 1e-3
133
+ self.n[k] += len(starts)
134
+ self.start_events = collections.defaultdict(list)
135
+ self.end_events = collections.defaultdict(list)
136
+
137
+
138
+ # Used to measure the time taken for multiple events
139
+ class Timer:
140
+ def __init__(self, keys):
141
+ self.keys = keys
142
+ self.n = {}
143
+ self.running_time = {}
144
+ self.total_time = {}
145
+ self.reset()
146
+
147
+ def start(self, key):
148
+ self.running_time[key] = time.time()
149
+ return self
150
+
151
+ def stop(self, key):
152
+ self.total_time[key] = time.time() - self.running_time[key]
153
+ self.n[key] += 1
154
+ self.running_time[key] = None
155
+ return self
156
+
157
+ def reset(self):
158
+ for k in self.keys:
159
+ self.total_time[k] = 0
160
+ self.running_time[k] = None
161
+ self.n[k] = 0
162
+ return self
163
+
164
+ def value(self):
165
+ vals = {}
166
+ for k in self.keys:
167
+ if self.n[k] == 0:
168
+ raise ValueError("Trying to divide by zero in TimeMeter")
169
+ else:
170
+ vals[k] = self.total_time[k] / self.n[k]
171
+ return vals
omni_speech/infer/fairseq/examples/textless_nlp/gslm/unit2speech/tacotron2/waveglow_denoiser.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import sys
2
+ # sys.path.append('tacotron2')
3
+ import torch
4
+ from .layers import STFT
5
+
6
+
7
+ class Denoiser(torch.nn.Module):
8
+ """ Removes model bias from audio produced with waveglow """
9
+
10
+ def __init__(self, waveglow, filter_length=1024, n_overlap=4,
11
+ win_length=1024, mode='zeros'):
12
+ super(Denoiser, self).__init__()
13
+ self.stft = STFT(filter_length=filter_length,
14
+ hop_length=int(filter_length/n_overlap),
15
+ win_length=win_length).cuda()
16
+ if mode == 'zeros':
17
+ mel_input = torch.zeros(
18
+ (1, 80, 88),
19
+ dtype=waveglow.upsample.weight.dtype,
20
+ device=waveglow.upsample.weight.device)
21
+ elif mode == 'normal':
22
+ mel_input = torch.randn(
23
+ (1, 80, 88),
24
+ dtype=waveglow.upsample.weight.dtype,
25
+ device=waveglow.upsample.weight.device)
26
+ else:
27
+ raise Exception("Mode {} if not supported".format(mode))
28
+
29
+ with torch.no_grad():
30
+ bias_audio = waveglow.infer(mel_input, sigma=0.0).float()
31
+ bias_spec, _ = self.stft.transform(bias_audio)
32
+
33
+ self.register_buffer('bias_spec', bias_spec[:, :, 0][:, :, None])
34
+
35
+ def forward(self, audio, strength=0.1):
36
+ audio_spec, audio_angles = self.stft.transform(audio.cuda().float())
37
+ audio_spec_denoised = audio_spec - self.bias_spec * strength
38
+ audio_spec_denoised = torch.clamp(audio_spec_denoised, 0.0)
39
+ audio_denoised = self.stft.inverse(audio_spec_denoised, audio_angles)
40
+ return audio_denoised
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/README.md ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Text-Free Prosody-Aware Generative Spoken Language Modeling
2
+
3
+ This folder contains code and recipes to reproduce results reported in a paper _Text-Free Prosody-Aware Generative Spoken Language Modeling_,
4
+ Eugene Kharitonov*, Ann Lee*, Adam Polyak, Yossi Adi, Jade Copet, Kushal Lakhotia, Tu-Anh Nguyen, Morgane Rivière, Abdelrahman Mohamed, Emmanuel Dupoux, Wei-Ning Hsu, 2021. arxiv/2109.03264 [[arxiv]](https://arxiv.org/abs/2109.03264).
5
+
6
+ `*` denotes equal contribution.
7
+
8
+ You can find demo samples [[here]](https://speechbot.github.io/pgslm/index.html).
9
+
10
+ <details>
11
+ <summary>If you find this code useful, please consider citing our work using this bibtex </summary>
12
+
13
+ ```
14
+ @misc{Kharitonov2021,
15
+ title={Text-Free Prosody-Aware Generative Spoken Language Modeling},
16
+ author={Eugene Kharitonov and Ann Lee and Adam Polyak and Yossi Adi and Jade Copet and Kushal Lakhotia and Tu-Anh Nguyen and Morgane Rivière and Abdelrahman Mohamed and Emmanuel Dupoux and Wei-Ning Hsu},
17
+ year={2021},
18
+ eprint={2109.03264},
19
+ archivePrefix={arXiv},
20
+ primaryClass={cs.CL}
21
+ }
22
+ ```
23
+ </details>
24
+
25
+
26
+ ## Additional requirements
27
+ Three packages are required in addition to fairseq, they are installable with pip:
28
+ ```bash
29
+ pip install AMFM-decompy SoundFile scipy sklearn torchaudio npy-append-array
30
+ ```
31
+
32
+ ## Data preprocessing
33
+
34
+ ### Prepare unit pseudo-text transcriptions of the audio
35
+ To get unit trascripts of the speech data we rely on the preprocessing steps of [GSLM](https://github.com/pytorch/fairseq/tree/main/examples/textless_nlp/gslm/speech2unit/) work.
36
+
37
+ Firstly, we will need to prepare manifest files for the dataset we want to preprocess
38
+ ```
39
+ mkdir manifests/
40
+ python examples/wav2vec/wav2vec_manifest.py --valid-percent=0.0 $DATA_PATH --dest=manifests/train/
41
+ ```
42
+ Next, we need a pre-trained HuBERT-base-ls960 model [[download]](https://dl.fbaipublicfiles.com/hubert/hubert_base_ls960.pt) and a corresponding kmeans-100 quantizer [[download]](https://dl.fbaipublicfiles.com/textless_nlp/gslm/hubert/km100/km.bin). Having those we can quantize the dataset:
43
+ ```
44
+ python examples/textless_nlp/gslm/speech2unit/clustering/quantize_with_kmeans.py \
45
+ --feature_type hubert \
46
+ --kmeans_model_path km.bin \
47
+ --acoustic_model_path hubert_base_ls960.pt \
48
+ --layer 6 \
49
+ --manifest_path manifests/train/train.tsv \
50
+ --out_quantized_file_path manifests/train/units
51
+ ```
52
+
53
+ Finally, by running
54
+ ```
55
+ python examples/textless_nlp/pgslm/scripts/join_units_manifest.py --manifest=manifests/train/train.tsv --units=manifests/train/units --output=train.txt
56
+ ```
57
+ We will get the training data description `train.txt` in the format that pGSLM expects. The above steps have to be repeated for
58
+ dev/test sets. Importantly, we rely on an assumption that the directories are structured as in LibriSpeech, i.e. the file paths follow the
59
+ `<spk_id>/<session_id>/<sample_id>.wav` format.
60
+
61
+ ### Preprocess data for pGSLM
62
+ The very first step is to obtain the F0 quantization bins.
63
+ Assume the vocoder training manifest is `vocoder_train.txt` (in pGSLM data format prepared with the same process above).
64
+ We prepare the quantized F0 from the vocoder training data by running
65
+ ```sh
66
+ bash examples/textless_nlp/pgslm/scripts/prepare_f0_quantization.sh \
67
+ vocoder_train.txt <sample_rate> 32 <preprocessed_dir> <output_prefix> # we use 32 bins in the paper
68
+ ```
69
+ - `<sample_rate>`: sampling rate of the audio files in the manifest
70
+ - `<preprocessed_dir>`: where to output the output files
71
+ - `<output_prefix>`: prefix of the output files
72
+
73
+ The script will generate
74
+ - `<output_prefix>.f0_stat.pt`: the speaker-level F0 statistics, which can be used in vocoder training
75
+ - `<output_prefix>_mean_norm_log_f0_bin.th`: the quantized F0, which should be used in `prepare_data.sh` below
76
+
77
+ **Note:** See "Pre-trained models" for the pre-computed speaker-level F0 statistics and quantized F0 bins. We suggest using the pre-computed statistics for the data preparation below in order to take advantage of the pre-trained vocoder for waveform generation.
78
+
79
+ Next prepare the pGSLM data.
80
+ Assume train/valid/test manifests are `{train,valid,test}.txt`.
81
+ Here is an example of how to preprocess data:
82
+
83
+ ```sh
84
+ bash examples/textless_nlp/pgslm/scripts/prepare_data.sh \
85
+ train.txt valid.txt test.txt <n_unit> <hop_size> <sample_rate> \
86
+ <preprocessed_dir>/<output_prefix>_mean_norm_log_f0_bin.th <preprocessed_dir>
87
+ ```
88
+ - `<n_unit>`: discrete unit vocabulary size (we used a kmeans quantizer with the number of units equal to 100 in the example above)
89
+ - `<hop_size>`: downsampling rate relative to the waveform (e.g., 320 for HuBERT units)
90
+ - `<sample_rate>`: sampling rate of the audio files in the manifest
91
+ - `<preprocessed_dir>`: where to output the preprocessed files
92
+
93
+ This will create the dataset json config used for the next section at
94
+ `<preprocessed_dir>/data_config.json`.
95
+
96
+ Note that the example script uses only one thread to compute F0, which can take
97
+ _very long_ for preprocessing large datasets. It is suggested to distribute
98
+ jobs over multiple nodes/processes with `--nshards=x` and `--rank=z` (where z is
99
+ in [1, x]) in `preprocess_f0.py`, and set `--nshards_list=x` in
100
+ `prepare_data.py` correspondingly to collect sharded F0 data.
101
+
102
+ Now, everything is ready for training a model.
103
+
104
+ ## Training Multi-Stream Transformer Unit Language Model (MS-TLM)
105
+
106
+ Below is an example command that trains Multi-Stream Transformer Language Model (MS-TLM) on a prepared dataset:
107
+ ```bash
108
+ DATASET=data_config.json
109
+
110
+ fairseq-train $DATASET \
111
+ --task=speech_unit_modeling \
112
+ --arch="transformer_ulm_tiny" \
113
+ --criterion=speech_unit_lm_criterion \
114
+ --share-decoder-input-output-embed \
115
+ --dropout=0.1 \
116
+ --attention-dropout=0.1 \
117
+ --optimizer="adam" \
118
+ --adam-betas="(0.9, 0.98)" \
119
+ --clip-norm=1.0 \
120
+ --lr=0.0005 \
121
+ --lr-scheduler="inverse_sqrt" \
122
+ --warmup-updates=4000 \
123
+ --warmup-init-lr=1e-07 \
124
+ --tokens-per-sample=3072 \
125
+ --max-tokens=3072 \
126
+ --update-freq=4 \
127
+ --max-epoch=70 \
128
+ --num-workers=0 \
129
+ --skip-invalid-size-inputs-valid-test \
130
+ --loss-weights="1.0;0.5;0.0" \
131
+ --ignore-f0-input \
132
+ --checkpoint-activations \
133
+ --fp16 \
134
+ --max-target-positions=4096 \
135
+ --stream-shifts="1,1" \
136
+ --log-f0 --normalize-f0-mean --interpolate-f0 \
137
+ --ignore-unused-valid-subsets \
138
+ --discrete-duration --discrete-f0
139
+ ```
140
+
141
+ Some of the important parameters that are specific to MS-TLM:
142
+ * `arch`: specifies the Transformer architecture used. Supported options are:
143
+ * `transformer_ulm_tiny` - a tiny model that can be used for debugging; it has 2 layers, 1 attention head, FFN and embedding dimensions of 64,
144
+ * `transformer_ulm` - a base model with 6 layers, 8 heads, embedding dimension 512, and FFN dimensionality of 2048,
145
+ * `transformer_ulm_big` - the largest model we experiment with in the paper: 12-layer/16 heads, 1024/4096 embedding and FFN dimensions;
146
+ * `loss-weights`: this parameter sets importance weights (must be non-negative) for the components of the loss that correspond to unit, duration, and F0 streams. To turn off a component of the loss, its weight has to be set to 0. For instance, to predict only unit stream the parameter should be set to "1;0;0";
147
+ * `stream-shifts`: specifies relative shifts of the two prosodic streams w.r.t. the unit stream (duration and F0, respectively). No shift corresponds to "0,0";
148
+ * `ignore-duration-input`/`ignore-f0-input`: setting these flags would zero-out correpsonding input streams;
149
+ * `max-token-duration`: duration values would be max-capped by the specified value;
150
+ * `discrete-duration`/`discrete-f0`: whether duration and F0 streams should be quantized;
151
+ * `log_f0`, `normalize-f0-mean`, `normalize-f0-std`, `interpolate-f0`: configure how F0 stream is treated. `log_f0` sets up modelling in the log-space, `normalize-f0-mean`/`normalize-f0-std` control per-speaker normalization, and `interpolate-f0` enables F0 interpolation for unvoiced regions where F0 was set to 0,
152
+ * `mask-dur-prob`, `mask-f0-prob`, `mask-dur-seg-prob`, `mask-f0-seg-prob`, `mask-unit-seg-prob`, `mask-unit-seg-leng`: this family of parameters sets the probababilities of masking individual steps and spans on each stream as well as lengths of the maked spans.
153
+
154
+
155
+ ## Pre-trained models
156
+ ### MS-TLM
157
+ Below you can find checkpoints for four best-performing models from the paper (IDs 9..12 in Table 1). These models are trained on Hubert-100 transcripts of the LibriLight-6K dataset. They have the prosody streams shifted by 1 w.r.t. the unit stream. All models predict all three streams (units, duration, and F0), but two
158
+ of them only have unit steam in their input.
159
+
160
+ | | Continuous prosody | Quantized prosody |
161
+ |-------------------|--------------------|-------------------|
162
+ | No prosody input | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/ulm_checkpoints/continuous_no_prosody_shift_1_1.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/ulm_checkpoints/discrete_no_prosody_shift_1_1.pt) |
163
+ | Has prosody input | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/ulm_checkpoints/continuous_prosody_shift_1_1.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/ulm_checkpoints/discrete_prosody_shift_1_1.pt)|
164
+
165
+ The optimal per-stream sampling temperatures/scaling parameters that we have identified for these models, in the (`T-token, T-duration, T-f0`) format:
166
+
167
+ | | Continuous prosody | Quantized prosody |
168
+ |-------------------|--------------------|-------------------|
169
+ | No prosody input | 0.7, 0.125, 0.0003125| 0.7, 0.25, 0.5 |
170
+ | Has prosody input | 0.7, 0.125, 0.00125 | 0.7, 0.25, 0.7 |
171
+
172
+ ## Vocoder
173
+ | Units | Prosody | F0 stats | Checkpoint | Config |
174
+ |-------------------|---------|--------------|------------|--------|
175
+ | HuBERT-base-ls960, kmeans-100 | [[Quantized 32 bins]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/mean_norm_log_f0_seg_bin.th) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/f0_stats.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/naive_quant_32_norm_log_seg_hubert/checkpoint.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/naive_quant_32_norm_log_seg_hubert/config.json) |
176
+ | HuBERT-base-ls960, kmeans-100 | Continuous | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/f0_stats.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/mean_norm_log_f0_hubert/checkpoint.pt) | [[download]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/mean_norm_log_f0_hubert/config.json) |
177
+
178
+
179
+ ## Evaluating a trained model
180
+ Evaluation is done with the `eval/cont_metrics.py` scripts. As described in the paper, there are several metrics used.
181
+
182
+ **Teacher-forced metrics**
183
+ ```bash
184
+ SET=valid
185
+ CHECKPOINT_PATH=discrete_prosody_shift_1_1.pt
186
+ DATA=data_config.json
187
+
188
+ python examples/textless_nlp/pgslm/eval/cont_metrics.py $DATA \
189
+ --metric=teacher_force_everything \
190
+ --path=$CHECKPOINT_PATH \
191
+ --batch-size=16 \
192
+ --fp16 \
193
+ --seed=111 \
194
+ --eval-subset=$SET \
195
+ --f0-discretization-bounds=mean_norm_log_f0_seg_bin.th --dequantize-prosody
196
+ ```
197
+ (Using this command, our provided `discrete_prosody_shift_1_1.pt` checkpoint should produce `{'token_loss': 1.408..., 'duration_loss': 0.5424..., 'f0_loss': 0.0474...}` on LibriSpeech dev-clean).
198
+
199
+ The parameters `--f0-discretization-bounds=mean_norm_log_f0_seg_bin.th --dequantize-prosody` are specific for quantized-prosody models. They signal that the prosody streams must be decoded into the continuous domain before calculating correlation. It is the same `*_mean_norm_log_f0_bin.th` file as we prepared before.
200
+ The `mean_norm_log_f0_seg_bin.th` file we used with the pre-trained models can be downloaded [[here]](https://dl.fbaipublicfiles.com/textless_nlp/pgslm/vocoder/blizzard2013/mean_norm_log_f0_seg_bin.th).
201
+
202
+
203
+ **Consistency (aka Correlation) metrics**
204
+
205
+ The following command estimates correlation between mean values of the F0 stream in the prompt and in the generated continuation (unit and duration steams are fixed).
206
+
207
+ ```bash
208
+ T_F0=0.7
209
+ EXPLOSION=20
210
+ SET=test
211
+ CHECKPOINT_PATH=discrete_prosody_shift_1_1.pt
212
+ DATA=data_config.json
213
+
214
+ python examples/textless_nlp/pgslm/eval/cont_metrics.py $DATA \
215
+ --prefix-length=150 \
216
+ --metric=correlation \
217
+ --path=$CHECKPOINT_PATH \
218
+ --batch-size=16 \
219
+ --fp16 \
220
+ --seed=111 \
221
+ --teacher-force-tokens \
222
+ --teacher-force-duration \
223
+ --min-length=300 \
224
+ --batch-explosion-rate=$EXPLOSION \
225
+ --T-f0=$T_F0 \
226
+ --eval-subset=$SET \
227
+ --f0-discretization-bounds=mean_norm_log_f0_seg_bin.th \
228
+ --dequantize-prosody --n-workers=8
229
+ ```
230
+ (Using this command, our provided `discrete_prosody_shift_1_1.pt` checkpoint should produce `{...'F0 corr': 0.315 ..}` on LibriSpeech test-clean).
231
+
232
+ * By using flags `--teacher-force-tokens, --teacher-force-duration, --teacher-force-f0` one can calculate correlations along each stream while having other two streams fixed to ground-truth values (or freeze all three streams to get ground-truth correlation values);
233
+ * The parameters `T-f0`, `T-duration`, and `T-token` specify per-stream temperatures and, in the case of continuous-valued prosody, scaling parameter of the corresponding Laplace distribution (setting a temperature to 0 will enforce greedy sampling);
234
+ * `min-length` filters out sequences that are shorter then 300 duration units (i.e. 6s in the case of Hubert units);
235
+ * `prefix-length` specifies that we want to use first 150 duration units are prompt (i.e. 3s in the case of Hubert units)
236
+
237
+
238
+ **Correctness (aka Continuation) and Expressiveness (aka Std) metrics**
239
+
240
+ By running the following command, we can get minMAE and Std for the log-F0 stream for the model with quantized prosody.
241
+ ```bash
242
+ DATA=data_config.json
243
+ EXPLOSION=20
244
+ SET=test
245
+ CHECKPOINT_PATH=discrete_prosody_shift_1_1.pt
246
+ T_F0=0.7
247
+
248
+ python examples/textless_nlp/pgslm/eval/cont_metrics.py $DATA \
249
+ --prefix-length=150 \
250
+ --metric=continuation \
251
+ --path=$CHECKPOINT_PATH \
252
+ --batch-size=16 \
253
+ --fp16 \
254
+ --seed=111 \
255
+ --batch-explosion-rate=$EXPLOSION \
256
+ --teacher-force-tokens \
257
+ --teacher-force-duration \
258
+ --T-f0=$T_F0 \
259
+ --eval-subset=$SET \
260
+ --f0-discretization-bounds=mean_norm_log_f0_seg_bin.th --dequantize-prosody
261
+ ```
262
+ (Using this command, our provided `discrete_prosody_shift_1_1.pt` checkpoint should produce `{...'F0 MAE': 0.0772, 'F0 Std': 0.1489...}` on LibriSpeech test-clean).
263
+
264
+ Again, by setting `--teacher-force-tokens, --teacher-force-duration, --teacher-force-f0` we can calculate Token BLEU for the token stream (when `--teacher-force-duration` & `--teacher-force-f0` are on) and per-stream min MAE for each prosody stream individually.
265
+
266
+ Finally, `cont_metrics.py` allows to specify the number of workers (e.g., `n-workers=8`) which allows to speed up the computation by spreading multiple worker processes
267
+ over the available GPUs.
268
+
269
+ **Cont Word BLEU**
270
+
271
+ We used the code and the evaluation protocol of [(Lakhotia et al., 2021)](https://arxiv.org/abs/2102.01192).
272
+
273
+ ## Sampling from a trained model
274
+
275
+ To get (prompted or not) samples from a trained model it is enough to run `sample.py`:
276
+ ```bash
277
+ CHECKPOINT_PATH=checkpoints/checkpoint_best.pt
278
+ DATASET=examples/textless_nlp/pgslm/repro/dataset/data_config.json
279
+ python examples/textless_nlp/pgslm/sample/sample.py $DATASET \
280
+ --output=$SAMPLES \
281
+ --path=$CHECKPOINT_PATH \
282
+ --sampling \
283
+ --T-token=0.7 \
284
+ --T-duration=0.25 \
285
+ --T-f0=0.7 \
286
+ --max-length=500 \
287
+ --prefix-length=150 \
288
+ --subset=valid \
289
+ --seed=1 \
290
+ --match-duration \
291
+ --code-type=hubert \
292
+ --batch-explosion-rate=2
293
+ ```
294
+
295
+ Some useful parameters:
296
+ * `T-token`, `T-duration`, `T-f0` specify sampling temperature for the three streams. Setting a temperature to `0` switches sample to the greedy (argmax) one;
297
+ * `prefix-length`: length of the prompt, measured in timesteps (e.g. for Hubert (CPC) each timestep is 20 (10) ms);
298
+ * `subset`: which subset of the dataset to use as prompts (can be `train`, `valid`, `test`);
299
+ * `teacher-force-tokens`, `teacher-force-duration`, `teacher-force-f0`: if set, at each autoregressive step, ground-truth values replace the produced one;
300
+ * `short-curcuit`: replace sampling by ground-truth inputs;
301
+ * `match-duration`: forces the produced sample to have the same duration (in time), as the entire sequence (beyond the prompt if there is any);
302
+ * `batch-explosion-rate`: number of samples per prompt;
303
+ * `f0-discretization-bounds`: path to a file with quantization boundaries. If it is set, F0 values are de-quantized back to the continuous domain
304
+ (the model must be a quanized one);
305
+ * `max-length` sets the maximal number of segment steps to be produced.
306
+
307
+ Note that `sample.py` automatically uses all available GPUs, to avoid that please use environment variable `CUDA_VISIBLE_DEVICES`.
308
+
309
+ ## Vocoding samples
310
+ To generate audios for output from `sample.py` (`$IN_FILE`):
311
+ ```bash
312
+ python examples/textless_nlp/pgslm/generate_waveform.py \
313
+ --in-file=$IN_FILE \
314
+ --vocoder=$VODOER \
315
+ --vocoder-cfg=$VOCODER_CFG \
316
+ --results-path=$RESULTS_PATH
317
+ ```
318
+ See "Pre-trained model" for `$VOCODER` and `VOCODER_CFG`.
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/data_utils.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch
8
+
9
+ from tqdm import tqdm
10
+
11
+
12
+ class Stat:
13
+ def __init__(self, keep_raw=False):
14
+ self.x = 0.0
15
+ self.x2 = 0.0
16
+ self.z = 0.0 # z = logx
17
+ self.z2 = 0.0
18
+ self.n = 0.0
19
+ self.u = 0.0
20
+ self.keep_raw = keep_raw
21
+ self.raw = []
22
+
23
+ def update(self, new_x):
24
+ new_z = new_x.log()
25
+
26
+ self.x += new_x.sum()
27
+ self.x2 += (new_x**2).sum()
28
+ self.z += new_z.sum()
29
+ self.z2 += (new_z**2).sum()
30
+ self.n += len(new_x)
31
+ self.u += 1
32
+
33
+ if self.keep_raw:
34
+ self.raw.append(new_x)
35
+
36
+ @property
37
+ def mean(self):
38
+ return self.x / self.n
39
+
40
+ @property
41
+ def std(self):
42
+ return (self.x2 / self.n - self.mean**2) ** 0.5
43
+
44
+ @property
45
+ def mean_log(self):
46
+ return self.z / self.n
47
+
48
+ @property
49
+ def std_log(self):
50
+ return (self.z2 / self.n - self.mean_log**2) ** 0.5
51
+
52
+ @property
53
+ def n_frms(self):
54
+ return self.n
55
+
56
+ @property
57
+ def n_utts(self):
58
+ return self.u
59
+
60
+ @property
61
+ def raw_data(self):
62
+ assert self.keep_raw, "does not support storing raw data!"
63
+ return torch.cat(self.raw)
64
+
65
+
66
+ class F0Stat(Stat):
67
+ def update(self, new_x):
68
+ # assume unvoiced frames are 0 and consider only voiced frames
69
+ if new_x is not None:
70
+ super().update(new_x[new_x != 0])
71
+
72
+
73
+ def dump_speaker_f0_stat(speaker_to_f0_stat, out_prefix):
74
+ path = f"{out_prefix}.f0_stat.pt"
75
+ assert not os.path.exists(path)
76
+
77
+ d = {
78
+ speaker: {
79
+ "f0_mean": speaker_to_f0_stat[speaker].mean,
80
+ "f0_std": speaker_to_f0_stat[speaker].std,
81
+ "logf0_mean": speaker_to_f0_stat[speaker].mean_log,
82
+ "logf0_std": speaker_to_f0_stat[speaker].std_log,
83
+ }
84
+ for speaker in speaker_to_f0_stat
85
+ }
86
+ torch.save(d, path)
87
+
88
+ return d
89
+
90
+
91
+ def load_audio_path(path):
92
+ audio_paths = []
93
+ with open(path) as f:
94
+ for line in f.readlines():
95
+ sample = eval(line.strip())
96
+ audio_paths.append(sample["audio"])
97
+
98
+ return audio_paths
99
+
100
+
101
+ def load_f0(f0_dir, nshards):
102
+ path_to_f0 = {}
103
+ for rank in tqdm(range(1, nshards + 1), desc=f"load f0"):
104
+ f0_shard_path = f"{f0_dir}/f0_{rank}_{nshards}.pt"
105
+ shard_path_to_f0 = torch.load(f0_shard_path)
106
+ path_to_f0.update(shard_path_to_f0)
107
+ return path_to_f0
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/eval/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/eval/cont_metrics.py ADDED
@@ -0,0 +1,730 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 numpy as np
8
+ import scipy
9
+
10
+ import torch
11
+ import torch.multiprocessing as mp
12
+ from fairseq import checkpoint_utils, options
13
+ from fairseq.data.codedataset import CodeDataset, ExpressiveCodeDataConfig
14
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
15
+ from torch.utils.data import DataLoader, DistributedSampler
16
+ from fairseq.utils import move_to_cuda
17
+ from fairseq import utils
18
+ from fairseq.criterions.speech_ulm_criterion import nll_loss, mae_loss
19
+
20
+ import time
21
+ from types import SimpleNamespace
22
+
23
+ import sys, pathlib
24
+
25
+ sys.path.append(str(pathlib.Path(__file__).parent.parent.resolve()))
26
+
27
+ from naive_decoder import Naive_F0_Decoder
28
+ from inference_dataset import InferenceDataset, explode_batch
29
+ from sample.sample import do_sampling, TemperatureDecoder, FilterNamesDataset
30
+
31
+ try:
32
+ from nltk.translate.bleu_score import sentence_bleu
33
+ except ImportError:
34
+ print("Please install nltk: `pip install --user -U nltk`")
35
+ raise
36
+
37
+
38
+ @torch.no_grad()
39
+ def teacher_force_everything(
40
+ args, dataset, model, criterion, tgt_dict, rank, world_size
41
+ ):
42
+ prefix = args.prefix_length
43
+
44
+ f0_decoder = None
45
+ if args.dequantize_prosody:
46
+ assert dataset.discrete_f0
47
+ print("Reporting MAE for a discrete model")
48
+ f0_decoder = Naive_F0_Decoder(
49
+ args.f0_discretization_bounds, dataset.config.f0_vq_n_units
50
+ ).cuda()
51
+
52
+ dataset = InferenceDataset(
53
+ dataset,
54
+ prefix=args.prefix_length,
55
+ only_prefix=False,
56
+ filter_short=True,
57
+ presort_by_length=True,
58
+ )
59
+ sampler = (
60
+ None
61
+ if world_size == 1
62
+ else DistributedSampler(
63
+ dataset, num_replicas=world_size, rank=rank, shuffle=False
64
+ )
65
+ )
66
+ dataloader = DataLoader(
67
+ dataset,
68
+ args.batch_size,
69
+ shuffle=False,
70
+ collate_fn=dataset.collater,
71
+ sampler=sampler,
72
+ )
73
+
74
+ total_token_loss, total_duration_loss, total_f0_loss, total_tokens = (
75
+ 0.0,
76
+ 0.0,
77
+ 0.0,
78
+ 0.0,
79
+ )
80
+
81
+ i = 0
82
+ for batch in dataloader:
83
+ i += 1
84
+ batch = move_to_cuda(batch)
85
+ output = model(**batch["net_input"])
86
+
87
+ tokens, durations, f0 = output["token"], output["duration"], output["f0"]
88
+ durations, f0 = durations.squeeze(), f0.squeeze()
89
+
90
+ token_loss = nll_loss(
91
+ tokens[:, prefix - 1 :],
92
+ batch["target"][:, prefix - 1 :].contiguous(),
93
+ batch["mask"][:, prefix - 1 :].contiguous(),
94
+ reduce=True,
95
+ )
96
+
97
+ if args.dequantize_prosody:
98
+ durations = durations.argmax(dim=-1)
99
+ duration_loss = mae_loss(
100
+ durations[:, prefix - 1 :].contiguous().float(),
101
+ batch["dur_target"][:, prefix - 1 :].contiguous().float(),
102
+ batch["dur_mask"][:, prefix - 1 :].contiguous(),
103
+ reduce=True,
104
+ )
105
+ else:
106
+ duration_loss = criterion.dur_loss_fn(
107
+ durations[:, prefix - 1 :].contiguous(),
108
+ batch["dur_target"][:, prefix - 1 :].contiguous(),
109
+ batch["dur_mask"][:, prefix - 1 :].contiguous(),
110
+ reduce=True,
111
+ )
112
+
113
+ if f0_decoder:
114
+ f0 = f0.argmax(dim=-1)
115
+ f0 = f0_decoder(f0).squeeze(-1)
116
+
117
+ f0_target = batch["raw_f0"]
118
+ f0_loss = mae_loss(
119
+ f0[:, prefix - 1 :].contiguous(),
120
+ f0_target[:, prefix - 1 :].contiguous(),
121
+ batch["f0_mask"][:, prefix - 1 :].contiguous(),
122
+ reduce=True,
123
+ )
124
+ else:
125
+ f0_loss = criterion.f0_loss_fn(
126
+ f0[:, prefix - 1 :].contiguous(),
127
+ batch["f0_target"][:, prefix - 1 :].contiguous(),
128
+ batch["f0_mask"][:, prefix - 1 :].contiguous(),
129
+ reduce=True,
130
+ )
131
+
132
+ n_tokens = (~batch["dur_mask"])[:, prefix - 1 :].sum()
133
+
134
+ total_token_loss += token_loss.item()
135
+ total_duration_loss += duration_loss.item()
136
+ total_f0_loss += f0_loss.item()
137
+
138
+ total_tokens += n_tokens.item()
139
+ if args.debug and i > 5:
140
+ break
141
+
142
+ values = torch.tensor([total_token_loss, total_duration_loss, total_f0_loss])
143
+ normalizers = torch.tensor([total_tokens for _ in range(3)])
144
+
145
+ return values, normalizers
146
+
147
+
148
+ def get_bleu(produced_tokens, target_tokens, tgt_dict):
149
+ assert target_tokens.ndim == 1
150
+ assert produced_tokens.size(1) == target_tokens.size(0)
151
+
152
+ # we can have padding due to shifted channels
153
+ shift = 0
154
+ for token in reversed(target_tokens.cpu().tolist()):
155
+ if token in [tgt_dict.pad(), tgt_dict.eos()]:
156
+ shift += 1
157
+ else:
158
+ break
159
+ target_tokens = target_tokens[:-shift]
160
+ produced_tokens = produced_tokens[:, :-shift]
161
+
162
+ string_target = tgt_dict.string(target_tokens).split()
163
+ string_candidates = [
164
+ tgt_dict.string(produced_tokens[i, :]).split()
165
+ for i in range(produced_tokens.size(0))
166
+ ]
167
+
168
+ bleu3 = sentence_bleu(
169
+ references=string_candidates,
170
+ hypothesis=string_target,
171
+ weights=(1.0 / 3, 1.0 / 3, 1.0 / 3),
172
+ )
173
+ return bleu3
174
+
175
+
176
+ @torch.no_grad()
177
+ def continuation(args, dataset, model, criterion, tgt_dict, rank, world_size):
178
+ is_discrete_duration = dataset.discrete_dur
179
+ is_discrete_f0 = dataset.discrete_f0
180
+
181
+ f0_decoder = None
182
+ if args.dequantize_prosody:
183
+ assert dataset.discrete_f0
184
+ print("Reporting MAE F0 for a discrete model")
185
+ f0_decoder = Naive_F0_Decoder(
186
+ args.f0_discretization_bounds, dataset.config.f0_vq_n_units
187
+ ).cuda()
188
+
189
+ dataset = InferenceDataset(
190
+ dataset, args.prefix_length, filter_short=True, presort_by_length=True
191
+ )
192
+ sampler = (
193
+ None
194
+ if world_size == 1
195
+ else DistributedSampler(
196
+ dataset, num_replicas=world_size, rank=rank, shuffle=False
197
+ )
198
+ )
199
+ dataloader = DataLoader(
200
+ dataset,
201
+ batch_size=1,
202
+ shuffle=False,
203
+ collate_fn=dataset.collater,
204
+ sampler=sampler,
205
+ )
206
+
207
+ Ts = args.T_token, args.T_duration, args.T_f0
208
+ decoder = TemperatureDecoder(
209
+ Ts, discrete_dur=is_discrete_duration, discrete_f0=is_discrete_f0
210
+ )
211
+
212
+ running_stats = SimpleNamespace(
213
+ token_bleu=0.0,
214
+ duration_nll=0.0,
215
+ duration_mae=0.0,
216
+ f0_nll=0.0,
217
+ f0_mae=0.0,
218
+ n_tokens=0.0,
219
+ n_sentences=0.0,
220
+ f0_sum=0.0,
221
+ f0_sum_sq=0.0,
222
+ dur_sum=0.0,
223
+ dur_sum_sq=0.0,
224
+ )
225
+
226
+ for i, batch in enumerate(dataloader):
227
+ batch = explode_batch(batch, args.batch_explosion_rate)
228
+ bsz = batch["target"].size(0)
229
+
230
+ batch = move_to_cuda(batch)
231
+ prefix = batch["prefix"][0]
232
+
233
+ max_length_to_unroll = batch["target"].size(1)
234
+ prefix_length = batch["net_input"]["src_tokens"].size(1)
235
+ steps = max_length_to_unroll - prefix_length + 1
236
+
237
+ assert steps > 0
238
+ produced_tokens, produced_durations, produced_f0, outputs = do_sampling(
239
+ model,
240
+ batch,
241
+ tgt_dict.eos(),
242
+ decoder,
243
+ autoregressive_steps=steps,
244
+ teacher_force_tokens=args.teacher_force_tokens,
245
+ teacher_force_duration=args.teacher_force_duration,
246
+ teacher_force_f0=args.teacher_force_f0,
247
+ )
248
+
249
+ if args.teacher_force_tokens:
250
+ assert (produced_tokens[:, 1:] == batch["target"]).all()
251
+ if args.teacher_force_duration:
252
+ assert (produced_durations[:, 1:] == batch["dur_target"]).all()
253
+ if args.teacher_force_f0:
254
+ assert (produced_f0[:, 1:] == batch["f0_target"]).all()
255
+
256
+ dur_target = batch["dur_target"][:, prefix - 1 :].contiguous()
257
+ f0_target = batch["f0_target"][:, prefix - 1 :].contiguous()
258
+
259
+ f0_mask = batch["f0_mask"][:, prefix - 1 :].contiguous()
260
+ dur_mask = batch["dur_mask"][:, prefix - 1 :].contiguous()
261
+
262
+ duration_mae = mae_loss(
263
+ produced_durations[:, prefix:].float(),
264
+ dur_target.float(),
265
+ dur_mask,
266
+ reduce=False,
267
+ )
268
+ min_duration_mae = duration_mae.view(bsz, -1).sum(dim=-1).min(dim=0)[0]
269
+ running_stats.duration_mae += min_duration_mae
270
+
271
+ running_stats.dur_sum += (
272
+ produced_durations[:, prefix:].float() * (~dur_mask)
273
+ ).sum() / args.batch_explosion_rate
274
+ running_stats.dur_sum_sq += (
275
+ produced_durations[:, prefix:].float() * (~dur_mask)
276
+ ).pow(2.0).sum() / args.batch_explosion_rate
277
+
278
+ if is_discrete_duration:
279
+ duration_loss = criterion.dur_loss_fn(
280
+ torch.stack([x[1] for x in outputs], dim=1),
281
+ dur_target,
282
+ dur_mask,
283
+ reduce=False,
284
+ )
285
+ min_duration_loss = duration_loss.view(bsz, -1).sum(dim=-1).min(dim=0)[0]
286
+ running_stats.duration_nll += min_duration_loss
287
+
288
+ if f0_decoder: # can only exist for discrete F0 models
289
+ decoded_produced_f0 = f0_decoder(produced_f0[:, prefix:])
290
+ decoded_f0_target = batch["raw_f0"][:, prefix - 1 :].contiguous()
291
+
292
+ if produced_f0.ndim == 3:
293
+ decoded_produced_f0 = decoded_produced_f0.squeeze(2)
294
+ decoded_f0_target = decoded_f0_target.squeeze(2)
295
+
296
+ f0_mae = mae_loss(
297
+ decoded_produced_f0, decoded_f0_target, f0_mask, reduce=False
298
+ )
299
+ f0_mae = f0_mae.view(bsz, -1).sum(dim=-1).min(dim=0)[0]
300
+ running_stats.f0_mae += f0_mae
301
+
302
+ f0_loss = criterion.f0_loss_fn(
303
+ torch.stack([x[2] for x in outputs], dim=1),
304
+ f0_target.long(),
305
+ f0_mask,
306
+ reduce=False,
307
+ )
308
+ f0_loss = f0_loss.view(bsz, -1).sum(dim=-1).min(dim=0)[0]
309
+ running_stats.f0_nll += f0_loss
310
+
311
+ running_stats.f0_sum += (
312
+ decoded_produced_f0 * (~f0_mask)
313
+ ).sum() / args.batch_explosion_rate
314
+ running_stats.f0_sum_sq += (decoded_produced_f0 * (~f0_mask)).pow(
315
+ 2.0
316
+ ).sum() / args.batch_explosion_rate
317
+
318
+ else:
319
+ assert not is_discrete_duration
320
+
321
+ f0_loss = mae_loss(
322
+ produced_f0[:, prefix:], f0_target, f0_mask, reduce=False
323
+ )
324
+ f0_loss = f0_loss.view(bsz, -1).sum(dim=-1).min(dim=0)[0]
325
+ running_stats.f0_mae += f0_loss
326
+
327
+ running_stats.f0_sum += (
328
+ produced_f0[:, prefix:].sum() / args.batch_explosion_rate
329
+ )
330
+ running_stats.f0_sum_sq += (
331
+ produced_f0[:, prefix:].pow(2.0).sum() / args.batch_explosion_rate
332
+ )
333
+
334
+ running_stats.n_tokens += (~dur_mask)[0, ...].sum()
335
+
336
+ token_loss = get_bleu(
337
+ produced_tokens[:, prefix:], batch["target"][0, prefix - 1 :], tgt_dict
338
+ )
339
+ running_stats.token_bleu += token_loss
340
+ running_stats.n_sentences += 1
341
+
342
+ if args.debug:
343
+ break
344
+
345
+ values = torch.tensor(
346
+ [
347
+ running_stats.token_bleu,
348
+ running_stats.duration_nll,
349
+ running_stats.duration_mae,
350
+ running_stats.f0_nll,
351
+ running_stats.f0_mae,
352
+ running_stats.f0_sum,
353
+ running_stats.f0_sum_sq,
354
+ running_stats.dur_sum,
355
+ running_stats.dur_sum_sq,
356
+ ]
357
+ )
358
+ normalizers = torch.tensor(
359
+ [running_stats.n_sentences] + [running_stats.n_tokens] * 8
360
+ )
361
+
362
+ return values, normalizers
363
+
364
+
365
+ @torch.no_grad()
366
+ def correlation(args, dataset, model, criterion, tgt_dict, rank, world_size):
367
+ is_discrete_duration = dataset.discrete_dur
368
+ is_discrete_f0 = dataset.discrete_f0
369
+
370
+ f0_decoder = None
371
+ if is_discrete_f0:
372
+ assert dataset.discrete_f0
373
+ f0_decoder = Naive_F0_Decoder(
374
+ args.f0_discretization_bounds, dataset.config.f0_vq_n_units
375
+ ).cuda()
376
+
377
+ if is_discrete_f0:
378
+ assert f0_decoder # correlation on tokens is meaningless
379
+
380
+ dataset = InferenceDataset(
381
+ dataset,
382
+ args.prefix_length,
383
+ filter_short=True,
384
+ presort_by_length=True,
385
+ min_length=args.min_length,
386
+ )
387
+ sampler = (
388
+ None
389
+ if world_size == 1
390
+ else DistributedSampler(
391
+ dataset, num_replicas=world_size, rank=rank, shuffle=False
392
+ )
393
+ )
394
+ dataloader = DataLoader(
395
+ dataset,
396
+ batch_size=1,
397
+ shuffle=False,
398
+ collate_fn=dataset.collater,
399
+ sampler=sampler,
400
+ )
401
+
402
+ Ts = args.T_token, args.T_duration, args.T_f0
403
+ decoder = TemperatureDecoder(
404
+ Ts, discrete_dur=is_discrete_duration, discrete_f0=is_discrete_f0
405
+ )
406
+
407
+ mean_dur_prefix, mean_dur_cont = [], []
408
+ mean_f0_prefix, mean_f0_cont = [], []
409
+
410
+ for batch in dataloader:
411
+ batch = explode_batch(batch, args.batch_explosion_rate)
412
+ batch = move_to_cuda(batch)
413
+
414
+ assert len(batch["prefix"]) == 1
415
+
416
+ if args.teacher_force_tokens:
417
+ autoregressive_steps = batch["target"].size(1) - args.prefix_length - 1
418
+ else:
419
+ autoregressive_steps = args.max_length - args.prefix_length # + max_shift?
420
+
421
+ if args.copy_target:
422
+ produced_durations, produced_f0 = batch["dur_target"], batch["f0_target"]
423
+ else:
424
+ _, produced_durations, produced_f0, outputs = do_sampling(
425
+ model,
426
+ batch,
427
+ tgt_dict.eos(),
428
+ decoder,
429
+ autoregressive_steps=autoregressive_steps,
430
+ teacher_force_tokens=args.teacher_force_tokens,
431
+ teacher_force_duration=args.teacher_force_duration,
432
+ teacher_force_f0=args.teacher_force_f0,
433
+ )
434
+
435
+ # first tokens actually correspond to BOS
436
+ produced_durations = produced_durations[:, 1:]
437
+ produced_f0 = produced_f0[:, 1:]
438
+
439
+ dur_target = batch["dur_target"]
440
+ if is_discrete_duration:
441
+ produced_durations = produced_durations.float()
442
+ dur_target = dur_target.float()
443
+
444
+ if is_discrete_f0:
445
+ produced_f0 = f0_decoder(produced_f0).squeeze(-1)
446
+ f0_target = batch["raw_f0"]
447
+ else:
448
+ f0_target = batch["f0_target"]
449
+
450
+ # prefix values
451
+ prefix = batch["prefix"][0]
452
+ dur_prefix_mean = dur_target[:, :prefix].sum(dim=-1) / (
453
+ (~batch["dur_mask"][:, :prefix]).sum(dim=-1)
454
+ )
455
+
456
+ non_voiced = f0_target[:, :prefix] == 0.0
457
+ f0_mask = batch["f0_mask"][:, :prefix].logical_or(non_voiced)
458
+ f0_prefix_mean = f0_target[:, :prefix].sum(dim=-1) / ((~f0_mask).sum(dim=-1))
459
+
460
+ # continuation values
461
+ dur_cont_mean = produced_durations[:, prefix:].sum(dim=-1) / (
462
+ (~batch["dur_mask"][:, prefix:]).sum(dim=-1)
463
+ )
464
+
465
+ non_voiced = produced_f0[:, prefix:] == 0.0
466
+ f0_mask = non_voiced
467
+ f0_cont_mean = produced_f0[:, prefix:].sum(dim=-1) / ((~f0_mask).sum(dim=-1))
468
+
469
+ assert not f0_cont_mean.isnan().any()
470
+
471
+ mean_dur_prefix.append(dur_prefix_mean.cpu())
472
+ mean_dur_cont.append(dur_cont_mean.cpu())
473
+
474
+ mean_f0_prefix.append(f0_prefix_mean.cpu())
475
+ mean_f0_cont.append(f0_cont_mean.cpu())
476
+
477
+ if args.debug and len(mean_dur_prefix) > 10:
478
+ break
479
+
480
+ mean_dur_prefix, mean_dur_cont = torch.cat(mean_dur_prefix), torch.cat(
481
+ mean_dur_cont
482
+ )
483
+ mean_f0_prefix, mean_f0_cont = torch.cat(mean_f0_prefix), torch.cat(mean_f0_cont)
484
+
485
+ return mean_dur_prefix, mean_dur_cont, mean_f0_prefix, mean_f0_cont
486
+
487
+
488
+ def main(rank, world_size, args):
489
+ start = time.time()
490
+
491
+ if world_size > 1:
492
+ torch.distributed.init_process_group(
493
+ backend="gloo", init_method="env://", world_size=world_size, rank=rank
494
+ )
495
+ torch.cuda.set_device(rank % torch.cuda.device_count())
496
+
497
+ raw_args = args
498
+
499
+ args = convert_namespace_to_omegaconf(args)
500
+ if args.common.seed is not None:
501
+ np.random.seed(args.common.seed)
502
+ utils.set_torch_seed(args.common.seed)
503
+
504
+ models, model_args, task = checkpoint_utils.load_model_ensemble_and_task(
505
+ [raw_args.path], arg_overrides={"data": args.task.data}
506
+ )
507
+
508
+ tgt_dict = task.target_dictionary
509
+
510
+ for model in models:
511
+ model.prepare_for_inference_(args)
512
+ model.cuda().eval()
513
+ if raw_args.fp16:
514
+ model = model.half()
515
+ model = models[0]
516
+
517
+ config = ExpressiveCodeDataConfig(args.task.data)
518
+
519
+ dataset = CodeDataset(
520
+ manifest=config.manifests[raw_args.eval_subset],
521
+ dictionary=task.source_dictionary,
522
+ dur_dictionary=task.source_duration_dictionary,
523
+ f0_dictionary=task.source_f0_dictionary,
524
+ config=config,
525
+ discrete_dur=task.cfg.discrete_duration,
526
+ discrete_f0=task.cfg.discrete_f0,
527
+ log_f0=task.cfg.log_f0,
528
+ normalize_f0_mean=task.cfg.normalize_f0_mean,
529
+ normalize_f0_std=task.cfg.normalize_f0_std,
530
+ interpolate_f0=task.cfg.interpolate_f0,
531
+ shifts=task.cfg.stream_shifts,
532
+ return_filename=True,
533
+ strip_filename=False,
534
+ return_continuous_f0=raw_args.dequantize_prosody,
535
+ )
536
+
537
+ if raw_args.filter_names:
538
+ dataset = FilterNamesDataset(dataset, raw_args.filter_names)
539
+
540
+ criterion = task.build_criterion(model_args.criterion)
541
+
542
+ name2metric = {
543
+ "continuation": continuation,
544
+ "teacher_force_everything": teacher_force_everything,
545
+ "correlation": correlation,
546
+ }
547
+
548
+ name2keys = {
549
+ "continuation": (
550
+ "Token BLEU3",
551
+ "Duration NLL",
552
+ "Duration MAE",
553
+ "F0 NLL",
554
+ "F0 MAE",
555
+ "F0 sum",
556
+ "F0 sum_sq",
557
+ "Dur sum",
558
+ "Dur sum_sq",
559
+ ),
560
+ "teacher_force_everything": ("token_loss", "duration_loss", "f0_loss"),
561
+ "correlation": ("Duration corr", "F0 corr"),
562
+ }
563
+ metric_name = raw_args.metric
564
+
565
+ metric = name2metric[metric_name]
566
+ results = metric(raw_args, dataset, model, criterion, tgt_dict, rank, world_size)
567
+
568
+ values = None
569
+
570
+ if metric_name not in [
571
+ "correlation",
572
+ ]:
573
+ values, normalizers = results
574
+ values = maybe_aggregate_normalize(values, normalizers, world_size)
575
+ elif metric_name == "correlation":
576
+ values = maybe_aggregate_correlations(results, world_size)
577
+ else:
578
+ assert False
579
+
580
+ assert values is not None
581
+ summary = dict(zip(name2keys[raw_args.metric], values.tolist()))
582
+ if metric_name == "continuation":
583
+ summary["F0 Std"] = np.sqrt(-summary["F0 sum"] ** 2 + summary["F0 sum_sq"])
584
+ summary["Dur Std"] = np.sqrt(-summary["Dur sum"] ** 2 + summary["Dur sum_sq"])
585
+ del summary["F0 sum"]
586
+ del summary["F0 sum_sq"]
587
+ del summary["Dur sum"]
588
+ del summary["Dur sum_sq"]
589
+
590
+ summary["metric"] = metric_name
591
+
592
+ if rank == 0:
593
+ print(summary)
594
+ if raw_args.wandb:
595
+ wandb_results(summary, raw_args)
596
+ print("# finished in ", time.time() - start, "seconds")
597
+
598
+
599
+ def wandb_results(summary, raw_args):
600
+ import wandb
601
+
602
+ run = wandb.init(
603
+ project=raw_args.wandb_project_name, tags=raw_args.wandb_tags.split(",")
604
+ )
605
+ run.config.metric = raw_args.metric
606
+ run.config.model = raw_args.path
607
+ run.config.data = raw_args.data
608
+
609
+ if raw_args.wandb_run_name:
610
+ run.name = raw_args.wandb_run_name
611
+ run.save()
612
+
613
+ wandb.log(summary)
614
+ wandb.finish()
615
+
616
+
617
+ def maybe_aggregate_normalize(values, normalizers, world_size):
618
+ if world_size > 1:
619
+ torch.distributed.barrier()
620
+
621
+ torch.distributed.all_reduce_multigpu([values])
622
+ torch.distributed.all_reduce_multigpu([normalizers])
623
+
624
+ return values / normalizers
625
+
626
+
627
+ def maybe_aggregate_correlations(results, world_size):
628
+ if world_size > 1:
629
+ output = [None for _ in range(world_size)]
630
+ torch.distributed.all_gather_object(output, results)
631
+ mean_dur_prefix, mean_dur_cont, mean_f0_prefix, mean_f0_cont = [
632
+ torch.cat([x[i] for x in output]) for i in range(4)
633
+ ]
634
+ else:
635
+ mean_dur_prefix, mean_dur_cont, mean_f0_prefix, mean_f0_cont = results
636
+
637
+ corr_dur = scipy.stats.pearsonr(mean_dur_prefix.numpy(), mean_dur_cont.numpy())[0]
638
+ corr_f0 = scipy.stats.pearsonr(mean_f0_prefix.numpy(), mean_f0_cont.numpy())[0]
639
+ values = torch.tensor([corr_dur, corr_f0])
640
+
641
+ return values
642
+
643
+
644
+ def cli_main():
645
+ parser = options.get_interactive_generation_parser()
646
+ parser.add_argument(
647
+ "--prefix-length",
648
+ type=int,
649
+ default=1,
650
+ help="Prompt prefix length (including <s>)",
651
+ )
652
+ parser.add_argument(
653
+ "--duration-scale",
654
+ type=float,
655
+ default=1,
656
+ help="Multiply durations by the given scaler",
657
+ )
658
+ parser.add_argument(
659
+ "--debug", action="store_true", help="Process only the first batch"
660
+ )
661
+ parser.add_argument("--n_hypotheses", type=int, default=1)
662
+ parser.add_argument("--filter-names", type=str, default=None)
663
+ parser.add_argument(
664
+ "--max-length", type=int, default=200, help="Maximal produced length"
665
+ )
666
+
667
+ parser.add_argument("--teacher-force-tokens", action="store_true", default=False)
668
+ parser.add_argument("--teacher-force-duration", action="store_true", default=False)
669
+ parser.add_argument("--teacher-force-f0", action="store_true", default=False)
670
+
671
+ parser.add_argument("--copy-target", action="store_true", default=False)
672
+ parser.add_argument("--min-length", type=int, default=None)
673
+ parser.add_argument("--f0-discretization-bounds", type=str, default=None)
674
+ parser.add_argument("--dequantize-prosody", action="store_true")
675
+ parser.add_argument("--batch-explosion-rate", type=int, default=1)
676
+
677
+ parser.add_argument(
678
+ "--metric",
679
+ choices=["continuation", "teacher_force_everything", "correlation"],
680
+ required=True,
681
+ )
682
+
683
+ parser.add_argument("--wandb", action="store_true")
684
+ parser.add_argument("--wandb-project-name", type=str, default="eslm")
685
+ parser.add_argument("--wandb-tags", type=str, default="")
686
+ parser.add_argument("--wandb-run-name", type=str, default="")
687
+
688
+ parser.add_argument("--T-token", type=float, default=1.0)
689
+ parser.add_argument("--T-duration", type=float, default=1.0)
690
+ parser.add_argument("--T-f0", type=float, default=1.0)
691
+
692
+ parser.add_argument("--n-workers", type=int, default=1)
693
+
694
+ parser.add_argument(
695
+ "--eval-subset", type=str, default="valid", choices=["valid", "test"]
696
+ )
697
+
698
+ args = options.parse_args_and_arch(parser)
699
+
700
+ assert (
701
+ args.prefix_length >= 1
702
+ ), "Prefix length includes bos token <s>, hence the minimum is 1."
703
+ assert args.temperature >= 0.0, "T must be non-negative!"
704
+
705
+ if args.dequantize_prosody:
706
+ assert args.f0_discretization_bounds
707
+
708
+ world_size = args.n_workers or torch.cuda.device_count()
709
+ if world_size > 1:
710
+ import random
711
+
712
+ mp.set_start_method("spawn", force=True)
713
+ os.environ["MASTER_ADDR"] = "localhost"
714
+ os.environ["MASTER_PORT"] = str(random.randint(10_000, 50_000))
715
+
716
+ mp.spawn(
717
+ main,
718
+ nprocs=world_size,
719
+ args=(
720
+ world_size,
721
+ args,
722
+ ),
723
+ join=True,
724
+ )
725
+ else:
726
+ main(rank=0, world_size=world_size, args=args)
727
+
728
+
729
+ if __name__ == "__main__":
730
+ cli_main()
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/generate_waveform.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import ast
7
+ import argparse
8
+ import json
9
+ import logging
10
+ from pathlib import Path
11
+ import soundfile as sf
12
+ import torch
13
+
14
+ from tqdm import tqdm
15
+
16
+ from fairseq import utils
17
+ from fairseq.models.text_to_speech.vocoder import CodeHiFiGANVocoder
18
+
19
+
20
+ logging.basicConfig()
21
+ logging.root.setLevel(logging.INFO)
22
+ logging.basicConfig(level=logging.INFO)
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def dump_result(args, data, sample_id, pred_wav):
27
+ assert "audio" in data or args.results_path is not None
28
+ if args.results_path:
29
+ fname = Path(data["audio"]).name if "audio" in data else f"{sample_id}_pred.wav"
30
+ out_file = Path(args.results_path) / fname
31
+
32
+ sf.write(
33
+ out_file.as_posix(),
34
+ pred_wav.detach().cpu().numpy(),
35
+ args.sample_rate,
36
+ )
37
+
38
+
39
+ def load_data(in_file):
40
+ with open(in_file) as f:
41
+ data = [ast.literal_eval(line.strip()) for line in f]
42
+
43
+ return data
44
+
45
+
46
+ def get_f0_upsample_ratio(code_hop_size, f_hop_size):
47
+ ratio = (code_hop_size // 160) // (f_hop_size // 256) * 2
48
+ return ratio
49
+
50
+
51
+ def main(args):
52
+ logger.info(args)
53
+
54
+ use_cuda = torch.cuda.is_available() and not args.cpu
55
+
56
+ with open(args.vocoder_cfg) as f:
57
+ vocoder_cfg = json.load(f)
58
+ vocoder = CodeHiFiGANVocoder(args.vocoder, vocoder_cfg)
59
+ if use_cuda:
60
+ vocoder = vocoder.cuda()
61
+
62
+ data = load_data(args.in_file)
63
+
64
+ if args.results_path:
65
+ Path(args.results_path).mkdir(exist_ok=True, parents=True)
66
+
67
+ for i, d in tqdm(enumerate(data), total=len(data)):
68
+ code_key = "cpc_km100" if "cpc_km100" in d else "hubert"
69
+ code = list(map(int, d[code_key].split()))
70
+
71
+ x = {
72
+ "code": torch.LongTensor(code).view(1, -1),
73
+ "f0": torch.Tensor(d["f0"]).view(1, -1),
74
+ }
75
+
76
+ f0_up_ratio = get_f0_upsample_ratio(
77
+ vocoder_cfg["code_hop_size"], vocoder_cfg["hop_size"]
78
+ )
79
+ if f0_up_ratio > 1:
80
+ bsz, cond_length = x["f0"].size()
81
+ x["f0"] = x["f0"].unsqueeze(2).repeat(1, 1, f0_up_ratio).view(bsz, -1)
82
+
83
+ x = utils.move_to_cuda(x) if use_cuda else x
84
+ wav = vocoder(x)
85
+ dump_result(args, d, i, wav)
86
+
87
+
88
+ def cli_main():
89
+ parser = argparse.ArgumentParser()
90
+ parser.add_argument(
91
+ "--in-file",
92
+ type=str,
93
+ required=True,
94
+ help="Input file following the same format of the output from sample.py ('f0' and 'cpc_km100/hubert' are required fields)",
95
+ )
96
+ parser.add_argument(
97
+ "--vocoder", type=str, required=True, help="path to the vocoder"
98
+ )
99
+ parser.add_argument(
100
+ "--vocoder-cfg",
101
+ type=str,
102
+ required=True,
103
+ help="path to the vocoder config",
104
+ )
105
+ parser.add_argument("--sample-rate", type=int, default=16_000)
106
+ parser.add_argument(
107
+ "--results-path",
108
+ type=str,
109
+ default=None,
110
+ help="Output directory. If not set, the audios will be stored following the 'audio' field specified in the input file.",
111
+ )
112
+ parser.add_argument("--cpu", action="store_true", help="run on CPU")
113
+
114
+ args = parser.parse_args()
115
+
116
+ main(args)
117
+
118
+
119
+ if __name__ == "__main__":
120
+ cli_main()
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/inference_dataset.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+
7
+ import torch
8
+
9
+
10
+ class InferenceDataset:
11
+ def __init__(
12
+ self,
13
+ dataset,
14
+ prefix,
15
+ only_prefix=True,
16
+ presort_by_length=True,
17
+ filter_short=False,
18
+ min_length=None,
19
+ ):
20
+ self.dataset = dataset
21
+ self.collater = self.dataset.collater
22
+ self.prefix = prefix
23
+ self.only_prefix = only_prefix
24
+ self.filter_short = filter_short
25
+
26
+ self.remapping = list(range(len(self.dataset)))
27
+ if min_length:
28
+ assert min_length >= prefix + 1
29
+
30
+ length_thr = prefix + 1 if not min_length else min_length
31
+
32
+ if filter_short:
33
+ self.remapping = list(
34
+ filter(
35
+ lambda i: self.dataset[i]["dur_source"].sum() > length_thr,
36
+ self.remapping,
37
+ )
38
+ )
39
+ print(
40
+ f"# the initial dataset of {len(self.dataset)} examples became {len(self.remapping)} after filtering"
41
+ f" examples shorter than {length_thr} (in duration units)"
42
+ )
43
+
44
+ if presort_by_length:
45
+ lengths = {index: dataset.size(index) for index in self.remapping}
46
+ self.remapping.sort(key=lambda i: lengths[i])
47
+
48
+ @property
49
+ def pads(self):
50
+ return self.dataset.pads
51
+
52
+ def __len__(self):
53
+ return len(self.remapping)
54
+
55
+ def original_size(self, k):
56
+ k = self.remapping[k]
57
+ return self.dataset.size(k)
58
+
59
+ def __getitem__(self, k):
60
+ k = self.remapping[k]
61
+ channels = self.dataset[k]
62
+
63
+ if self.prefix and self.only_prefix:
64
+ dur_channel = channels["dur_source"]
65
+ assert dur_channel.sum() >= self.prefix
66
+
67
+ token_times = dur_channel.cumsum(dim=-1)
68
+ cut_after = torch.searchsorted(token_times, torch.tensor(self.prefix))
69
+
70
+ r = {}
71
+ for channel_name, value in channels.items():
72
+ if isinstance(value, torch.Tensor) and "source" in channel_name:
73
+ # if self.filter_short: assert value.size(0) >= self.prefix
74
+ r[channel_name] = value[: cut_after + 1]
75
+ else:
76
+ r[channel_name] = value
77
+
78
+ r["prefix"] = cut_after + 1
79
+ else:
80
+ r = channels
81
+
82
+ return r
83
+
84
+
85
+ def explode_batch(batch, times):
86
+ if times == 1:
87
+ return batch
88
+
89
+ new_batch = {}
90
+
91
+ for key, value in batch.items():
92
+ if isinstance(value, torch.Tensor):
93
+ assert value.size(0) == 1
94
+ new_batch[key] = torch.cat([value] * times)
95
+ elif key in ["ntokens", "nsentences"]:
96
+ new_batch[key] = value * times
97
+ elif key in ["prefix", "filename"]:
98
+ new_batch[key] = value
99
+ elif key == "net_input":
100
+ new_batch[key] = explode_batch(value, times)
101
+ else:
102
+ assert False, key
103
+ return new_batch
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/naive_decoder.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+ import warnings
8
+
9
+
10
+ class Naive_F0_Decoder(torch.nn.Module):
11
+ def __init__(self, bounds_path, n_units=32):
12
+ super().__init__()
13
+
14
+ bounds = torch.load(bounds_path)
15
+ bounds = torch.from_numpy(bounds[n_units])
16
+ assert bounds.ndim == 1
17
+
18
+ pad = torch.tensor([-5.0, -5.0]) # bos, eos, pad are in the dictionary
19
+ centers = torch.cat(
20
+ [bounds[0:1], 0.5 * (bounds[1:] + bounds[:-1]), bounds[-1:], pad[:]]
21
+ )
22
+
23
+ self.embedding = torch.nn.Embedding.from_pretrained(
24
+ centers.unsqueeze(-1), freeze=True
25
+ )
26
+ self.max_n = self.embedding.weight.numel()
27
+
28
+ def forward(self, discrete_f0: torch.Tensor):
29
+ in_bounds = (0 <= discrete_f0).all() and (discrete_f0 < self.max_n).all()
30
+ if not in_bounds:
31
+ warnings.warn(
32
+ f"F0 contains some weird outputs: discrete_f0.max().item()={discrete_f0.max().item()} discrete_f0.min().item()={discrete_f0.min().item()}; "
33
+ f"while we have embeddings for {self.max_n} values. "
34
+ "Assuming this is a no-prosody model -- but be careful!"
35
+ )
36
+
37
+ mask = discrete_f0 >= self.max_n
38
+ discrete_f0 = discrete_f0.masked_fill(mask, self.max_n - 1)
39
+
40
+ return self.embedding(discrete_f0).squeeze(-1)
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/prepare_dataset.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 multiprocessing import Pool
7
+
8
+ import os
9
+ from collections import defaultdict
10
+ from itertools import starmap
11
+
12
+ import torch
13
+ from npy_append_array import NpyAppendArray
14
+ from tqdm import tqdm
15
+
16
+ from data_utils import dump_speaker_f0_stat, F0Stat, load_f0
17
+ from fairseq.data.codedataset import (
18
+ ExpressiveCodeDataConfig,
19
+ parse_manifest,
20
+ F0_FRAME_SPACE,
21
+ align_f0_to_durations,
22
+ )
23
+ from fairseq.tasks.speech_ulm_task import UnitDictionary
24
+
25
+
26
+ def load_meta(meta_path, split):
27
+ config = ExpressiveCodeDataConfig(meta_path)
28
+ manifest_path = config.manifests[split]
29
+ dictionary = UnitDictionary(n_units=config.n_units)
30
+ audio_paths, codes, durs, speakers = parse_manifest(manifest_path, dictionary)
31
+ return config, audio_paths, codes, durs, speakers
32
+
33
+
34
+ def _align_f0(f0, dur, ratio, frm_tol=5):
35
+ if f0 is None:
36
+ seg_f0 = torch.zeros_like(dur, dtype=torch.float)
37
+ else:
38
+ seg_f0 = align_f0_to_durations(f0, dur, ratio, tol=frm_tol * ratio)
39
+ return seg_f0.numpy() # try a hacky stuff
40
+
41
+
42
+ def align_f0(path_to_f0, audio_paths, durs, ratio, mp=False):
43
+ chunk_size = 2000
44
+ num_procs = 40
45
+ iterable = ((path_to_f0[p], d, ratio) for p, d in zip(audio_paths, durs))
46
+
47
+ seg_f0s = []
48
+ if mp:
49
+ with Pool(num_procs) as pool:
50
+ iterator = tqdm(
51
+ pool.istarmap(_align_f0, iterable, chunk_size),
52
+ desc="align f0",
53
+ total=len(durs),
54
+ )
55
+ for seg_f0 in iterator:
56
+ seg_f0s.append(torch.from_numpy(seg_f0).float())
57
+ else:
58
+ iterator = tqdm(starmap(_align_f0, iterable), desc="align f0", total=len(durs))
59
+ for seg_f0 in iterator:
60
+ seg_f0s.append(torch.from_numpy(seg_f0).float())
61
+
62
+ return seg_f0s
63
+
64
+
65
+ def prepare_seg_data(config, audio_paths, codes, durs, speakers, path_to_f0):
66
+ ratio = config.code_hop_size / (config.sampling_rate * F0_FRAME_SPACE)
67
+ seg_f0s = align_f0(path_to_f0, audio_paths, durs, ratio)
68
+ data = {
69
+ "codes": codes,
70
+ "duration": durs,
71
+ "f0": seg_f0s,
72
+ "speaker": speakers,
73
+ "path": audio_paths,
74
+ }
75
+ return data
76
+
77
+
78
+ def dump_seg_data(data, out_prefix):
79
+ key_targs = {
80
+ "codes": f"{out_prefix}.code.npy",
81
+ "duration": f"{out_prefix}.dur.npy",
82
+ "f0": f"{out_prefix}.f0.npy",
83
+ }
84
+ for key, targ in key_targs.items():
85
+ assert not os.path.exists(targ)
86
+ npaa = NpyAppendArray(targ)
87
+ for utt_data in tqdm(data[key], desc=f"dumping {key}"):
88
+ npaa.append(utt_data.numpy())
89
+
90
+ assert not os.path.exists(f"{out_prefix}.path.txt")
91
+ with open(f"{out_prefix}.path.txt", "w") as f:
92
+ for x in data["path"]:
93
+ f.write(f"{str(x)}\n")
94
+
95
+ assert not os.path.exists(f"{out_prefix}.leng.txt")
96
+ with open(f"{out_prefix}.leng.txt", "w") as f:
97
+ for x in data["codes"]:
98
+ f.write(f"{len(x)}\n")
99
+
100
+ assert not os.path.exists(f"{out_prefix}.speaker.txt")
101
+ with open(f"{out_prefix}.speaker.txt", "w") as f:
102
+ for x in data["speaker"]:
103
+ f.write(f"{str(x)}\n")
104
+
105
+ print(f"wrote to files with prefix {out_prefix}")
106
+
107
+
108
+ def main(meta_path, f0_dir, splits, nshards_list):
109
+ speaker_to_stat = defaultdict(F0Stat)
110
+ if len(nshards_list) == 1:
111
+ nshards_list = nshards_list * len(splits)
112
+ else:
113
+ assert len(nshards_list) == len(splits)
114
+
115
+ for split, nshards in zip(splits, nshards_list):
116
+ config, audio_paths, codes, durs, speakers = load_meta(meta_path, split)
117
+ path_to_f0 = load_f0(f"{f0_dir}/{split}", nshards)
118
+
119
+ # segment-level data
120
+ data = prepare_seg_data(config, audio_paths, codes, durs, speakers, path_to_f0)
121
+ dump_seg_data(data, config.manifests[split])
122
+
123
+ # speaker f0
124
+ for audio_path, speaker in tqdm(zip(audio_paths, speakers)):
125
+ f0 = path_to_f0[audio_path]
126
+ speaker_to_stat[speaker].update(f0)
127
+ dump_speaker_f0_stat(speaker_to_stat, config.manifests[split])
128
+
129
+
130
+ if __name__ == "__main__":
131
+ import argparse
132
+
133
+ parser = argparse.ArgumentParser()
134
+ parser.add_argument("meta_path")
135
+ parser.add_argument("f0_dir", help="out_dir from preprocess_f0")
136
+ parser.add_argument("--splits", nargs="+", default=["train", "valid"])
137
+ parser.add_argument(
138
+ "--nshards_list", type=int, nargs="+", default=[20], help="number of f0 shards"
139
+ )
140
+ args = parser.parse_args()
141
+ print(args)
142
+
143
+ main(**vars(args))
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/preprocess_f0.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 os
7
+ import torch
8
+ from tqdm import tqdm
9
+ from data_utils import load_audio_path
10
+ from fairseq.data.codedataset import get_f0_by_filename
11
+
12
+
13
+ def process_one(path, sr):
14
+ """
15
+ Args:
16
+ path: audio file path
17
+ sr: sampling rate
18
+ """
19
+ try:
20
+ # YAAPT throws errors in some rare cases
21
+ f0 = get_f0_by_filename(path, sr)
22
+ except Exception as e:
23
+ print(
24
+ f"WARNING: error when processing {path}. set f0 to zero. original error message:\n{e}"
25
+ )
26
+ f0 = None
27
+ return f0
28
+
29
+
30
+ def main(file_path, out_dir, nshards, rank, sampling_rate):
31
+ # load data
32
+ audio_paths = load_audio_path(file_path)
33
+
34
+ # shard
35
+ assert nshards <= len(audio_paths) and nshards > 0
36
+ shard_size = len(audio_paths) / nshards
37
+ s = int(round((rank - 1) * shard_size))
38
+ e = int(round(rank * shard_size))
39
+ audio_paths = audio_paths[s:e]
40
+
41
+ # process
42
+ path_to_f0 = {}
43
+ for i, audio_path in enumerate(tqdm(audio_paths)):
44
+ f0 = process_one(audio_path, sampling_rate)
45
+ path_to_f0[audio_path] = f0
46
+ print(f"finished processing {len(path_to_f0)} utterances ({s}-{e})")
47
+
48
+ f0_path = f"{out_dir}/f0_{rank}_{nshards}.pt"
49
+ os.makedirs(out_dir, exist_ok=True)
50
+ torch.save(path_to_f0, f0_path)
51
+ print(f"saved to {f0_path}")
52
+
53
+
54
+ if __name__ == "__main__":
55
+ import argparse
56
+
57
+ parser = argparse.ArgumentParser()
58
+ parser.add_argument("file_path")
59
+ parser.add_argument("out_dir")
60
+ parser.add_argument("--nshards", type=int, default=20)
61
+ parser.add_argument("--rank", type=int, default=1)
62
+ parser.add_argument("--sampling_rate", type=int, default=16000)
63
+ args = parser.parse_args()
64
+
65
+ main(**vars(args))
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/quantize_f0.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from collections import defaultdict
7
+ from functools import partial
8
+
9
+ import numpy as np
10
+ import torch
11
+ from tqdm import tqdm
12
+
13
+ from data_utils import dump_speaker_f0_stat, F0Stat, load_audio_path, load_f0
14
+
15
+
16
+ def load_speaker(path):
17
+ speakers = []
18
+ with open(path) as f:
19
+ for line in f.readlines():
20
+ sample = eval(line.strip())
21
+ assert "speaker" in sample
22
+ speakers.append(sample["speaker"])
23
+ return speakers
24
+
25
+
26
+ def quantize_f0(speaker_to_f0, f0_stats, nbins, normalize, log):
27
+ f0_all = []
28
+ for speaker, f0 in speaker_to_f0.items():
29
+ f0 = f0.raw_data
30
+ if log:
31
+ f0 = f0.log()
32
+ mean = f0_stats[speaker]["logf0_mean"] if log else f0_stats[speaker]["f0_mean"]
33
+ std = f0_stats[speaker]["logf0_std"] if log else f0_stats[speaker]["f0_std"]
34
+ if normalize == "mean":
35
+ f0 = f0 - mean
36
+ elif normalize == "meanstd":
37
+ f0 = (f0 - mean) / std
38
+ f0_all.extend(f0.tolist())
39
+
40
+ hist, bin_x = np.histogram(f0_all, 100000)
41
+ cum_hist = np.cumsum(hist) / len(f0_all) * 100
42
+
43
+ f0_bin = {}
44
+ for num_bin in nbins:
45
+ bin_offset = []
46
+ bin_size = 100 / num_bin
47
+ threshold = bin_size
48
+ for i in range(num_bin - 1):
49
+ index = (np.abs(cum_hist - threshold)).argmin()
50
+ bin_offset.append(bin_x[index])
51
+ threshold += bin_size
52
+ f0_bin[num_bin] = np.array(bin_offset)
53
+
54
+ return f0_bin
55
+
56
+
57
+ def main(file_path, f0_dir, out_dir, out_prefix, nbins, nshards, normalize, log):
58
+ audio_paths = load_audio_path(file_path)
59
+ path_to_f0 = load_f0(f0_dir, nshards)
60
+
61
+ speakers = load_speaker(file_path)
62
+ speaker_to_f0 = defaultdict(partial(F0Stat, True))
63
+
64
+ # speaker f0 stats
65
+ for audio_path, speaker in tqdm(zip(audio_paths, speakers)):
66
+ f0 = path_to_f0[audio_path]
67
+ speaker_to_f0[speaker].update(f0)
68
+ f0_stats = dump_speaker_f0_stat(speaker_to_f0, f"{out_dir}/{out_prefix}")
69
+
70
+ # quantize
71
+ f0_bin = quantize_f0(speaker_to_f0, f0_stats, nbins, normalize, log)
72
+ log_suffix = "_log" if log else ""
73
+ f0_bin_out_file = f"{out_dir}/{out_prefix}_{normalize}_norm{log_suffix}_f0_bin.th"
74
+ torch.save(f0_bin, f0_bin_out_file)
75
+
76
+
77
+ if __name__ == "__main__":
78
+ import argparse
79
+
80
+ parser = argparse.ArgumentParser()
81
+ parser.add_argument("file_path")
82
+ parser.add_argument("f0_dir", help="out_dir from preprocess_f0")
83
+ parser.add_argument("out_dir")
84
+ parser.add_argument("out_prefix")
85
+ parser.add_argument("--nbins", nargs="+", type=int, default=[32])
86
+ parser.add_argument("--nshards", type=int, default=20, help="number of f0 shards")
87
+ parser.add_argument(
88
+ "--normalize", type=str, choices=["meanstd", "mean", "none"], default="mean"
89
+ )
90
+ parser.add_argument("--log", action="store_true")
91
+ args = parser.parse_args()
92
+ print(args)
93
+
94
+ main(**vars(args))
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/sample/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/sample/sample.py ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 torch.multiprocessing as mp
8
+ import numpy as np
9
+ import json
10
+
11
+ import torch
12
+ from torch.distributions.categorical import Categorical
13
+
14
+ from fairseq import checkpoint_utils, options, utils
15
+ from fairseq.data.codedataset import CodeDataset, ExpressiveCodeDataConfig
16
+ from fairseq.dataclass.utils import convert_namespace_to_omegaconf
17
+ from torch.utils.data import DataLoader, DistributedSampler
18
+ from fairseq.utils import move_to_cuda
19
+
20
+ import tqdm
21
+ import random
22
+ import pathlib
23
+
24
+ import sys, pathlib
25
+
26
+ sys.path.append(str(pathlib.Path(__file__).parent.parent))
27
+ from inference_dataset import InferenceDataset, explode_batch
28
+ from naive_decoder import Naive_F0_Decoder
29
+ from truncated_laplace import truncated_laplace
30
+
31
+ CODETYPE_TO_FRAMETIME = {"cpc_km100": 0.01, "hubert": 0.02} # 10ms # 20ms
32
+
33
+
34
+ class TemperatureDecoder:
35
+ def __init__(self, Ts, discrete_dur=False, discrete_f0=False):
36
+ self.T_token, self.T_dur, self.T_f0 = Ts
37
+ self.discrete_dur = discrete_dur
38
+ self.discrete_f0 = discrete_f0
39
+
40
+ def __call__(self, output):
41
+ def sample_multinomial(key, T):
42
+ logits = output[key][:, -1, :].float()
43
+ return Categorical(logits=logits / T).sample().unsqueeze(-1)
44
+
45
+ def sample_laplace(key, T, truncate_at_zero):
46
+ mean = output[key][:, -1, :].float()
47
+ return truncated_laplace(mean=mean, T=T, truncate_by_zero=truncate_at_zero)
48
+
49
+ if self.T_token > 0:
50
+ new_tokens = sample_multinomial("token", self.T_token)
51
+ else:
52
+ new_tokens = output["token"][:, -1, :].argmax(dim=-1, keepdim=True)
53
+
54
+ if not self.discrete_dur and self.T_dur == 0:
55
+ new_durations = output["duration"][:, -1].round().int()
56
+ elif not self.discrete_dur and self.T_dur > 0:
57
+ new_durations = (
58
+ sample_laplace("duration", self.T_dur, truncate_at_zero=True)
59
+ .round()
60
+ .int()
61
+ )
62
+ elif self.discrete_dur and self.T_dur > 0:
63
+ new_durations = sample_multinomial("duration", self.T_dur)
64
+ elif self.discrete_dur and self.T_dur == 0:
65
+ new_durations = output["duration"][:, -1, :].argmax(dim=-1, keepdim=True)
66
+ else:
67
+ assert False
68
+
69
+ if not self.discrete_f0 and self.T_f0 == 0:
70
+ new_f0 = output["f0"][:, -1]
71
+ elif not self.discrete_f0 and self.T_f0 > 0:
72
+ new_f0 = sample_laplace("f0", self.T_f0, truncate_at_zero=False)
73
+ elif self.discrete_f0 and self.T_f0 > 0:
74
+ new_f0 = sample_multinomial("f0", self.T_f0)
75
+ elif self.discrete_f0 and self.T_f0 == 0:
76
+ new_f0 = output["f0"][:, -1, :].argmax(dim=-1, keepdim=True)
77
+ else:
78
+ assert False
79
+
80
+ return new_tokens, new_durations, new_f0
81
+
82
+
83
+ class FilterNamesDataset:
84
+ def __init__(self, dataset, fnames_path):
85
+ self.dataset = dataset
86
+
87
+ with open(fnames_path, "r") as fin:
88
+ fnames = set((eval(line)["audio"] for line in fin))
89
+ print(f"# will retrict the dataset for {len(fnames)} files")
90
+
91
+ self.indexes = []
92
+
93
+ for i, datapoint in enumerate(dataset):
94
+ if datapoint["filename"] in fnames:
95
+ self.indexes.append(i)
96
+ assert len(self.indexes) == len(fnames), f"{len(self.indexes)} {len(fnames)}"
97
+
98
+ self.collater = self.dataset.collater
99
+ self.discrete_dur = self.dataset.discrete_dur
100
+ self.discrete_f0 = self.dataset.discrete_f0
101
+
102
+ def __len__(self):
103
+ return len(self.indexes)
104
+
105
+ def __getitem__(self, k):
106
+ k = self.indexes[k]
107
+ return self.dataset[k]
108
+
109
+ def size(self, k):
110
+ k = self.indexes[k]
111
+ return self.dataset.size(k)
112
+
113
+
114
+ @torch.no_grad()
115
+ def do_sampling(
116
+ model,
117
+ batch,
118
+ eos_token,
119
+ decoder,
120
+ autoregressive_steps=100,
121
+ teacher_force_tokens=False,
122
+ teacher_force_duration=False,
123
+ teacher_force_f0=False,
124
+ match_duration=False,
125
+ ):
126
+ def autoregressive_step_(output, autoregressive_steps):
127
+ new_tokens, new_durations, new_f0 = decoder(output)
128
+
129
+ n = output["token"].size(1) if output["token"].ndim == 3 else 1
130
+
131
+ if teacher_force_tokens:
132
+ new_tokens = batch["target"][:, n - 1].unsqueeze(-1)
133
+ if teacher_force_duration:
134
+ new_durations = batch["dur_target"][:, n - 1].unsqueeze(-1)
135
+ if teacher_force_f0:
136
+ new_f0 = batch["f0_target"][:, n - 1].unsqueeze(-1)
137
+
138
+ batch["net_input"]["src_tokens"] = torch.cat(
139
+ [batch["net_input"]["src_tokens"], new_tokens], dim=1
140
+ )
141
+ batch["net_input"]["dur_src"] = torch.cat(
142
+ [batch["net_input"]["dur_src"], new_durations], dim=1
143
+ )
144
+ batch["net_input"]["f0_src"] = torch.cat(
145
+ [batch["net_input"]["f0_src"], new_f0], dim=1
146
+ )
147
+
148
+ outputs = []
149
+
150
+ if teacher_force_tokens or teacher_force_duration or teacher_force_f0:
151
+ max_time = batch["target"].size(1)
152
+ prefix_time = batch["net_input"]["src_tokens"].size(1)
153
+
154
+ autoregressive_steps = max_time - prefix_time + 1 # should be 0
155
+
156
+ for _ in range(autoregressive_steps):
157
+ output = model(**batch["net_input"])
158
+
159
+ last_steps = (
160
+ output["token"][:, -1, ...],
161
+ output["duration"][:, -1, ...],
162
+ output["f0"][:, -1, ...],
163
+ )
164
+ outputs.append(last_steps)
165
+
166
+ autoregressive_step_(output, autoregressive_steps)
167
+ tokens, duration, f0 = (
168
+ batch["net_input"]["src_tokens"],
169
+ batch["net_input"]["dur_src"],
170
+ batch["net_input"]["f0_src"],
171
+ )
172
+
173
+ if (
174
+ match_duration
175
+ and (batch["dur_target"].sum(dim=-1) < duration.sum(dim=-1)).all()
176
+ ):
177
+ break
178
+
179
+ return tokens, duration, f0, outputs
180
+
181
+
182
+ def unroll_duration(token_stream, duration_stream):
183
+ assert len(token_stream) == len(
184
+ duration_stream
185
+ ), f"{len(token_stream)} != {len(duration_stream)}"
186
+ non_positive_durations = sum(d <= 0 for d in duration_stream)
187
+ if non_positive_durations > 0:
188
+ print(
189
+ f"# {non_positive_durations} durations are non-positive, they will be capped to 1"
190
+ )
191
+
192
+ result = []
193
+
194
+ duration_stream_rounded_capped = [max(1, int(round(x))) for x in duration_stream]
195
+ for t, d in zip(token_stream, duration_stream_rounded_capped):
196
+ result.extend([t] * d)
197
+
198
+ return result
199
+
200
+
201
+ def realign_shifted_streams(tokens, durations, F0s, shifts):
202
+ """
203
+ Durations are shifted by 1, F0 by 2
204
+ >>> tokens = ["<s>", "t1", "t2", "t3", "</s>", "x", "x"]
205
+ >>> durations = ["<0>", "<0>", "d1", "d2", "d3", "<0>", "x"]
206
+ >>> F0s = ["<0>", "<0>", "<0>", "f1", "f2", "f3", "<0>"]
207
+ >>> shifts = [1,2]
208
+ >>> realign_shifted_streams(tokens, durations, F0s, shifts)
209
+ (['<s>', 't1', 't2', 't3', '</s>'], ['<0>', 'd1', 'd2', 'd3', '<0>'], ['<0>', 'f1', 'f2', 'f3', '<0>'])
210
+ """
211
+ max_shift = max(shifts)
212
+ if max_shift > 0:
213
+ shift_durations, shift_F0s = shifts
214
+
215
+ tokens = tokens[:-max_shift]
216
+ durations = durations[shift_durations:]
217
+ if shift_durations < max_shift:
218
+ durations = durations[: -(max_shift - shift_durations)]
219
+
220
+ if F0s is not None:
221
+ F0s = F0s[shift_F0s:]
222
+ if shift_F0s < max_shift:
223
+ F0s = F0s[: -(max_shift - shift_F0s)]
224
+
225
+ assert len(tokens) == len(durations), f"{len(tokens)} =! {len(durations)}"
226
+ if F0s is not None:
227
+ assert len(tokens) == len(F0s), f"{len(tokens)} =! {len(F0s)}"
228
+
229
+ return tokens, durations, F0s
230
+
231
+
232
+ def maybe_cut_eos(produced_tokens, produced_duration, produced_f0, eos_idx):
233
+ if eos_idx in produced_tokens:
234
+ eos_index = produced_tokens.index(eos_idx)
235
+ produced_tokens = produced_tokens[:eos_index]
236
+ produced_duration = produced_duration[:eos_index]
237
+ produced_f0 = produced_f0[:eos_index]
238
+ return produced_tokens, produced_duration, produced_f0
239
+
240
+
241
+ def maybe_filter_pad(produced_tokens, produced_duration, produced_f0, pad_idx):
242
+ if pad_idx not in produced_tokens:
243
+ return produced_tokens, produced_duration, produced_f0
244
+
245
+ assert len(produced_tokens) == len(produced_duration) == len(produced_f0)
246
+
247
+ print("<pad> is detected in the output!")
248
+ filtered_tokens, filtered_duration, filtered_f0 = [], [], []
249
+
250
+ for t, d, f in zip(produced_tokens, produced_duration, produced_f0):
251
+ if t != pad_idx:
252
+ filtered_tokens.append(t)
253
+ filtered_duration.append(d)
254
+ filtered_f0.append(f)
255
+ return filtered_tokens, filtered_duration, filtered_f0
256
+
257
+
258
+ def match_duration(produced_tokens, produced_duration, produced_f0, target_duration):
259
+ """
260
+ >>> tokens = ['t'] * 4
261
+ >>> F0s = ['f0'] * 4
262
+ >>> produced_duration = [1, 10, 10, 10]
263
+ >>> match_duration(tokens, produced_duration, F0s, target_duration=100)
264
+ (['t', 't', 't', 't'], [1, 10, 10, 10], ['f0', 'f0', 'f0', 'f0'])
265
+ >>> match_duration(tokens, produced_duration, F0s, target_duration=5)
266
+ (['t', 't'], [1, 4], ['f0', 'f0'])
267
+ """
268
+ if sum(produced_duration) <= target_duration:
269
+ return produced_tokens, produced_duration, produced_f0
270
+
271
+ running_duration = 0
272
+ filtered_duration = []
273
+
274
+ for next_tok_duration in produced_duration:
275
+ if running_duration + next_tok_duration < target_duration:
276
+ filtered_duration.append(next_tok_duration)
277
+ running_duration += next_tok_duration
278
+ else:
279
+ to_add = target_duration - running_duration
280
+ assert to_add <= next_tok_duration
281
+ filtered_duration.append(to_add)
282
+ break
283
+
284
+ produced_duration = filtered_duration
285
+ assert sum(produced_duration) == target_duration
286
+
287
+ n_tok = len(filtered_duration)
288
+
289
+ return produced_tokens[:n_tok], produced_duration, produced_f0[:n_tok]
290
+
291
+
292
+ def main(rank, world_size, args):
293
+ if world_size > 1:
294
+ torch.distributed.init_process_group(
295
+ backend="gloo", init_method="env://", world_size=world_size, rank=rank
296
+ )
297
+ torch.cuda.set_device(rank)
298
+
299
+ raw_args = args
300
+ args = convert_namespace_to_omegaconf(args)
301
+ if args.common.seed is not None:
302
+ random.seed(args.common.seed)
303
+ np.random.seed(args.common.seed)
304
+ utils.set_torch_seed(args.common.seed)
305
+
306
+ models, model_args, task = checkpoint_utils.load_model_ensemble_and_task(
307
+ [raw_args.path], arg_overrides={"data": args.task.data}
308
+ )
309
+ tgt_dict = task.target_dictionary
310
+
311
+ for model in models:
312
+ model.prepare_for_inference_(args)
313
+ model.cuda().eval()
314
+ if raw_args.fp16:
315
+ model = model.half()
316
+ model = models[0]
317
+
318
+ config = ExpressiveCodeDataConfig(args.task.data)
319
+
320
+ dataset = CodeDataset(
321
+ manifest=config.manifests[raw_args.subset],
322
+ dictionary=task.source_dictionary,
323
+ dur_dictionary=task.source_duration_dictionary,
324
+ f0_dictionary=task.source_f0_dictionary,
325
+ config=config,
326
+ discrete_dur=task.cfg.discrete_duration,
327
+ discrete_f0=task.cfg.discrete_f0,
328
+ log_f0=task.cfg.log_f0,
329
+ normalize_f0_mean=task.cfg.normalize_f0_mean,
330
+ normalize_f0_std=task.cfg.normalize_f0_std,
331
+ interpolate_f0=task.cfg.interpolate_f0,
332
+ shifts=task.cfg.stream_shifts,
333
+ return_filename=True,
334
+ strip_filename=False,
335
+ )
336
+ tgt_dict = task.target_dictionary
337
+ shifts = dataset.shifts.dur, dataset.shifts.f0
338
+ max_shift = max(shifts)
339
+
340
+ fname = raw_args.output
341
+ if world_size > 1:
342
+ fname += f"_{rank}"
343
+ output_file = open(fname, "w")
344
+
345
+ if raw_args.filter_names:
346
+ dataset = FilterNamesDataset(dataset, raw_args.filter_names)
347
+
348
+ dataset = InferenceDataset(dataset, raw_args.prefix_length, filter_short=True)
349
+ print(f"Dataset size {len(dataset)}")
350
+ sampler = (
351
+ None
352
+ if world_size == 1
353
+ else DistributedSampler(
354
+ dataset, num_replicas=world_size, rank=rank, shuffle=False
355
+ )
356
+ )
357
+ dataloader = DataLoader(
358
+ dataset,
359
+ batch_size=1,
360
+ shuffle=False,
361
+ collate_fn=dataset.collater,
362
+ sampler=sampler,
363
+ )
364
+
365
+ Ts = raw_args.T_token, raw_args.T_duration, raw_args.T_f0
366
+ decoder = TemperatureDecoder(
367
+ Ts, discrete_dur=task.cfg.discrete_duration, discrete_f0=task.cfg.discrete_f0
368
+ )
369
+
370
+ dataset_size = len(dataset)
371
+
372
+ f0_decoder = None
373
+ if raw_args.f0_discretization_bounds:
374
+ assert task.cfg.discrete_f0
375
+ f0_decoder = Naive_F0_Decoder(raw_args.f0_discretization_bounds).cuda()
376
+
377
+ pbar = (
378
+ tqdm.tqdm(
379
+ total=dataset_size
380
+ if raw_args.max_samples is None
381
+ else min(raw_args.max_samples, dataset_size)
382
+ )
383
+ if world_size == 1
384
+ else None
385
+ )
386
+
387
+ samples_produced = 0
388
+
389
+ for batch in dataloader:
390
+ if (
391
+ raw_args.max_samples is not None
392
+ and samples_produced >= raw_args.max_samples
393
+ ):
394
+ break
395
+
396
+ prefix = batch["prefix"][0]
397
+
398
+ batch = explode_batch(batch, raw_args.batch_explosion_rate)
399
+ batch = move_to_cuda(batch)
400
+
401
+ if not raw_args.short_curcuit:
402
+ produced_tokens, produced_durations, produced_f0, _ = do_sampling(
403
+ models[0],
404
+ batch,
405
+ tgt_dict.eos(),
406
+ decoder,
407
+ autoregressive_steps=raw_args.max_length - prefix + max_shift,
408
+ teacher_force_tokens=raw_args.teacher_force_tokens,
409
+ match_duration=raw_args.match_duration,
410
+ teacher_force_duration=raw_args.teacher_force_duration,
411
+ teacher_force_f0=raw_args.teacher_force_f0,
412
+ )
413
+
414
+ # stip entries corresponding to <s>
415
+ produced_tokens = produced_tokens[:, 1:]
416
+ produced_durations = produced_durations[:, 1:]
417
+ produced_f0 = produced_f0[:, 1:]
418
+
419
+ else:
420
+ max_length = raw_args.max_length + max_shift
421
+ produced_tokens, produced_durations, produced_f0 = (
422
+ batch["target"][:, :max_length],
423
+ batch["dur_target"][:, :max_length],
424
+ batch["f0_target"][:, :max_length],
425
+ )
426
+
427
+ if f0_decoder is not None:
428
+ produced_f0 = f0_decoder(produced_f0)
429
+
430
+ produced_tokens, produced_durations, produced_f0 = (
431
+ produced_tokens.cpu().tolist(),
432
+ produced_durations.cpu().tolist(),
433
+ produced_f0.cpu().tolist(),
434
+ )
435
+
436
+ bsz = batch["target"].size(0)
437
+ assert bsz == raw_args.batch_explosion_rate
438
+
439
+ for i in range(bsz):
440
+ if (
441
+ raw_args.max_samples is not None
442
+ and samples_produced >= raw_args.max_samples
443
+ ):
444
+ break
445
+
446
+ produced_tokens_i = produced_tokens[i]
447
+ produced_durations_i = produced_durations[i]
448
+ produced_f0_i = produced_f0[i]
449
+
450
+ (
451
+ produced_tokens_i,
452
+ produced_durations_i,
453
+ produced_f0_i,
454
+ ) = realign_shifted_streams(
455
+ produced_tokens_i, produced_durations_i, produced_f0_i, shifts
456
+ )
457
+
458
+ produced_tokens_i, produced_durations_i, produced_f0_i = maybe_cut_eos(
459
+ produced_tokens_i, produced_durations_i, produced_f0_i, tgt_dict.eos()
460
+ )
461
+
462
+ produced_tokens_i, produced_durations_i, produced_f0_i = maybe_filter_pad(
463
+ produced_tokens_i, produced_durations_i, produced_f0_i, tgt_dict.pad()
464
+ )
465
+
466
+ if raw_args.match_duration:
467
+ # NB: here we cheat a bit and use that padding has duration 0
468
+ # so no need to re-align and remove padding
469
+ dur_target_i = batch["dur_target"][i, :].sum().item()
470
+ produced_tokens_i, produced_durations_i, produced_f0_i = match_duration(
471
+ produced_tokens_i, produced_durations_i, produced_f0_i, dur_target_i
472
+ )
473
+
474
+ if raw_args.cut_prompt:
475
+ produced_tokens_i, produced_durations_i, produced_f0_i = (
476
+ produced_tokens_i[prefix:],
477
+ produced_durations_i[prefix:],
478
+ produced_f0_i[prefix:],
479
+ )
480
+
481
+ prompt_fname = batch["filename"][0]
482
+ fname = str(pathlib.Path(prompt_fname).with_suffix("")) + f"__{i}.wav"
483
+
484
+ token_stream = unroll_duration(produced_tokens_i, produced_durations_i)
485
+ f0_stream = unroll_duration(produced_f0_i, produced_durations_i)
486
+ output_line = json.dumps(
487
+ {
488
+ "audio": fname,
489
+ "prompt": prompt_fname,
490
+ raw_args.code_type: " ".join(map(str, token_stream)),
491
+ "duration": round(
492
+ sum(produced_durations_i)
493
+ * CODETYPE_TO_FRAMETIME[raw_args.code_type],
494
+ 3,
495
+ ),
496
+ "raw_duration": produced_durations_i,
497
+ "raw_f0": produced_f0_i,
498
+ "f0": [round(f0, 3) for f0 in f0_stream],
499
+ }
500
+ )
501
+ print(output_line, file=output_file)
502
+
503
+ if pbar:
504
+ pbar.update(1)
505
+ samples_produced += 1
506
+
507
+ if raw_args.debug:
508
+ break
509
+
510
+ output_file.close()
511
+
512
+ if world_size > 1:
513
+ # important that everything is flushed before aggregating
514
+ torch.distributed.barrier()
515
+
516
+ if world_size > 1 and rank == 0:
517
+ with open(raw_args.output, "w") as fout:
518
+ for i in range(world_size):
519
+ f = raw_args.output + f"_{i}"
520
+ with open(f, "r") as fin:
521
+ fout.write(fin.read())
522
+ os.remove(f)
523
+
524
+
525
+ def cli_main():
526
+ parser = options.get_interactive_generation_parser()
527
+ parser.add_argument(
528
+ "--prefix-length",
529
+ type=int,
530
+ default=1,
531
+ help="Prompt prefix length (including <s>)",
532
+ )
533
+ parser.add_argument("--output", type=str, default=None, required=True)
534
+ parser.add_argument(
535
+ "--debug", action="store_true", help="Process only the first batch"
536
+ )
537
+ parser.add_argument(
538
+ "--ignore-durations",
539
+ action="store_true",
540
+ help="If set, the duration stream is ignored",
541
+ )
542
+ parser.add_argument(
543
+ "--max-length", type=int, default=200, help="Maximal produced length"
544
+ )
545
+ parser.add_argument(
546
+ "--code-type", choices=["cpc_km100", "hubert"], default="cpc_km100"
547
+ )
548
+ parser.add_argument("--max-samples", type=int, default=None)
549
+ parser.add_argument("--prompt-duration-scaler", type=float, default=1.0)
550
+ parser.add_argument("--teacher-force-tokens", action="store_true", default=False)
551
+ parser.add_argument("--teacher-force-duration", action="store_true", default=False)
552
+ parser.add_argument("--teacher-force-f0", action="store_true", default=False)
553
+ parser.add_argument("--filter-names", type=str, default=None)
554
+ parser.add_argument(
555
+ "--match-duration",
556
+ action="store_true",
557
+ help="Do not produce sequences longer that ground-truth",
558
+ )
559
+ parser.add_argument(
560
+ "--cut-prompt",
561
+ action="store_true",
562
+ help="Remove prompt from the produced audio",
563
+ )
564
+ parser.add_argument(
565
+ "--short-curcuit", action="store_true", help="Use 'target' as a sample"
566
+ )
567
+ parser.add_argument("--f0-discretization-bounds", type=str, default=None)
568
+
569
+ parser.add_argument("--batch-explosion-rate", type=int, default=1)
570
+
571
+ parser.add_argument("--T-token", type=float, default=1.0)
572
+ parser.add_argument("--T-duration", type=float, default=1.0)
573
+ parser.add_argument("--T-f0", type=float, default=1.0)
574
+
575
+ parser.add_argument(
576
+ "--subset", type=str, default="valid", choices=["test", "valid"]
577
+ )
578
+
579
+ args = options.parse_args_and_arch(parser)
580
+
581
+ assert (
582
+ args.prefix_length >= 1
583
+ ), "Prefix length includes bos token <s>, hence the minimum is 1."
584
+ assert all(
585
+ t >= 0 for t in [args.T_token, args.T_f0, args.T_duration]
586
+ ), "T must be non-negative!"
587
+
588
+ world_size = torch.cuda.device_count()
589
+ if world_size > 1:
590
+ import random
591
+
592
+ mp.set_start_method("spawn", force=True)
593
+ os.environ["MASTER_ADDR"] = "localhost"
594
+ os.environ["MASTER_PORT"] = str(random.randint(10_000, 50_000))
595
+
596
+ print(f"Using {world_size} devices, master port {os.environ['MASTER_PORT']}")
597
+
598
+ mp.spawn(
599
+ main,
600
+ nprocs=world_size,
601
+ args=(
602
+ world_size,
603
+ args,
604
+ ),
605
+ join=True,
606
+ )
607
+ else:
608
+ main(rank=0, world_size=world_size, args=args)
609
+
610
+
611
+ if __name__ == "__main__":
612
+ cli_main()
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/join_units_manifest.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import json
7
+ import argparse
8
+ import pathlib
9
+
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser()
13
+ parser.add_argument("--manifest", required=True)
14
+ parser.add_argument("--units", required=True)
15
+ parser.add_argument("--output", required=True)
16
+ parser.add_argument("--sample_rate", type=int, default=16_000)
17
+
18
+ args = parser.parse_args()
19
+
20
+ with open(args.manifest, "r") as manifest, open(args.units, "r") as units, open(
21
+ args.output, "w"
22
+ ) as outp:
23
+ root = manifest.readline().strip()
24
+ root = pathlib.Path(root)
25
+
26
+ for manifest_line, unit_line in zip(manifest.readlines(), units.readlines()):
27
+ path, frames = manifest_line.split()
28
+ duration = int(frames) / float(args.sample_rate)
29
+ fname = root / path
30
+ speaker = fname.parent.parent.name
31
+
32
+ units = unit_line.split("|")[1]
33
+
34
+ print(
35
+ json.dumps(
36
+ dict(
37
+ audio=str(root / path),
38
+ duration=duration,
39
+ hubert_km100=units.strip(),
40
+ speaker=speaker,
41
+ )
42
+ ),
43
+ file=outp,
44
+ )
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/prepare_data.sh ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
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
+ set -eu
8
+
9
+ train_json=$1
10
+ valid_json=$2
11
+ test_json=$3
12
+ n_units=$4
13
+ hop_size=$5
14
+ sr=$6
15
+ f0_quantizer=$7
16
+ out_dir=$8
17
+
18
+ meta_path="$out_dir/data_config.json"
19
+ f0_dir="$out_dir/f0"
20
+
21
+ mkdir -p $out_dir
22
+ ln -sf $train_json $out_dir/train.txt
23
+ ln -sf $valid_json $out_dir/valid.txt
24
+ ln -sf $test_json $out_dir/test.txt
25
+
26
+ cat <<EOF >$meta_path
27
+ {
28
+ "manifests": {
29
+ "train": "$out_dir/train.txt",
30
+ "valid": "$out_dir/valid.txt",
31
+ "test": "$out_dir/test.txt"
32
+ },
33
+ "n_units": $n_units,
34
+ "code_hop_size": $hop_size,
35
+ "sampling_rate": $sr,
36
+ "multispkr": "parent_parent_name",
37
+
38
+ "f0_vq_type": "naive",
39
+ "f0_vq_naive_quantizer": {
40
+ "log_mean_norm": "$f0_quantizer"
41
+ },
42
+ "f0_vq_n_units": 32
43
+ }
44
+ EOF
45
+
46
+ for split in train valid test; do
47
+ python examples/textless_nlp/pgslm/preprocess_f0.py \
48
+ $out_dir/$split.txt $f0_dir/$split --nshards=1 --rank=1 --sampling_rate=$sr
49
+
50
+ #NSHARDS=16
51
+ #seq 1 $NSHARDS | parallel -j $NSHARDS python examples/textless_nlp/pgslm/preprocess_f0.py \
52
+ # $out_dir/$split.txt $f0_dir/$split --nshards=$NSHARDS --sampling_rate=$sr --rank
53
+ done
54
+
55
+ # Please make sure that the number of shards (--nshards_list) is consistent across commands
56
+ python examples/textless_nlp/pgslm/prepare_dataset.py \
57
+ $meta_path $f0_dir --splits test valid train --nshards_list 1
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/scripts/prepare_f0_quantization.sh ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
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
+ set -eu
8
+
9
+ train_json=$1
10
+ sr=$2
11
+ nbins=$3
12
+ out_dir=$4
13
+ out_prefix=$5
14
+
15
+ f0_dir="$out_dir/f0"
16
+
17
+ python examples/textless_nlp/pgslm/preprocess_f0.py \
18
+ $train_json $f0_dir/${out_prefix}_f0_quant --nshards 1 --rank 1 --sampling_rate $sr
19
+
20
+ # NB: one can use parallel here:
21
+ # NSHARDS=16
22
+ #
23
+ #seq 1 $NSHARDS | parallel -j $NSHARDS python examples/textless_nlp/pgslm/preprocess_f0.py \
24
+ # $train_json $f0_dir/${out_prefix}_f0_quant --nshards $NSHARDS --sampling_rate $sr --rank
25
+
26
+ python examples/textless_nlp/pgslm/quantize_f0.py \
27
+ $train_json $f0_dir/${out_prefix}_f0_quant $out_dir $out_prefix --nbins $nbins --nshards 1 --normalize mean --log
omni_speech/infer/fairseq/examples/textless_nlp/pgslm/truncated_laplace.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 torch
7
+ import warnings
8
+
9
+
10
+ def truncated_laplace(mean, T, truncate_by_zero=False):
11
+ """Generating a sample from a Laplace distribution, possible left-truncated at zero.
12
+ A bit of explanation here https://stats.stackexchange.com/a/357598 .
13
+ """
14
+ assert isinstance(mean, torch.Tensor)
15
+
16
+ if not truncate_by_zero:
17
+ percentile = 0.0
18
+ else:
19
+ if not (mean >= 0.0).all():
20
+ warnings.warn(f"means are supposed to be non-negative, but got {mean}")
21
+ mean = torch.clamp_min(mean, 0.0)
22
+
23
+ lower_bound = mean.new_tensor([0.0])
24
+ percentile = 0.5 + 0.5 * torch.sign(lower_bound - mean) * (
25
+ 1.0 - torch.exp(-1.0 / T * torch.abs(mean - lower_bound))
26
+ )
27
+
28
+ p = torch.empty_like(mean).uniform_() * (1.0 - percentile) + percentile
29
+ return mean - T * torch.sign(p - 0.5) * torch.log(1 - 2 * torch.abs(p - 0.5))
omni_speech/infer/fairseq/examples/textless_nlp/speech-resynth/README.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Speech Resynthesis from Discrete Disentangled Self-Supervised Representations
3
+ Landing page with usfull resources for the [Speech Resynthesis from Discrete Disentangled Self-Supervised Representations](https://arxiv.org/abs/2104.00355) paper.
4
+
5
+ <p align="center"><img width="70%" src="img/fig.png" /></p>
6
+
7
+ __Abstract__: We propose using self-supervised discrete representations for the task of speech resynthesis. To generate disentangled representation, we separately extract low-bitrate representations for speech content, prosodic information, and speaker identity. This allows to synthesize speech in a controllable manner. We analyze various state-of-the-art, self-supervised representation learning methods and shed light on the advantages of each method while considering reconstruction quality and disentanglement properties. Specifically, we evaluate the F0 reconstruction, speaker identification performance (for both resynthesis and voice conversion), recordings' intelligibility, and overall quality using subjective human evaluation. Lastly, we demonstrate how these representations can be used for an ultra-lightweight speech codec. Using the obtained representations, we can get to a rate of 365 bits per second while providing better speech quality than the baseline methods.
8
+
9
+
10
+ ## Quick Links
11
+ - [Paper](https://arxiv.org/pdf/2104.00355.pdf)
12
+ - [Samples](https://speechbot.github.io/resynthesis/index.html)
13
+ - [Code](https://github.com/facebookresearch/speech-resynthesis)
14
+
15
+ The codebase for the [Speech Resynthesis from Discrete Disentangled Self-Supervised Representations](https://arxiv.org/abs/2104.00355) paper can be found under the following [repository](https://github.com/facebookresearch/speech-resynthesis).
16
+
17
+
18
+ ## Citation
19
+ ```
20
+ @inproceedings{polyak21_interspeech,
21
+ author={Adam Polyak and Yossi Adi and Jade Copet and
22
+ Eugene Kharitonov and Kushal Lakhotia and
23
+ Wei-Ning Hsu and Abdelrahman Mohamed and Emmanuel Dupoux},
24
+ title={{Speech Resynthesis from Discrete Disentangled Self-Supervised Representations}},
25
+ year=2021,
26
+ booktitle={Proc. Interspeech 2021},
27
+ }
28
+ ```
omni_speech/infer/fairseq/examples/translation/README.md ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Neural Machine Translation
2
+
3
+ This README contains instructions for [using pretrained translation models](#example-usage-torchhub)
4
+ as well as [training new models](#training-a-new-model).
5
+
6
+ ## Pre-trained models
7
+
8
+ Model | Description | Dataset | Download
9
+ ---|---|---|---
10
+ `conv.wmt14.en-fr` | Convolutional <br> ([Gehring et al., 2017](https://arxiv.org/abs/1705.03122)) | [WMT14 English-French](http://statmt.org/wmt14/translation-task.html#Download) | model: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/models/wmt14.v2.en-fr.fconv-py.tar.bz2) <br> newstest2014: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt14.v2.en-fr.newstest2014.tar.bz2) <br> newstest2012/2013: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt14.v2.en-fr.ntst1213.tar.bz2)
11
+ `conv.wmt14.en-de` | Convolutional <br> ([Gehring et al., 2017](https://arxiv.org/abs/1705.03122)) | [WMT14 English-German](http://statmt.org/wmt14/translation-task.html#Download) | model: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/models/wmt14.en-de.fconv-py.tar.bz2) <br> newstest2014: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt14.en-de.newstest2014.tar.bz2)
12
+ `conv.wmt17.en-de` | Convolutional <br> ([Gehring et al., 2017](https://arxiv.org/abs/1705.03122)) | [WMT17 English-German](http://statmt.org/wmt17/translation-task.html#Download) | model: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/models/wmt17.v2.en-de.fconv-py.tar.bz2) <br> newstest2014: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt17.v2.en-de.newstest2014.tar.bz2)
13
+ `transformer.wmt14.en-fr` | Transformer <br> ([Ott et al., 2018](https://arxiv.org/abs/1806.00187)) | [WMT14 English-French](http://statmt.org/wmt14/translation-task.html#Download) | model: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/models/wmt14.en-fr.joined-dict.transformer.tar.bz2) <br> newstest2014: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt14.en-fr.joined-dict.newstest2014.tar.bz2)
14
+ `transformer.wmt16.en-de` | Transformer <br> ([Ott et al., 2018](https://arxiv.org/abs/1806.00187)) | [WMT16 English-German](https://drive.google.com/uc?export=download&id=0B_bZck-ksdkpM25jRUN2X2UxMm8) | model: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/models/wmt16.en-de.joined-dict.transformer.tar.bz2) <br> newstest2014: <br> [download (.tar.bz2)](https://dl.fbaipublicfiles.com/fairseq/data/wmt16.en-de.joined-dict.newstest2014.tar.bz2)
15
+ `transformer.wmt18.en-de` | Transformer <br> ([Edunov et al., 2018](https://arxiv.org/abs/1808.09381)) <br> WMT'18 winner | [WMT'18 English-German](http://www.statmt.org/wmt18/translation-task.html) | model: <br> [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt18.en-de.ensemble.tar.gz) <br> See NOTE in the archive
16
+ `transformer.wmt19.en-de` | Transformer <br> ([Ng et al., 2019](https://arxiv.org/abs/1907.06616)) <br> WMT'19 winner | [WMT'19 English-German](http://www.statmt.org/wmt19/translation-task.html) | model: <br> [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-de.joined-dict.ensemble.tar.gz)
17
+ `transformer.wmt19.de-en` | Transformer <br> ([Ng et al., 2019](https://arxiv.org/abs/1907.06616)) <br> WMT'19 winner | [WMT'19 German-English](http://www.statmt.org/wmt19/translation-task.html) | model: <br> [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.de-en.joined-dict.ensemble.tar.gz)
18
+ `transformer.wmt19.en-ru` | Transformer <br> ([Ng et al., 2019](https://arxiv.org/abs/1907.06616)) <br> WMT'19 winner | [WMT'19 English-Russian](http://www.statmt.org/wmt19/translation-task.html) | model: <br> [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.en-ru.ensemble.tar.gz)
19
+ `transformer.wmt19.ru-en` | Transformer <br> ([Ng et al., 2019](https://arxiv.org/abs/1907.06616)) <br> WMT'19 winner | [WMT'19 Russian-English](http://www.statmt.org/wmt19/translation-task.html) | model: <br> [download (.tar.gz)](https://dl.fbaipublicfiles.com/fairseq/models/wmt19.ru-en.ensemble.tar.gz)
20
+
21
+ ## Example usage (torch.hub)
22
+
23
+ We require a few additional Python dependencies for preprocessing:
24
+ ```bash
25
+ pip install fastBPE sacremoses subword_nmt
26
+ ```
27
+
28
+ Interactive translation via PyTorch Hub:
29
+ ```python
30
+ import torch
31
+
32
+ # List available models
33
+ torch.hub.list('pytorch/fairseq') # [..., 'transformer.wmt16.en-de', ... ]
34
+
35
+ # Load a transformer trained on WMT'16 En-De
36
+ # Note: WMT'19 models use fastBPE instead of subword_nmt, see instructions below
37
+ en2de = torch.hub.load('pytorch/fairseq', 'transformer.wmt16.en-de',
38
+ tokenizer='moses', bpe='subword_nmt')
39
+ en2de.eval() # disable dropout
40
+
41
+ # The underlying model is available under the *models* attribute
42
+ assert isinstance(en2de.models[0], fairseq.models.transformer.TransformerModel)
43
+
44
+ # Move model to GPU for faster translation
45
+ en2de.cuda()
46
+
47
+ # Translate a sentence
48
+ en2de.translate('Hello world!')
49
+ # 'Hallo Welt!'
50
+
51
+ # Batched translation
52
+ en2de.translate(['Hello world!', 'The cat sat on the mat.'])
53
+ # ['Hallo Welt!', 'Die Katze saß auf der Matte.']
54
+ ```
55
+
56
+ Loading custom models:
57
+ ```python
58
+ from fairseq.models.transformer import TransformerModel
59
+ zh2en = TransformerModel.from_pretrained(
60
+ '/path/to/checkpoints',
61
+ checkpoint_file='checkpoint_best.pt',
62
+ data_name_or_path='data-bin/wmt17_zh_en_full',
63
+ bpe='subword_nmt',
64
+ bpe_codes='data-bin/wmt17_zh_en_full/zh.code'
65
+ )
66
+ zh2en.translate('你好 世界')
67
+ # 'Hello World'
68
+ ```
69
+
70
+ If you are using a `transformer.wmt19` models, you will need to set the `bpe`
71
+ argument to `'fastbpe'` and (optionally) load the 4-model ensemble:
72
+ ```python
73
+ en2de = torch.hub.load('pytorch/fairseq', 'transformer.wmt19.en-de',
74
+ checkpoint_file='model1.pt:model2.pt:model3.pt:model4.pt',
75
+ tokenizer='moses', bpe='fastbpe')
76
+ en2de.eval() # disable dropout
77
+ ```
78
+
79
+ ## Example usage (CLI tools)
80
+
81
+ Generation with the binarized test sets can be run in batch mode as follows, e.g. for WMT 2014 English-French on a GTX-1080ti:
82
+ ```bash
83
+ mkdir -p data-bin
84
+ curl https://dl.fbaipublicfiles.com/fairseq/models/wmt14.v2.en-fr.fconv-py.tar.bz2 | tar xvjf - -C data-bin
85
+ curl https://dl.fbaipublicfiles.com/fairseq/data/wmt14.v2.en-fr.newstest2014.tar.bz2 | tar xvjf - -C data-bin
86
+ fairseq-generate data-bin/wmt14.en-fr.newstest2014 \
87
+ --path data-bin/wmt14.en-fr.fconv-py/model.pt \
88
+ --beam 5 --batch-size 128 --remove-bpe | tee /tmp/gen.out
89
+ # ...
90
+ # | Translated 3003 sentences (96311 tokens) in 166.0s (580.04 tokens/s)
91
+ # | Generate test with beam=5: BLEU4 = 40.83, 67.5/46.9/34.4/25.5 (BP=1.000, ratio=1.006, syslen=83262, reflen=82787)
92
+
93
+ # Compute BLEU score
94
+ grep ^H /tmp/gen.out | cut -f3- > /tmp/gen.out.sys
95
+ grep ^T /tmp/gen.out | cut -f2- > /tmp/gen.out.ref
96
+ fairseq-score --sys /tmp/gen.out.sys --ref /tmp/gen.out.ref
97
+ # BLEU4 = 40.83, 67.5/46.9/34.4/25.5 (BP=1.000, ratio=1.006, syslen=83262, reflen=82787)
98
+ ```
99
+
100
+ ## Training a new model
101
+
102
+ ### IWSLT'14 German to English (Transformer)
103
+
104
+ The following instructions can be used to train a Transformer model on the [IWSLT'14 German to English dataset](http://workshop2014.iwslt.org/downloads/proceeding.pdf).
105
+
106
+ First download and preprocess the data:
107
+ ```bash
108
+ # Download and prepare the data
109
+ cd examples/translation/
110
+ bash prepare-iwslt14.sh
111
+ cd ../..
112
+
113
+ # Preprocess/binarize the data
114
+ TEXT=examples/translation/iwslt14.tokenized.de-en
115
+ fairseq-preprocess --source-lang de --target-lang en \
116
+ --trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
117
+ --destdir data-bin/iwslt14.tokenized.de-en \
118
+ --workers 20
119
+ ```
120
+
121
+ Next we'll train a Transformer translation model over this data:
122
+ ```bash
123
+ CUDA_VISIBLE_DEVICES=0 fairseq-train \
124
+ data-bin/iwslt14.tokenized.de-en \
125
+ --arch transformer_iwslt_de_en --share-decoder-input-output-embed \
126
+ --optimizer adam --adam-betas '(0.9, 0.98)' --clip-norm 0.0 \
127
+ --lr 5e-4 --lr-scheduler inverse_sqrt --warmup-updates 4000 \
128
+ --dropout 0.3 --weight-decay 0.0001 \
129
+ --criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
130
+ --max-tokens 4096 \
131
+ --eval-bleu \
132
+ --eval-bleu-args '{"beam": 5, "max_len_a": 1.2, "max_len_b": 10}' \
133
+ --eval-bleu-detok moses \
134
+ --eval-bleu-remove-bpe \
135
+ --eval-bleu-print-samples \
136
+ --best-checkpoint-metric bleu --maximize-best-checkpoint-metric
137
+ ```
138
+
139
+ Finally we can evaluate our trained model:
140
+ ```bash
141
+ fairseq-generate data-bin/iwslt14.tokenized.de-en \
142
+ --path checkpoints/checkpoint_best.pt \
143
+ --batch-size 128 --beam 5 --remove-bpe
144
+ ```
145
+
146
+ ### WMT'14 English to German (Convolutional)
147
+
148
+ The following instructions can be used to train a Convolutional translation model on the WMT English to German dataset.
149
+ See the [Scaling NMT README](../scaling_nmt/README.md) for instructions to train a Transformer translation model on this data.
150
+
151
+ The WMT English to German dataset can be preprocessed using the `prepare-wmt14en2de.sh` script.
152
+ By default it will produce a dataset that was modeled after [Attention Is All You Need (Vaswani et al., 2017)](https://arxiv.org/abs/1706.03762), but with additional news-commentary-v12 data from WMT'17.
153
+
154
+ To use only data available in WMT'14 or to replicate results obtained in the original [Convolutional Sequence to Sequence Learning (Gehring et al., 2017)](https://arxiv.org/abs/1705.03122) paper, please use the `--icml17` option.
155
+
156
+ ```bash
157
+ # Download and prepare the data
158
+ cd examples/translation/
159
+ # WMT'17 data:
160
+ bash prepare-wmt14en2de.sh
161
+ # or to use WMT'14 data:
162
+ # bash prepare-wmt14en2de.sh --icml17
163
+ cd ../..
164
+
165
+ # Binarize the dataset
166
+ TEXT=examples/translation/wmt17_en_de
167
+ fairseq-preprocess \
168
+ --source-lang en --target-lang de \
169
+ --trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
170
+ --destdir data-bin/wmt17_en_de --thresholdtgt 0 --thresholdsrc 0 \
171
+ --workers 20
172
+
173
+ # Train the model
174
+ mkdir -p checkpoints/fconv_wmt_en_de
175
+ fairseq-train \
176
+ data-bin/wmt17_en_de \
177
+ --arch fconv_wmt_en_de \
178
+ --dropout 0.2 \
179
+ --criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
180
+ --optimizer nag --clip-norm 0.1 \
181
+ --lr 0.5 --lr-scheduler fixed --force-anneal 50 \
182
+ --max-tokens 4000 \
183
+ --save-dir checkpoints/fconv_wmt_en_de
184
+
185
+ # Evaluate
186
+ fairseq-generate data-bin/wmt17_en_de \
187
+ --path checkpoints/fconv_wmt_en_de/checkpoint_best.pt \
188
+ --beam 5 --remove-bpe
189
+ ```
190
+
191
+ ### WMT'14 English to French
192
+ ```bash
193
+ # Download and prepare the data
194
+ cd examples/translation/
195
+ bash prepare-wmt14en2fr.sh
196
+ cd ../..
197
+
198
+ # Binarize the dataset
199
+ TEXT=examples/translation/wmt14_en_fr
200
+ fairseq-preprocess \
201
+ --source-lang en --target-lang fr \
202
+ --trainpref $TEXT/train --validpref $TEXT/valid --testpref $TEXT/test \
203
+ --destdir data-bin/wmt14_en_fr --thresholdtgt 0 --thresholdsrc 0 \
204
+ --workers 60
205
+
206
+ # Train the model
207
+ mkdir -p checkpoints/fconv_wmt_en_fr
208
+ fairseq-train \
209
+ data-bin/wmt14_en_fr \
210
+ --arch fconv_wmt_en_fr \
211
+ --dropout 0.1 \
212
+ --criterion label_smoothed_cross_entropy --label-smoothing 0.1 \
213
+ --optimizer nag --clip-norm 0.1 \
214
+ --lr 0.5 --lr-scheduler fixed --force-anneal 50 \
215
+ --max-tokens 3000 \
216
+ --save-dir checkpoints/fconv_wmt_en_fr
217
+
218
+ # Evaluate
219
+ fairseq-generate \
220
+ data-bin/fconv_wmt_en_fr \
221
+ --path checkpoints/fconv_wmt_en_fr/checkpoint_best.pt \
222
+ --beam 5 --remove-bpe
223
+ ```
224
+
225
+ ## Multilingual Translation
226
+
227
+ We also support training multilingual translation models. In this example we'll
228
+ train a multilingual `{de,fr}-en` translation model using the IWSLT'17 datasets.
229
+
230
+ Note that we use slightly different preprocessing here than for the IWSLT'14
231
+ En-De data above. In particular we learn a joint BPE code for all three
232
+ languages and use fairseq-interactive and sacrebleu for scoring the test set.
233
+
234
+ ```bash
235
+ # First install sacrebleu and sentencepiece
236
+ pip install sacrebleu sentencepiece
237
+
238
+ # Then download and preprocess the data
239
+ cd examples/translation/
240
+ bash prepare-iwslt17-multilingual.sh
241
+ cd ../..
242
+
243
+ # Binarize the de-en dataset
244
+ TEXT=examples/translation/iwslt17.de_fr.en.bpe16k
245
+ fairseq-preprocess --source-lang de --target-lang en \
246
+ --trainpref $TEXT/train.bpe.de-en \
247
+ --validpref $TEXT/valid0.bpe.de-en,$TEXT/valid1.bpe.de-en,$TEXT/valid2.bpe.de-en,$TEXT/valid3.bpe.de-en,$TEXT/valid4.bpe.de-en,$TEXT/valid5.bpe.de-en \
248
+ --destdir data-bin/iwslt17.de_fr.en.bpe16k \
249
+ --workers 10
250
+
251
+ # Binarize the fr-en dataset
252
+ # NOTE: it's important to reuse the en dictionary from the previous step
253
+ fairseq-preprocess --source-lang fr --target-lang en \
254
+ --trainpref $TEXT/train.bpe.fr-en \
255
+ --validpref $TEXT/valid0.bpe.fr-en,$TEXT/valid1.bpe.fr-en,$TEXT/valid2.bpe.fr-en,$TEXT/valid3.bpe.fr-en,$TEXT/valid4.bpe.fr-en,$TEXT/valid5.bpe.fr-en \
256
+ --tgtdict data-bin/iwslt17.de_fr.en.bpe16k/dict.en.txt \
257
+ --destdir data-bin/iwslt17.de_fr.en.bpe16k \
258
+ --workers 10
259
+
260
+ # Train a multilingual transformer model
261
+ # NOTE: the command below assumes 1 GPU, but accumulates gradients from
262
+ # 8 fwd/bwd passes to simulate training on 8 GPUs
263
+ mkdir -p checkpoints/multilingual_transformer
264
+ CUDA_VISIBLE_DEVICES=0 fairseq-train data-bin/iwslt17.de_fr.en.bpe16k/ \
265
+ --max-epoch 50 \
266
+ --ddp-backend=legacy_ddp \
267
+ --task multilingual_translation --lang-pairs de-en,fr-en \
268
+ --arch multilingual_transformer_iwslt_de_en \
269
+ --share-decoders --share-decoder-input-output-embed \
270
+ --optimizer adam --adam-betas '(0.9, 0.98)' \
271
+ --lr 0.0005 --lr-scheduler inverse_sqrt \
272
+ --warmup-updates 4000 --warmup-init-lr '1e-07' \
273
+ --label-smoothing 0.1 --criterion label_smoothed_cross_entropy \
274
+ --dropout 0.3 --weight-decay 0.0001 \
275
+ --save-dir checkpoints/multilingual_transformer \
276
+ --max-tokens 4000 \
277
+ --update-freq 8
278
+
279
+ # Generate and score the test set with sacrebleu
280
+ SRC=de
281
+ sacrebleu --test-set iwslt17 --language-pair ${SRC}-en --echo src \
282
+ | python scripts/spm_encode.py --model examples/translation/iwslt17.de_fr.en.bpe16k/sentencepiece.bpe.model \
283
+ > iwslt17.test.${SRC}-en.${SRC}.bpe
284
+ cat iwslt17.test.${SRC}-en.${SRC}.bpe \
285
+ | fairseq-interactive data-bin/iwslt17.de_fr.en.bpe16k/ \
286
+ --task multilingual_translation --lang-pairs de-en,fr-en \
287
+ --source-lang ${SRC} --target-lang en \
288
+ --path checkpoints/multilingual_transformer/checkpoint_best.pt \
289
+ --buffer-size 2000 --batch-size 128 \
290
+ --beam 5 --remove-bpe=sentencepiece \
291
+ > iwslt17.test.${SRC}-en.en.sys
292
+ grep ^H iwslt17.test.${SRC}-en.en.sys | cut -f3 \
293
+ | sacrebleu --test-set iwslt17 --language-pair ${SRC}-en
294
+ ```
295
+
296
+ ##### Argument format during inference
297
+
298
+ During inference it is required to specify a single `--source-lang` and
299
+ `--target-lang`, which indicates the inference langauge direction.
300
+ `--lang-pairs`, `--encoder-langtok`, `--decoder-langtok` have to be set to
301
+ the same value as training.
omni_speech/infer/fairseq/examples/translation/prepare-iwslt14.sh ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Adapted from https://github.com/facebookresearch/MIXER/blob/master/prepareData.sh
4
+
5
+ echo 'Cloning Moses github repository (for tokenization scripts)...'
6
+ git clone https://github.com/moses-smt/mosesdecoder.git
7
+
8
+ echo 'Cloning Subword NMT repository (for BPE pre-processing)...'
9
+ git clone https://github.com/rsennrich/subword-nmt.git
10
+
11
+ SCRIPTS=mosesdecoder/scripts
12
+ TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
13
+ LC=$SCRIPTS/tokenizer/lowercase.perl
14
+ CLEAN=$SCRIPTS/training/clean-corpus-n.perl
15
+ BPEROOT=subword-nmt/subword_nmt
16
+ BPE_TOKENS=10000
17
+
18
+ URL="http://dl.fbaipublicfiles.com/fairseq/data/iwslt14/de-en.tgz"
19
+ GZ=de-en.tgz
20
+
21
+ if [ ! -d "$SCRIPTS" ]; then
22
+ echo "Please set SCRIPTS variable correctly to point to Moses scripts."
23
+ exit
24
+ fi
25
+
26
+ src=de
27
+ tgt=en
28
+ lang=de-en
29
+ prep=iwslt14.tokenized.de-en
30
+ tmp=$prep/tmp
31
+ orig=orig
32
+
33
+ mkdir -p $orig $tmp $prep
34
+
35
+ echo "Downloading data from ${URL}..."
36
+ cd $orig
37
+ wget "$URL"
38
+
39
+ if [ -f $GZ ]; then
40
+ echo "Data successfully downloaded."
41
+ else
42
+ echo "Data not successfully downloaded."
43
+ exit
44
+ fi
45
+
46
+ tar zxvf $GZ
47
+ cd ..
48
+
49
+ echo "pre-processing train data..."
50
+ for l in $src $tgt; do
51
+ f=train.tags.$lang.$l
52
+ tok=train.tags.$lang.tok.$l
53
+
54
+ cat $orig/$lang/$f | \
55
+ grep -v '<url>' | \
56
+ grep -v '<talkid>' | \
57
+ grep -v '<keywords>' | \
58
+ sed -e 's/<title>//g' | \
59
+ sed -e 's/<\/title>//g' | \
60
+ sed -e 's/<description>//g' | \
61
+ sed -e 's/<\/description>//g' | \
62
+ perl $TOKENIZER -threads 8 -l $l > $tmp/$tok
63
+ echo ""
64
+ done
65
+ perl $CLEAN -ratio 1.5 $tmp/train.tags.$lang.tok $src $tgt $tmp/train.tags.$lang.clean 1 175
66
+ for l in $src $tgt; do
67
+ perl $LC < $tmp/train.tags.$lang.clean.$l > $tmp/train.tags.$lang.$l
68
+ done
69
+
70
+ echo "pre-processing valid/test data..."
71
+ for l in $src $tgt; do
72
+ for o in `ls $orig/$lang/IWSLT14.TED*.$l.xml`; do
73
+ fname=${o##*/}
74
+ f=$tmp/${fname%.*}
75
+ echo $o $f
76
+ grep '<seg id' $o | \
77
+ sed -e 's/<seg id="[0-9]*">\s*//g' | \
78
+ sed -e 's/\s*<\/seg>\s*//g' | \
79
+ sed -e "s/\’/\'/g" | \
80
+ perl $TOKENIZER -threads 8 -l $l | \
81
+ perl $LC > $f
82
+ echo ""
83
+ done
84
+ done
85
+
86
+
87
+ echo "creating train, valid, test..."
88
+ for l in $src $tgt; do
89
+ awk '{if (NR%23 == 0) print $0; }' $tmp/train.tags.de-en.$l > $tmp/valid.$l
90
+ awk '{if (NR%23 != 0) print $0; }' $tmp/train.tags.de-en.$l > $tmp/train.$l
91
+
92
+ cat $tmp/IWSLT14.TED.dev2010.de-en.$l \
93
+ $tmp/IWSLT14.TEDX.dev2012.de-en.$l \
94
+ $tmp/IWSLT14.TED.tst2010.de-en.$l \
95
+ $tmp/IWSLT14.TED.tst2011.de-en.$l \
96
+ $tmp/IWSLT14.TED.tst2012.de-en.$l \
97
+ > $tmp/test.$l
98
+ done
99
+
100
+ TRAIN=$tmp/train.en-de
101
+ BPE_CODE=$prep/code
102
+ rm -f $TRAIN
103
+ for l in $src $tgt; do
104
+ cat $tmp/train.$l >> $TRAIN
105
+ done
106
+
107
+ echo "learn_bpe.py on ${TRAIN}..."
108
+ python $BPEROOT/learn_bpe.py -s $BPE_TOKENS < $TRAIN > $BPE_CODE
109
+
110
+ for L in $src $tgt; do
111
+ for f in train.$L valid.$L test.$L; do
112
+ echo "apply_bpe.py to ${f}..."
113
+ python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $prep/$f
114
+ done
115
+ done
omni_speech/infer/fairseq/examples/translation/prepare-iwslt17-multilingual.sh ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+ # All rights reserved.
4
+ #
5
+ # This source code is licensed under the license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ SRCS=(
9
+ "de"
10
+ "fr"
11
+ )
12
+ TGT=en
13
+
14
+ ROOT=$(dirname "$0")
15
+ SCRIPTS=$ROOT/../../scripts
16
+ SPM_TRAIN=$SCRIPTS/spm_train.py
17
+ SPM_ENCODE=$SCRIPTS/spm_encode.py
18
+
19
+ BPESIZE=16384
20
+ ORIG=$ROOT/iwslt17_orig
21
+ DATA=$ROOT/iwslt17.de_fr.en.bpe16k
22
+ mkdir -p "$ORIG" "$DATA"
23
+
24
+ TRAIN_MINLEN=1 # remove sentences with <1 BPE token
25
+ TRAIN_MAXLEN=250 # remove sentences with >250 BPE tokens
26
+
27
+ URLS=(
28
+ "https://wit3.fbk.eu/archive/2017-01-trnted/texts/de/en/de-en.tgz"
29
+ "https://wit3.fbk.eu/archive/2017-01-trnted/texts/fr/en/fr-en.tgz"
30
+ )
31
+ ARCHIVES=(
32
+ "de-en.tgz"
33
+ "fr-en.tgz"
34
+ )
35
+ VALID_SETS=(
36
+ "IWSLT17.TED.dev2010.de-en IWSLT17.TED.tst2010.de-en IWSLT17.TED.tst2011.de-en IWSLT17.TED.tst2012.de-en IWSLT17.TED.tst2013.de-en IWSLT17.TED.tst2014.de-en IWSLT17.TED.tst2015.de-en"
37
+ "IWSLT17.TED.dev2010.fr-en IWSLT17.TED.tst2010.fr-en IWSLT17.TED.tst2011.fr-en IWSLT17.TED.tst2012.fr-en IWSLT17.TED.tst2013.fr-en IWSLT17.TED.tst2014.fr-en IWSLT17.TED.tst2015.fr-en"
38
+ )
39
+
40
+ # download and extract data
41
+ for ((i=0;i<${#URLS[@]};++i)); do
42
+ ARCHIVE=$ORIG/${ARCHIVES[i]}
43
+ if [ -f "$ARCHIVE" ]; then
44
+ echo "$ARCHIVE already exists, skipping download"
45
+ else
46
+ URL=${URLS[i]}
47
+ wget -P "$ORIG" "$URL"
48
+ if [ -f "$ARCHIVE" ]; then
49
+ echo "$URL successfully downloaded."
50
+ else
51
+ echo "$URL not successfully downloaded."
52
+ exit 1
53
+ fi
54
+ fi
55
+ FILE=${ARCHIVE: -4}
56
+ if [ -e "$FILE" ]; then
57
+ echo "$FILE already exists, skipping extraction"
58
+ else
59
+ tar -C "$ORIG" -xzvf "$ARCHIVE"
60
+ fi
61
+ done
62
+
63
+ echo "pre-processing train data..."
64
+ for SRC in "${SRCS[@]}"; do
65
+ for LANG in "${SRC}" "${TGT}"; do
66
+ cat "$ORIG/${SRC}-${TGT}/train.tags.${SRC}-${TGT}.${LANG}" \
67
+ | grep -v '<url>' \
68
+ | grep -v '<talkid>' \
69
+ | grep -v '<keywords>' \
70
+ | grep -v '<speaker>' \
71
+ | grep -v '<reviewer' \
72
+ | grep -v '<translator' \
73
+ | grep -v '<doc' \
74
+ | grep -v '</doc>' \
75
+ | sed -e 's/<title>//g' \
76
+ | sed -e 's/<\/title>//g' \
77
+ | sed -e 's/<description>//g' \
78
+ | sed -e 's/<\/description>//g' \
79
+ | sed 's/^\s*//g' \
80
+ | sed 's/\s*$//g' \
81
+ > "$DATA/train.${SRC}-${TGT}.${LANG}"
82
+ done
83
+ done
84
+
85
+ echo "pre-processing valid data..."
86
+ for ((i=0;i<${#SRCS[@]};++i)); do
87
+ SRC=${SRCS[i]}
88
+ VALID_SET=(${VALID_SETS[i]})
89
+ for ((j=0;j<${#VALID_SET[@]};++j)); do
90
+ FILE=${VALID_SET[j]}
91
+ for LANG in "$SRC" "$TGT"; do
92
+ grep '<seg id' "$ORIG/${SRC}-${TGT}/${FILE}.${LANG}.xml" \
93
+ | sed -e 's/<seg id="[0-9]*">\s*//g' \
94
+ | sed -e 's/\s*<\/seg>\s*//g' \
95
+ | sed -e "s/\’/\'/g" \
96
+ > "$DATA/valid${j}.${SRC}-${TGT}.${LANG}"
97
+ done
98
+ done
99
+ done
100
+
101
+ # learn BPE with sentencepiece
102
+ TRAIN_FILES=$(for SRC in "${SRCS[@]}"; do echo $DATA/train.${SRC}-${TGT}.${SRC}; echo $DATA/train.${SRC}-${TGT}.${TGT}; done | tr "\n" ",")
103
+ echo "learning joint BPE over ${TRAIN_FILES}..."
104
+ python "$SPM_TRAIN" \
105
+ --input=$TRAIN_FILES \
106
+ --model_prefix=$DATA/sentencepiece.bpe \
107
+ --vocab_size=$BPESIZE \
108
+ --character_coverage=1.0 \
109
+ --model_type=bpe
110
+
111
+ # encode train/valid
112
+ echo "encoding train with learned BPE..."
113
+ for SRC in "${SRCS[@]}"; do
114
+ python "$SPM_ENCODE" \
115
+ --model "$DATA/sentencepiece.bpe.model" \
116
+ --output_format=piece \
117
+ --inputs $DATA/train.${SRC}-${TGT}.${SRC} $DATA/train.${SRC}-${TGT}.${TGT} \
118
+ --outputs $DATA/train.bpe.${SRC}-${TGT}.${SRC} $DATA/train.bpe.${SRC}-${TGT}.${TGT} \
119
+ --min-len $TRAIN_MINLEN --max-len $TRAIN_MAXLEN
120
+ done
121
+
122
+ echo "encoding valid with learned BPE..."
123
+ for ((i=0;i<${#SRCS[@]};++i)); do
124
+ SRC=${SRCS[i]}
125
+ VALID_SET=(${VALID_SETS[i]})
126
+ for ((j=0;j<${#VALID_SET[@]};++j)); do
127
+ python "$SPM_ENCODE" \
128
+ --model "$DATA/sentencepiece.bpe.model" \
129
+ --output_format=piece \
130
+ --inputs $DATA/valid${j}.${SRC}-${TGT}.${SRC} $DATA/valid${j}.${SRC}-${TGT}.${TGT} \
131
+ --outputs $DATA/valid${j}.bpe.${SRC}-${TGT}.${SRC} $DATA/valid${j}.bpe.${SRC}-${TGT}.${TGT}
132
+ done
133
+ done
omni_speech/infer/fairseq/examples/translation/prepare-wmt14en2de.sh ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Adapted from https://github.com/facebookresearch/MIXER/blob/master/prepareData.sh
3
+
4
+ echo 'Cloning Moses github repository (for tokenization scripts)...'
5
+ git clone https://github.com/moses-smt/mosesdecoder.git
6
+
7
+ echo 'Cloning Subword NMT repository (for BPE pre-processing)...'
8
+ git clone https://github.com/rsennrich/subword-nmt.git
9
+
10
+ SCRIPTS=mosesdecoder/scripts
11
+ TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
12
+ CLEAN=$SCRIPTS/training/clean-corpus-n.perl
13
+ NORM_PUNC=$SCRIPTS/tokenizer/normalize-punctuation.perl
14
+ REM_NON_PRINT_CHAR=$SCRIPTS/tokenizer/remove-non-printing-char.perl
15
+ BPEROOT=subword-nmt/subword_nmt
16
+ BPE_TOKENS=40000
17
+
18
+ URLS=(
19
+ "http://statmt.org/wmt13/training-parallel-europarl-v7.tgz"
20
+ "http://statmt.org/wmt13/training-parallel-commoncrawl.tgz"
21
+ "http://data.statmt.org/wmt17/translation-task/training-parallel-nc-v12.tgz"
22
+ "http://data.statmt.org/wmt17/translation-task/dev.tgz"
23
+ "http://statmt.org/wmt14/test-full.tgz"
24
+ )
25
+ FILES=(
26
+ "training-parallel-europarl-v7.tgz"
27
+ "training-parallel-commoncrawl.tgz"
28
+ "training-parallel-nc-v12.tgz"
29
+ "dev.tgz"
30
+ "test-full.tgz"
31
+ )
32
+ CORPORA=(
33
+ "training/europarl-v7.de-en"
34
+ "commoncrawl.de-en"
35
+ "training/news-commentary-v12.de-en"
36
+ )
37
+
38
+ # This will make the dataset compatible to the one used in "Convolutional Sequence to Sequence Learning"
39
+ # https://arxiv.org/abs/1705.03122
40
+ if [ "$1" == "--icml17" ]; then
41
+ URLS[2]="http://statmt.org/wmt14/training-parallel-nc-v9.tgz"
42
+ FILES[2]="training-parallel-nc-v9.tgz"
43
+ CORPORA[2]="training/news-commentary-v9.de-en"
44
+ OUTDIR=wmt14_en_de
45
+ else
46
+ OUTDIR=wmt17_en_de
47
+ fi
48
+
49
+ if [ ! -d "$SCRIPTS" ]; then
50
+ echo "Please set SCRIPTS variable correctly to point to Moses scripts."
51
+ exit
52
+ fi
53
+
54
+ src=en
55
+ tgt=de
56
+ lang=en-de
57
+ prep=$OUTDIR
58
+ tmp=$prep/tmp
59
+ orig=orig
60
+ dev=dev/newstest2013
61
+
62
+ mkdir -p $orig $tmp $prep
63
+
64
+ cd $orig
65
+
66
+ for ((i=0;i<${#URLS[@]};++i)); do
67
+ file=${FILES[i]}
68
+ if [ -f $file ]; then
69
+ echo "$file already exists, skipping download"
70
+ else
71
+ url=${URLS[i]}
72
+ wget "$url"
73
+ if [ -f $file ]; then
74
+ echo "$url successfully downloaded."
75
+ else
76
+ echo "$url not successfully downloaded."
77
+ exit -1
78
+ fi
79
+ if [ ${file: -4} == ".tgz" ]; then
80
+ tar zxvf $file
81
+ elif [ ${file: -4} == ".tar" ]; then
82
+ tar xvf $file
83
+ fi
84
+ fi
85
+ done
86
+ cd ..
87
+
88
+ echo "pre-processing train data..."
89
+ for l in $src $tgt; do
90
+ rm $tmp/train.tags.$lang.tok.$l
91
+ for f in "${CORPORA[@]}"; do
92
+ cat $orig/$f.$l | \
93
+ perl $NORM_PUNC $l | \
94
+ perl $REM_NON_PRINT_CHAR | \
95
+ perl $TOKENIZER -threads 8 -a -l $l >> $tmp/train.tags.$lang.tok.$l
96
+ done
97
+ done
98
+
99
+ echo "pre-processing test data..."
100
+ for l in $src $tgt; do
101
+ if [ "$l" == "$src" ]; then
102
+ t="src"
103
+ else
104
+ t="ref"
105
+ fi
106
+ grep '<seg id' $orig/test-full/newstest2014-deen-$t.$l.sgm | \
107
+ sed -e 's/<seg id="[0-9]*">\s*//g' | \
108
+ sed -e 's/\s*<\/seg>\s*//g' | \
109
+ sed -e "s/\’/\'/g" | \
110
+ perl $TOKENIZER -threads 8 -a -l $l > $tmp/test.$l
111
+ echo ""
112
+ done
113
+
114
+ echo "splitting train and valid..."
115
+ for l in $src $tgt; do
116
+ awk '{if (NR%100 == 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/valid.$l
117
+ awk '{if (NR%100 != 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/train.$l
118
+ done
119
+
120
+ TRAIN=$tmp/train.de-en
121
+ BPE_CODE=$prep/code
122
+ rm -f $TRAIN
123
+ for l in $src $tgt; do
124
+ cat $tmp/train.$l >> $TRAIN
125
+ done
126
+
127
+ echo "learn_bpe.py on ${TRAIN}..."
128
+ python $BPEROOT/learn_bpe.py -s $BPE_TOKENS < $TRAIN > $BPE_CODE
129
+
130
+ for L in $src $tgt; do
131
+ for f in train.$L valid.$L test.$L; do
132
+ echo "apply_bpe.py to ${f}..."
133
+ python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $tmp/bpe.$f
134
+ done
135
+ done
136
+
137
+ perl $CLEAN -ratio 1.5 $tmp/bpe.train $src $tgt $prep/train 1 250
138
+ perl $CLEAN -ratio 1.5 $tmp/bpe.valid $src $tgt $prep/valid 1 250
139
+
140
+ for L in $src $tgt; do
141
+ cp $tmp/bpe.test.$L $prep/test.$L
142
+ done
omni_speech/infer/fairseq/examples/translation/prepare-wmt14en2fr.sh ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Adapted from https://github.com/facebookresearch/MIXER/blob/master/prepareData.sh
3
+
4
+ echo 'Cloning Moses github repository (for tokenization scripts)...'
5
+ git clone https://github.com/moses-smt/mosesdecoder.git
6
+
7
+ echo 'Cloning Subword NMT repository (for BPE pre-processing)...'
8
+ git clone https://github.com/rsennrich/subword-nmt.git
9
+
10
+ SCRIPTS=mosesdecoder/scripts
11
+ TOKENIZER=$SCRIPTS/tokenizer/tokenizer.perl
12
+ CLEAN=$SCRIPTS/training/clean-corpus-n.perl
13
+ NORM_PUNC=$SCRIPTS/tokenizer/normalize-punctuation.perl
14
+ REM_NON_PRINT_CHAR=$SCRIPTS/tokenizer/remove-non-printing-char.perl
15
+ BPEROOT=subword-nmt/subword_nmt
16
+ BPE_TOKENS=40000
17
+
18
+ URLS=(
19
+ "http://statmt.org/wmt13/training-parallel-europarl-v7.tgz"
20
+ "http://statmt.org/wmt13/training-parallel-commoncrawl.tgz"
21
+ "http://statmt.org/wmt13/training-parallel-un.tgz"
22
+ "http://statmt.org/wmt14/training-parallel-nc-v9.tgz"
23
+ "http://statmt.org/wmt10/training-giga-fren.tar"
24
+ "http://statmt.org/wmt14/test-full.tgz"
25
+ )
26
+ FILES=(
27
+ "training-parallel-europarl-v7.tgz"
28
+ "training-parallel-commoncrawl.tgz"
29
+ "training-parallel-un.tgz"
30
+ "training-parallel-nc-v9.tgz"
31
+ "training-giga-fren.tar"
32
+ "test-full.tgz"
33
+ )
34
+ CORPORA=(
35
+ "training/europarl-v7.fr-en"
36
+ "commoncrawl.fr-en"
37
+ "un/undoc.2000.fr-en"
38
+ "training/news-commentary-v9.fr-en"
39
+ "giga-fren.release2.fixed"
40
+ )
41
+
42
+ if [ ! -d "$SCRIPTS" ]; then
43
+ echo "Please set SCRIPTS variable correctly to point to Moses scripts."
44
+ exit
45
+ fi
46
+
47
+ src=en
48
+ tgt=fr
49
+ lang=en-fr
50
+ prep=wmt14_en_fr
51
+ tmp=$prep/tmp
52
+ orig=orig
53
+
54
+ mkdir -p $orig $tmp $prep
55
+
56
+ cd $orig
57
+
58
+ for ((i=0;i<${#URLS[@]};++i)); do
59
+ file=${FILES[i]}
60
+ if [ -f $file ]; then
61
+ echo "$file already exists, skipping download"
62
+ else
63
+ url=${URLS[i]}
64
+ wget "$url"
65
+ if [ -f $file ]; then
66
+ echo "$url successfully downloaded."
67
+ else
68
+ echo "$url not successfully downloaded."
69
+ exit -1
70
+ fi
71
+ if [ ${file: -4} == ".tgz" ]; then
72
+ tar zxvf $file
73
+ elif [ ${file: -4} == ".tar" ]; then
74
+ tar xvf $file
75
+ fi
76
+ fi
77
+ done
78
+
79
+ gunzip giga-fren.release2.fixed.*.gz
80
+ cd ..
81
+
82
+ echo "pre-processing train data..."
83
+ for l in $src $tgt; do
84
+ rm $tmp/train.tags.$lang.tok.$l
85
+ for f in "${CORPORA[@]}"; do
86
+ cat $orig/$f.$l | \
87
+ perl $NORM_PUNC $l | \
88
+ perl $REM_NON_PRINT_CHAR | \
89
+ perl $TOKENIZER -threads 8 -a -l $l >> $tmp/train.tags.$lang.tok.$l
90
+ done
91
+ done
92
+
93
+ echo "pre-processing test data..."
94
+ for l in $src $tgt; do
95
+ if [ "$l" == "$src" ]; then
96
+ t="src"
97
+ else
98
+ t="ref"
99
+ fi
100
+ grep '<seg id' $orig/test-full/newstest2014-fren-$t.$l.sgm | \
101
+ sed -e 's/<seg id="[0-9]*">\s*//g' | \
102
+ sed -e 's/\s*<\/seg>\s*//g' | \
103
+ sed -e "s/\’/\'/g" | \
104
+ perl $TOKENIZER -threads 8 -a -l $l > $tmp/test.$l
105
+ echo ""
106
+ done
107
+
108
+ echo "splitting train and valid..."
109
+ for l in $src $tgt; do
110
+ awk '{if (NR%1333 == 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/valid.$l
111
+ awk '{if (NR%1333 != 0) print $0; }' $tmp/train.tags.$lang.tok.$l > $tmp/train.$l
112
+ done
113
+
114
+ TRAIN=$tmp/train.fr-en
115
+ BPE_CODE=$prep/code
116
+ rm -f $TRAIN
117
+ for l in $src $tgt; do
118
+ cat $tmp/train.$l >> $TRAIN
119
+ done
120
+
121
+ echo "learn_bpe.py on ${TRAIN}..."
122
+ python $BPEROOT/learn_bpe.py -s $BPE_TOKENS < $TRAIN > $BPE_CODE
123
+
124
+ for L in $src $tgt; do
125
+ for f in train.$L valid.$L test.$L; do
126
+ echo "apply_bpe.py to ${f}..."
127
+ python $BPEROOT/apply_bpe.py -c $BPE_CODE < $tmp/$f > $tmp/bpe.$f
128
+ done
129
+ done
130
+
131
+ perl $CLEAN -ratio 1.5 $tmp/bpe.train $src $tgt $prep/train 1 250
132
+ perl $CLEAN -ratio 1.5 $tmp/bpe.valid $src $tgt $prep/valid 1 250
133
+
134
+ for L in $src $tgt; do
135
+ cp $tmp/bpe.test.$L $prep/test.$L
136
+ done
omni_speech/infer/fairseq/examples/translation_moe/README.md ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Mixture Models for Diverse Machine Translation: Tricks of the Trade (Shen et al., 2019)
2
+
3
+ This page includes instructions for reproducing results from the paper [Mixture Models for Diverse Machine Translation: Tricks of the Trade (Shen et al., 2019)](https://arxiv.org/abs/1902.07816).
4
+
5
+ ## Download data
6
+
7
+ First, follow the [instructions to download and preprocess the WMT'17 En-De dataset](../translation#prepare-wmt14en2desh).
8
+ Make sure to learn a joint vocabulary by passing the `--joined-dictionary` option to `fairseq-preprocess`.
9
+
10
+ ## Train a model
11
+
12
+ Then we can train a mixture of experts model using the `translation_moe` task.
13
+ Use the `--method` flag to choose the MoE variant; we support hard mixtures with a learned or uniform prior (`--method hMoElp` and `hMoEup`, respectively) and soft mixures (`--method sMoElp` and `sMoEup`).
14
+ The model is trained with online responsibility assignment and shared parameterization.
15
+
16
+ The following command will train a `hMoElp` model with `3` experts:
17
+ ```bash
18
+ fairseq-train --ddp-backend='legacy_ddp' \
19
+ data-bin/wmt17_en_de \
20
+ --max-update 100000 \
21
+ --task translation_moe --user-dir examples/translation_moe/translation_moe_src \
22
+ --method hMoElp --mean-pool-gating-network \
23
+ --num-experts 3 \
24
+ --arch transformer_wmt_en_de --share-all-embeddings \
25
+ --optimizer adam --adam-betas '(0.9, 0.98)' --clip-norm 0.0 \
26
+ --lr-scheduler inverse_sqrt --warmup-init-lr 1e-07 --warmup-updates 4000 \
27
+ --lr 0.0007 \
28
+ --dropout 0.1 --weight-decay 0.0 --criterion cross_entropy \
29
+ --max-tokens 3584
30
+ ```
31
+
32
+ ## Translate
33
+
34
+ Once a model is trained, we can generate translations from different experts using the `--gen-expert` option.
35
+ For example, to generate from expert 0:
36
+ ```bash
37
+ fairseq-generate data-bin/wmt17_en_de \
38
+ --path checkpoints/checkpoint_best.pt \
39
+ --beam 1 --remove-bpe \
40
+ --task translation_moe --user-dir examples/translation_moe/translation_moe_src \
41
+ --method hMoElp --mean-pool-gating-network \
42
+ --num-experts 3 \
43
+ --gen-expert 0
44
+ ```
45
+
46
+ ## Evaluate
47
+
48
+ First download a tokenized version of the WMT'14 En-De test set with multiple references:
49
+ ```bash
50
+ wget dl.fbaipublicfiles.com/fairseq/data/wmt14-en-de.extra_refs.tok
51
+ ```
52
+
53
+ Next apply BPE on the fly and run generation for each expert:
54
+ ```bash
55
+ BPE_CODE=examples/translation/wmt17_en_de/code
56
+ for EXPERT in $(seq 0 2); do \
57
+ cat wmt14-en-de.extra_refs.tok \
58
+ | grep ^S | cut -f 2 \
59
+ | fairseq-interactive data-bin/wmt17_en_de \
60
+ --path checkpoints/checkpoint_best.pt \
61
+ --beam 1 \
62
+ --bpe subword_nmt --bpe-codes $BPE_CODE \
63
+ --buffer-size 500 --max-tokens 6000 \
64
+ --task translation_moe --user-dir examples/translation_moe/translation_moe_src \
65
+ --method hMoElp --mean-pool-gating-network \
66
+ --num-experts 3 \
67
+ --gen-expert $EXPERT ; \
68
+ done > wmt14-en-de.extra_refs.tok.gen.3experts
69
+ ```
70
+
71
+ Finally use `score_moe.py` to compute pairwise BLUE and average oracle BLEU:
72
+ ```bash
73
+ python examples/translation_moe/score.py --sys wmt14-en-de.extra_refs.tok.gen.3experts --ref wmt14-en-de.extra_refs.tok
74
+ # pairwise BLEU: 48.26
75
+ # #refs covered: 2.11
76
+ # multi-reference BLEU (leave-one-out): 59.46
77
+ ```
78
+ This matches row 3 from Table 7 in the paper.
79
+
80
+ ## Citation
81
+
82
+ ```bibtex
83
+ @article{shen2019mixture,
84
+ title = {Mixture Models for Diverse Machine Translation: Tricks of the Trade},
85
+ author = {Tianxiao Shen and Myle Ott and Michael Auli and Marc'Aurelio Ranzato},
86
+ journal = {International Conference on Machine Learning},
87
+ year = 2019,
88
+ }
89
+ ```
omni_speech/infer/fairseq/examples/translation_moe/score.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ Scoring script for computing pairwise BLEU and multi-ref BLEU over a set of
8
+ candidate hypotheses.
9
+
10
+ See `"Mixture Models for Diverse Machine Translation: Tricks of the Trade"
11
+ (Shen et al., 2019) <https://arxiv.org/abs/1902.07816>`_.
12
+ """
13
+
14
+ import argparse
15
+ import random
16
+ import sys
17
+ from itertools import chain
18
+
19
+ import numpy as np
20
+ import sacrebleu
21
+ from sacrebleu import corpus_bleu as _corpus_bleu
22
+
23
+ def main():
24
+ parser = argparse.ArgumentParser(sys.argv[0])
25
+ parser.add_argument(
26
+ "--sys", nargs="*", default="", metavar="FILE", help="path to system output"
27
+ )
28
+ parser.add_argument("--ref", default="", metavar="FILE", help="path to references")
29
+ parser.add_argument(
30
+ "--output",
31
+ default="",
32
+ metavar="FILE",
33
+ help="print outputs into a pretty format",
34
+ )
35
+ args = parser.parse_args()
36
+
37
+ if args.sys:
38
+ src, tgt, hypos, log_probs = load_sys(args.sys)
39
+ print("pairwise BLEU: %.2f" % pairwise(hypos))
40
+ if args.output:
41
+ merge(src, tgt, hypos, log_probs, args.output)
42
+
43
+ if args.ref:
44
+ _, _, refs = load_ref(args.ref)
45
+ if args.sys:
46
+ multi_ref(refs, hypos)
47
+ else:
48
+ intra_ref(refs)
49
+
50
+
51
+ def dictolist(d):
52
+ a = sorted(d.items(), key=lambda i: i[0])
53
+ return [i[1] for i in a]
54
+
55
+
56
+ def load_sys(paths):
57
+ src, tgt, hypos, log_probs = {}, {}, {}, {}
58
+ for path in paths:
59
+ with open(path) as f:
60
+ for line in f:
61
+ line = line.rstrip()
62
+ # S: source
63
+ # T: target
64
+ # D: detokenized system output
65
+ if line.startswith(("S-", "T-", "D-")):
66
+ i = int(line[line.find("-") + 1 : line.find("\t")])
67
+ if line.startswith("S-"):
68
+ src[i] = line.split("\t")[1]
69
+ if line.startswith("T-"):
70
+ tgt[i] = line.split("\t")[1]
71
+ if line.startswith("D-"):
72
+ if i not in hypos:
73
+ hypos[i] = []
74
+ log_probs[i] = []
75
+ hypos[i].append(line.split("\t")[2])
76
+ log_probs[i].append(float(line.split("\t")[1]))
77
+ return dictolist(src), dictolist(tgt), dictolist(hypos), dictolist(log_probs)
78
+
79
+
80
+ def load_ref(path):
81
+ with open(path) as f:
82
+ lines = f.readlines()
83
+ src, tgt, refs = [], [], []
84
+ i = 0
85
+ while i < len(lines):
86
+ if lines[i].startswith("S-"):
87
+ src.append(lines[i].split("\t")[1].rstrip())
88
+ i += 1
89
+ elif lines[i].startswith("T-"):
90
+ tgt.append(lines[i].split("\t")[1].rstrip())
91
+ i += 1
92
+ else:
93
+ a = []
94
+ while i < len(lines) and lines[i].startswith("R"):
95
+ a.append(lines[i].split("\t")[1].rstrip())
96
+ i += 1
97
+ refs.append(a)
98
+ return src, tgt, refs
99
+
100
+
101
+ def merge(src, tgt, hypos, log_probs, path):
102
+ with open(path, "w") as f:
103
+ for s, t, hs, lps in zip(src, tgt, hypos, log_probs):
104
+ f.write(s + "\n")
105
+ f.write(t + "\n")
106
+ f.write("\n")
107
+ for h, lp in zip(hs, lps):
108
+ f.write("\t%f\t%s\n" % (lp, h.strip()))
109
+ f.write("------------------------------------------------------\n")
110
+
111
+
112
+ def corpus_bleu(sys_stream, ref_streams):
113
+ bleu = _corpus_bleu(sys_stream, ref_streams, tokenize="none")
114
+ return bleu.score
115
+
116
+
117
+ def sentence_bleu(hypothesis, reference):
118
+ bleu = _corpus_bleu(hypothesis, reference)
119
+ for i in range(1, 4):
120
+ bleu.counts[i] += 1
121
+ bleu.totals[i] += 1
122
+ bleu = sacrebleu.BLEU.compute_bleu(
123
+ bleu.counts,
124
+ bleu.totals,
125
+ bleu.sys_len,
126
+ bleu.ref_len,
127
+ smooth_method="exp",
128
+ )
129
+ return bleu.score
130
+
131
+
132
+ def pairwise(sents):
133
+ _ref, _hypo = [], []
134
+ for s in sents:
135
+ for i in range(len(s)):
136
+ for j in range(len(s)):
137
+ if i != j:
138
+ _ref.append(s[i])
139
+ _hypo.append(s[j])
140
+ return corpus_bleu(_hypo, [_ref])
141
+
142
+
143
+ def multi_ref(refs, hypos):
144
+ _ref, _hypo = [], []
145
+ ref_cnt = 0
146
+ assert len(refs) == len(hypos)
147
+
148
+ # count number of refs covered
149
+ for rs, hs in zip(refs, hypos):
150
+ a = set()
151
+ for h in hs:
152
+ s = [sentence_bleu(h, r) for r in rs]
153
+ j = np.argmax(s)
154
+ _ref.append(rs[j])
155
+ _hypo.append(h)
156
+ best = [k for k in range(len(rs)) if s[k] == s[j]]
157
+ a.add(random.choice(best))
158
+ ref_cnt += len(a)
159
+ print("#refs covered: %.2f" % (ref_cnt / len(refs)))
160
+
161
+ # transpose refs and hypos
162
+ refs = list(zip(*refs))
163
+ hypos = list(zip(*hypos))
164
+
165
+ # compute multi-ref corpus BLEU (leave-one-out to be comparable to intra_ref)
166
+ k = len(hypos)
167
+ m = len(refs)
168
+ flat_hypos = [hypos[j][i] for i in range(len(hypos[0])) for j in range(k)]
169
+ duplicated_refs = [[ref for ref in refs_i for _ in range(k)] for refs_i in refs]
170
+ loo_bleus = []
171
+ for held_out_ref in range(m):
172
+ remaining_refs = (
173
+ duplicated_refs[:held_out_ref] + duplicated_refs[held_out_ref + 1 :]
174
+ )
175
+ assert len(remaining_refs) == m - 1
176
+ loo_bleus.append(corpus_bleu(flat_hypos, remaining_refs))
177
+ print("average multi-reference BLEU (leave-one-out): %.2f" % np.mean(loo_bleus))
178
+
179
+
180
+ def intra_ref(refs):
181
+ print("ref pairwise BLEU: %.2f" % pairwise(refs))
182
+ refs = list(zip(*refs))
183
+ m = len(refs)
184
+ concat_h = []
185
+ concat_rest = [[] for j in range(m - 1)]
186
+ for i, h in enumerate(refs):
187
+ rest = refs[:i] + refs[i + 1 :]
188
+ concat_h.append(h)
189
+ for j in range(m - 1):
190
+ concat_rest[j].extend(rest[j])
191
+ concat_h = list(chain.from_iterable(concat_h))
192
+ bleu = corpus_bleu(concat_h, concat_rest)
193
+ print("multi-reference BLEU (leave-one-out): %.2f" % bleu)
194
+
195
+
196
+ if __name__ == "__main__":
197
+ main()
omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from . import translation_moe # noqa
omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/logsumexp_moe.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+
8
+
9
+ class LogSumExpMoE(torch.autograd.Function):
10
+ """Standard LogSumExp forward pass, but use *posterior* for the backward.
11
+
12
+ See `"Mixture Models for Diverse Machine Translation: Tricks of the Trade"
13
+ (Shen et al., 2019) <https://arxiv.org/abs/1902.07816>`_.
14
+ """
15
+
16
+ @staticmethod
17
+ def forward(ctx, logp, posterior, dim=-1):
18
+ ctx.save_for_backward(posterior)
19
+ ctx.dim = dim
20
+ return torch.logsumexp(logp, dim=dim)
21
+
22
+ @staticmethod
23
+ def backward(ctx, grad_output):
24
+ (posterior,) = ctx.saved_tensors
25
+ grad_logp = grad_output.unsqueeze(ctx.dim) * posterior
26
+ return grad_logp, None, None
omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/mean_pool_gating_network.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import torch
7
+ import torch.nn.functional as F
8
+
9
+
10
+ class MeanPoolGatingNetwork(torch.nn.Module):
11
+ """A simple mean-pooling gating network for selecting experts.
12
+
13
+ This module applies mean pooling over an encoder's output and returns
14
+ reponsibilities for each expert. The encoder format is expected to match
15
+ :class:`fairseq.models.transformer.TransformerEncoder`.
16
+ """
17
+
18
+ def __init__(self, embed_dim, num_experts, dropout=None):
19
+ super().__init__()
20
+ self.embed_dim = embed_dim
21
+ self.num_experts = num_experts
22
+
23
+ self.fc1 = torch.nn.Linear(embed_dim, embed_dim)
24
+ self.dropout = torch.nn.Dropout(dropout) if dropout is not None else None
25
+ self.fc2 = torch.nn.Linear(embed_dim, num_experts)
26
+
27
+ def forward(self, encoder_out):
28
+ if not (
29
+ "encoder_out" in encoder_out
30
+ and "encoder_padding_mask" in encoder_out
31
+ and encoder_out["encoder_out"][0].size(2) == self.embed_dim
32
+ ):
33
+ raise ValueError("Unexpected format for encoder_out")
34
+
35
+ # mean pooling over time
36
+ encoder_padding_mask = encoder_out["encoder_padding_mask"][0] # B x T
37
+ encoder_out = encoder_out["encoder_out"][0].transpose(0, 1) # B x T x C
38
+ if encoder_padding_mask is not None:
39
+ encoder_out = encoder_out.clone() # required because of transpose above
40
+ encoder_out[encoder_padding_mask] = 0
41
+ ntokens = torch.sum(~encoder_padding_mask, dim=1, keepdim=True)
42
+ x = torch.sum(encoder_out, dim=1) / ntokens.type_as(encoder_out)
43
+ else:
44
+ x = torch.mean(encoder_out, dim=1)
45
+
46
+ x = torch.tanh(self.fc1(x))
47
+ if self.dropout is not None:
48
+ x = self.dropout(x)
49
+ x = self.fc2(x)
50
+ return F.log_softmax(x, dim=-1, dtype=torch.float32).type_as(x)
omni_speech/infer/fairseq/examples/translation_moe/translation_moe_src/translation_moe.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 dataclasses import dataclass, field
7
+ import torch
8
+ from omegaconf import II
9
+
10
+ from fairseq import utils
11
+ from fairseq.logging import metrics
12
+ from fairseq.dataclass import ChoiceEnum
13
+ from fairseq.tasks import register_task
14
+ from fairseq.tasks.translation import TranslationConfig, TranslationTask
15
+
16
+ from .logsumexp_moe import LogSumExpMoE
17
+ from .mean_pool_gating_network import MeanPoolGatingNetwork
18
+
19
+
20
+ METHOD_CHOICES = ChoiceEnum(["sMoElp", "sMoEup", "hMoElp", "hMoEup"])
21
+
22
+
23
+ @dataclass
24
+ class TranslationMoEConfig(TranslationConfig):
25
+ method: METHOD_CHOICES = field(
26
+ default="hMoEup",
27
+ metadata={"help": "MoE method"},
28
+ )
29
+ num_experts: int = field(
30
+ default=3,
31
+ metadata={"help": "number of experts"},
32
+ )
33
+ mean_pool_gating_network: bool = field(
34
+ default=False,
35
+ metadata={"help": "use a simple mean-pooling gating network"},
36
+ )
37
+ mean_pool_gating_network_dropout: float = field(
38
+ default=0,
39
+ metadata={"help": "dropout for mean-pooling gating network"},
40
+ )
41
+ mean_pool_gating_network_encoder_dim: int = field(
42
+ default=0,
43
+ metadata={"help": "encoder output dim for mean-pooling gating network"},
44
+ )
45
+ gen_expert: int = field(
46
+ default=0,
47
+ metadata={"help": "which expert to use for generation"},
48
+ )
49
+ sentence_avg: bool = II("optimization.sentence_avg")
50
+
51
+
52
+ @register_task("translation_moe", dataclass=TranslationMoEConfig)
53
+ class TranslationMoETask(TranslationTask):
54
+ """
55
+ Translation task for Mixture of Experts (MoE) models.
56
+
57
+ See `"Mixture Models for Diverse Machine Translation: Tricks of the Trade"
58
+ (Shen et al., 2019) <https://arxiv.org/abs/1902.07816>`_.
59
+
60
+ Args:
61
+ src_dict (~fairseq.data.Dictionary): dictionary for the source language
62
+ tgt_dict (~fairseq.data.Dictionary): dictionary for the target language
63
+
64
+ .. note::
65
+
66
+ The translation task is compatible with :mod:`fairseq-train`,
67
+ :mod:`fairseq-generate` and :mod:`fairseq-interactive`.
68
+
69
+ The translation task provides the following additional command-line
70
+ arguments:
71
+
72
+ .. argparse::
73
+ :ref: fairseq.tasks.translation_parser
74
+ :prog:
75
+ """
76
+
77
+ cfg: TranslationMoEConfig
78
+
79
+ def __init__(self, cfg: TranslationMoEConfig, src_dict, tgt_dict):
80
+ if cfg.method == "sMoElp":
81
+ # soft MoE with learned prior
82
+ self.uniform_prior = False
83
+ self.hard_selection = False
84
+ elif cfg.method == "sMoEup":
85
+ # soft MoE with uniform prior
86
+ self.uniform_prior = True
87
+ self.hard_selection = False
88
+ elif cfg.method == "hMoElp":
89
+ # hard MoE with learned prior
90
+ self.uniform_prior = False
91
+ self.hard_selection = True
92
+ elif cfg.method == "hMoEup":
93
+ # hard MoE with uniform prior
94
+ self.uniform_prior = True
95
+ self.hard_selection = True
96
+
97
+ # add indicator tokens for each expert
98
+ for i in range(cfg.num_experts):
99
+ # add to both dictionaries in case we're sharing embeddings
100
+ src_dict.add_symbol("<expert_{}>".format(i))
101
+ tgt_dict.add_symbol("<expert_{}>".format(i))
102
+
103
+ super().__init__(cfg, src_dict, tgt_dict)
104
+
105
+ def build_model(self, cfg, from_checkpoint=False):
106
+ from fairseq import models
107
+
108
+ model = models.build_model(cfg, self)
109
+ if not self.uniform_prior and not hasattr(model, "gating_network"):
110
+ if self.cfg.mean_pool_gating_network:
111
+ if self.cfg.mean_pool_gating_network_encoder_dim > 0:
112
+ encoder_dim = self.cfg.mean_pool_gating_network_encoder_dim
113
+ elif getattr(cfg, "encoder_embed_dim", None):
114
+ # assume that encoder_embed_dim is the encoder's output dimension
115
+ encoder_dim = cfg.encoder_embed_dim
116
+ else:
117
+ raise ValueError(
118
+ "Must specify --mean-pool-gating-network-encoder-dim"
119
+ )
120
+
121
+ if self.cfg.mean_pool_gating_network_dropout > 0:
122
+ dropout = self.cfg.mean_pool_gating_network_dropout
123
+ elif getattr(cfg, "dropout", None):
124
+ dropout = cfg.dropout
125
+ else:
126
+ raise ValueError("Must specify task.mean_pool_gating_network_dropout")
127
+
128
+ model.gating_network = MeanPoolGatingNetwork(
129
+ encoder_dim,
130
+ self.cfg.num_experts,
131
+ dropout,
132
+ )
133
+ else:
134
+ raise ValueError(
135
+ "translation_moe task with learned prior requires the model to "
136
+ "have a gating network; try using --mean-pool-gating-network"
137
+ )
138
+ return model
139
+
140
+ def expert_index(self, i):
141
+ return i + self.tgt_dict.index("<expert_0>")
142
+
143
+ def _get_loss(self, sample, model, criterion):
144
+ assert hasattr(
145
+ criterion, "compute_loss"
146
+ ), "translation_moe task requires the criterion to implement the compute_loss() method"
147
+
148
+ k = self.cfg.num_experts
149
+ bsz = sample["target"].size(0)
150
+
151
+ def get_lprob_y(encoder_out, prev_output_tokens_k):
152
+ net_output = model.decoder(
153
+ prev_output_tokens=prev_output_tokens_k,
154
+ encoder_out=encoder_out,
155
+ )
156
+ loss, _ = criterion.compute_loss(model, net_output, sample, reduce=False)
157
+ loss = loss.view(bsz, -1)
158
+ return -loss.sum(dim=1, keepdim=True) # -> B x 1
159
+
160
+ def get_lprob_yz(winners=None):
161
+ encoder_out = model.encoder(
162
+ src_tokens=sample["net_input"]["src_tokens"],
163
+ src_lengths=sample["net_input"]["src_lengths"],
164
+ )
165
+
166
+ if winners is None:
167
+ lprob_y = []
168
+ for i in range(k):
169
+ prev_output_tokens_k = sample["net_input"][
170
+ "prev_output_tokens"
171
+ ].clone()
172
+ assert not prev_output_tokens_k.requires_grad
173
+ prev_output_tokens_k[:, 0] = self.expert_index(i)
174
+ lprob_y.append(get_lprob_y(encoder_out, prev_output_tokens_k))
175
+ lprob_y = torch.cat(lprob_y, dim=1) # -> B x K
176
+ else:
177
+ prev_output_tokens_k = sample["net_input"]["prev_output_tokens"].clone()
178
+ prev_output_tokens_k[:, 0] = self.expert_index(winners)
179
+ lprob_y = get_lprob_y(encoder_out, prev_output_tokens_k) # -> B
180
+
181
+ if self.uniform_prior:
182
+ lprob_yz = lprob_y
183
+ else:
184
+ lprob_z = model.gating_network(encoder_out) # B x K
185
+ if winners is not None:
186
+ lprob_z = lprob_z.gather(dim=1, index=winners.unsqueeze(-1))
187
+ lprob_yz = lprob_y + lprob_z.type_as(lprob_y) # B x K
188
+
189
+ return lprob_yz
190
+
191
+ # compute responsibilities without dropout
192
+ with utils.model_eval(model): # disable dropout
193
+ with torch.no_grad(): # disable autograd
194
+ lprob_yz = get_lprob_yz() # B x K
195
+ prob_z_xy = torch.nn.functional.softmax(lprob_yz, dim=1)
196
+ assert not prob_z_xy.requires_grad
197
+
198
+ # compute loss with dropout
199
+ if self.hard_selection:
200
+ winners = prob_z_xy.max(dim=1)[1]
201
+ loss = -get_lprob_yz(winners)
202
+ else:
203
+ lprob_yz = get_lprob_yz() # B x K
204
+ loss = -LogSumExpMoE.apply(lprob_yz, prob_z_xy, 1)
205
+
206
+ loss = loss.sum()
207
+ sample_size = (
208
+ sample["target"].size(0) if self.cfg.sentence_avg else sample["ntokens"]
209
+ )
210
+ logging_output = {
211
+ "loss": utils.item(loss.data),
212
+ "ntokens": sample["ntokens"],
213
+ "nsentences": bsz,
214
+ "sample_size": sample_size,
215
+ "posterior": prob_z_xy.float().sum(dim=0).cpu(),
216
+ }
217
+ return loss, sample_size, logging_output
218
+
219
+ def train_step(
220
+ self, sample, model, criterion, optimizer, update_num, ignore_grad=False
221
+ ):
222
+ model.train()
223
+ loss, sample_size, logging_output = self._get_loss(sample, model, criterion)
224
+ if ignore_grad:
225
+ loss *= 0
226
+ optimizer.backward(loss)
227
+ return loss, sample_size, logging_output
228
+
229
+ def valid_step(self, sample, model, criterion):
230
+ model.eval()
231
+ with torch.no_grad():
232
+ loss, sample_size, logging_output = self._get_loss(sample, model, criterion)
233
+ return loss, sample_size, logging_output
234
+
235
+ def inference_step(
236
+ self,
237
+ generator,
238
+ models,
239
+ sample,
240
+ prefix_tokens=None,
241
+ expert=None,
242
+ constraints=None,
243
+ ):
244
+ expert = expert or self.cfg.gen_expert
245
+ with torch.no_grad():
246
+ return generator.generate(
247
+ models,
248
+ sample,
249
+ prefix_tokens=prefix_tokens,
250
+ constraints=constraints,
251
+ bos_token=self.expert_index(expert),
252
+ )
253
+
254
+ def reduce_metrics(self, logging_outputs, criterion):
255
+ super().reduce_metrics(logging_outputs, criterion)
256
+ metrics.log_scalar(
257
+ "posterior",
258
+ sum(log["posterior"] for log in logging_outputs if "posterior" in log),
259
+ )
omni_speech/infer/fairseq/examples/truncated_bptt/README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Truncated Backpropagation Through Time (BPTT)
2
+
3
+ Truncated BPTT is a useful technique for training language models on very long
4
+ sequences. Typically a long sequences is split into chunks and a language model
5
+ is trained over the chunks sequentially. The LM may condition on previous
6
+ chunks, but gradients only flow through the current chunk. This technique was
7
+ the basis for the paper: [Transformer-XL: Attentive Language Models Beyond a
8
+ Fixed-Length Context](https://arxiv.org/abs/1901.02860), which achieved
9
+ state-of-the-art language modeling results at the time of publication.
10
+
11
+ It is slightly tricky to implement Truncated BPTT efficiently in fairseq, since
12
+ we need to iterate over the data sequentially and disable any batch shuffling
13
+ logic. The code provided in this example illustrates how to implement Truncated
14
+ BPTT in fairseq by overriding ``FairseqTask::get_batch_iterator`` to iterate
15
+ over the data sequentially. Crucially, this example supports batching and
16
+ multi-GPU (data parallel) training.
17
+
18
+ ##### 0. Setup
19
+
20
+ First, see the general [language modeling README](README.md) for instructions on
21
+ preprocessing the WikiText-103 data.
22
+
23
+ ##### 1. Train a Transformer-XL model on WikiText-103
24
+
25
+ We will train a 16-layer Transformer-XL model following the [hyperparameters
26
+ used in the original
27
+ paper](https://github.com/kimiyoung/transformer-xl/blob/master/pytorch/run_wt103_base.sh).
28
+
29
+ The following command assumes 4 GPUs, so that the total batch size is 60
30
+ sequences (15 x 4). Training should take ~24 hours on 4 V100 GPUs:
31
+ ```bash
32
+ CUDA_VISIBLE_DEVICES=0,1,2,3 fairseq-train \
33
+ --user-dir examples/truncated_bptt \
34
+ data-bin/wikitext-103/ \
35
+ --task truncated_bptt_lm --tokens-per-sample 150 \
36
+ --batch-size 15 --max-update 200000 \
37
+ --arch transformer_xl --n-layer 16 --d-model 410 --n-head 10 \
38
+ --d-head 41 --d-inner 2100 --dropout 0.1 --dropatt 0.0 --mem-len 150 \
39
+ --optimizer adam --clip-norm 0.25 \
40
+ --lr-scheduler cosine --warmup-updates 0 --min-lr 0.0 --lr 0.00025 \
41
+ --log-format json --log-interval 25 \
42
+ --fp16
43
+ ```
44
+
45
+ If training on a single GPU, set `--update-freq=4` to accumulate 4x gradients
46
+ and simulate training on 4 GPUs.
47
+
48
+ ##### 2. Evaluate
49
+
50
+ ```bash
51
+ fairseq-eval-lm data-bin/wikitext-103/ \
52
+ --path checkpoints/checkpoint_best.pt \
53
+ --user-dir examples/truncated_bptt/ \
54
+ --task truncated_bptt_lm \
55
+ --batch-size 1 --required-batch-size-multiple 1 \
56
+ --model-overrides '{"mem_len":640,"clamp_len":400,"same_length":True}' \
57
+ --tokens-per-sample 64
58
+ # ... | INFO | fairseq_cli.eval_lm | num. model params: 151123537
59
+ # ... | INFO | fairseq_cli.eval_lm | Evaluated 245569 tokens in 83.1s (2956.82 tokens/s)
60
+ # ... | INFO | fairseq_cli.eval_lm | Loss (base 2): 4.5668, Perplexity: 23.70
61
+ # Compare to 24.0 test perplexity from the paper
62
+ ```
63
+
64
+ *Note:* During training the model saw 150 tokens of context
65
+ (``--tokens-per-sample=150``) and 150 extra memory tokens (``--mem-len=150``).
66
+ During evaluation we measure perplexity on sequences of 64 tokens
67
+ (``--tokens-per-sample=64``) and increase the memory length
68
+ (``--model-overrides='{"mem_len":640}'``). These settings match the evaluation
69
+ settings from [the original
70
+ paper](https://github.com/kimiyoung/transformer-xl/blob/master/pytorch/run_wt103_base.sh).
omni_speech/infer/fairseq/examples/truncated_bptt/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from . import transformer_xl_model, truncated_bptt_lm_task # noqa
omni_speech/infer/fairseq/examples/truncated_bptt/transformer_xl_model.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import logging
7
+ from dataclasses import dataclass, field
8
+ from typing import Dict, List, Optional
9
+
10
+ import torch
11
+ from fairseq.dataclass import FairseqDataclass
12
+ from fairseq.models import (
13
+ FairseqIncrementalDecoder,
14
+ FairseqLanguageModel,
15
+ register_model,
16
+ )
17
+ from fairseq.modules.checkpoint_activations import checkpoint_wrapper
18
+ from omegaconf import II
19
+
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ @dataclass
25
+ class TransformerXLConfig(FairseqDataclass):
26
+ # defaults come from the original Transformer-XL code
27
+ cutoffs: List[int] = field(default_factory=lambda: [20000, 40000, 200000])
28
+ d_model: int = 500
29
+ n_head: int = 10
30
+ d_head: int = 50
31
+ d_inner: int = 1000
32
+ div_val: int = 1
33
+ n_layer: int = 12
34
+ mem_len: int = 0
35
+ clamp_len: int = -1
36
+ same_length: bool = False
37
+ dropout: float = 0.0
38
+ dropatt: float = 0.0
39
+ checkpoint_activations: bool = False
40
+ offload_activations: bool = False
41
+ max_target_positions: int = II("task.max_target_positions")
42
+
43
+
44
+ @register_model("transformer_xl", dataclass=TransformerXLConfig)
45
+ class TransformerXLLanguageModel(FairseqLanguageModel):
46
+ @classmethod
47
+ def build_model(cls, cfg: TransformerXLConfig, task):
48
+ return cls(TransformerXLDecoder(cfg, task))
49
+
50
+
51
+ class TransformerXLDecoder(FairseqIncrementalDecoder):
52
+ def __init__(self, cfg, task):
53
+ try:
54
+ from transformers.models.transfo_xl import (
55
+ TransfoXLConfig,
56
+ TransfoXLLMHeadModel,
57
+ )
58
+ except ImportError:
59
+ from transformers.configuration_transfo_xl import TransfoXLConfig
60
+ from transformers.modeling_transfo_xl import TransfoXLLMHeadModel
61
+
62
+ super().__init__(task.target_dictionary)
63
+ self.cfg = cfg
64
+
65
+ # remove any cutoffs larger than the vocab size
66
+ cutoffs = [
67
+ cutoff for cutoff in cfg.cutoffs if cutoff < len(task.target_dictionary)
68
+ ]
69
+
70
+ config = TransfoXLConfig(
71
+ vocab_size=len(task.target_dictionary),
72
+ cutoffs=cutoffs,
73
+ d_model=cfg.d_model,
74
+ d_embed=cfg.d_model,
75
+ n_head=cfg.n_head,
76
+ d_head=cfg.d_head,
77
+ d_inner=cfg.d_inner,
78
+ div_val=cfg.div_val,
79
+ n_layer=cfg.n_layer,
80
+ mem_len=cfg.mem_len,
81
+ clamp_len=cfg.clamp_len,
82
+ same_length=cfg.same_length,
83
+ dropout=cfg.dropout,
84
+ dropatt=cfg.dropatt,
85
+ )
86
+ logger.info(config)
87
+ self.model = TransfoXLLMHeadModel(config)
88
+
89
+ if cfg.checkpoint_activations or cfg.offload_activations:
90
+ for i in range(len(self.model.transformer.layers)):
91
+ self.model.transformer.layers[i] = checkpoint_wrapper(
92
+ self.model.transformer.layers[i],
93
+ offload_to_cpu=cfg.offload_activations,
94
+ )
95
+ # TODO: may save mem to wrap(layer.pos_ff.CoreNet[3])
96
+
97
+ self._mems = None
98
+
99
+ def forward(
100
+ self,
101
+ src_tokens,
102
+ src_lengths=None, # unused
103
+ incremental_state: Optional[Dict[str, List[torch.Tensor]]] = None,
104
+ encoder_out=None,
105
+ ):
106
+ if incremental_state is not None: # used during inference
107
+ mems = self.get_incremental_state(incremental_state, "mems")
108
+ src_tokens = src_tokens[:, -1:] # only keep the most recent token
109
+ else:
110
+ mems = self._mems
111
+
112
+ output = self.model(
113
+ input_ids=src_tokens,
114
+ mems=mems,
115
+ return_dict=False,
116
+ )
117
+
118
+ if len(output) >= 2:
119
+ if incremental_state is not None:
120
+ self.set_incremental_state(incremental_state, "mems", output[1])
121
+ else:
122
+ self._mems = output[1]
123
+
124
+ return (output[0],)
125
+
126
+ def max_positions(self):
127
+ return self.cfg.max_target_positions
128
+
129
+ def reorder_incremental_state(
130
+ self,
131
+ incremental_state: Dict[str, Dict[str, Optional[torch.Tensor]]],
132
+ new_order: torch.Tensor,
133
+ ):
134
+ """Reorder incremental state.
135
+
136
+ This will be called when the order of the input has changed from the
137
+ previous time step. A typical use case is beam search, where the input
138
+ order changes between time steps based on the selection of beams.
139
+ """
140
+ mems = self.get_incremental_state(incremental_state, "mems")
141
+ if mems is not None:
142
+ new_mems = [mems_i.index_select(1, new_order) for mems_i in mems]
143
+ self.set_incremental_state(incremental_state, "mems", new_mems)
omni_speech/infer/fairseq/examples/truncated_bptt/truncated_bptt_lm_task.py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import logging
7
+ import os
8
+ from dataclasses import dataclass, field
9
+ from typing import List, Optional, Tuple
10
+
11
+ import torch
12
+ from fairseq import utils
13
+ from fairseq.data import (
14
+ Dictionary,
15
+ TokenBlockDataset,
16
+ data_utils,
17
+ iterators,
18
+ )
19
+ from fairseq.dataclass import FairseqDataclass
20
+ from fairseq.distributed import utils as dist_utils
21
+ from fairseq.tasks import FairseqTask, register_task
22
+ from omegaconf import II
23
+
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @dataclass
29
+ class TruncatedBPTTLMConfig(FairseqDataclass):
30
+ data: str = field(default="???", metadata={"help": "path to data directory"})
31
+ tokens_per_sample: int = field(
32
+ default=1024, metadata={"help": "max number of tokens per sequence"},
33
+ )
34
+ batch_size: int = II("dataset.batch_size")
35
+ # Some models use *max_target_positions* to know how many positional
36
+ # embeddings to learn. We use II(...) to make it default to
37
+ # *tokens_per_sample*, but in principle there could be more positional
38
+ # embeddings than tokens in a single batch. This may also be irrelevant for
39
+ # custom model implementations.
40
+ max_target_positions: int = II("task.tokens_per_sample")
41
+ # these will be populated automatically if not provided
42
+ data_parallel_rank: Optional[int] = None
43
+ data_parallel_size: Optional[int] = None
44
+
45
+
46
+ @register_task("truncated_bptt_lm", dataclass=TruncatedBPTTLMConfig)
47
+ class TruncatedBPTTLMTask(FairseqTask):
48
+ def __init__(self, cfg: TruncatedBPTTLMConfig):
49
+ super().__init__(cfg)
50
+
51
+ if cfg.data_parallel_rank is None or cfg.data_parallel_size is None:
52
+ if torch.distributed.is_initialized():
53
+ cfg.data_parallel_rank = dist_utils.get_data_parallel_rank()
54
+ cfg.data_parallel_size = dist_utils.get_data_parallel_world_size()
55
+ else:
56
+ cfg.data_parallel_rank = 0
57
+ cfg.data_parallel_size = 1
58
+
59
+ # load the dictionary
60
+ paths = utils.split_paths(cfg.data)
61
+ assert len(paths) > 0
62
+ self.dictionary = Dictionary.load(os.path.join(paths[0], "dict.txt"))
63
+ logger.info("dictionary: {} types".format(len(self.dictionary)))
64
+
65
+ def load_dataset(self, split, epoch=1, combine=False, **kwargs):
66
+ """Load a given dataset split (e.g., train, valid, test)"""
67
+
68
+ # support sharded datasets
69
+ paths = utils.split_paths(self.cfg.data)
70
+ assert len(paths) > 0
71
+ data_path = paths[(epoch - 1) % len(paths)]
72
+ split_path = os.path.join(data_path, split)
73
+
74
+ # each element of *data* will be a tensorized line from the original
75
+ # text dataset, similar to ``open(split_path).readlines()``
76
+ data = data_utils.load_indexed_dataset(
77
+ split_path, self.dictionary, combine=combine
78
+ )
79
+ if data is None:
80
+ raise FileNotFoundError(
81
+ "Dataset not found: {} ({})".format(split, split_path)
82
+ )
83
+
84
+ # this is similar to ``data.view(-1).split(tokens_per_sample)``
85
+ data = TokenBlockDataset(
86
+ data,
87
+ data.sizes,
88
+ block_size=self.cfg.tokens_per_sample,
89
+ pad=None, # unused
90
+ eos=None, # unused
91
+ break_mode="none",
92
+ )
93
+
94
+ self.datasets[split] = TruncatedBPTTDataset(
95
+ data=data,
96
+ bsz_per_shard=self.cfg.batch_size,
97
+ shard_id=self.cfg.data_parallel_rank,
98
+ num_shards=self.cfg.data_parallel_size,
99
+ )
100
+
101
+ def dataset(self, split):
102
+ return self.datasets[split]
103
+
104
+ def get_batch_iterator(
105
+ self,
106
+ dataset,
107
+ num_workers=0,
108
+ epoch=1,
109
+ data_buffer_size=0,
110
+ skip_remainder_batch=False,
111
+ **kwargs
112
+ ):
113
+ return iterators.EpochBatchIterator(
114
+ dataset=dataset,
115
+ collate_fn=self._collate_fn,
116
+ num_workers=num_workers,
117
+ epoch=epoch,
118
+ buffer_size=data_buffer_size,
119
+ # we don't use the batching functionality from EpochBatchIterator;
120
+ # instead every item in *dataset* is a whole batch
121
+ batch_sampler=[[i] for i in range(len(dataset))],
122
+ disable_shuffling=True,
123
+ skip_remainder_batch=skip_remainder_batch,
124
+ )
125
+
126
+ def _collate_fn(self, items: List[List[torch.Tensor]]):
127
+ # we don't use fairseq's batching functionality, so we expect a single
128
+ # Tensor of type List[torch.Tensor]
129
+ assert len(items) == 1
130
+
131
+ # item will have shape B x T (the last batch may have length < T)
132
+ id, item = items[0]
133
+ item = data_utils.collate_tokens(item, pad_idx=self.source_dictionary.pad())
134
+ B, T = item.size()
135
+
136
+ # shift item one position over and append a padding token for the target
137
+ target = torch.nn.functional.pad(
138
+ item[:, 1:], (0, 1, 0, 0), value=self.target_dictionary.pad()
139
+ )
140
+
141
+ # fairseq expects batches to have the following structure
142
+ return {
143
+ "id": torch.tensor([id] * item.size(0)),
144
+ "net_input": {"src_tokens": item,},
145
+ "target": target,
146
+ "nsentences": item.size(0),
147
+ "ntokens": item.numel(),
148
+ }
149
+
150
+ def build_dataset_for_inference(
151
+ self, src_tokens: List[torch.Tensor], src_lengths: List[int], **kwargs
152
+ ) -> torch.utils.data.Dataset:
153
+ eos = self.source_dictionary.eos()
154
+ dataset = TokenBlockDataset(
155
+ src_tokens,
156
+ src_lengths,
157
+ block_size=None, # ignored for "eos" break mode
158
+ pad=self.source_dictionary.pad(),
159
+ eos=eos,
160
+ break_mode="eos",
161
+ )
162
+
163
+ class Dataset(torch.utils.data.Dataset):
164
+ def __getitem__(self, i):
165
+ item = dataset[i]
166
+ if item[-1] == eos:
167
+ # remove eos to support generating with a prefix
168
+ item = item[:-1]
169
+ return (i, [item])
170
+
171
+ def __len__(self):
172
+ return len(dataset)
173
+
174
+ return Dataset()
175
+
176
+ def inference_step(
177
+ self, generator, models, sample, prefix_tokens=None, constraints=None
178
+ ):
179
+ with torch.no_grad():
180
+ if constraints is not None:
181
+ raise NotImplementedError
182
+
183
+ # SequenceGenerator doesn't use *src_tokens* directly, we need to
184
+ # pass the *prefix_tokens* argument instead.
185
+ if prefix_tokens is None and sample["net_input"]["src_tokens"].nelement():
186
+ prefix_tokens = sample["net_input"]["src_tokens"]
187
+
188
+ # begin generation with the end-of-sentence token
189
+ bos_token = self.source_dictionary.eos()
190
+
191
+ return generator.generate(
192
+ models, sample, prefix_tokens=prefix_tokens, bos_token=bos_token
193
+ )
194
+
195
+ def eval_lm_dataloader(
196
+ self,
197
+ dataset,
198
+ max_tokens: Optional[int] = 36000,
199
+ batch_size: Optional[int] = None,
200
+ max_positions: Optional[int] = None,
201
+ num_shards: int = 1,
202
+ shard_id: int = 0,
203
+ num_workers: int = 1,
204
+ data_buffer_size: int = 10,
205
+ context_window: int = 0,
206
+ ):
207
+ if context_window > 0:
208
+ raise NotImplementedError(
209
+ "Transformer-XL doesn't need --context-window, try "
210
+ "--model-overrides '{\"mem_len\":42}' instead "
211
+ )
212
+ return self.get_batch_iterator(
213
+ dataset=dataset,
214
+ max_tokens=max_tokens,
215
+ max_sentences=batch_size,
216
+ max_positions=max_positions,
217
+ ignore_invalid_inputs=True,
218
+ num_shards=num_shards,
219
+ shard_id=shard_id,
220
+ num_workers=num_workers,
221
+ data_buffer_size=data_buffer_size,
222
+ ).next_epoch_itr(shuffle=False)
223
+
224
+ @property
225
+ def source_dictionary(self):
226
+ return self.dictionary
227
+
228
+ @property
229
+ def target_dictionary(self):
230
+ return self.dictionary
231
+
232
+
233
+ class TruncatedBPTTDataset(torch.utils.data.Dataset):
234
+ def __init__(
235
+ self,
236
+ data: List[torch.Tensor], # ordered list of items
237
+ bsz_per_shard, # number of items processed per GPUs per forward
238
+ shard_id, # current GPU ID
239
+ num_shards, # number of GPUs
240
+ ):
241
+ super().__init__()
242
+ self.data = data
243
+
244
+ def batchify(data, bsz):
245
+ # Work out how cleanly we can divide the dataset into bsz parts.
246
+ nbatch = data.size(0) // bsz
247
+ # Trim off any extra elements that wouldn't cleanly fit (remainders).
248
+ data = data.narrow(0, 0, nbatch * bsz)
249
+ # Evenly divide the data across the bsz batches.
250
+ data = data.view(bsz, -1).contiguous()
251
+ return data
252
+
253
+ # total number of sequences processed by all GPUs in each forward pass
254
+ global_batch_size = bsz_per_shard * num_shards
255
+
256
+ """
257
+ With a 16 item dataset, bsz_per_shard=2 and num_shards=3,
258
+ *indices* might look like:
259
+
260
+ indices = [[0, 1],
261
+ [2, 3],
262
+ [4, 5],
263
+ [6, 7],
264
+ [8, 9],
265
+ [10, 11]]
266
+
267
+ The size of the TruncatedBPTTDataset instance will be 2,
268
+ and shard 1 will see items:
269
+
270
+ [(0, [data[4], data[6]]),
271
+ (1, [data[5], data[7]])]
272
+ """
273
+ indices = batchify(torch.arange(len(data)), global_batch_size)
274
+ assert indices.size(0) == global_batch_size
275
+
276
+ self.my_indices = indices[
277
+ shard_id * bsz_per_shard : (shard_id + 1) * bsz_per_shard
278
+ ]
279
+ assert self.my_indices.size(0) == bsz_per_shard
280
+
281
+ def __len__(self):
282
+ return self.my_indices.size(1)
283
+
284
+ def __getitem__(self, i) -> Tuple[int, List[torch.Tensor]]:
285
+ return (i, [self.data[idx] for idx in self.my_indices[:, i]])
omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/README.md ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Unsupervised Quality Estimation for Neural Machine Translation (Fomicheva et al., 2020)
2
+
3
+ This page includes instructions for reproducing results from the paper [Unsupervised Quality Estimation for Neural
4
+ Machine Translation (Fomicheva et al., 2020)](https://arxiv.org/abs/2005.10608)
5
+
6
+ ## Requirements:
7
+
8
+ * mosesdecoder: https://github.com/moses-smt/mosesdecoder
9
+ * subword-nmt: https://github.com/rsennrich/subword-nmt
10
+ * flores: https://github.com/facebookresearch/flores
11
+
12
+ ## Download Models and Test Data
13
+
14
+ Download translation models and test data from [MLQE dataset repository](https://github.com/facebookresearch/mlqe).
15
+
16
+ ## Set up:
17
+
18
+ Given a testset consisting of source sentences and reference translations:
19
+
20
+ * `SRC_LANG`: source language
21
+ * `TGT_LANG`: target language
22
+ * `INPUT`: input prefix, such that the file `$INPUT.$SRC_LANG` contains source sentences and `$INPUT.$TGT_LANG`
23
+ contains the reference sentences
24
+ * `OUTPUT_DIR`: output path to store results
25
+ * `MOSES_DECODER`: path to mosesdecoder installation
26
+ * `BPE_ROOT`: path to subword-nmt installation
27
+ * `BPE`: path to BPE model
28
+ * `MODEL_DIR`: directory containing the NMT model `.pt` file as well as the source and target vocabularies.
29
+ * `TMP`: directory for intermediate temporary files
30
+ * `GPU`: if translating with GPU, id of the GPU to use for inference
31
+ * `DROPOUT_N`: number of stochastic forward passes
32
+
33
+ `$DROPOUT_N` is set to 30 in the experiments reported in the paper. However, we observed that increasing it beyond 10
34
+ does not bring substantial improvements.
35
+
36
+ ## Translate the data using standard decoding
37
+
38
+ Preprocess the input data:
39
+ ```
40
+ for LANG in $SRC_LANG $TGT_LANG; do
41
+ perl $MOSES_DECODER/scripts/tokenizer/tokenizer.perl -threads 80 -a -l $LANG < $INPUT.$LANG > $TMP/preprocessed.tok.$LANG
42
+ python $BPE_ROOT/apply_bpe.py -c ${BPE} < $TMP/preprocessed.tok.$LANG > $TMP/preprocessed.tok.bpe.$LANG
43
+ done
44
+ ```
45
+
46
+ Binarize the data for faster translation:
47
+
48
+ ```
49
+ fairseq-preprocess --srcdict $MODEL_DIR/dict.$SRC_LANG.txt --tgtdict $MODEL_DIR/dict.$TGT_LANG.txt
50
+ --source-lang ${SRC_LANG} --target-lang ${TGT_LANG} --testpref $TMP/preprocessed.tok.bpe --destdir $TMP/bin --workers 4
51
+ ```
52
+
53
+ Translate
54
+
55
+ ```
56
+ CUDA_VISIBLE_DEVICES=$GPU fairseq-generate $TMP/bin --path ${MODEL_DIR}/${SRC_LANG}-${TGT_LANG}.pt --beam 5
57
+ --source-lang $SRC_LANG --target-lang $TGT_LANG --no-progress-bar --unkpen 5 > $TMP/fairseq.out
58
+ grep ^H $TMP/fairseq.out | cut -d- -f2- | sort -n | cut -f3- > $TMP/mt.out
59
+ ```
60
+
61
+ Post-process
62
+
63
+ ```
64
+ sed -r 's/(@@ )| (@@ ?$)//g' < $TMP/mt.out | perl $MOSES_DECODER/scripts/tokenizer/detokenizer.perl
65
+ -l $TGT_LANG > $OUTPUT_DIR/mt.out
66
+ ```
67
+
68
+ ## Produce uncertainty estimates
69
+
70
+ ### Scoring
71
+
72
+ Make temporary files to store the translations repeated N times.
73
+
74
+ ```
75
+ python ${SCRIPTS}/scripts/uncertainty/repeat_lines.py -i $TMP/preprocessed.tok.bpe.$SRC_LANG -n $DROPOUT_N
76
+ -o $TMP/repeated.$SRC_LANG
77
+ python ${SCRIPTS}/scripts/uncertainty/repeat_lines.py -i $TMP/mt.out -n $DROPOUT_N -o $TMP/repeated.$TGT_LANG
78
+
79
+ fairseq-preprocess --srcdict ${MODEL_DIR}/dict.${SRC_LANG}.txt $TGT_DIC --source-lang ${SRC_LANG}
80
+ --target-lang ${TGT_LANG} --testpref ${TMP}/repeated --destdir ${TMP}/bin-repeated
81
+ ```
82
+
83
+ Produce model scores for the generated translations using `--retain-dropout` option to apply dropout at inference time:
84
+
85
+ ```
86
+ CUDA_VISIBLE_DEVICES=${GPU} fairseq-generate ${TMP}/bin-repeated --path ${MODEL_DIR}/${LP}.pt --beam 5
87
+ --source-lang $SRC_LANG --target-lang $TGT_LANG --no-progress-bar --unkpen 5 --score-reference --retain-dropout
88
+ --retain-dropout-modules '["TransformerModel","TransformerEncoder","TransformerDecoder","TransformerEncoderLayer"]'
89
+ TransformerDecoderLayer --seed 46 > $TMP/dropout.scoring.out
90
+
91
+ grep ^H $TMP/dropout.scoring.out | cut -d- -f2- | sort -n | cut -f2 > $TMP/dropout.scores
92
+
93
+ ```
94
+
95
+ Use `--retain-dropout-modules` to specify the modules. By default, dropout is applied in the same places
96
+ as for training.
97
+
98
+ Compute the mean of the resulting output distribution:
99
+
100
+ ```
101
+ python $SCRIPTS/scripts/uncertainty/aggregate_scores.py -i $TMP/dropout.scores -o $OUTPUT_DIR/dropout.scores.mean
102
+ -n $DROPOUT_N
103
+ ```
104
+
105
+ ### Generation
106
+
107
+ Produce multiple translation hypotheses for the same source using `--retain-dropout` option:
108
+
109
+ ```
110
+ CUDA_VISIBLE_DEVICES=${GPU} fairseq-generate ${TMP}/bin-repeated --path ${MODEL_DIR}/${LP}.pt
111
+ --beam 5 --source-lang $SRC_LANG --target-lang $TGT_LANG --no-progress-bar --retain-dropout
112
+ --unkpen 5 --retain-dropout-modules TransformerModel TransformerEncoder TransformerDecoder
113
+ TransformerEncoderLayer TransformerDecoderLayer --seed 46 > $TMP/dropout.generation.out
114
+
115
+ grep ^H $TMP/dropout.generation.out | cut -d- -f2- | sort -n | cut -f3- > $TMP/dropout.hypotheses_
116
+
117
+ sed -r 's/(@@ )| (@@ ?$)//g' < $TMP/dropout.hypotheses_ | perl $MOSES_DECODER/scripts/tokenizer/detokenizer.perl
118
+ -l $TGT_LANG > $TMP/dropout.hypotheses
119
+ ```
120
+
121
+ Compute similarity between multiple hypotheses corresponding to the same source sentence using Meteor
122
+ evaluation metric:
123
+ ```
124
+ python meteor.py -i $TMP/dropout.hypotheses -m <path_to_meteor_installation> -n $DROPOUT_N -o
125
+ $OUTPUT_DIR/dropout.gen.sim.meteor
126
+ ```
omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/aggregate_scores.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 sys
8
+
9
+ import numpy as np
10
+
11
+
12
+ aggregate_funcs = {
13
+ "std": np.std,
14
+ "var": np.var,
15
+ "median": np.median,
16
+ "mean": np.mean,
17
+ "min": np.min,
18
+ "max": np.max,
19
+ }
20
+
21
+
22
+ def main():
23
+ parser = argparse.ArgumentParser()
24
+ parser.add_argument("-i", "--input_file", required=True, type=str)
25
+ parser.add_argument("-n", "--repeat_times", required=True, type=int)
26
+ parser.add_argument("-o", "--output_file", required=False)
27
+ parser.add_argument("-f", "--func", required=False, default="mean")
28
+ args = parser.parse_args()
29
+
30
+ stream = open(args.output_file, "w") if args.output_file else sys.stdout
31
+
32
+ segment_scores = []
33
+ for line in open(args.input_file):
34
+ segment_scores.append(float(line.strip()))
35
+ if len(segment_scores) == args.repeat_times:
36
+ stream.write("{}\n".format(aggregate_funcs[args.func](segment_scores)))
37
+ segment_scores = []
38
+
39
+
40
+ if __name__ == "__main__":
41
+ main()
omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/meteor.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ import math
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ import tempfile
12
+ from collections import defaultdict
13
+ from itertools import combinations
14
+
15
+
16
+ def read_translations(path, n_repeats):
17
+ segment_counter = 0
18
+ segment_translations = []
19
+ translations = defaultdict(list)
20
+ for line in open(path):
21
+ segment_translations.append(" ".join(line.split()))
22
+ if len(segment_translations) == n_repeats:
23
+ translations[segment_counter] = segment_translations
24
+ segment_translations = []
25
+ segment_counter += 1
26
+ return translations
27
+
28
+
29
+ def generate_input(translations, n_repeats):
30
+ _, ref_path = tempfile.mkstemp()
31
+ _, mt_path = tempfile.mkstemp()
32
+ ref_fh = open(ref_path, "w")
33
+ mt_fh = open(mt_path, "w")
34
+ for segid in sorted(translations.keys()):
35
+ assert len(translations[segid]) == n_repeats
36
+ indexes = combinations(range(n_repeats), 2)
37
+ for idx1, idx2 in indexes:
38
+ mt_fh.write(translations[segid][idx1].strip() + "\n")
39
+ ref_fh.write(translations[segid][idx2].strip() + "\n")
40
+ sys.stderr.write("\nSaved translations to %s and %s" % (ref_path, mt_path))
41
+ return ref_path, mt_path
42
+
43
+
44
+ def run_meteor(ref_path, mt_path, metric_path, lang="en"):
45
+ _, out_path = tempfile.mkstemp()
46
+ subprocess.call(
47
+ [
48
+ "java",
49
+ "-Xmx2G",
50
+ "-jar",
51
+ metric_path,
52
+ mt_path,
53
+ ref_path,
54
+ "-p",
55
+ "0.5 0.2 0.6 0.75", # default parameters, only changed alpha to give equal weight to P and R
56
+ "-norm",
57
+ "-l",
58
+ lang,
59
+ ],
60
+ stdout=open(out_path, "w"),
61
+ )
62
+ os.remove(ref_path)
63
+ os.remove(mt_path)
64
+ sys.stderr.write("\nSaved Meteor output to %s" % out_path)
65
+ return out_path
66
+
67
+
68
+ def read_output(meteor_output_path, n_repeats):
69
+ n_combinations = math.factorial(n_repeats) / (
70
+ math.factorial(2) * math.factorial(n_repeats - 2)
71
+ )
72
+ raw_scores = []
73
+ average_scores = []
74
+ for line in open(meteor_output_path):
75
+ if not line.startswith("Segment "):
76
+ continue
77
+ score = float(line.strip().split("\t")[1])
78
+ raw_scores.append(score)
79
+ if len(raw_scores) == n_combinations:
80
+ average_scores.append(sum(raw_scores) / n_combinations)
81
+ raw_scores = []
82
+ os.remove(meteor_output_path)
83
+ return average_scores
84
+
85
+
86
+ def main():
87
+ parser = argparse.ArgumentParser()
88
+ parser.add_argument("-i", "--infile")
89
+ parser.add_argument("-n", "--repeat_times", type=int)
90
+ parser.add_argument("-m", "--meteor")
91
+ parser.add_argument("-o", "--output")
92
+ args = parser.parse_args()
93
+
94
+ translations = read_translations(args.infile, args.repeat_times)
95
+ sys.stderr.write("\nGenerating input for Meteor...")
96
+ ref_path, mt_path = generate_input(translations, args.repeat_times)
97
+ sys.stderr.write("\nRunning Meteor...")
98
+ out_path = run_meteor(ref_path, mt_path, args.meteor)
99
+ sys.stderr.write("\nReading output...")
100
+ scores = read_output(out_path, args.repeat_times)
101
+ sys.stderr.write("\nWriting results...")
102
+ with open(args.output, "w") as o:
103
+ for scr in scores:
104
+ o.write("{}\n".format(scr))
105
+ o.close()
106
+
107
+
108
+ if __name__ == "__main__":
109
+ main()
omni_speech/infer/fairseq/examples/unsupervised_quality_estimation/repeat_lines.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 argparse
7
+ import sys
8
+
9
+
10
+ def _normalize_spaces(line):
11
+ return " ".join(line.split())
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser()
16
+ parser.add_argument("-i", "--input_file", required=True, type=str)
17
+ parser.add_argument("-n", "--repeat_times", required=True, type=int)
18
+ parser.add_argument("-o", "--output_file", required=False, type=str)
19
+ args = parser.parse_args()
20
+ stream = open(args.output_file, "w") if args.output_file else sys.stdout
21
+
22
+ for line in open(args.input_file):
23
+ for _ in range(args.repeat_times):
24
+ stream.write(_normalize_spaces(line) + "\n")
25
+
26
+
27
+ if __name__ == "__main__":
28
+ main()
omni_speech/infer/fairseq/examples/wav2vec/README.md ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # wav2vec 2.0
2
+
3
+ wav2vec 2.0 learns speech representations on unlabeled data as described in [wav2vec 2.0: A Framework for Self-Supervised Learning of Speech Representations (Baevski et al., 2020)](https://arxiv.org/abs/2006.11477).
4
+
5
+ We learned speech representations in multiple languages as well in [Unsupervised Cross-lingual Representation Learning for Speech Recognition (Conneau et al., 2020)](https://arxiv.org/abs/2006.13979).
6
+
7
+ We also combined wav2vec 2.0 with self-training in [Self-training and Pre-training are Complementary for Speech Recognition (Xu et al., 2020)](https://arxiv.org/abs/2010.11430).
8
+
9
+ We combined speech data from multiple domains in [Robust wav2vec 2.0: Analyzing Domain Shift in Self-Supervised Pre-Training (Hsu, et al., 2021)](https://arxiv.org/abs/2104.01027).
10
+
11
+ We finetuned XLSR-53 on multiple languages to transcribe unseen languages in [Simple and Effective Zero-shot Cross-lingual Phoneme Recognition (Xu et al., 2021)](https://arxiv.org/abs/2109.11680).
12
+
13
+ ## Pre-trained models
14
+
15
+ Model | Finetuning split | Dataset | Model
16
+ |---|---|---|---
17
+ Wav2Vec 2.0 Base | No finetuning | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_small.pt)
18
+ Wav2Vec 2.0 Base | 10 minutes | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_small_10m.pt)
19
+ Wav2Vec 2.0 Base | 100 hours | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_small_100h.pt)
20
+ Wav2Vec 2.0 Base | 960 hours | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_small_960h.pt)
21
+ Wav2Vec 2.0 Large | No finetuning | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/libri960_big.pt)
22
+ Wav2Vec 2.0 Large | 10 minutes | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_big_10m.pt)
23
+ Wav2Vec 2.0 Large | 100 hours | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_big_100h.pt)
24
+ Wav2Vec 2.0 Large | 960 hours | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_big_960h.pt)
25
+ Wav2Vec 2.0 Large (LV-60)* | No finetuning | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_new.pt)
26
+ Wav2Vec 2.0 Large conformer - rel_pos (LV-60)* | No finetuning | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_relpos_PT_no_FT)
27
+ Wav2Vec 2.0 Large conformer - rope (LV-60)* | No finetuning | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_rope_PT_no_FT)
28
+ Wav2Vec 2.0 Large (LV-60)* | 10 minutes | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_10m_new.pt)
29
+ Wav2Vec 2.0 Large (LV-60)* | 100 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_100h_new.pt)
30
+ Wav2Vec 2.0 Large conformer - rel_pos (LV-60)* | 100 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_relpos_PT_100h_FT.pt)
31
+ Wav2Vec 2.0 Large conformer - rope (LV-60)* | 100 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_rope_PT_100h_FT.pt)
32
+ Wav2Vec 2.0 Large (LV-60)* | 960 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec2_vox_960h_new.pt)
33
+ Wav2Vec 2.0 Large conformer - rel_pos (LV-60)* | 960 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_relpos_PT_960h_FT.pt)
34
+ Wav2Vec 2.0 Large conformer - rope (LV-60)* | 960 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) | [download](s3://dl.fbaipublicfiles.com/fairseq/conformer/wav2vec2/librilight/LL_rope_PT_960h_FT.pt)
35
+ Wav2Vec 2.0 Large (LV-60) + Self Training * | 10 minutes | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_10m_pl.pt)
36
+ Wav2Vec 2.0 Large (LV-60) + Self Training * | 100 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_100h_pl.pt)
37
+ Wav2Vec 2.0 Large (LV-60) + Self Training * | 960 hours | [Libri-Light](https://github.com/facebookresearch/libri-light) + [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_vox_960h_pl.pt)
38
+ Wav2Vec 2.0 Large (LV-60 + CV + SWBD + FSH) ** | No finetuning | [Libri-Light](https://github.com/facebookresearch/libri-light) + [CommonVoice](https://commonvoice.mozilla.org/en/languages) + [Switchboard](https://catalog.ldc.upenn.edu/LDC97S62) + [Fisher](https://catalog.ldc.upenn.edu/LDC2004T19) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/w2v_large_lv_fsh_swbd_cv.pt)
39
+ Wav2Vec 2.0 Large (LV-60 + CV + SWBD + FSH) ** | 960 hours Librispeech | [Libri-Light](https://github.com/facebookresearch/libri-light) + [CommonVoice](https://commonvoice.mozilla.org/en/languages) + [Switchboard](https://catalog.ldc.upenn.edu/LDC97S62) + [Fisher](https://catalog.ldc.upenn.edu/LDC2004T19) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/w2v_large_lv_fsh_swbd_cv_ftls960_updated.pt)
40
+ Wav2Vec 2.0 Large (LV-60 + CV + SWBD + FSH) ** | 300 hours Switchboard | [Libri-Light](https://github.com/facebookresearch/libri-light) + [CommonVoice](https://commonvoice.mozilla.org/en/languages) + [Switchboard](https://catalog.ldc.upenn.edu/LDC97S62) + [Fisher](https://catalog.ldc.upenn.edu/LDC2004T19) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/w2v_large_lv_fsh_swbd_cv_ftsb300_updated.pt)
41
+
42
+ \* updated (Oct. 24, 2020)\
43
+ ** updated (Nov. 13, 2021)
44
+
45
+ We also release multilingual pre-trained wav2vec 2.0 (XLSR) models:
46
+
47
+ Model | Architecture | Hours | Languages | Datasets | Model
48
+ |---|---|---|---|---|---
49
+ XLSR-53 | Large | 56k | 53 | MLS, CommonVoice, BABEL | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/xlsr_53_56k.pt)
50
+
51
+ The XLSR model uses the following datasets for multilingual pretraining:
52
+
53
+ * **[MLS: Multilingual LibriSpeech](https://indico2.conference4me.psnc.pl/event/35/contributions/3585/attachments/1060/1101/Wed-2-6-10.pdf)** (8 languages, 50.7k hours): *Dutch, English, French, German, Italian, Polish, Portuguese, Spanish*
54
+
55
+ * **[CommonVoice](https://commonvoice.mozilla.org/en/languages)** (36 languages, 3.6k hours): *Arabic, Basque, Breton, Chinese (CN), Chinese (HK), Chinese (TW), Chuvash, Dhivehi, Dutch, English, Esperanto, Estonian, French, German, Hakh-Chin, Indonesian, Interlingua, Irish, Italian, Japanese, Kabyle, Kinyarwanda, Kyrgyz, Latvian, Mongolian, Persian, Portuguese, Russian, Sakha, Slovenian, Spanish, Swedish, Tamil, Tatar, Turkish, Welsh* (see also [finetuning splits]([https://dl.fbaipublicfiles.com/cpc_audio/common_voices_splits.tar.gz]) from [this paper](https://arxiv.org/abs/2002.02848)).
56
+
57
+ * **[Babel](https://catalog.ldc.upenn.edu/byyear)** (17 languages, 1.7k hours): *Assamese, Bengali, Cantonese, Cebuano, Georgian, Haitian, Kazakh, Kurmanji, Lao, Pashto, Swahili, Tagalog, Tamil, Tok, Turkish, Vietnamese, Zulu*
58
+
59
+ We also finetuned several models on languages from [CommonVoice](https://commonvoice.mozilla.org/en/languages) (version 6.1) and [Babel](https://catalog.ldc.upenn.edu/byyear). Please refer to [our paper](https://arxiv.org/abs/2109.11680) for details about which languages are used.
60
+
61
+ Pretrained Model | Fintune Dataset | # Languages | Phonemizer | Model | Dictionary
62
+ |---|---|---|---|---|---
63
+ LV-60 | CommonVoice | 26 | [Espeak](https://github.com/espeak-ng/espeak-ng/blob/master/docs/languages.md) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/espeak_en_26lang_m10.pt) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/espeak_dict.txt)
64
+ XLSR-53 | CommonVoice | 26 | [Espeak](https://github.com/espeak-ng/espeak-ng/blob/master/docs/languages.md) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/espeak_26lang_m10.pt) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/espeak_dict.txt)
65
+ XLSR-53 | CommonVoice | 21 | [Phonetisaurus](https://github.com/AdolfVonKleist/Phonetisaurus) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/phonetisaurus_21lang_m10.pt) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/phonetisaurus_dict.txt)
66
+ XLSR-53 | CommonVoice, BABEL | 21, 19 | [Phonetisaurus](https://github.com/AdolfVonKleist/Phonetisaurus) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/phonetisaurus_40lang_m10.pt) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/zero_shot/phonetisaurus_40lang.dict.txt)
67
+
68
+ We release 2 models that are finetuned on data from 2 different phonemizers. Although the phonemes are all [IPA](https://en.wikipedia.org/wiki/International_Phonetic_Alphabet) symbols, there are still subtle differences between the phonemized transcriptions from the 2 phonemizers. Thus, it's better to use the corresponding model, if your data is phonemized by either phonemizer above.
69
+
70
+ ## Training a new model with the CLI tools
71
+
72
+ Given a directory containing wav files to be used for pretraining (we recommend splitting each file into separate file 10 to 30 seconds in length)
73
+
74
+ ### Prepare training data manifest
75
+
76
+ First, install the `soundfile` library:
77
+
78
+ ```shell script
79
+ pip install soundfile
80
+ ```
81
+
82
+ Next, run:
83
+
84
+ ```shell script
85
+ python examples/wav2vec/wav2vec_manifest.py /path/to/waves --dest /manifest/path --ext $ext --valid-percent $valid
86
+ ```
87
+
88
+ $ext should be set to flac, wav, or whatever format your dataset happens to use that soundfile can read.
89
+
90
+ $valid should be set to some reasonable percentage (like 0.01) of training data to use for validation.
91
+ To use a pre-defined validation set (like dev-other from librispeech), set to it 0 and then overwrite valid.tsv with a
92
+ separately pre-processed manifest file.
93
+
94
+ ### Train a wav2vec 2.0 base model
95
+
96
+ This configuration was used for the base model trained on the Librispeech dataset in the wav2vec 2.0 paper
97
+
98
+ Note that the input is expected to be single channel, sampled at 16 kHz
99
+
100
+ ```shell script
101
+ $ fairseq-hydra-train \
102
+ task.data=/path/to/data \
103
+ --config-dir /path/to/fairseq-py/examples/wav2vec/config/pretraining \
104
+ --config-name wav2vec2_base_librispeech
105
+ ```
106
+
107
+ Note: you can simulate 64 GPUs by using k GPUs and adding command line parameters (before `--config-dir`)
108
+ `distributed_training.distributed_world_size=k` `+optimization.update_freq='[x]'` where x = 64/k
109
+
110
+ ### Train a wav2vec 2.0 large model
111
+
112
+ This configuration was used for the large model trained on the Libri-light dataset in the wav2vec 2.0 paper
113
+
114
+ ```shell script
115
+ $ fairseq-hydra-train \
116
+ task.data=/path/to/data \
117
+ --config-dir /path/to/fairseq-py/examples/wav2vec/config/pretraining \
118
+ --config-name wav2vec2_large_librivox
119
+ ```
120
+
121
+ Note: you can simulate 128 GPUs by using k GPUs and adding command line parameters (before `--config-dir`)
122
+ `distributed_training.distributed_world_size=k` `+optimization.update_freq='[x]'` where x = 128/k
123
+
124
+ ### Train a wav2vec 2.0 model with conformer backbone
125
+
126
+ To replace the transformer layers in the encoder with the conformer layers, set `--layer-type conformer --attn-type espnet --pos-enc-type ${POS_ENC_TYPE}`. `POS_ENC_TYPE` refers to positional encoding to be used in the conformer encoder.
127
+ Set it to `abs`, `rope` or `rel_pos` to use the absolute positional encoding, rotary positional encoding or relative positional encoding in the conformer layer respectively.
128
+
129
+ To train a base model with conformer:
130
+
131
+ ```shell script
132
+ $ fairseq-hydra-train \
133
+ task.data=/path/to/data \
134
+ --config-dir /path/to/fairseq-py/examples/wav2vec/config/pretraining \
135
+ --config-name wav2vec2_conformer_base_librispeech \
136
+ --attn-type espnet --pos-enc-type ${POS_ENC_TYPE}
137
+ ```
138
+
139
+ To train a large model with conformer:
140
+
141
+ ```shell script
142
+ $ fairseq-hydra-train \
143
+ task.data=/path/to/data \
144
+ --config-dir /path/to/fairseq-py/examples/wav2vec/config/pretraining \
145
+ --config-name wav2vec2_conformer_large_librivox
146
+ --attn-type espnet --pos-enc-type ${POS_ENC_TYPE}
147
+
148
+ ```
149
+
150
+ ### Fine-tune a pre-trained model with CTC
151
+
152
+ Fine-tuning a model requires parallel audio and labels file, as well as a vocabulary file in fairseq format.
153
+ A letter vocabulary can be downloaded [here](https://dl.fbaipublicfiles.com/fairseq/wav2vec/dict.ltr.txt).
154
+ An example [script](libri_labels.py) that generates labels for the Librispeech dataset from the tsv file produced by wav2vec_manifest.py can be used as follows:
155
+
156
+ ```shell script
157
+ split=train
158
+ $ python libri_labels.py /path/to/tsv --output-dir /output/dir --output-name $split
159
+ ```
160
+
161
+ Fine-tuning on 100h of Librispeech with letter targets:
162
+
163
+ ```shell script
164
+ $ fairseq-hydra-train \
165
+ distributed_training.distributed_port=$PORT \
166
+ task.data=/path/to/data \
167
+ model.w2v_path=/path/to/model.pt \
168
+ --config-dir /path/to/fairseq-py/examples/wav2vec/config/finetuning \
169
+ --config-name base_100h
170
+ ```
171
+
172
+ There are other config files in the config/finetuning directory that can be used to fine-tune on other splits.
173
+ You can specify the right config via the `--config-name` parameter.
174
+
175
+ Note: you can simulate 24 GPUs by using k GPUs and adding command line parameters (before `--config-dir`)
176
+ `distributed_training.distributed_world_size=k` `+optimization.update_freq='[x]'` where x = 24/k
177
+
178
+ Decoding with a language model during training requires flashlight [python bindings](https://github.com/facebookresearch/flashlight/tree/master/bindings/python) (previously called [wav2letter](https://github.com/facebookresearch/wav2letter).
179
+ If you want to use a language model, add `+criterion.wer_args='[/path/to/kenlm, /path/to/lexicon, 2, -1]'` to the command line.
180
+
181
+ ### Evaluating a CTC model
182
+
183
+ Evaluating a CTC model with a language model requires [flashlight python bindings](https://github.com/facebookresearch/flashlight/tree/master/bindings/python) (previously called [wav2letter](https://github.com/facebookresearch/wav2letter) to be installed.
184
+
185
+ Fairseq transformer language model used in the wav2vec 2.0 paper can be obtained from the [wav2letter model repository](https://github.com/facebookresearch/wav2letter/tree/master/recipes/sota/2019).
186
+ Be sure to upper-case the language model vocab after downloading it.
187
+
188
+ Letter dictionary for pre-trained models can be found [here](https://dl.fbaipublicfiles.com/fairseq/wav2vec/dict.ltr.txt).
189
+
190
+ Next, run the evaluation command:
191
+
192
+ ```shell script
193
+ $subset=dev_other
194
+ python examples/speech_recognition/infer.py /checkpoint/abaevski/data/speech/libri/10h/wav2vec/raw --task audio_finetuning \
195
+ --nbest 1 --path /path/to/model --gen-subset $subset --results-path /path/to/save/results/for/sclite --w2l-decoder kenlm \
196
+ --lm-model /path/to/kenlm.bin --lm-weight 2 --word-score -1 --sil-weight 0 --criterion ctc --labels ltr --max-tokens 4000000 \
197
+ --post-process letter
198
+ ```
199
+
200
+ To get raw numbers, use --w2l-decoder viterbi and omit the lexicon. To use the transformer language model, use --w2l-decoder fairseqlm.
201
+
202
+ ## Use wav2vec 2.0 with 🤗Transformers
203
+
204
+ Wav2Vec2 is also available in the [🤗Transformers library](https://github.com/huggingface/transformers) since version 4.4.
205
+
206
+ Pretrained Models can be found on the [hub](https://huggingface.co/models?filter=wav2vec2)
207
+ and documentation can be found [here](https://huggingface.co/transformers/master/model_doc/wav2vec2.html).
208
+
209
+ Usage example:
210
+
211
+ ```python
212
+ # !pip install transformers
213
+ # !pip install datasets
214
+ import soundfile as sf
215
+ import torch
216
+ from datasets import load_dataset
217
+ from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor
218
+
219
+ # load pretrained model
220
+ processor = Wav2Vec2Processor.from_pretrained("facebook/wav2vec2-base-960h")
221
+ model = Wav2Vec2ForCTC.from_pretrained("facebook/wav2vec2-base-960h")
222
+
223
+
224
+ librispeech_samples_ds = load_dataset("patrickvonplaten/librispeech_asr_dummy", "clean", split="validation")
225
+
226
+ # load audio
227
+ audio_input, sample_rate = sf.read(librispeech_samples_ds[0]["file"])
228
+
229
+ # pad input values and return pt tensor
230
+ input_values = processor(audio_input, sampling_rate=sample_rate, return_tensors="pt").input_values
231
+
232
+ # INFERENCE
233
+
234
+ # retrieve logits & take argmax
235
+ logits = model(input_values).logits
236
+ predicted_ids = torch.argmax(logits, dim=-1)
237
+
238
+ # transcribe
239
+ transcription = processor.decode(predicted_ids[0])
240
+
241
+ # FINE-TUNE
242
+
243
+ target_transcription = "A MAN SAID TO THE UNIVERSE I EXIST"
244
+
245
+ # encode labels
246
+ with processor.as_target_processor():
247
+ labels = processor(target_transcription, return_tensors="pt").input_ids
248
+
249
+ # compute loss by passing labels
250
+ loss = model(input_values, labels=labels).loss
251
+ loss.backward()
252
+ ```
253
+
254
+ # wav2vec
255
+
256
+ Example to train a wav2vec model as described in [wav2vec: Unsupervised Pre-training for Speech Recognition (Schneider et al., 2019)](https://arxiv.org/abs/1904.05862).
257
+
258
+ ## Pre-trained models
259
+
260
+ Description | Dataset | Model
261
+ ---|---|---
262
+ Wav2Vec large | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/wav2vec_large.pt)
263
+
264
+ #### Example usage
265
+
266
+ ```python
267
+ import torch
268
+ import fairseq
269
+
270
+ cp_path = '/path/to/wav2vec.pt'
271
+ model, cfg, task = fairseq.checkpoint_utils.load_model_ensemble_and_task([cp_path])
272
+ model = model[0]
273
+ model.eval()
274
+
275
+ wav_input_16khz = torch.randn(1,10000)
276
+ z = model.feature_extractor(wav_input_16khz)
277
+ c = model.feature_aggregator(z)
278
+ ```
279
+
280
+ ## Training a new model with the CLI tools
281
+
282
+ Given a directory containing wav files to be used for pretraining (we recommend splitting each file into separate files 10 to 30 seconds in length)
283
+
284
+ ### Prepare training data manifest
285
+
286
+ ```
287
+ python examples/wav2vec/wav2vec_manifest.py /path/to/waves --dest /manifest/path --ext wav
288
+ ```
289
+
290
+ ### Train a wav2vec model
291
+
292
+ ```
293
+ $ python train.py /manifest/path --save-dir /model/path --num-workers 6 --fp16 --max-update 400000 --save-interval 1 --no-epoch-checkpoints \
294
+ --arch wav2vec --task audio_pretraining --min-lr 1e-06 --stop-min-lr 1e-09 --optimizer adam --lr 0.005 --lr-scheduler cosine \
295
+ --conv-feature-layers [(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1)] \
296
+ --conv-aggregator-layers [(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)] \
297
+ --skip-connections-agg --residual-scale 0.5 --log-compression --warmup-updates 500 --warmup-init-lr 1e-07 --criterion wav2vec --num-negatives 10 \
298
+ --max-sample-size 150000 --max-tokens 1500000 --skip-invalid-size-inputs-valid-test
299
+ ```
300
+
301
+ ### Run wav2vec2 pre-training on Google Cloud TPUs
302
+
303
+ Wav2Vec2 is now supported on TPUs! It's currently pre-training only.
304
+
305
+ #### Using hydra on a v3-8
306
+
307
+ ```
308
+ $ OMP_NUM_THREADS=1 fairseq-hydra-train \
309
+ task.data=/manifest/path \
310
+ --config-dir /PATH/TO/FAIRSEQ/examples/wav2vec/config/pretraining \
311
+ --config-name wav2vec2_large_librivox_tpu.yaml
312
+ ```
313
+
314
+ #### Using command line arguments on a v3-8
315
+
316
+ Note: Commandline arguments way of execution has a [known-problem](https://github.com/pytorch/fairseq/issues/3741) currently.
317
+
318
+ ```
319
+ $ OMP_NUM_THREADS=1 python train.py /manifest/path --save-dir /model/path --num-workers 6 --fp16 --max-update 400000 --save-interval 1 --no-epoch-checkpoints \
320
+ --arch wav2vec2 --task audio_pretraining --min-lr 1e-06 --stop-min-lr 1e-09 --optimizer adam --lr 0.005 --lr-scheduler cosine \
321
+ --conv-feature-layers [(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1)] \
322
+ --conv-aggregator-layers [(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)] \
323
+ --skip-connections-agg --residual-scale 0.5 --log-compression --warmup-updates 500 --warmup-init-lr 1e-07 --criterion wav2vec --num-negatives 10 \
324
+ --max-sample-size 150000 --max-tokens 1500000 --skip-invalid-size-inputs-valid-test \
325
+ --tpu --distributed-world-size 8 --num-batch-buckets 3 --enable-padding \
326
+ --encoder-layerdrop 0 --mask-channel-prob 0.1
327
+ ```
328
+
329
+ #### Using hydra on a pod slice (v3-N with N > 8)
330
+
331
+ ```
332
+ $ OMP_NUM_THREADS=1 fairseq-hydra-train \
333
+ task.data=/manifest/path \
334
+ --config-dir /PATH/TO/FAIRSEQ/examples/wav2vec/config/pretraining \
335
+ --config-name wav2vec2_large_librivox_tpu-pod.yaml # edit distributed-world-size accordingly
336
+ ```
337
+
338
+ #### Using command line arguments on a pod slice (v3-N with N > 8)
339
+
340
+ Note: Commandline arguments way of execution has a [known-problem](https://github.com/pytorch/fairseq/issues/3741) currently.
341
+
342
+ ```
343
+ $ python -m torch_xla.distributed.xla_dist \
344
+ --tpu ${TPUNAME} --conda-env=torch-xla-${TORCH_XLA_VERSION} --env OMP_NUM_THREADS=1 \
345
+ -- \
346
+ python train.py /manifest/path --save-dir /model/path --num-workers 6 --fp16 --max-update 400000 --save-interval 1 --no-epoch-checkpoints \
347
+ --arch wav2vec2 --task audio_pretraining --min-lr 1e-06 --stop-min-lr 1e-09 --optimizer adam --lr 0.005 --lr-scheduler cosine \
348
+ --conv-feature-layers [(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1)] \
349
+ --conv-aggregator-layers [(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)] \
350
+ --skip-connections-agg --residual-scale 0.5 --log-compression --warmup-updates 500 --warmup-init-lr 1e-07 --criterion wav2vec --num-negatives 10 \
351
+ --max-sample-size 150000 --max-tokens 1500000 --skip-invalid-size-inputs-valid-test \
352
+ --tpu --distributed-world-size ${WORLD_SIZE} --num-batch-buckets 3 --enable-padding \
353
+ --encoder-layerdrop 0 --mask-channel-prob 0.1
354
+ ```
355
+
356
+ ### Extract embeddings from the downstream task data
357
+
358
+ ```
359
+ $ PYTHONPATH=/path/to/fairseq python examples/wav2vec/wav2vec_featurize.py --input /path/to/task/waves --output /path/to/output \
360
+ --model /model/path/checkpoint_best.pt --split train valid test
361
+ ```
362
+
363
+ # vq-wav2vec
364
+
365
+ Example to train a vq-wav2vec model as described in [vq-wav2vec: Self-Supervised Learning of Discrete Speech Representations (Baevski et al., 2019)](https://arxiv.org/abs/1910.05453).
366
+
367
+ These models are also used in [Effectiveness of self-supervised pre-training for speech recognition (Baevski et al., 2019)](https://arxiv.org/abs/1911.03912).
368
+
369
+ ## Pre-trained models
370
+
371
+ Description | Dataset | Model
372
+ ---|---|---
373
+ vq-wav2vec Gumbel | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/vq-wav2vec.pt)
374
+ vq-wav2vec K-means | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/vq-wav2vec_kmeans.pt)
375
+ Roberta on K-means codes | [Librispeech](http://www.openslr.org/12) | [download](https://dl.fbaipublicfiles.com/fairseq/wav2vec/bert_kmeans.tar)
376
+
377
+ #### Example usage
378
+
379
+ ```python
380
+ import torch
381
+ import fairseq
382
+
383
+ cp = torch.load('/path/to/vq-wav2vec.pt')
384
+ model, cfg, task = fairseq.checkpoint_utils.load_model_ensemble_and_task([cp])
385
+ model = model[0]
386
+ model.eval()
387
+
388
+ wav_input_16khz = torch.randn(1,10000)
389
+ z = model.feature_extractor(wav_input_16khz)
390
+ _, idxs = model.vector_quantizer.forward_idx(z)
391
+ print(idxs.shape) # output: torch.Size([1, 60, 2]), 60 timesteps with 2 indexes corresponding to 2 groups in the model
392
+ ```
393
+
394
+ ## Training a new model with the CLI tools
395
+
396
+ Given a directory containing wav files to be used for pretraining (we recommend splitting each file into separate file 10 to 30 seconds in length)
397
+
398
+ ### Prepare training data manifest
399
+
400
+ ```
401
+ python examples/wav2vec/wav2vec_manifest.py /path/to/waves --dest /manifest/path --ext wav
402
+ ```
403
+
404
+ ### Train a gumbel vq-wav2vec model
405
+
406
+ ```
407
+ $ python train.py /manifest/path --save-dir /model/path --num-workers 6 --fp16 --max-update 400000 \
408
+ --save-interval 1 --no-epoch-checkpoints --arch wav2vec --task audio_pretraining --min-lr 1e-06 --stop-min-lr 1e-09 \
409
+ --optimizer adam --lr 1e-05 --lr-scheduler cosine \
410
+ --conv-feature-layers [(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1), (512, 1, 1)] \
411
+ --conv-aggregator-layers [(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)] \
412
+ --activation gelu --offset auto --skip-connections-agg --residual-scale 0.5 \
413
+ --log-keys ["prob_perplexity","code_perplexity","temp"] --vq-type gumbel --vq-groups 2 --vq-depth 2 \
414
+ --combine-groups --vq-vars 320 --vq-temp (2,0.5,0.999995) --prediction-steps 12 --warmup-updates 1000 \
415
+ --warmup-init-lr 1e-07 --criterion wav2vec --num-negatives 10 --max-sample-size 150000 \
416
+ --max-tokens 300000 --cross-sample-negatives 0 --update-freq 1 --seed 2 --skip-invalid-size-inputs-valid-test
417
+ ```
418
+
419
+ for k-means training, set vq-type with "kmeans" and add --loss-weights [1] argument. Pre-trained models were trained on 16 GPUs.
420
+
421
+ ### Tokenize audio data (e.g. for BERT training)
422
+
423
+ ```
424
+ $ PYTHONPATH=/path/to/fairseq python examples/wav2vec/vq-wav2vec_featurize.py --data-dir /manifest/path --output-dir /path/to/output \
425
+ --checkpoint /model/path/checkpoint_best.pt --split train valid test --extension tsv
426
+ ```
omni_speech/infer/fairseq/examples/wav2vec/__init__.py ADDED
File without changes
omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_100h.yaml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _group_
2
+
3
+ common:
4
+ fp16: true
5
+ log_format: json
6
+ log_interval: 200
7
+
8
+ checkpoint:
9
+ no_epoch_checkpoints: true
10
+ best_checkpoint_metric: wer
11
+
12
+ task:
13
+ _name: audio_finetuning
14
+ data: ???
15
+ normalize: false
16
+ labels: ltr
17
+
18
+ dataset:
19
+ num_workers: 6
20
+ max_tokens: 3200000
21
+ skip_invalid_size_inputs_valid_test: true
22
+ valid_subset: dev_other
23
+
24
+ distributed_training:
25
+ ddp_backend: legacy_ddp
26
+ distributed_world_size: 2
27
+
28
+ criterion:
29
+ _name: ctc
30
+ zero_infinity: true
31
+
32
+ optimization:
33
+ max_update: 80000
34
+ lr: [0.00003]
35
+ sentence_avg: true
36
+ update_freq: [4]
37
+
38
+ optimizer:
39
+ _name: adam
40
+ adam_betas: (0.9,0.98)
41
+ adam_eps: 1e-08
42
+
43
+ lr_scheduler:
44
+ _name: tri_stage
45
+ phase_ratio: [0.1, 0.4, 0.5]
46
+ final_lr_scale: 0.05
47
+
48
+ model:
49
+ _name: wav2vec_ctc
50
+ w2v_path: ???
51
+ apply_mask: true
52
+ mask_prob: 0.65
53
+ mask_channel_prob: 0.5
54
+ mask_channel_length: 64
55
+ layerdrop: 0.1
56
+ activation_dropout: 0.1
57
+ feature_grad_mult: 0.0
58
+ freeze_finetune_updates: 0
omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_10h.yaml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _group_
2
+
3
+ common:
4
+ fp16: true
5
+ log_format: json
6
+ log_interval: 200
7
+
8
+ checkpoint:
9
+ save_interval: 50
10
+ save_interval_updates: 10000
11
+ keep_interval_updates: 1
12
+ no_epoch_checkpoints: true
13
+ best_checkpoint_metric: wer
14
+
15
+ task:
16
+ _name: audio_finetuning
17
+ data: ???
18
+ normalize: false
19
+ labels: ltr
20
+
21
+ dataset:
22
+ num_workers: 6
23
+ max_tokens: 3200000
24
+ skip_invalid_size_inputs_valid_test: true
25
+ validate_after_updates: 10000
26
+ validate_interval: 50
27
+ valid_subset: dev_other
28
+
29
+ distributed_training:
30
+ ddp_backend: legacy_ddp
31
+ distributed_world_size: 2
32
+
33
+ criterion:
34
+ _name: ctc
35
+ zero_infinity: true
36
+
37
+ optimization:
38
+ max_update: 20000
39
+ lr: [0.00005]
40
+ sentence_avg: true
41
+ update_freq: [4]
42
+
43
+ optimizer:
44
+ _name: adam
45
+ adam_betas: (0.9,0.98)
46
+ adam_eps: 1e-08
47
+
48
+ lr_scheduler:
49
+ _name: tri_stage
50
+ phase_ratio: [0.1, 0.4, 0.5]
51
+ final_lr_scale: 0.05
52
+
53
+ model:
54
+ _name: wav2vec_ctc
55
+ w2v_path: ???
56
+ apply_mask: true
57
+ mask_prob: 0.65
58
+ mask_channel_prob: 0.5
59
+ mask_channel_length: 64
60
+ layerdrop: 0.05
61
+ activation_dropout: 0.1
62
+ feature_grad_mult: 0.0
63
+ freeze_finetune_updates: 10000
omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_10m.yaml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _group_
2
+
3
+ common:
4
+ fp16: true
5
+ log_format: json
6
+ log_interval: 200
7
+
8
+ checkpoint:
9
+ save_interval: 1000
10
+ save_interval_updates: 50
11
+ keep_interval_updates: 1
12
+ no_epoch_checkpoints: true
13
+ best_checkpoint_metric: wer
14
+
15
+ task:
16
+ _name: audio_finetuning
17
+ data: ???
18
+ normalize: false
19
+ labels: ltr
20
+
21
+ dataset:
22
+ num_workers: 6
23
+ max_tokens: 3200000
24
+ skip_invalid_size_inputs_valid_test: true
25
+ validate_after_updates: 10000
26
+ validate_interval: 1000
27
+ valid_subset: dev_other
28
+
29
+ distributed_training:
30
+ ddp_backend: legacy_ddp
31
+ distributed_world_size: 2
32
+
33
+ criterion:
34
+ _name: ctc
35
+ zero_infinity: true
36
+
37
+ optimization:
38
+ max_update: 13000
39
+ lr: [0.00005]
40
+ sentence_avg: true
41
+ update_freq: [4]
42
+
43
+ optimizer:
44
+ _name: adam
45
+ adam_betas: (0.9,0.98)
46
+ adam_eps: 1e-08
47
+
48
+ lr_scheduler:
49
+ _name: tri_stage
50
+ phase_ratio: [0.1, 0.4, 0.5]
51
+ final_lr_scale: 0.05
52
+
53
+ model:
54
+ _name: wav2vec_ctc
55
+ w2v_path: ???
56
+ apply_mask: true
57
+ mask_prob: 0.65
58
+ mask_channel_prob: 0.25
59
+ mask_channel_length: 64
60
+ layerdrop: 0.1
61
+ activation_dropout: 0.1
62
+ feature_grad_mult: 0.0
63
+ freeze_finetune_updates: 10000
omni_speech/infer/fairseq/examples/wav2vec/config/finetuning/base_1h.yaml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _group_
2
+
3
+ common:
4
+ fp16: true
5
+ log_format: json
6
+ log_interval: 200
7
+
8
+ checkpoint:
9
+ save_interval: 50
10
+ save_interval_updates: 1000
11
+ keep_interval_updates: 1
12
+ no_epoch_checkpoints: true
13
+ best_checkpoint_metric: wer
14
+
15
+ task:
16
+ _name: audio_finetuning
17
+ data: ???
18
+ normalize: false
19
+ labels: ltr
20
+
21
+ dataset:
22
+ num_workers: 6
23
+ max_tokens: 3200000
24
+ skip_invalid_size_inputs_valid_test: true
25
+ validate_after_updates: 10000
26
+ validate_interval: 1000
27
+ valid_subset: dev_other
28
+
29
+ distributed_training:
30
+ ddp_backend: legacy_ddp
31
+ distributed_world_size: 2
32
+
33
+ criterion:
34
+ _name: ctc
35
+ zero_infinity: true
36
+
37
+ optimization:
38
+ max_update: 13000
39
+ lr: [0.00005]
40
+ sentence_avg: true
41
+ update_freq: [4]
42
+
43
+ optimizer:
44
+ _name: adam
45
+ adam_betas: (0.9,0.98)
46
+ adam_eps: 1e-08
47
+
48
+ lr_scheduler:
49
+ _name: tri_stage
50
+ phase_ratio: [0.1, 0.4, 0.5]
51
+ final_lr_scale: 0.05
52
+
53
+ model:
54
+ _name: wav2vec_ctc
55
+ w2v_path: ???
56
+ apply_mask: true
57
+ mask_prob: 0.65
58
+ mask_channel_prob: 0.25
59
+ mask_channel_length: 64
60
+ layerdrop: 0.1
61
+ activation_dropout: 0.1
62
+ feature_grad_mult: 0.0
63
+ freeze_finetune_updates: 10000