deepsafe commited on
Commit
ad8bd83
·
verified ·
1 Parent(s): 85261de

chore: remove non-weight files (batch 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. audio/safeear/safeear_repo/fairseq_ours/examples/simultaneous_translation/utils/monotonic_attention.py +0 -180
  2. audio/safeear/safeear_repo/fairseq_ours/examples/simultaneous_translation/utils/p_choose_strategy.py +0 -127
  3. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/README.md +0 -83
  4. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/__init__.py +0 -1
  5. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/ASG_loss.py +0 -170
  6. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/__init__.py +0 -16
  7. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/cross_entropy_acc.py +0 -130
  8. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/__init__.py +0 -10
  9. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/asr_dataset.py +0 -122
  10. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/collaters.py +0 -131
  11. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/data_utils.py +0 -100
  12. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/replabels.py +0 -70
  13. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/datasets/asr_prep_json.py +0 -124
  14. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/datasets/prepare-librispeech.sh +0 -88
  15. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/infer.py +0 -435
  16. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/__init__.py +0 -0
  17. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/add-self-loop-simple.cc +0 -94
  18. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/config/kaldi_initializer.yaml +0 -8
  19. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/kaldi_decoder.py +0 -243
  20. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/kaldi_initializer.py +0 -698
  21. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/__init__.py +0 -7
  22. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/vggtransformer.py +0 -1020
  23. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/w2l_conv_glu_enc.py +0 -176
  24. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/README.md +0 -43
  25. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/__init__.py +0 -0
  26. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/conf/hydra/sweeper/ax.yaml +0 -29
  27. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/conf/infer.yaml +0 -25
  28. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/__init__.py +0 -0
  29. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/base_decoder.py +0 -62
  30. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/decoder.py +0 -32
  31. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/decoder_config.py +0 -69
  32. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/flashlight_decoder.py +0 -429
  33. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/viterbi_decoder.py +0 -24
  34. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/infer.py +0 -474
  35. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/tasks/__init__.py +0 -7
  36. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/tasks/speech_recognition.py +0 -157
  37. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/utils/wer_utils.py +0 -380
  38. audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/w2l_decoder.py +0 -491
  39. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/README.md +0 -38
  40. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/__init__.py +0 -4
  41. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/data_utils.py +0 -347
  42. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/common_voice_example.md +0 -67
  43. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/ljspeech_example.md +0 -137
  44. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/vctk_example.md +0 -61
  45. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/__init__.py +0 -4
  46. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_asr.py +0 -128
  47. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_f0.py +0 -267
  48. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_sp.py +0 -130
  49. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/get_eval_manifest.py +0 -64
  50. audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/generate_waveform.py +0 -191
audio/safeear/safeear_repo/fairseq_ours/examples/simultaneous_translation/utils/monotonic_attention.py DELETED
@@ -1,180 +0,0 @@
1
- from typing import Optional
2
-
3
- import torch
4
- from examples.simultaneous_translation.utils.functions import (
5
- exclusive_cumprod,
6
- moving_sum,
7
- prob_check,
8
- )
9
- from torch import Tensor
10
-
11
-
12
- def expected_alignment_from_p_choose(
13
- p_choose: Tensor,
14
- padding_mask: Optional[Tensor] = None,
15
- eps: float = 1e-6
16
- ):
17
- """
18
- Calculating expected alignment for from stepwise probability
19
-
20
- Reference:
21
- Online and Linear-Time Attention by Enforcing Monotonic Alignments
22
- https://arxiv.org/pdf/1704.00784.pdf
23
-
24
- q_ij = (1 − p_{ij−1})q_{ij−1} + a+{i−1j}
25
- a_ij = p_ij q_ij
26
-
27
- Parallel solution:
28
- ai = p_i * cumprod(1 − pi) * cumsum(a_i / cumprod(1 − pi))
29
-
30
- ============================================================
31
- Expected input size
32
- p_choose: bsz, tgt_len, src_len
33
- """
34
- prob_check(p_choose)
35
-
36
- # p_choose: bsz, tgt_len, src_len
37
- bsz, tgt_len, src_len = p_choose.size()
38
- dtype = p_choose.dtype
39
-
40
- p_choose = p_choose.float()
41
-
42
- if padding_mask is not None:
43
- p_choose = p_choose.masked_fill(padding_mask.unsqueeze(1), 0.0)
44
-
45
- if p_choose.is_cuda:
46
- p_choose = p_choose.contiguous()
47
- from alignment_train_cuda_binding import alignment_train_cuda as alignment_train
48
- else:
49
- from alignment_train_cpu_binding import alignment_train_cpu as alignment_train
50
-
51
- alpha = p_choose.new_zeros([bsz, tgt_len, src_len])
52
- alignment_train(p_choose, alpha, eps)
53
-
54
- # Mix precision to prevent overflow for fp16
55
- alpha = alpha.type(dtype)
56
-
57
- prob_check(alpha)
58
-
59
- return alpha
60
-
61
-
62
- def expected_soft_attention(
63
- alpha: Tensor,
64
- soft_energy: Tensor,
65
- padding_mask: Optional[Tensor] = None,
66
- chunk_size: Optional[int] = None,
67
- eps: float = 1e-10
68
- ):
69
- """
70
- Function to compute expected soft attention for
71
- monotonic infinite lookback attention from
72
- expected alignment and soft energy.
73
-
74
- Reference:
75
- Monotonic Chunkwise Attention
76
- https://arxiv.org/abs/1712.05382
77
-
78
- Monotonic Infinite Lookback Attention for Simultaneous Machine Translation
79
- https://arxiv.org/abs/1906.05218
80
-
81
- alpha: bsz, tgt_len, src_len
82
- soft_energy: bsz, tgt_len, src_len
83
- padding_mask: bsz, src_len
84
- left_padding: bool
85
- """
86
- if padding_mask is not None:
87
- alpha = alpha.masked_fill(padding_mask.unsqueeze(1), 0.0)
88
- soft_energy = soft_energy.masked_fill(
89
- padding_mask.unsqueeze(1), -float("inf")
90
- )
91
-
92
- prob_check(alpha)
93
-
94
- dtype = alpha.dtype
95
-
96
- alpha = alpha.float()
97
- soft_energy = soft_energy.float()
98
-
99
- soft_energy = soft_energy - soft_energy.max(dim=2, keepdim=True)[0]
100
- exp_soft_energy = torch.exp(soft_energy) + eps
101
-
102
- if chunk_size is not None:
103
- # Chunkwise
104
- beta = (
105
- exp_soft_energy
106
- * moving_sum(
107
- alpha / (eps + moving_sum(exp_soft_energy, chunk_size, 1)),
108
- 1, chunk_size
109
- )
110
- )
111
- else:
112
- # Infinite lookback
113
- # Notice that infinite lookback is a special case of chunkwise
114
- # where chunksize = inf
115
- inner_items = alpha / (eps + torch.cumsum(exp_soft_energy, dim=2))
116
-
117
- beta = (
118
- exp_soft_energy
119
- * torch.cumsum(inner_items.flip(dims=[2]), dim=2)
120
- .flip(dims=[2])
121
- )
122
-
123
- if padding_mask is not None:
124
- beta = beta.masked_fill(
125
- padding_mask.unsqueeze(1).to(torch.bool), 0.0)
126
-
127
- # Mix precision to prevent overflow for fp16
128
- beta = beta.type(dtype)
129
-
130
- beta = beta.clamp(0, 1)
131
-
132
- prob_check(beta)
133
-
134
- return beta
135
-
136
-
137
- def mass_preservation(
138
- alpha: Tensor,
139
- padding_mask: Optional[Tensor] = None,
140
- left_padding: bool = False
141
- ):
142
- """
143
- Function to compute the mass perservation for alpha.
144
- This means that the residual weights of alpha will be assigned
145
- to the last token.
146
-
147
- Reference:
148
- Monotonic Infinite Lookback Attention for Simultaneous Machine Translation
149
- https://arxiv.org/abs/1906.05218
150
-
151
- alpha: bsz, tgt_len, src_len
152
- padding_mask: bsz, src_len
153
- left_padding: bool
154
- """
155
-
156
- prob_check(alpha)
157
-
158
- if padding_mask is not None:
159
- if not left_padding:
160
- assert not padding_mask[:, 0].any(), (
161
- "Find padding on the beginning of the sequence."
162
- )
163
- alpha = alpha.masked_fill(padding_mask.unsqueeze(1), 0.0)
164
-
165
- if left_padding or padding_mask is None:
166
- residuals = 1 - alpha[:, :, :-1].sum(dim=-1).clamp(0, 1)
167
- alpha[:, :, -1] = residuals
168
- else:
169
- # right padding
170
- _, tgt_len, src_len = alpha.size()
171
- residuals = 1 - alpha.sum(dim=-1, keepdim=True).clamp(0, 1)
172
- src_lens = src_len - padding_mask.sum(dim=1, keepdim=True)
173
- src_lens = src_lens.expand(-1, tgt_len).contiguous()
174
- # add back the last value
175
- residuals += alpha.gather(2, src_lens.unsqueeze(2) - 1)
176
- alpha = alpha.scatter(2, src_lens.unsqueeze(2) - 1, residuals)
177
-
178
- prob_check(alpha)
179
-
180
- return alpha
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/simultaneous_translation/utils/p_choose_strategy.py DELETED
@@ -1,127 +0,0 @@
1
- from typing import Dict, Optional
2
-
3
- import torch
4
- from torch import Tensor
5
-
6
-
7
- def waitk_p_choose(
8
- tgt_len: int,
9
- src_len: int,
10
- bsz: int,
11
- waitk_lagging: int,
12
- key_padding_mask: Optional[Tensor] = None,
13
- incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None
14
- ):
15
-
16
- max_src_len = src_len
17
- if incremental_state is not None:
18
- # Retrieve target length from incremental states
19
- # For inference the length of query is always 1
20
- max_tgt_len = incremental_state["steps"]["tgt"]
21
- assert max_tgt_len is not None
22
- max_tgt_len = int(max_tgt_len)
23
- else:
24
- max_tgt_len = tgt_len
25
-
26
- if max_src_len < waitk_lagging:
27
- if incremental_state is not None:
28
- max_tgt_len = 1
29
- return torch.zeros(
30
- bsz, max_tgt_len, max_src_len
31
- )
32
-
33
- # Assuming the p_choose looks like this for wait k=3
34
- # src_len = 6, max_tgt_len = 5
35
- # [0, 0, 1, 0, 0, 0, 0]
36
- # [0, 0, 0, 1, 0, 0, 0]
37
- # [0, 0, 0, 0, 1, 0, 0]
38
- # [0, 0, 0, 0, 0, 1, 0]
39
- # [0, 0, 0, 0, 0, 0, 1]
40
- # linearize the p_choose matrix:
41
- # [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0...]
42
- # The indices of linearized matrix that equals 1 is
43
- # 2 + 6 * 0
44
- # 3 + 6 * 1
45
- # ...
46
- # n + src_len * n + k - 1 = n * (src_len + 1) + k - 1
47
- # n from 0 to max_tgt_len - 1
48
- #
49
- # First, generate the indices (activate_indices_offset: bsz, max_tgt_len)
50
- # Second, scatter a zeros tensor (bsz, max_tgt_len * src_len)
51
- # with activate_indices_offset
52
- # Third, resize the tensor to (bsz, max_tgt_len, src_len)
53
-
54
- activate_indices_offset = (
55
- (
56
- torch.arange(max_tgt_len) * (max_src_len + 1)
57
- + waitk_lagging - 1
58
- )
59
- .unsqueeze(0)
60
- .expand(bsz, max_tgt_len)
61
- .long()
62
- )
63
-
64
- if key_padding_mask is not None:
65
- if key_padding_mask[:, 0].any():
66
- # Left padding
67
- activate_indices_offset += (
68
- key_padding_mask.sum(dim=1, keepdim=True)
69
- )
70
-
71
- # Need to clamp the indices that are too large
72
- activate_indices_offset = (
73
- activate_indices_offset
74
- .clamp(
75
- 0,
76
- min(
77
- [
78
- max_tgt_len,
79
- max_src_len - waitk_lagging + 1
80
- ]
81
- ) * max_src_len - 1
82
- )
83
- )
84
-
85
- p_choose = torch.zeros(bsz, max_tgt_len * max_src_len)
86
-
87
- p_choose = p_choose.scatter(
88
- 1,
89
- activate_indices_offset,
90
- 1.0
91
- ).view(bsz, max_tgt_len, max_src_len)
92
-
93
- if key_padding_mask is not None:
94
- p_choose = p_choose.to(key_padding_mask)
95
- p_choose = p_choose.masked_fill(key_padding_mask.unsqueeze(1), 0)
96
-
97
- if incremental_state is not None:
98
- p_choose = p_choose[:, -1:]
99
-
100
- return p_choose.float()
101
-
102
-
103
- def learnable_p_choose(
104
- energy,
105
- noise_mean: float = 0.0,
106
- noise_var: float = 0.0,
107
- training: bool = True
108
- ):
109
- """
110
- Calculating step wise prob for reading and writing
111
- 1 to read, 0 to write
112
- energy: bsz, tgt_len, src_len
113
- """
114
-
115
- noise = 0
116
- if training:
117
- # add noise here to encourage discretness
118
- noise = (
119
- torch.normal(noise_mean, noise_var, energy.size())
120
- .type_as(energy)
121
- .to(energy.device)
122
- )
123
-
124
- p_choose = torch.sigmoid(energy + noise)
125
-
126
- # p_choose: bsz * self.num_heads, tgt_len, src_len
127
- return p_choose
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/README.md DELETED
@@ -1,83 +0,0 @@
1
- ### 2021 Update: We are merging this example into the [S2T framework](../speech_to_text), which supports more generic speech-to-text tasks (e.g. speech translation) and more flexible data processing pipelines. Please stay tuned.
2
-
3
- # Speech Recognition
4
- `examples/speech_recognition` is implementing ASR task in Fairseq, along with needed features, datasets, models and loss functions to train and infer model described in [Transformers with convolutional context for ASR (Abdelrahman Mohamed et al., 2019)](https://arxiv.org/abs/1904.11660).
5
-
6
-
7
- ## Additional dependencies
8
- On top of main fairseq dependencies there are couple more additional requirements.
9
-
10
- 1) Please follow the instructions to install [torchaudio](https://github.com/pytorch/audio). This is required to compute audio fbank features.
11
- 2) [Sclite](http://www1.icsi.berkeley.edu/Speech/docs/sctk-1.2/sclite.htm#sclite_name_0) is used to measure WER. Sclite can be downloaded and installed from source from sctk package [here](http://www.openslr.org/4/). Training and inference doesn't require Sclite dependency.
12
- 3) [sentencepiece](https://github.com/google/sentencepiece) is required in order to create dataset with word-piece targets.
13
-
14
- ## Preparing librispeech data
15
- ```
16
- ./examples/speech_recognition/datasets/prepare-librispeech.sh $DIR_TO_SAVE_RAW_DATA $DIR_FOR_PREPROCESSED_DATA
17
- ```
18
-
19
- ## Training librispeech data
20
- ```
21
- python train.py $DIR_FOR_PREPROCESSED_DATA --save-dir $MODEL_PATH --max-epoch 80 --task speech_recognition --arch vggtransformer_2 --optimizer adadelta --lr 1.0 --adadelta-eps 1e-8 --adadelta-rho 0.95 --clip-norm 10.0 --max-tokens 5000 --log-format json --log-interval 1 --criterion cross_entropy_acc --user-dir examples/speech_recognition/
22
- ```
23
-
24
- ## Inference for librispeech
25
- `$SET` can be `test_clean` or `test_other`
26
- Any checkpoint in `$MODEL_PATH` can be selected. In this example we are working with `checkpoint_last.pt`
27
- ```
28
- python examples/speech_recognition/infer.py $DIR_FOR_PREPROCESSED_DATA --task speech_recognition --max-tokens 25000 --nbest 1 --path $MODEL_PATH/checkpoint_last.pt --beam 20 --results-path $RES_DIR --batch-size 40 --gen-subset $SET --user-dir examples/speech_recognition/
29
- ```
30
-
31
- ## Inference for librispeech
32
- ```
33
- sclite -r ${RES_DIR}/ref.word-checkpoint_last.pt-${SET}.txt -h ${RES_DIR}/hypo.word-checkpoint_last.pt-${SET}.txt -i rm -o all stdout > $RES_REPORT
34
- ```
35
- `Sum/Avg` row from first table of the report has WER
36
-
37
- ## Using flashlight (previously called [wav2letter](https://github.com/facebookresearch/wav2letter)) components
38
- [flashlight](https://github.com/facebookresearch/flashlight) now has integration with fairseq. Currently this includes:
39
-
40
- * AutoSegmentationCriterion (ASG)
41
- * flashlight-style Conv/GLU model
42
- * flashlight's beam search decoder
43
-
44
- To use these, follow the instructions on [this page](https://github.com/facebookresearch/flashlight/tree/master/bindings/python) to install python bindings.
45
-
46
- ## Training librispeech data (flashlight style, Conv/GLU + ASG loss)
47
- Training command:
48
- ```
49
- python train.py $DIR_FOR_PREPROCESSED_DATA --save-dir $MODEL_PATH --max-epoch 100 --task speech_recognition --arch w2l_conv_glu_enc --batch-size 4 --optimizer sgd --lr 0.3,0.8 --momentum 0.8 --clip-norm 0.2 --max-tokens 50000 --log-format json --log-interval 100 --num-workers 0 --sentence-avg --criterion asg_loss --asg-transitions-init 5 --max-replabel 2 --linseg-updates 8789 --user-dir examples/speech_recognition
50
- ```
51
-
52
- Note that ASG loss currently doesn't do well with word-pieces. You should prepare a dataset with character targets by setting `nbpe=31` in `prepare-librispeech.sh`.
53
-
54
- ## Inference for librispeech (flashlight decoder, n-gram LM)
55
- Inference command:
56
- ```
57
- python examples/speech_recognition/infer.py $DIR_FOR_PREPROCESSED_DATA --task speech_recognition --seed 1 --nbest 1 --path $MODEL_PATH/checkpoint_last.pt --gen-subset $SET --results-path $RES_DIR --w2l-decoder kenlm --kenlm-model $KENLM_MODEL_PATH --lexicon $LEXICON_PATH --beam 200 --beam-threshold 15 --lm-weight 1.5 --word-score 1.5 --sil-weight -0.3 --criterion asg_loss --max-replabel 2 --user-dir examples/speech_recognition
58
- ```
59
-
60
- `$KENLM_MODEL_PATH` should be a standard n-gram language model file. `$LEXICON_PATH` should be a flashlight-style lexicon (list of known words and their spellings). For ASG inference, a lexicon line should look like this (note the repetition labels):
61
- ```
62
- doorbell D O 1 R B E L 1 ▁
63
- ```
64
- For CTC inference with word-pieces, repetition labels are not used and the lexicon should have most common spellings for each word (one can use sentencepiece's `NBestEncodeAsPieces` for this):
65
- ```
66
- doorbell ▁DOOR BE LL
67
- doorbell ▁DOOR B E LL
68
- doorbell ▁DO OR BE LL
69
- doorbell ▁DOOR B EL L
70
- doorbell ▁DOOR BE L L
71
- doorbell ▁DO OR B E LL
72
- doorbell ▁DOOR B E L L
73
- doorbell ▁DO OR B EL L
74
- doorbell ▁DO O R BE LL
75
- doorbell ▁DO OR BE L L
76
- ```
77
- Lowercase vs. uppercase matters: the *word* should match the case of the n-gram language model (i.e. `$KENLM_MODEL_PATH`), while the *spelling* should match the case of the token dictionary (i.e. `$DIR_FOR_PREPROCESSED_DATA/dict.txt`).
78
-
79
- ## Inference for librispeech (flashlight decoder, viterbi only)
80
- Inference command:
81
- ```
82
- python examples/speech_recognition/infer.py $DIR_FOR_PREPROCESSED_DATA --task speech_recognition --seed 1 --nbest 1 --path $MODEL_PATH/checkpoint_last.pt --gen-subset $SET --results-path $RES_DIR --w2l-decoder viterbi --criterion asg_loss --max-replabel 2 --user-dir examples/speech_recognition
83
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/__init__.py DELETED
@@ -1 +0,0 @@
1
- from . import criterions, models, tasks # noqa
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/ASG_loss.py DELETED
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- import torch
9
- from examples.speech_recognition.data.replabels import pack_replabels
10
- from fairseq import utils
11
- from fairseq.criterions import FairseqCriterion, register_criterion
12
-
13
-
14
- @register_criterion("asg_loss")
15
- class ASGCriterion(FairseqCriterion):
16
- @staticmethod
17
- def add_args(parser):
18
- group = parser.add_argument_group("ASG Loss")
19
- group.add_argument(
20
- "--asg-transitions-init",
21
- help="initial diagonal value of transition matrix",
22
- type=float,
23
- default=0.0,
24
- )
25
- group.add_argument(
26
- "--max-replabel", help="maximum # of replabels", type=int, default=2
27
- )
28
- group.add_argument(
29
- "--linseg-updates",
30
- help="# of training updates to use LinSeg initialization",
31
- type=int,
32
- default=0,
33
- )
34
- group.add_argument(
35
- "--hide-linseg-messages",
36
- help="hide messages about LinSeg initialization",
37
- action="store_true",
38
- )
39
-
40
- def __init__(
41
- self,
42
- task,
43
- silence_token,
44
- asg_transitions_init,
45
- max_replabel,
46
- linseg_updates,
47
- hide_linseg_messages,
48
- ):
49
- from flashlight.lib.sequence.criterion import ASGLoss, CriterionScaleMode
50
-
51
- super().__init__(task)
52
- self.tgt_dict = task.target_dictionary
53
- self.eos = self.tgt_dict.eos()
54
- self.silence = (
55
- self.tgt_dict.index(silence_token)
56
- if silence_token in self.tgt_dict
57
- else None
58
- )
59
- self.max_replabel = max_replabel
60
-
61
- num_labels = len(self.tgt_dict)
62
- self.asg = ASGLoss(num_labels, scale_mode=CriterionScaleMode.TARGET_SZ_SQRT)
63
- self.asg.trans = torch.nn.Parameter(
64
- asg_transitions_init * torch.eye(num_labels), requires_grad=True
65
- )
66
-
67
- self.linseg_progress = torch.nn.Parameter(
68
- torch.tensor([0], dtype=torch.int), requires_grad=False
69
- )
70
- self.linseg_maximum = linseg_updates
71
- self.linseg_message_state = "none" if hide_linseg_messages else "start"
72
-
73
- @classmethod
74
- def build_criterion(cls, args, task):
75
- return cls(
76
- task,
77
- args.silence_token,
78
- args.asg_transitions_init,
79
- args.max_replabel,
80
- args.linseg_updates,
81
- args.hide_linseg_messages,
82
- )
83
-
84
- def linseg_step(self):
85
- if not self.training:
86
- return False
87
- if self.linseg_progress.item() < self.linseg_maximum:
88
- if self.linseg_message_state == "start":
89
- print("| using LinSeg to initialize ASG")
90
- self.linseg_message_state = "finish"
91
- self.linseg_progress.add_(1)
92
- return True
93
- elif self.linseg_message_state == "finish":
94
- print("| finished LinSeg initialization")
95
- self.linseg_message_state = "none"
96
- return False
97
-
98
- def replace_eos_with_silence(self, tgt):
99
- if tgt[-1] != self.eos:
100
- return tgt
101
- elif self.silence is None or (len(tgt) > 1 and tgt[-2] == self.silence):
102
- return tgt[:-1]
103
- else:
104
- return tgt[:-1] + [self.silence]
105
-
106
- def forward(self, model, sample, reduce=True):
107
- """Compute the loss for the given sample.
108
-
109
- Returns a tuple with three elements:
110
- 1) the loss
111
- 2) the sample size, which is used as the denominator for the gradient
112
- 3) logging outputs to display while training
113
- """
114
-
115
- net_output = model(**sample["net_input"])
116
- emissions = net_output["encoder_out"].transpose(0, 1).contiguous()
117
- B = emissions.size(0)
118
- T = emissions.size(1)
119
- device = emissions.device
120
-
121
- target = torch.IntTensor(B, T)
122
- target_size = torch.IntTensor(B)
123
- using_linseg = self.linseg_step()
124
-
125
- for b in range(B):
126
- initial_target_size = sample["target_lengths"][b].item()
127
- if initial_target_size == 0:
128
- raise ValueError("target size cannot be zero")
129
-
130
- tgt = sample["target"][b, :initial_target_size].tolist()
131
- tgt = self.replace_eos_with_silence(tgt)
132
- tgt = pack_replabels(tgt, self.tgt_dict, self.max_replabel)
133
- tgt = tgt[:T]
134
-
135
- if using_linseg:
136
- tgt = [tgt[t * len(tgt) // T] for t in range(T)]
137
-
138
- target[b][: len(tgt)] = torch.IntTensor(tgt)
139
- target_size[b] = len(tgt)
140
-
141
- loss = self.asg.forward(emissions, target.to(device), target_size.to(device))
142
-
143
- if reduce:
144
- loss = torch.sum(loss)
145
-
146
- sample_size = (
147
- sample["target"].size(0) if self.args.sentence_avg else sample["ntokens"]
148
- )
149
- logging_output = {
150
- "loss": utils.item(loss.data) if reduce else loss.data,
151
- "ntokens": sample["ntokens"],
152
- "nsentences": sample["target"].size(0),
153
- "sample_size": sample_size,
154
- }
155
- return loss, sample_size, logging_output
156
-
157
- @staticmethod
158
- def aggregate_logging_outputs(logging_outputs):
159
- """Aggregate logging outputs from data parallel training."""
160
- loss_sum = sum(log.get("loss", 0) for log in logging_outputs)
161
- ntokens = sum(log.get("ntokens", 0) for log in logging_outputs)
162
- nsentences = sum(log.get("nsentences", 0) for log in logging_outputs)
163
- sample_size = sum(log.get("sample_size", 0) for log in logging_outputs)
164
- agg_output = {
165
- "loss": loss_sum / nsentences,
166
- "ntokens": ntokens,
167
- "nsentences": nsentences,
168
- "sample_size": sample_size,
169
- }
170
- return agg_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/__init__.py DELETED
@@ -1,16 +0,0 @@
1
- import importlib
2
- import os
3
-
4
- # ASG loss requires flashlight bindings
5
- files_to_skip = set()
6
- try:
7
- import flashlight.lib.sequence.criterion
8
- except ImportError:
9
- files_to_skip.add("ASG_loss.py")
10
-
11
- for file in sorted(os.listdir(os.path.dirname(__file__))):
12
- if file.endswith(".py") and not file.startswith("_") and file not in files_to_skip:
13
- criterion_name = file[: file.find(".py")]
14
- importlib.import_module(
15
- "examples.speech_recognition.criterions." + criterion_name
16
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/criterions/cross_entropy_acc.py DELETED
@@ -1,130 +0,0 @@
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 __future__ import absolute_import, division, print_function, unicode_literals
7
-
8
- import logging
9
- import math
10
-
11
- import torch
12
- import torch.nn.functional as F
13
- from fairseq import utils
14
- from fairseq.criterions import FairseqCriterion, register_criterion
15
-
16
-
17
- @register_criterion("cross_entropy_acc")
18
- class CrossEntropyWithAccCriterion(FairseqCriterion):
19
- def __init__(self, task, sentence_avg):
20
- super().__init__(task)
21
- self.sentence_avg = sentence_avg
22
-
23
- def compute_loss(self, model, net_output, target, reduction, log_probs):
24
- # N, T -> N * T
25
- target = target.view(-1)
26
- lprobs = model.get_normalized_probs(net_output, log_probs=log_probs)
27
- if not hasattr(lprobs, "batch_first"):
28
- logging.warning(
29
- "ERROR: we need to know whether "
30
- "batch first for the net output; "
31
- "you need to set batch_first attribute for the return value of "
32
- "model.get_normalized_probs. Now, we assume this is true, but "
33
- "in the future, we will raise exception instead. "
34
- )
35
- batch_first = getattr(lprobs, "batch_first", True)
36
- if not batch_first:
37
- lprobs = lprobs.transpose(0, 1)
38
-
39
- # N, T, D -> N * T, D
40
- lprobs = lprobs.view(-1, lprobs.size(-1))
41
- loss = F.nll_loss(
42
- lprobs, target, ignore_index=self.padding_idx, reduction=reduction
43
- )
44
- return lprobs, loss
45
-
46
- def get_logging_output(self, sample, target, lprobs, loss):
47
- target = target.view(-1)
48
- mask = target != self.padding_idx
49
- correct = torch.sum(
50
- lprobs.argmax(1).masked_select(mask) == target.masked_select(mask)
51
- )
52
- total = torch.sum(mask)
53
- sample_size = (
54
- sample["target"].size(0) if self.sentence_avg else sample["ntokens"]
55
- )
56
-
57
- logging_output = {
58
- "loss": utils.item(loss.data), # * sample['ntokens'],
59
- "ntokens": sample["ntokens"],
60
- "nsentences": sample["target"].size(0),
61
- "sample_size": sample_size,
62
- "correct": utils.item(correct.data),
63
- "total": utils.item(total.data),
64
- "nframes": torch.sum(sample["net_input"]["src_lengths"]).item(),
65
- }
66
-
67
- return sample_size, logging_output
68
-
69
- def forward(self, model, sample, reduction="sum", log_probs=True):
70
- """Computes the cross entropy with accuracy metric for the given sample.
71
-
72
- This is similar to CrossEntropyCriterion in fairseq, but also
73
- computes accuracy metrics as part of logging
74
-
75
- Args:
76
- logprobs (Torch.tensor) of shape N, T, D i.e.
77
- batchsize, timesteps, dimensions
78
- targets (Torch.tensor) of shape N, T i.e batchsize, timesteps
79
-
80
- Returns:
81
- tuple: With three elements:
82
- 1) the loss
83
- 2) the sample size, which is used as the denominator for the gradient
84
- 3) logging outputs to display while training
85
-
86
- TODO:
87
- * Currently this Criterion will only work with LSTMEncoderModels or
88
- FairseqModels which have decoder, or Models which return TorchTensor
89
- as net_output.
90
- We need to make a change to support all FairseqEncoder models.
91
- """
92
- net_output = model(**sample["net_input"])
93
- target = model.get_targets(sample, net_output)
94
- lprobs, loss = self.compute_loss(
95
- model, net_output, target, reduction, log_probs
96
- )
97
- sample_size, logging_output = self.get_logging_output(
98
- sample, target, lprobs, loss
99
- )
100
- return loss, sample_size, logging_output
101
-
102
- @staticmethod
103
- def aggregate_logging_outputs(logging_outputs):
104
- """Aggregate logging outputs from data parallel training."""
105
- correct_sum = sum(log.get("correct", 0) for log in logging_outputs)
106
- total_sum = sum(log.get("total", 0) for log in logging_outputs)
107
- loss_sum = sum(log.get("loss", 0) for log in logging_outputs)
108
- ntokens = sum(log.get("ntokens", 0) for log in logging_outputs)
109
- nsentences = sum(log.get("nsentences", 0) for log in logging_outputs)
110
- sample_size = sum(log.get("sample_size", 0) for log in logging_outputs)
111
- nframes = sum(log.get("nframes", 0) for log in logging_outputs)
112
- agg_output = {
113
- "loss": loss_sum / sample_size / math.log(2) if sample_size > 0 else 0.0,
114
- # if args.sentence_avg, then sample_size is nsentences, then loss
115
- # is per-sentence loss; else sample_size is ntokens, the loss
116
- # becomes per-output token loss
117
- "ntokens": ntokens,
118
- "nsentences": nsentences,
119
- "nframes": nframes,
120
- "sample_size": sample_size,
121
- "acc": correct_sum * 100.0 / total_sum if total_sum > 0 else 0.0,
122
- "correct": correct_sum,
123
- "total": total_sum,
124
- # total is the number of validate tokens
125
- }
126
- if sample_size != ntokens:
127
- agg_output["nll_loss"] = loss_sum / ntokens / math.log(2)
128
- # loss: per output token loss
129
- # nll_loss: per sentence loss
130
- return agg_output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/__init__.py DELETED
@@ -1,10 +0,0 @@
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 .asr_dataset import AsrDataset
7
-
8
- __all__ = [
9
- "AsrDataset",
10
- ]
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/asr_dataset.py DELETED
@@ -1,122 +0,0 @@
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
-
8
- import numpy as np
9
- from fairseq.data import FairseqDataset
10
-
11
- from . import data_utils
12
- from .collaters import Seq2SeqCollater
13
-
14
-
15
- class AsrDataset(FairseqDataset):
16
- """
17
- A dataset representing speech and corresponding transcription.
18
-
19
- Args:
20
- aud_paths: (List[str]): A list of str with paths to audio files.
21
- aud_durations_ms (List[int]): A list of int containing the durations of
22
- audio files.
23
- tgt (List[torch.LongTensor]): A list of LongTensors containing the indices
24
- of target transcriptions.
25
- tgt_dict (~fairseq.data.Dictionary): target vocabulary.
26
- ids (List[str]): A list of utterance IDs.
27
- speakers (List[str]): A list of speakers corresponding to utterances.
28
- num_mel_bins (int): Number of triangular mel-frequency bins (default: 80)
29
- frame_length (float): Frame length in milliseconds (default: 25.0)
30
- frame_shift (float): Frame shift in milliseconds (default: 10.0)
31
- """
32
-
33
- def __init__(
34
- self,
35
- aud_paths,
36
- aud_durations_ms,
37
- tgt,
38
- tgt_dict,
39
- ids,
40
- speakers,
41
- num_mel_bins=80,
42
- frame_length=25.0,
43
- frame_shift=10.0,
44
- ):
45
- assert frame_length > 0
46
- assert frame_shift > 0
47
- assert all(x > frame_length for x in aud_durations_ms)
48
- self.frame_sizes = [
49
- int(1 + (d - frame_length) / frame_shift) for d in aud_durations_ms
50
- ]
51
-
52
- assert len(aud_paths) > 0
53
- assert len(aud_paths) == len(aud_durations_ms)
54
- assert len(aud_paths) == len(tgt)
55
- assert len(aud_paths) == len(ids)
56
- assert len(aud_paths) == len(speakers)
57
- self.aud_paths = aud_paths
58
- self.tgt_dict = tgt_dict
59
- self.tgt = tgt
60
- self.ids = ids
61
- self.speakers = speakers
62
- self.num_mel_bins = num_mel_bins
63
- self.frame_length = frame_length
64
- self.frame_shift = frame_shift
65
-
66
- self.s2s_collater = Seq2SeqCollater(
67
- 0,
68
- 1,
69
- pad_index=self.tgt_dict.pad(),
70
- eos_index=self.tgt_dict.eos(),
71
- move_eos_to_beginning=True,
72
- )
73
-
74
- def __getitem__(self, index):
75
- import torchaudio
76
- import torchaudio.compliance.kaldi as kaldi
77
-
78
- tgt_item = self.tgt[index] if self.tgt is not None else None
79
-
80
- path = self.aud_paths[index]
81
- if not os.path.exists(path):
82
- raise FileNotFoundError("Audio file not found: {}".format(path))
83
- sound, sample_rate = torchaudio.load_wav(path)
84
- output = kaldi.fbank(
85
- sound,
86
- num_mel_bins=self.num_mel_bins,
87
- frame_length=self.frame_length,
88
- frame_shift=self.frame_shift,
89
- )
90
- output_cmvn = data_utils.apply_mv_norm(output)
91
-
92
- return {"id": index, "data": [output_cmvn.detach(), tgt_item]}
93
-
94
- def __len__(self):
95
- return len(self.aud_paths)
96
-
97
- def collater(self, samples):
98
- """Merge a list of samples to form a mini-batch.
99
-
100
- Args:
101
- samples (List[int]): sample indices to collate
102
-
103
- Returns:
104
- dict: a mini-batch suitable for forwarding with a Model
105
- """
106
- return self.s2s_collater.collate(samples)
107
-
108
- def num_tokens(self, index):
109
- return self.frame_sizes[index]
110
-
111
- def size(self, index):
112
- """Return an example's size as a float or tuple. This value is used when
113
- filtering a dataset with ``--max-positions``."""
114
- return (
115
- self.frame_sizes[index],
116
- len(self.tgt[index]) if self.tgt is not None else 0,
117
- )
118
-
119
- def ordered_indices(self):
120
- """Return an ordered list of indices. Batches will be constructed based
121
- on this order."""
122
- return np.arange(len(self))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/collaters.py DELETED
@@ -1,131 +0,0 @@
1
- # Copyright (c) Facebook, Inc. and its affiliates.
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
- """
6
- This module contains collection of classes which implement
7
- collate functionalities for various tasks.
8
-
9
- Collaters should know what data to expect for each sample
10
- and they should pack / collate them into batches
11
- """
12
-
13
-
14
- from __future__ import absolute_import, division, print_function, unicode_literals
15
-
16
- import numpy as np
17
- import torch
18
- from fairseq.data import data_utils as fairseq_data_utils
19
-
20
-
21
- class Seq2SeqCollater(object):
22
- """
23
- Implements collate function mainly for seq2seq tasks
24
- This expects each sample to contain feature (src_tokens) and
25
- targets.
26
- This collator is also used for aligned training task.
27
- """
28
-
29
- def __init__(
30
- self,
31
- feature_index=0,
32
- label_index=1,
33
- pad_index=1,
34
- eos_index=2,
35
- move_eos_to_beginning=True,
36
- ):
37
- self.feature_index = feature_index
38
- self.label_index = label_index
39
- self.pad_index = pad_index
40
- self.eos_index = eos_index
41
- self.move_eos_to_beginning = move_eos_to_beginning
42
-
43
- def _collate_frames(self, frames):
44
- """Convert a list of 2d frames into a padded 3d tensor
45
- Args:
46
- frames (list): list of 2d frames of size L[i]*f_dim. Where L[i] is
47
- length of i-th frame and f_dim is static dimension of features
48
- Returns:
49
- 3d tensor of size len(frames)*len_max*f_dim where len_max is max of L[i]
50
- """
51
- len_max = max(frame.size(0) for frame in frames)
52
- f_dim = frames[0].size(1)
53
- res = frames[0].new(len(frames), len_max, f_dim).fill_(0.0)
54
-
55
- for i, v in enumerate(frames):
56
- res[i, : v.size(0)] = v
57
-
58
- return res
59
-
60
- def collate(self, samples):
61
- """
62
- utility function to collate samples into batch for speech recognition.
63
- """
64
- if len(samples) == 0:
65
- return {}
66
-
67
- # parse samples into torch tensors
68
- parsed_samples = []
69
- for s in samples:
70
- # skip invalid samples
71
- if s["data"][self.feature_index] is None:
72
- continue
73
- source = s["data"][self.feature_index]
74
- if isinstance(source, (np.ndarray, np.generic)):
75
- source = torch.from_numpy(source)
76
- target = s["data"][self.label_index]
77
- if isinstance(target, (np.ndarray, np.generic)):
78
- target = torch.from_numpy(target).long()
79
- elif isinstance(target, list):
80
- target = torch.LongTensor(target)
81
-
82
- parsed_sample = {"id": s["id"], "source": source, "target": target}
83
- parsed_samples.append(parsed_sample)
84
- samples = parsed_samples
85
-
86
- id = torch.LongTensor([s["id"] for s in samples])
87
- frames = self._collate_frames([s["source"] for s in samples])
88
- # sort samples by descending number of frames
89
- frames_lengths = torch.LongTensor([s["source"].size(0) for s in samples])
90
- frames_lengths, sort_order = frames_lengths.sort(descending=True)
91
- id = id.index_select(0, sort_order)
92
- frames = frames.index_select(0, sort_order)
93
-
94
- target = None
95
- target_lengths = None
96
- prev_output_tokens = None
97
- if samples[0].get("target", None) is not None:
98
- ntokens = sum(len(s["target"]) for s in samples)
99
- target = fairseq_data_utils.collate_tokens(
100
- [s["target"] for s in samples],
101
- self.pad_index,
102
- self.eos_index,
103
- left_pad=False,
104
- move_eos_to_beginning=False,
105
- )
106
- target = target.index_select(0, sort_order)
107
- target_lengths = torch.LongTensor(
108
- [s["target"].size(0) for s in samples]
109
- ).index_select(0, sort_order)
110
- prev_output_tokens = fairseq_data_utils.collate_tokens(
111
- [s["target"] for s in samples],
112
- self.pad_index,
113
- self.eos_index,
114
- left_pad=False,
115
- move_eos_to_beginning=self.move_eos_to_beginning,
116
- )
117
- prev_output_tokens = prev_output_tokens.index_select(0, sort_order)
118
- else:
119
- ntokens = sum(len(s["source"]) for s in samples)
120
-
121
- batch = {
122
- "id": id,
123
- "ntokens": ntokens,
124
- "net_input": {"src_tokens": frames, "src_lengths": frames_lengths},
125
- "target": target,
126
- "target_lengths": target_lengths,
127
- "nsentences": len(samples),
128
- }
129
- if prev_output_tokens is not None:
130
- batch["net_input"]["prev_output_tokens"] = prev_output_tokens
131
- return batch
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/data_utils.py DELETED
@@ -1,100 +0,0 @@
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
- def calc_mean_invstddev(feature):
10
- if len(feature.size()) != 2:
11
- raise ValueError("We expect the input feature to be 2-D tensor")
12
- mean = feature.mean(0)
13
- var = feature.var(0)
14
- # avoid division by ~zero
15
- eps = 1e-8
16
- if (var < eps).any():
17
- return mean, 1.0 / (torch.sqrt(var) + eps)
18
- return mean, 1.0 / torch.sqrt(var)
19
-
20
-
21
- def apply_mv_norm(features):
22
- # If there is less than 2 spectrograms, the variance cannot be computed (is NaN)
23
- # and normalization is not possible, so return the item as it is
24
- if features.size(0) < 2:
25
- return features
26
- mean, invstddev = calc_mean_invstddev(features)
27
- res = (features - mean) * invstddev
28
- return res
29
-
30
-
31
- def lengths_to_encoder_padding_mask(lengths, batch_first=False):
32
- """
33
- convert lengths (a 1-D Long/Int tensor) to 2-D binary tensor
34
-
35
- Args:
36
- lengths: a (B, )-shaped tensor
37
-
38
- Return:
39
- max_length: maximum length of B sequences
40
- encoder_padding_mask: a (max_length, B) binary mask, where
41
- [t, b] = 0 for t < lengths[b] and 1 otherwise
42
-
43
- TODO:
44
- kernelize this function if benchmarking shows this function is slow
45
- """
46
- max_lengths = torch.max(lengths).item()
47
- bsz = lengths.size(0)
48
- encoder_padding_mask = torch.arange(
49
- max_lengths
50
- ).to( # a (T, ) tensor with [0, ..., T-1]
51
- lengths.device
52
- ).view( # move to the right device
53
- 1, max_lengths
54
- ).expand( # reshape to (1, T)-shaped tensor
55
- bsz, -1
56
- ) >= lengths.view( # expand to (B, T)-shaped tensor
57
- bsz, 1
58
- ).expand(
59
- -1, max_lengths
60
- )
61
- if not batch_first:
62
- return encoder_padding_mask.t(), max_lengths
63
- else:
64
- return encoder_padding_mask, max_lengths
65
-
66
-
67
- def encoder_padding_mask_to_lengths(
68
- encoder_padding_mask, max_lengths, batch_size, device
69
- ):
70
- """
71
- convert encoder_padding_mask (2-D binary tensor) to a 1-D tensor
72
-
73
- Conventionally, encoder output contains a encoder_padding_mask, which is
74
- a 2-D mask in a shape (T, B), whose (t, b) element indicate whether
75
- encoder_out[t, b] is a valid output (=0) or not (=1). Occasionally, we
76
- need to convert this mask tensor to a 1-D tensor in shape (B, ), where
77
- [b] denotes the valid length of b-th sequence
78
-
79
- Args:
80
- encoder_padding_mask: a (T, B)-shaped binary tensor or None; if None,
81
- indicating all are valid
82
- Return:
83
- seq_lengths: a (B,)-shaped tensor, where its (b, )-th element is the
84
- number of valid elements of b-th sequence
85
-
86
- max_lengths: maximum length of all sequence, if encoder_padding_mask is
87
- not None, max_lengths must equal to encoder_padding_mask.size(0)
88
-
89
- batch_size: batch size; if encoder_padding_mask is
90
- not None, max_lengths must equal to encoder_padding_mask.size(1)
91
-
92
- device: which device to put the result on
93
- """
94
- if encoder_padding_mask is None:
95
- return torch.Tensor([max_lengths] * batch_size).to(torch.int32).to(device)
96
-
97
- assert encoder_padding_mask.size(0) == max_lengths, "max_lengths does not match"
98
- assert encoder_padding_mask.size(1) == batch_size, "batch_size does not match"
99
-
100
- return max_lengths - torch.sum(encoder_padding_mask, dim=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/data/replabels.py DELETED
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- """
9
- Replabel transforms for use with flashlight's ASG criterion.
10
- """
11
-
12
-
13
- def replabel_symbol(i):
14
- """
15
- Replabel symbols used in flashlight, currently just "1", "2", ...
16
- This prevents training with numeral tokens, so this might change in the future
17
- """
18
- return str(i)
19
-
20
-
21
- def pack_replabels(tokens, dictionary, max_reps):
22
- """
23
- Pack a token sequence so that repeated symbols are replaced by replabels
24
- """
25
- if len(tokens) == 0 or max_reps <= 0:
26
- return tokens
27
-
28
- replabel_value_to_idx = [0] * (max_reps + 1)
29
- for i in range(1, max_reps + 1):
30
- replabel_value_to_idx[i] = dictionary.index(replabel_symbol(i))
31
-
32
- result = []
33
- prev_token = -1
34
- num_reps = 0
35
- for token in tokens:
36
- if token == prev_token and num_reps < max_reps:
37
- num_reps += 1
38
- else:
39
- if num_reps > 0:
40
- result.append(replabel_value_to_idx[num_reps])
41
- num_reps = 0
42
- result.append(token)
43
- prev_token = token
44
- if num_reps > 0:
45
- result.append(replabel_value_to_idx[num_reps])
46
- return result
47
-
48
-
49
- def unpack_replabels(tokens, dictionary, max_reps):
50
- """
51
- Unpack a token sequence so that replabels are replaced by repeated symbols
52
- """
53
- if len(tokens) == 0 or max_reps <= 0:
54
- return tokens
55
-
56
- replabel_idx_to_value = {}
57
- for i in range(1, max_reps + 1):
58
- replabel_idx_to_value[dictionary.index(replabel_symbol(i))] = i
59
-
60
- result = []
61
- prev_token = -1
62
- for token in tokens:
63
- try:
64
- for _ in range(replabel_idx_to_value[token]):
65
- result.append(prev_token)
66
- prev_token = -1
67
- except KeyError:
68
- result.append(token)
69
- prev_token = token
70
- return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/datasets/asr_prep_json.py DELETED
@@ -1,124 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Copyright (c) Facebook, Inc. and its affiliates.
3
- #
4
- # This source code is licensed under the MIT license found in the
5
- # LICENSE file in the root directory of this source tree.
6
-
7
- from __future__ import absolute_import, division, print_function, unicode_literals
8
-
9
- import argparse
10
- import concurrent.futures
11
- import json
12
- import multiprocessing
13
- import os
14
- from collections import namedtuple
15
- from itertools import chain
16
-
17
- import sentencepiece as spm
18
- from fairseq.data import Dictionary
19
-
20
- MILLISECONDS_TO_SECONDS = 0.001
21
-
22
-
23
- def process_sample(aud_path, lable, utt_id, sp, tgt_dict):
24
- import torchaudio
25
-
26
- input = {}
27
- output = {}
28
- si, ei = torchaudio.info(aud_path)
29
- input["length_ms"] = int(
30
- si.length / si.channels / si.rate / MILLISECONDS_TO_SECONDS
31
- )
32
- input["path"] = aud_path
33
-
34
- token = " ".join(sp.EncodeAsPieces(lable))
35
- ids = tgt_dict.encode_line(token, append_eos=False)
36
- output["text"] = lable
37
- output["token"] = token
38
- output["tokenid"] = ", ".join(map(str, [t.tolist() for t in ids]))
39
- return {utt_id: {"input": input, "output": output}}
40
-
41
-
42
- def main():
43
- parser = argparse.ArgumentParser()
44
- parser.add_argument(
45
- "--audio-dirs",
46
- nargs="+",
47
- default=["-"],
48
- required=True,
49
- help="input directories with audio files",
50
- )
51
- parser.add_argument(
52
- "--labels",
53
- required=True,
54
- help="aggregated input labels with format <ID LABEL> per line",
55
- type=argparse.FileType("r", encoding="UTF-8"),
56
- )
57
- parser.add_argument(
58
- "--spm-model",
59
- required=True,
60
- help="sentencepiece model to use for encoding",
61
- type=argparse.FileType("r", encoding="UTF-8"),
62
- )
63
- parser.add_argument(
64
- "--dictionary",
65
- required=True,
66
- help="file to load fairseq dictionary from",
67
- type=argparse.FileType("r", encoding="UTF-8"),
68
- )
69
- parser.add_argument("--audio-format", choices=["flac", "wav"], default="wav")
70
- parser.add_argument(
71
- "--output",
72
- required=True,
73
- type=argparse.FileType("w"),
74
- help="path to save json output",
75
- )
76
- args = parser.parse_args()
77
-
78
- sp = spm.SentencePieceProcessor()
79
- sp.Load(args.spm_model.name)
80
-
81
- tgt_dict = Dictionary.load(args.dictionary)
82
-
83
- labels = {}
84
- for line in args.labels:
85
- (utt_id, label) = line.split(" ", 1)
86
- labels[utt_id] = label
87
- if len(labels) == 0:
88
- raise Exception("No labels found in ", args.labels_path)
89
-
90
- Sample = namedtuple("Sample", "aud_path utt_id")
91
- samples = []
92
- for path, _, files in chain.from_iterable(
93
- os.walk(path) for path in args.audio_dirs
94
- ):
95
- for f in files:
96
- if f.endswith(args.audio_format):
97
- if len(os.path.splitext(f)) != 2:
98
- raise Exception("Expect <utt_id.extension> file name. Got: ", f)
99
- utt_id = os.path.splitext(f)[0]
100
- if utt_id not in labels:
101
- continue
102
- samples.append(Sample(os.path.join(path, f), utt_id))
103
-
104
- utts = {}
105
- num_cpu = multiprocessing.cpu_count()
106
- with concurrent.futures.ThreadPoolExecutor(max_workers=num_cpu) as executor:
107
- future_to_sample = {
108
- executor.submit(
109
- process_sample, s.aud_path, labels[s.utt_id], s.utt_id, sp, tgt_dict
110
- ): s
111
- for s in samples
112
- }
113
- for future in concurrent.futures.as_completed(future_to_sample):
114
- try:
115
- data = future.result()
116
- except Exception as exc:
117
- print("generated an exception: ", exc)
118
- else:
119
- utts.update(data)
120
- json.dump({"utts": utts}, args.output, indent=4)
121
-
122
-
123
- if __name__ == "__main__":
124
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/datasets/prepare-librispeech.sh DELETED
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env 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
- # Prepare librispeech dataset
8
-
9
- base_url=www.openslr.org/resources/12
10
- train_dir=train_960
11
-
12
- if [ "$#" -ne 2 ]; then
13
- echo "Usage: $0 <download_dir> <out_dir>"
14
- echo "e.g.: $0 /tmp/librispeech_raw/ ~/data/librispeech_final"
15
- exit 1
16
- fi
17
-
18
- download_dir=${1%/}
19
- out_dir=${2%/}
20
-
21
- fairseq_root=~/fairseq-py/
22
- mkdir -p ${out_dir}
23
- cd ${out_dir} || exit
24
-
25
- nbpe=5000
26
- bpemode=unigram
27
-
28
- if [ ! -d "$fairseq_root" ]; then
29
- echo "$0: Please set correct fairseq_root"
30
- exit 1
31
- fi
32
-
33
- echo "Data Download"
34
- for part in dev-clean test-clean dev-other test-other train-clean-100 train-clean-360 train-other-500; do
35
- url=$base_url/$part.tar.gz
36
- if ! wget -P $download_dir $url; then
37
- echo "$0: wget failed for $url"
38
- exit 1
39
- fi
40
- if ! tar -C $download_dir -xvzf $download_dir/$part.tar.gz; then
41
- echo "$0: error un-tarring archive $download_dir/$part.tar.gz"
42
- exit 1
43
- fi
44
- done
45
-
46
- echo "Merge all train packs into one"
47
- mkdir -p ${download_dir}/LibriSpeech/${train_dir}/
48
- for part in train-clean-100 train-clean-360 train-other-500; do
49
- mv ${download_dir}/LibriSpeech/${part}/* $download_dir/LibriSpeech/${train_dir}/
50
- done
51
- echo "Merge train text"
52
- find ${download_dir}/LibriSpeech/${train_dir}/ -name '*.txt' -exec cat {} \; >> ${download_dir}/LibriSpeech/${train_dir}/text
53
-
54
- # Use combined dev-clean and dev-other as validation set
55
- find ${download_dir}/LibriSpeech/dev-clean/ ${download_dir}/LibriSpeech/dev-other/ -name '*.txt' -exec cat {} \; >> ${download_dir}/LibriSpeech/valid_text
56
- find ${download_dir}/LibriSpeech/test-clean/ -name '*.txt' -exec cat {} \; >> ${download_dir}/LibriSpeech/test-clean/text
57
- find ${download_dir}/LibriSpeech/test-other/ -name '*.txt' -exec cat {} \; >> ${download_dir}/LibriSpeech/test-other/text
58
-
59
-
60
- dict=data/lang_char/${train_dir}_${bpemode}${nbpe}_units.txt
61
- encoded=data/lang_char/${train_dir}_${bpemode}${nbpe}_encoded.txt
62
- fairseq_dict=data/lang_char/${train_dir}_${bpemode}${nbpe}_fairseq_dict.txt
63
- bpemodel=data/lang_char/${train_dir}_${bpemode}${nbpe}
64
- echo "dictionary: ${dict}"
65
- echo "Dictionary preparation"
66
- mkdir -p data/lang_char/
67
- echo "<unk> 3" > ${dict}
68
- echo "</s> 2" >> ${dict}
69
- echo "<pad> 1" >> ${dict}
70
- cut -f 2- -d" " ${download_dir}/LibriSpeech/${train_dir}/text > data/lang_char/input.txt
71
- spm_train --input=data/lang_char/input.txt --vocab_size=${nbpe} --model_type=${bpemode} --model_prefix=${bpemodel} --input_sentence_size=100000000 --unk_id=3 --eos_id=2 --pad_id=1 --bos_id=-1 --character_coverage=1
72
- spm_encode --model=${bpemodel}.model --output_format=piece < data/lang_char/input.txt > ${encoded}
73
- cat ${encoded} | tr ' ' '\n' | sort | uniq | awk '{print $0 " " NR+3}' >> ${dict}
74
- cat ${encoded} | tr ' ' '\n' | sort | uniq -c | awk '{print $2 " " $1}' > ${fairseq_dict}
75
- wc -l ${dict}
76
-
77
- echo "Prepare train and test jsons"
78
- for part in train_960 test-other test-clean; do
79
- python ${fairseq_root}/examples/speech_recognition/datasets/asr_prep_json.py --audio-dirs ${download_dir}/LibriSpeech/${part} --labels ${download_dir}/LibriSpeech/${part}/text --spm-model ${bpemodel}.model --audio-format flac --dictionary ${fairseq_dict} --output ${part}.json
80
- done
81
- # fairseq expects to find train.json and valid.json during training
82
- mv train_960.json train.json
83
-
84
- echo "Prepare valid json"
85
- python ${fairseq_root}/examples/speech_recognition/datasets/asr_prep_json.py --audio-dirs ${download_dir}/LibriSpeech/dev-clean ${download_dir}/LibriSpeech/dev-other --labels ${download_dir}/LibriSpeech/valid_text --spm-model ${bpemodel}.model --audio-format flac --dictionary ${fairseq_dict} --output valid.json
86
-
87
- cp ${fairseq_dict} ./dict.txt
88
- cp ${bpemodel}.model ./spm.model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/infer.py DELETED
@@ -1,435 +0,0 @@
1
- #!/usr/bin/env python3 -u
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
- """
8
- Run inference for pre-processed data with a trained model.
9
- """
10
-
11
- import ast
12
- import logging
13
- import math
14
- import os
15
- import sys
16
-
17
- import editdistance
18
- import numpy as np
19
- import torch
20
- from fairseq import checkpoint_utils, options, progress_bar, tasks, utils
21
- from fairseq.data.data_utils import post_process
22
- from fairseq.logging.meters import StopwatchMeter, TimeMeter
23
-
24
- logging.basicConfig()
25
- logging.root.setLevel(logging.INFO)
26
- logging.basicConfig(level=logging.INFO)
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- def add_asr_eval_argument(parser):
31
- parser.add_argument("--kspmodel", default=None, help="sentence piece model")
32
- parser.add_argument(
33
- "--wfstlm", default=None, help="wfstlm on dictonary output units"
34
- )
35
- parser.add_argument(
36
- "--rnnt_decoding_type",
37
- default="greedy",
38
- help="wfstlm on dictonary\
39
- output units",
40
- )
41
- try:
42
- parser.add_argument(
43
- "--lm-weight",
44
- "--lm_weight",
45
- type=float,
46
- default=0.2,
47
- help="weight for lm while interpolating with neural score",
48
- )
49
- except:
50
- pass
51
- parser.add_argument(
52
- "--rnnt_len_penalty", default=-0.5, help="rnnt length penalty on word level"
53
- )
54
- parser.add_argument(
55
- "--w2l-decoder",
56
- choices=["viterbi", "kenlm", "fairseqlm"],
57
- help="use a w2l decoder",
58
- )
59
- parser.add_argument("--lexicon", help="lexicon for w2l decoder")
60
- parser.add_argument("--unit-lm", action="store_true", help="if using a unit lm")
61
- parser.add_argument("--kenlm-model", "--lm-model", help="lm model for w2l decoder")
62
- parser.add_argument("--beam-threshold", type=float, default=25.0)
63
- parser.add_argument("--beam-size-token", type=float, default=100)
64
- parser.add_argument("--word-score", type=float, default=1.0)
65
- parser.add_argument("--unk-weight", type=float, default=-math.inf)
66
- parser.add_argument("--sil-weight", type=float, default=0.0)
67
- parser.add_argument(
68
- "--dump-emissions",
69
- type=str,
70
- default=None,
71
- help="if present, dumps emissions into this file and exits",
72
- )
73
- parser.add_argument(
74
- "--dump-features",
75
- type=str,
76
- default=None,
77
- help="if present, dumps features into this file and exits",
78
- )
79
- parser.add_argument(
80
- "--load-emissions",
81
- type=str,
82
- default=None,
83
- help="if present, loads emissions from this file",
84
- )
85
- return parser
86
-
87
-
88
- def check_args(args):
89
- # assert args.path is not None, "--path required for generation!"
90
- # assert args.results_path is not None, "--results_path required for generation!"
91
- assert (
92
- not args.sampling or args.nbest == args.beam
93
- ), "--sampling requires --nbest to be equal to --beam"
94
- assert (
95
- args.replace_unk is None or args.raw_text
96
- ), "--replace-unk requires a raw text dataset (--raw-text)"
97
-
98
-
99
- def get_dataset_itr(args, task, models):
100
- return task.get_batch_iterator(
101
- dataset=task.dataset(args.gen_subset),
102
- max_tokens=args.max_tokens,
103
- max_sentences=args.batch_size,
104
- max_positions=(sys.maxsize, sys.maxsize),
105
- ignore_invalid_inputs=args.skip_invalid_size_inputs_valid_test,
106
- required_batch_size_multiple=args.required_batch_size_multiple,
107
- num_shards=args.num_shards,
108
- shard_id=args.shard_id,
109
- num_workers=args.num_workers,
110
- data_buffer_size=args.data_buffer_size,
111
- ).next_epoch_itr(shuffle=False)
112
-
113
-
114
- def process_predictions(
115
- args, hypos, sp, tgt_dict, target_tokens, res_files, speaker, id
116
- ):
117
- for hypo in hypos[: min(len(hypos), args.nbest)]:
118
- hyp_pieces = tgt_dict.string(hypo["tokens"].int().cpu())
119
-
120
- if "words" in hypo:
121
- hyp_words = " ".join(hypo["words"])
122
- else:
123
- hyp_words = post_process(hyp_pieces, args.post_process)
124
-
125
- if res_files is not None:
126
- print(
127
- "{} ({}-{})".format(hyp_pieces, speaker, id),
128
- file=res_files["hypo.units"],
129
- )
130
- print(
131
- "{} ({}-{})".format(hyp_words, speaker, id),
132
- file=res_files["hypo.words"],
133
- )
134
-
135
- tgt_pieces = tgt_dict.string(target_tokens)
136
- tgt_words = post_process(tgt_pieces, args.post_process)
137
-
138
- if res_files is not None:
139
- print(
140
- "{} ({}-{})".format(tgt_pieces, speaker, id),
141
- file=res_files["ref.units"],
142
- )
143
- print(
144
- "{} ({}-{})".format(tgt_words, speaker, id), file=res_files["ref.words"]
145
- )
146
-
147
- if not args.quiet:
148
- logger.info("HYPO:" + hyp_words)
149
- logger.info("TARGET:" + tgt_words)
150
- logger.info("___________________")
151
-
152
- hyp_words = hyp_words.split()
153
- tgt_words = tgt_words.split()
154
- return editdistance.eval(hyp_words, tgt_words), len(tgt_words)
155
-
156
-
157
- def prepare_result_files(args):
158
- def get_res_file(file_prefix):
159
- if args.num_shards > 1:
160
- file_prefix = f"{args.shard_id}_{file_prefix}"
161
- path = os.path.join(
162
- args.results_path,
163
- "{}-{}-{}.txt".format(
164
- file_prefix, os.path.basename(args.path), args.gen_subset
165
- ),
166
- )
167
- return open(path, "w", buffering=1)
168
-
169
- if not args.results_path:
170
- return None
171
-
172
- return {
173
- "hypo.words": get_res_file("hypo.word"),
174
- "hypo.units": get_res_file("hypo.units"),
175
- "ref.words": get_res_file("ref.word"),
176
- "ref.units": get_res_file("ref.units"),
177
- }
178
-
179
-
180
- def optimize_models(args, use_cuda, models):
181
- """Optimize ensemble for generation"""
182
- for model in models:
183
- model.make_generation_fast_(
184
- beamable_mm_beam_size=None if args.no_beamable_mm else args.beam,
185
- need_attn=args.print_alignment,
186
- )
187
- if args.fp16:
188
- model.half()
189
- if use_cuda:
190
- model.cuda()
191
-
192
-
193
- def apply_half(t):
194
- if t.dtype is torch.float32:
195
- return t.to(dtype=torch.half)
196
- return t
197
-
198
-
199
- class ExistingEmissionsDecoder(object):
200
- def __init__(self, decoder, emissions):
201
- self.decoder = decoder
202
- self.emissions = emissions
203
-
204
- def generate(self, models, sample, **unused):
205
- ids = sample["id"].cpu().numpy()
206
- try:
207
- emissions = np.stack(self.emissions[ids])
208
- except:
209
- print([x.shape for x in self.emissions[ids]])
210
- raise Exception("invalid sizes")
211
- emissions = torch.from_numpy(emissions)
212
- return self.decoder.decode(emissions)
213
-
214
-
215
- def main(args, task=None, model_state=None):
216
- check_args(args)
217
-
218
- use_fp16 = args.fp16
219
- if args.max_tokens is None and args.batch_size is None:
220
- args.max_tokens = 4000000
221
- logger.info(args)
222
-
223
- use_cuda = torch.cuda.is_available() and not args.cpu
224
-
225
- logger.info("| decoding with criterion {}".format(args.criterion))
226
-
227
- task = tasks.setup_task(args)
228
-
229
- # Load ensemble
230
- if args.load_emissions:
231
- models, criterions = [], []
232
- task.load_dataset(args.gen_subset)
233
- else:
234
- logger.info("| loading model(s) from {}".format(args.path))
235
- models, saved_cfg, task = checkpoint_utils.load_model_ensemble_and_task(
236
- utils.split_paths(args.path, separator="\\"),
237
- arg_overrides=ast.literal_eval(args.model_overrides),
238
- task=task,
239
- suffix=args.checkpoint_suffix,
240
- strict=(args.checkpoint_shard_count == 1),
241
- num_shards=args.checkpoint_shard_count,
242
- state=model_state,
243
- )
244
- optimize_models(args, use_cuda, models)
245
- task.load_dataset(args.gen_subset, task_cfg=saved_cfg.task)
246
-
247
-
248
- # Set dictionary
249
- tgt_dict = task.target_dictionary
250
-
251
- logger.info(
252
- "| {} {} {} examples".format(
253
- args.data, args.gen_subset, len(task.dataset(args.gen_subset))
254
- )
255
- )
256
-
257
- # hack to pass transitions to W2lDecoder
258
- if args.criterion == "asg_loss":
259
- raise NotImplementedError("asg_loss is currently not supported")
260
- # trans = criterions[0].asg.trans.data
261
- # args.asg_transitions = torch.flatten(trans).tolist()
262
-
263
- # Load dataset (possibly sharded)
264
- itr = get_dataset_itr(args, task, models)
265
-
266
- # Initialize generator
267
- gen_timer = StopwatchMeter()
268
-
269
- def build_generator(args):
270
- w2l_decoder = getattr(args, "w2l_decoder", None)
271
- if w2l_decoder == "viterbi":
272
- from examples.speech_recognition.w2l_decoder import W2lViterbiDecoder
273
-
274
- return W2lViterbiDecoder(args, task.target_dictionary)
275
- elif w2l_decoder == "kenlm":
276
- from examples.speech_recognition.w2l_decoder import W2lKenLMDecoder
277
-
278
- return W2lKenLMDecoder(args, task.target_dictionary)
279
- elif w2l_decoder == "fairseqlm":
280
- from examples.speech_recognition.w2l_decoder import W2lFairseqLMDecoder
281
-
282
- return W2lFairseqLMDecoder(args, task.target_dictionary)
283
- else:
284
- print(
285
- "only flashlight decoders with (viterbi, kenlm, fairseqlm) options are supported at the moment"
286
- )
287
-
288
- # please do not touch this unless you test both generate.py and infer.py with audio_pretraining task
289
- generator = build_generator(args)
290
-
291
- if args.load_emissions:
292
- generator = ExistingEmissionsDecoder(
293
- generator, np.load(args.load_emissions, allow_pickle=True)
294
- )
295
- logger.info("loaded emissions from " + args.load_emissions)
296
-
297
- num_sentences = 0
298
-
299
- if args.results_path is not None and not os.path.exists(args.results_path):
300
- os.makedirs(args.results_path)
301
-
302
- max_source_pos = (
303
- utils.resolve_max_positions(
304
- task.max_positions(), *[model.max_positions() for model in models]
305
- ),
306
- )
307
-
308
- if max_source_pos is not None:
309
- max_source_pos = max_source_pos[0]
310
- if max_source_pos is not None:
311
- max_source_pos = max_source_pos[0] - 1
312
-
313
- if args.dump_emissions:
314
- emissions = {}
315
- if args.dump_features:
316
- features = {}
317
- models[0].bert.proj = None
318
- else:
319
- res_files = prepare_result_files(args)
320
- errs_t = 0
321
- lengths_t = 0
322
- with progress_bar.build_progress_bar(args, itr) as t:
323
- wps_meter = TimeMeter()
324
- for sample in t:
325
- sample = utils.move_to_cuda(sample) if use_cuda else sample
326
- if use_fp16:
327
- sample = utils.apply_to_sample(apply_half, sample)
328
- if "net_input" not in sample:
329
- continue
330
-
331
- prefix_tokens = None
332
- if args.prefix_size > 0:
333
- prefix_tokens = sample["target"][:, : args.prefix_size]
334
-
335
- gen_timer.start()
336
- if args.dump_emissions:
337
- with torch.no_grad():
338
- encoder_out = models[0](**sample["net_input"])
339
- emm = models[0].get_normalized_probs(encoder_out, log_probs=True)
340
- emm = emm.transpose(0, 1).cpu().numpy()
341
- for i, id in enumerate(sample["id"]):
342
- emissions[id.item()] = emm[i]
343
- continue
344
- elif args.dump_features:
345
- with torch.no_grad():
346
- encoder_out = models[0](**sample["net_input"])
347
- feat = encoder_out["encoder_out"].transpose(0, 1).cpu().numpy()
348
- for i, id in enumerate(sample["id"]):
349
- padding = (
350
- encoder_out["encoder_padding_mask"][i].cpu().numpy()
351
- if encoder_out["encoder_padding_mask"] is not None
352
- else None
353
- )
354
- features[id.item()] = (feat[i], padding)
355
- continue
356
- hypos = task.inference_step(generator, models, sample, prefix_tokens)
357
- num_generated_tokens = sum(len(h[0]["tokens"]) for h in hypos)
358
- gen_timer.stop(num_generated_tokens)
359
-
360
- for i, sample_id in enumerate(sample["id"].tolist()):
361
- speaker = None
362
- # id = task.dataset(args.gen_subset).ids[int(sample_id)]
363
- id = sample_id
364
- toks = (
365
- sample["target"][i, :]
366
- if "target_label" not in sample
367
- else sample["target_label"][i, :]
368
- )
369
- target_tokens = utils.strip_pad(toks, tgt_dict.pad()).int().cpu()
370
- # Process top predictions
371
- errs, length = process_predictions(
372
- args,
373
- hypos[i],
374
- None,
375
- tgt_dict,
376
- target_tokens,
377
- res_files,
378
- speaker,
379
- id,
380
- )
381
- errs_t += errs
382
- lengths_t += length
383
-
384
- wps_meter.update(num_generated_tokens)
385
- t.log({"wps": round(wps_meter.avg)})
386
- num_sentences += (
387
- sample["nsentences"] if "nsentences" in sample else sample["id"].numel()
388
- )
389
-
390
- wer = None
391
- if args.dump_emissions:
392
- emm_arr = []
393
- for i in range(len(emissions)):
394
- emm_arr.append(emissions[i])
395
- np.save(args.dump_emissions, emm_arr)
396
- logger.info(f"saved {len(emissions)} emissions to {args.dump_emissions}")
397
- elif args.dump_features:
398
- feat_arr = []
399
- for i in range(len(features)):
400
- feat_arr.append(features[i])
401
- np.save(args.dump_features, feat_arr)
402
- logger.info(f"saved {len(features)} emissions to {args.dump_features}")
403
- else:
404
- if lengths_t > 0:
405
- wer = errs_t * 100.0 / lengths_t
406
- logger.info(f"WER: {wer}")
407
-
408
- logger.info(
409
- "| Processed {} sentences ({} tokens) in {:.1f}s ({:.2f}"
410
- "sentences/s, {:.2f} tokens/s)".format(
411
- num_sentences,
412
- gen_timer.n,
413
- gen_timer.sum,
414
- num_sentences / gen_timer.sum,
415
- 1.0 / gen_timer.avg,
416
- )
417
- )
418
- logger.info("| Generate {} with beam={}".format(args.gen_subset, args.beam))
419
- return task, wer
420
-
421
-
422
- def make_parser():
423
- parser = options.get_generation_parser()
424
- parser = add_asr_eval_argument(parser)
425
- return parser
426
-
427
-
428
- def cli_main():
429
- parser = make_parser()
430
- args = options.parse_args_and_arch(parser)
431
- main(args)
432
-
433
-
434
- if __name__ == "__main__":
435
- cli_main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/__init__.py DELETED
File without changes
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/add-self-loop-simple.cc DELETED
@@ -1,94 +0,0 @@
1
- /*
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
-
8
- #include <iostream>
9
- #include "fstext/fstext-lib.h" // @manual
10
- #include "util/common-utils.h" // @manual
11
-
12
- /*
13
- * This program is to modify a FST without self-loop by:
14
- * for each incoming arc with non-eps input symbol, add a self-loop arc
15
- * with that non-eps symbol as input and eps as output.
16
- *
17
- * This is to make sure the resultant FST can do deduplication for repeated
18
- * symbols, which is very common in acoustic model
19
- *
20
- */
21
- namespace {
22
- int32 AddSelfLoopsSimple(fst::StdVectorFst* fst) {
23
- typedef fst::MutableArcIterator<fst::StdVectorFst> IterType;
24
-
25
- int32 num_states_before = fst->NumStates();
26
- fst::MakePrecedingInputSymbolsSame(false, fst);
27
- int32 num_states_after = fst->NumStates();
28
- KALDI_LOG << "There are " << num_states_before
29
- << " states in the original FST; "
30
- << " after MakePrecedingInputSymbolsSame, there are "
31
- << num_states_after << " states " << std::endl;
32
-
33
- auto weight_one = fst::StdArc::Weight::One();
34
-
35
- int32 num_arc_added = 0;
36
-
37
- fst::StdArc self_loop_arc;
38
- self_loop_arc.weight = weight_one;
39
-
40
- int32 num_states = fst->NumStates();
41
- std::vector<std::set<int32>> incoming_non_eps_label_per_state(num_states);
42
-
43
- for (int32 state = 0; state < num_states; state++) {
44
- for (IterType aiter(fst, state); !aiter.Done(); aiter.Next()) {
45
- fst::StdArc arc(aiter.Value());
46
- if (arc.ilabel != 0) {
47
- incoming_non_eps_label_per_state[arc.nextstate].insert(arc.ilabel);
48
- }
49
- }
50
- }
51
-
52
- for (int32 state = 0; state < num_states; state++) {
53
- if (!incoming_non_eps_label_per_state[state].empty()) {
54
- auto& ilabel_set = incoming_non_eps_label_per_state[state];
55
- for (auto it = ilabel_set.begin(); it != ilabel_set.end(); it++) {
56
- self_loop_arc.ilabel = *it;
57
- self_loop_arc.olabel = 0;
58
- self_loop_arc.nextstate = state;
59
- fst->AddArc(state, self_loop_arc);
60
- num_arc_added++;
61
- }
62
- }
63
- }
64
- return num_arc_added;
65
- }
66
-
67
- void print_usage() {
68
- std::cout << "add-self-loop-simple usage:\n"
69
- "\tadd-self-loop-simple <in-fst> <out-fst> \n";
70
- }
71
- } // namespace
72
-
73
- int main(int argc, char** argv) {
74
- if (argc != 3) {
75
- print_usage();
76
- exit(1);
77
- }
78
-
79
- auto input = argv[1];
80
- auto output = argv[2];
81
-
82
- auto fst = fst::ReadFstKaldi(input);
83
- auto num_states = fst->NumStates();
84
- KALDI_LOG << "Loading FST from " << input << " with " << num_states
85
- << " states." << std::endl;
86
-
87
- int32 num_arc_added = AddSelfLoopsSimple(fst);
88
- KALDI_LOG << "Adding " << num_arc_added << " self-loop arcs " << std::endl;
89
-
90
- fst::WriteFstKaldi(*fst, std::string(output));
91
- KALDI_LOG << "Writing FST to " << output << std::endl;
92
-
93
- delete fst;
94
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/config/kaldi_initializer.yaml DELETED
@@ -1,8 +0,0 @@
1
- # @package _group_
2
-
3
- data_dir: ???
4
- fst_dir: ???
5
- in_labels: ???
6
- kaldi_root: ???
7
- lm_arpa: ???
8
- blank_symbol: <s>
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/kaldi_decoder.py DELETED
@@ -1,243 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- import logging
9
- import os
10
- import warnings
11
- from concurrent.futures import ThreadPoolExecutor
12
- from dataclasses import dataclass
13
- from typing import Optional
14
-
15
- import torch
16
- from fairseq.dataclass import FairseqDataclass
17
- from omegaconf import MISSING
18
-
19
- from .kaldi_initializer import KaldiInitializerConfig, initalize_kaldi
20
-
21
- logger = logging.getLogger(__name__)
22
-
23
-
24
- @dataclass
25
- class KaldiDecoderConfig(FairseqDataclass):
26
- hlg_graph_path: Optional[str] = None
27
- output_dict: str = MISSING
28
-
29
- kaldi_initializer_config: Optional[KaldiInitializerConfig] = None
30
-
31
- acoustic_scale: float = 0.5
32
- max_active: int = 10000
33
- beam_delta: float = 0.5
34
- hash_ratio: float = 2.0
35
-
36
- is_lattice: bool = False
37
- lattice_beam: float = 10.0
38
- prune_interval: int = 25
39
- determinize_lattice: bool = True
40
- prune_scale: float = 0.1
41
- max_mem: int = 0
42
- phone_determinize: bool = True
43
- word_determinize: bool = True
44
- minimize: bool = True
45
-
46
- num_threads: int = 1
47
-
48
-
49
- class KaldiDecoder(object):
50
- def __init__(
51
- self,
52
- cfg: KaldiDecoderConfig,
53
- beam: int,
54
- nbest: int = 1,
55
- ):
56
- try:
57
- from kaldi.asr import FasterRecognizer, LatticeFasterRecognizer
58
- from kaldi.base import set_verbose_level
59
- from kaldi.decoder import (
60
- FasterDecoder,
61
- FasterDecoderOptions,
62
- LatticeFasterDecoder,
63
- LatticeFasterDecoderOptions,
64
- )
65
- from kaldi.fstext import SymbolTable, read_fst_kaldi
66
- from kaldi.lat.functions import DeterminizeLatticePhonePrunedOptions
67
- except:
68
- warnings.warn(
69
- "pykaldi is required for this functionality. Please install from https://github.com/pykaldi/pykaldi"
70
- )
71
-
72
- # set_verbose_level(2)
73
-
74
- self.acoustic_scale = cfg.acoustic_scale
75
- self.nbest = nbest
76
-
77
- if cfg.hlg_graph_path is None:
78
- assert (
79
- cfg.kaldi_initializer_config is not None
80
- ), "Must provide hlg graph path or kaldi initializer config"
81
- cfg.hlg_graph_path = initalize_kaldi(cfg.kaldi_initializer_config)
82
-
83
- assert os.path.exists(cfg.hlg_graph_path), cfg.hlg_graph_path
84
-
85
- if cfg.is_lattice:
86
- self.dec_cls = LatticeFasterDecoder
87
- opt_cls = LatticeFasterDecoderOptions
88
- self.rec_cls = LatticeFasterRecognizer
89
- else:
90
- assert self.nbest == 1, "nbest > 1 requires lattice decoder"
91
- self.dec_cls = FasterDecoder
92
- opt_cls = FasterDecoderOptions
93
- self.rec_cls = FasterRecognizer
94
-
95
- self.decoder_options = opt_cls()
96
- self.decoder_options.beam = beam
97
- self.decoder_options.max_active = cfg.max_active
98
- self.decoder_options.beam_delta = cfg.beam_delta
99
- self.decoder_options.hash_ratio = cfg.hash_ratio
100
-
101
- if cfg.is_lattice:
102
- self.decoder_options.lattice_beam = cfg.lattice_beam
103
- self.decoder_options.prune_interval = cfg.prune_interval
104
- self.decoder_options.determinize_lattice = cfg.determinize_lattice
105
- self.decoder_options.prune_scale = cfg.prune_scale
106
- det_opts = DeterminizeLatticePhonePrunedOptions()
107
- det_opts.max_mem = cfg.max_mem
108
- det_opts.phone_determinize = cfg.phone_determinize
109
- det_opts.word_determinize = cfg.word_determinize
110
- det_opts.minimize = cfg.minimize
111
- self.decoder_options.det_opts = det_opts
112
-
113
- self.output_symbols = {}
114
- with open(cfg.output_dict, "r") as f:
115
- for line in f:
116
- items = line.rstrip().split()
117
- assert len(items) == 2
118
- self.output_symbols[int(items[1])] = items[0]
119
-
120
- logger.info(f"Loading FST from {cfg.hlg_graph_path}")
121
- self.fst = read_fst_kaldi(cfg.hlg_graph_path)
122
- self.symbol_table = SymbolTable.read_text(cfg.output_dict)
123
-
124
- self.executor = ThreadPoolExecutor(max_workers=cfg.num_threads)
125
-
126
- def generate(self, models, sample, **unused):
127
- """Generate a batch of inferences."""
128
- # model.forward normally channels prev_output_tokens into the decoder
129
- # separately, but SequenceGenerator directly calls model.encoder
130
- encoder_input = {
131
- k: v for k, v in sample["net_input"].items() if k != "prev_output_tokens"
132
- }
133
- emissions, padding = self.get_emissions(models, encoder_input)
134
- return self.decode(emissions, padding)
135
-
136
- def get_emissions(self, models, encoder_input):
137
- """Run encoder and normalize emissions"""
138
- model = models[0]
139
-
140
- all_encoder_out = [m(**encoder_input) for m in models]
141
-
142
- if len(all_encoder_out) > 1:
143
-
144
- if "encoder_out" in all_encoder_out[0]:
145
- encoder_out = {
146
- "encoder_out": sum(e["encoder_out"] for e in all_encoder_out)
147
- / len(all_encoder_out),
148
- "encoder_padding_mask": all_encoder_out[0]["encoder_padding_mask"],
149
- }
150
- padding = encoder_out["encoder_padding_mask"]
151
- else:
152
- encoder_out = {
153
- "logits": sum(e["logits"] for e in all_encoder_out)
154
- / len(all_encoder_out),
155
- "padding_mask": all_encoder_out[0]["padding_mask"],
156
- }
157
- padding = encoder_out["padding_mask"]
158
- else:
159
- encoder_out = all_encoder_out[0]
160
- padding = (
161
- encoder_out["padding_mask"]
162
- if "padding_mask" in encoder_out
163
- else encoder_out["encoder_padding_mask"]
164
- )
165
-
166
- if hasattr(model, "get_logits"):
167
- emissions = model.get_logits(encoder_out, normalize=True)
168
- else:
169
- emissions = model.get_normalized_probs(encoder_out, log_probs=True)
170
-
171
- return (
172
- emissions.cpu().float().transpose(0, 1),
173
- padding.cpu() if padding is not None and padding.any() else None,
174
- )
175
-
176
- def decode_one(self, logits, padding):
177
- from kaldi.matrix import Matrix
178
-
179
- decoder = self.dec_cls(self.fst, self.decoder_options)
180
- asr = self.rec_cls(
181
- decoder, self.symbol_table, acoustic_scale=self.acoustic_scale
182
- )
183
-
184
- if padding is not None:
185
- logits = logits[~padding]
186
-
187
- mat = Matrix(logits.numpy())
188
-
189
- out = asr.decode(mat)
190
-
191
- if self.nbest > 1:
192
- from kaldi.fstext import shortestpath
193
- from kaldi.fstext.utils import (
194
- convert_compact_lattice_to_lattice,
195
- convert_lattice_to_std,
196
- convert_nbest_to_list,
197
- get_linear_symbol_sequence,
198
- )
199
-
200
- lat = out["lattice"]
201
-
202
- sp = shortestpath(lat, nshortest=self.nbest)
203
-
204
- sp = convert_compact_lattice_to_lattice(sp)
205
- sp = convert_lattice_to_std(sp)
206
- seq = convert_nbest_to_list(sp)
207
-
208
- results = []
209
- for s in seq:
210
- _, o, w = get_linear_symbol_sequence(s)
211
- words = list(self.output_symbols[z] for z in o)
212
- results.append(
213
- {
214
- "tokens": words,
215
- "words": words,
216
- "score": w.value,
217
- "emissions": logits,
218
- }
219
- )
220
- return results
221
- else:
222
- words = out["text"].split()
223
- return [
224
- {
225
- "tokens": words,
226
- "words": words,
227
- "score": out["likelihood"],
228
- "emissions": logits,
229
- }
230
- ]
231
-
232
- def decode(self, emissions, padding):
233
- if padding is None:
234
- padding = [None] * len(emissions)
235
-
236
- ret = list(
237
- map(
238
- lambda e, p: self.executor.submit(self.decode_one, e, p),
239
- emissions,
240
- padding,
241
- )
242
- )
243
- return ret
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/kaldi/kaldi_initializer.py DELETED
@@ -1,698 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- import logging
9
- import os
10
- import os.path as osp
11
- import subprocess
12
- from dataclasses import dataclass
13
- from pathlib import Path
14
- from typing import Optional
15
-
16
- import hydra
17
- from fairseq.data.dictionary import Dictionary
18
- from fairseq.dataclass import FairseqDataclass
19
- from hydra.core.config_store import ConfigStore
20
- from omegaconf import MISSING, OmegaConf
21
-
22
- script_dir = Path(__file__).resolve().parent
23
- config_path = script_dir / "config"
24
-
25
-
26
- logger = logging.getLogger(__name__)
27
-
28
-
29
- @dataclass
30
- class KaldiInitializerConfig(FairseqDataclass):
31
- data_dir: str = MISSING
32
- fst_dir: Optional[str] = None
33
- in_labels: str = MISSING
34
- out_labels: Optional[str] = None
35
- wav2letter_lexicon: Optional[str] = None
36
- lm_arpa: str = MISSING
37
- kaldi_root: str = MISSING
38
- blank_symbol: str = "<s>"
39
- silence_symbol: Optional[str] = None
40
-
41
-
42
- def create_units(fst_dir: Path, in_labels: str, vocab: Dictionary) -> Path:
43
- in_units_file = fst_dir / f"kaldi_dict.{in_labels}.txt"
44
- if not in_units_file.exists():
45
-
46
- logger.info(f"Creating {in_units_file}")
47
-
48
- with open(in_units_file, "w") as f:
49
- print("<eps> 0", file=f)
50
- i = 1
51
- for symb in vocab.symbols[vocab.nspecial :]:
52
- if not symb.startswith("madeupword"):
53
- print(f"{symb} {i}", file=f)
54
- i += 1
55
- return in_units_file
56
-
57
-
58
- def create_lexicon(
59
- cfg: KaldiInitializerConfig,
60
- fst_dir: Path,
61
- unique_label: str,
62
- in_units_file: Path,
63
- out_words_file: Path,
64
- ) -> (Path, Path):
65
-
66
- disambig_in_units_file = fst_dir / f"kaldi_dict.{cfg.in_labels}_disambig.txt"
67
- lexicon_file = fst_dir / f"kaldi_lexicon.{unique_label}.txt"
68
- disambig_lexicon_file = fst_dir / f"kaldi_lexicon.{unique_label}_disambig.txt"
69
- if (
70
- not lexicon_file.exists()
71
- or not disambig_lexicon_file.exists()
72
- or not disambig_in_units_file.exists()
73
- ):
74
- logger.info(f"Creating {lexicon_file} (in units file: {in_units_file})")
75
-
76
- assert cfg.wav2letter_lexicon is not None or cfg.in_labels == cfg.out_labels
77
-
78
- if cfg.wav2letter_lexicon is not None:
79
- lm_words = set()
80
- with open(out_words_file, "r") as lm_dict_f:
81
- for line in lm_dict_f:
82
- lm_words.add(line.split()[0])
83
-
84
- num_skipped = 0
85
- total = 0
86
- with open(cfg.wav2letter_lexicon, "r") as w2l_lex_f, open(
87
- lexicon_file, "w"
88
- ) as out_f:
89
- for line in w2l_lex_f:
90
- items = line.rstrip().split("\t")
91
- assert len(items) == 2, items
92
- if items[0] in lm_words:
93
- print(items[0], items[1], file=out_f)
94
- else:
95
- num_skipped += 1
96
- logger.debug(
97
- f"Skipping word {items[0]} as it was not found in LM"
98
- )
99
- total += 1
100
- if num_skipped > 0:
101
- logger.warning(
102
- f"Skipped {num_skipped} out of {total} words as they were not found in LM"
103
- )
104
- else:
105
- with open(in_units_file, "r") as in_f, open(lexicon_file, "w") as out_f:
106
- for line in in_f:
107
- symb = line.split()[0]
108
- if symb != "<eps>" and symb != "<ctc_blank>" and symb != "<SIL>":
109
- print(symb, symb, file=out_f)
110
-
111
- lex_disambig_path = (
112
- Path(cfg.kaldi_root) / "egs/wsj/s5/utils/add_lex_disambig.pl"
113
- )
114
- res = subprocess.run(
115
- [lex_disambig_path, lexicon_file, disambig_lexicon_file],
116
- check=True,
117
- capture_output=True,
118
- )
119
- ndisambig = int(res.stdout)
120
- disamib_path = Path(cfg.kaldi_root) / "egs/wsj/s5/utils/add_disambig.pl"
121
- res = subprocess.run(
122
- [disamib_path, "--include-zero", in_units_file, str(ndisambig)],
123
- check=True,
124
- capture_output=True,
125
- )
126
- with open(disambig_in_units_file, "wb") as f:
127
- f.write(res.stdout)
128
-
129
- return disambig_lexicon_file, disambig_in_units_file
130
-
131
-
132
- def create_G(
133
- kaldi_root: Path, fst_dir: Path, lm_arpa: Path, arpa_base: str
134
- ) -> (Path, Path):
135
-
136
- out_words_file = fst_dir / f"kaldi_dict.{arpa_base}.txt"
137
- grammar_graph = fst_dir / f"G_{arpa_base}.fst"
138
- if not grammar_graph.exists() or not out_words_file.exists():
139
- logger.info(f"Creating {grammar_graph}")
140
- arpa2fst = kaldi_root / "src/lmbin/arpa2fst"
141
- subprocess.run(
142
- [
143
- arpa2fst,
144
- "--disambig-symbol=#0",
145
- f"--write-symbol-table={out_words_file}",
146
- lm_arpa,
147
- grammar_graph,
148
- ],
149
- check=True,
150
- )
151
- return grammar_graph, out_words_file
152
-
153
-
154
- def create_L(
155
- kaldi_root: Path,
156
- fst_dir: Path,
157
- unique_label: str,
158
- lexicon_file: Path,
159
- in_units_file: Path,
160
- out_words_file: Path,
161
- ) -> Path:
162
- lexicon_graph = fst_dir / f"L.{unique_label}.fst"
163
-
164
- if not lexicon_graph.exists():
165
- logger.info(f"Creating {lexicon_graph} (in units: {in_units_file})")
166
- make_lex = kaldi_root / "egs/wsj/s5/utils/make_lexicon_fst.pl"
167
- fstcompile = kaldi_root / "tools/openfst-1.6.7/bin/fstcompile"
168
- fstaddselfloops = kaldi_root / "src/fstbin/fstaddselfloops"
169
- fstarcsort = kaldi_root / "tools/openfst-1.6.7/bin/fstarcsort"
170
-
171
- def write_disambig_symbol(file):
172
- with open(file, "r") as f:
173
- for line in f:
174
- items = line.rstrip().split()
175
- if items[0] == "#0":
176
- out_path = str(file) + "_disamig"
177
- with open(out_path, "w") as out_f:
178
- print(items[1], file=out_f)
179
- return out_path
180
-
181
- return None
182
-
183
- in_disambig_sym = write_disambig_symbol(in_units_file)
184
- assert in_disambig_sym is not None
185
- out_disambig_sym = write_disambig_symbol(out_words_file)
186
- assert out_disambig_sym is not None
187
-
188
- try:
189
- with open(lexicon_graph, "wb") as out_f:
190
- res = subprocess.run(
191
- [make_lex, lexicon_file], capture_output=True, check=True
192
- )
193
- assert len(res.stderr) == 0, res.stderr.decode("utf-8")
194
- res = subprocess.run(
195
- [
196
- fstcompile,
197
- f"--isymbols={in_units_file}",
198
- f"--osymbols={out_words_file}",
199
- "--keep_isymbols=false",
200
- "--keep_osymbols=false",
201
- ],
202
- input=res.stdout,
203
- capture_output=True,
204
- )
205
- assert len(res.stderr) == 0, res.stderr.decode("utf-8")
206
- res = subprocess.run(
207
- [fstaddselfloops, in_disambig_sym, out_disambig_sym],
208
- input=res.stdout,
209
- capture_output=True,
210
- check=True,
211
- )
212
- res = subprocess.run(
213
- [fstarcsort, "--sort_type=olabel"],
214
- input=res.stdout,
215
- capture_output=True,
216
- check=True,
217
- )
218
- out_f.write(res.stdout)
219
- except subprocess.CalledProcessError as e:
220
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
221
- os.remove(lexicon_graph)
222
- raise
223
- except AssertionError:
224
- os.remove(lexicon_graph)
225
- raise
226
-
227
- return lexicon_graph
228
-
229
-
230
- def create_LG(
231
- kaldi_root: Path,
232
- fst_dir: Path,
233
- unique_label: str,
234
- lexicon_graph: Path,
235
- grammar_graph: Path,
236
- ) -> Path:
237
- lg_graph = fst_dir / f"LG.{unique_label}.fst"
238
-
239
- if not lg_graph.exists():
240
- logger.info(f"Creating {lg_graph}")
241
-
242
- fsttablecompose = kaldi_root / "src/fstbin/fsttablecompose"
243
- fstdeterminizestar = kaldi_root / "src/fstbin/fstdeterminizestar"
244
- fstminimizeencoded = kaldi_root / "src/fstbin/fstminimizeencoded"
245
- fstpushspecial = kaldi_root / "src/fstbin/fstpushspecial"
246
- fstarcsort = kaldi_root / "tools/openfst-1.6.7/bin/fstarcsort"
247
-
248
- try:
249
- with open(lg_graph, "wb") as out_f:
250
- res = subprocess.run(
251
- [fsttablecompose, lexicon_graph, grammar_graph],
252
- capture_output=True,
253
- check=True,
254
- )
255
- res = subprocess.run(
256
- [
257
- fstdeterminizestar,
258
- "--use-log=true",
259
- ],
260
- input=res.stdout,
261
- capture_output=True,
262
- )
263
- res = subprocess.run(
264
- [fstminimizeencoded],
265
- input=res.stdout,
266
- capture_output=True,
267
- check=True,
268
- )
269
- res = subprocess.run(
270
- [fstpushspecial],
271
- input=res.stdout,
272
- capture_output=True,
273
- check=True,
274
- )
275
- res = subprocess.run(
276
- [fstarcsort, "--sort_type=ilabel"],
277
- input=res.stdout,
278
- capture_output=True,
279
- check=True,
280
- )
281
- out_f.write(res.stdout)
282
- except subprocess.CalledProcessError as e:
283
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
284
- os.remove(lg_graph)
285
- raise
286
-
287
- return lg_graph
288
-
289
-
290
- def create_H(
291
- kaldi_root: Path,
292
- fst_dir: Path,
293
- disambig_out_units_file: Path,
294
- in_labels: str,
295
- vocab: Dictionary,
296
- blk_sym: str,
297
- silence_symbol: Optional[str],
298
- ) -> (Path, Path, Path):
299
- h_graph = (
300
- fst_dir / f"H.{in_labels}{'_' + silence_symbol if silence_symbol else ''}.fst"
301
- )
302
- h_out_units_file = fst_dir / f"kaldi_dict.h_out.{in_labels}.txt"
303
- disambig_in_units_file_int = Path(str(h_graph) + "isym_disambig.int")
304
- disambig_out_units_file_int = Path(str(disambig_out_units_file) + ".int")
305
- if (
306
- not h_graph.exists()
307
- or not h_out_units_file.exists()
308
- or not disambig_in_units_file_int.exists()
309
- ):
310
- logger.info(f"Creating {h_graph}")
311
- eps_sym = "<eps>"
312
-
313
- num_disambig = 0
314
- osymbols = []
315
-
316
- with open(disambig_out_units_file, "r") as f, open(
317
- disambig_out_units_file_int, "w"
318
- ) as out_f:
319
- for line in f:
320
- symb, id = line.rstrip().split()
321
- if line.startswith("#"):
322
- num_disambig += 1
323
- print(id, file=out_f)
324
- else:
325
- if len(osymbols) == 0:
326
- assert symb == eps_sym, symb
327
- osymbols.append((symb, id))
328
-
329
- i_idx = 0
330
- isymbols = [(eps_sym, 0)]
331
-
332
- imap = {}
333
-
334
- for i, s in enumerate(vocab.symbols):
335
- i_idx += 1
336
- isymbols.append((s, i_idx))
337
- imap[s] = i_idx
338
-
339
- fst_str = []
340
-
341
- node_idx = 0
342
- root_node = node_idx
343
-
344
- special_symbols = [blk_sym]
345
- if silence_symbol is not None:
346
- special_symbols.append(silence_symbol)
347
-
348
- for ss in special_symbols:
349
- fst_str.append("{} {} {} {}".format(root_node, root_node, ss, eps_sym))
350
-
351
- for symbol, _ in osymbols:
352
- if symbol == eps_sym or symbol.startswith("#"):
353
- continue
354
-
355
- node_idx += 1
356
- # 1. from root to emitting state
357
- fst_str.append("{} {} {} {}".format(root_node, node_idx, symbol, symbol))
358
- # 2. from emitting state back to root
359
- fst_str.append("{} {} {} {}".format(node_idx, root_node, eps_sym, eps_sym))
360
- # 3. from emitting state to optional blank state
361
- pre_node = node_idx
362
- node_idx += 1
363
- for ss in special_symbols:
364
- fst_str.append("{} {} {} {}".format(pre_node, node_idx, ss, eps_sym))
365
- # 4. from blank state back to root
366
- fst_str.append("{} {} {} {}".format(node_idx, root_node, eps_sym, eps_sym))
367
-
368
- fst_str.append("{}".format(root_node))
369
-
370
- fst_str = "\n".join(fst_str)
371
- h_str = str(h_graph)
372
- isym_file = h_str + ".isym"
373
-
374
- with open(isym_file, "w") as f:
375
- for sym, id in isymbols:
376
- f.write("{} {}\n".format(sym, id))
377
-
378
- with open(h_out_units_file, "w") as f:
379
- for sym, id in osymbols:
380
- f.write("{} {}\n".format(sym, id))
381
-
382
- with open(disambig_in_units_file_int, "w") as f:
383
- disam_sym_id = len(isymbols)
384
- for _ in range(num_disambig):
385
- f.write("{}\n".format(disam_sym_id))
386
- disam_sym_id += 1
387
-
388
- fstcompile = kaldi_root / "tools/openfst-1.6.7/bin/fstcompile"
389
- fstaddselfloops = kaldi_root / "src/fstbin/fstaddselfloops"
390
- fstarcsort = kaldi_root / "tools/openfst-1.6.7/bin/fstarcsort"
391
-
392
- try:
393
- with open(h_graph, "wb") as out_f:
394
- res = subprocess.run(
395
- [
396
- fstcompile,
397
- f"--isymbols={isym_file}",
398
- f"--osymbols={h_out_units_file}",
399
- "--keep_isymbols=false",
400
- "--keep_osymbols=false",
401
- ],
402
- input=str.encode(fst_str),
403
- capture_output=True,
404
- check=True,
405
- )
406
- res = subprocess.run(
407
- [
408
- fstaddselfloops,
409
- disambig_in_units_file_int,
410
- disambig_out_units_file_int,
411
- ],
412
- input=res.stdout,
413
- capture_output=True,
414
- check=True,
415
- )
416
- res = subprocess.run(
417
- [fstarcsort, "--sort_type=olabel"],
418
- input=res.stdout,
419
- capture_output=True,
420
- check=True,
421
- )
422
- out_f.write(res.stdout)
423
- except subprocess.CalledProcessError as e:
424
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
425
- os.remove(h_graph)
426
- raise
427
- return h_graph, h_out_units_file, disambig_in_units_file_int
428
-
429
-
430
- def create_HLGa(
431
- kaldi_root: Path,
432
- fst_dir: Path,
433
- unique_label: str,
434
- h_graph: Path,
435
- lg_graph: Path,
436
- disambig_in_words_file_int: Path,
437
- ) -> Path:
438
- hlga_graph = fst_dir / f"HLGa.{unique_label}.fst"
439
-
440
- if not hlga_graph.exists():
441
- logger.info(f"Creating {hlga_graph}")
442
-
443
- fsttablecompose = kaldi_root / "src/fstbin/fsttablecompose"
444
- fstdeterminizestar = kaldi_root / "src/fstbin/fstdeterminizestar"
445
- fstrmsymbols = kaldi_root / "src/fstbin/fstrmsymbols"
446
- fstrmepslocal = kaldi_root / "src/fstbin/fstrmepslocal"
447
- fstminimizeencoded = kaldi_root / "src/fstbin/fstminimizeencoded"
448
-
449
- try:
450
- with open(hlga_graph, "wb") as out_f:
451
- res = subprocess.run(
452
- [
453
- fsttablecompose,
454
- h_graph,
455
- lg_graph,
456
- ],
457
- capture_output=True,
458
- check=True,
459
- )
460
- res = subprocess.run(
461
- [fstdeterminizestar, "--use-log=true"],
462
- input=res.stdout,
463
- capture_output=True,
464
- check=True,
465
- )
466
- res = subprocess.run(
467
- [fstrmsymbols, disambig_in_words_file_int],
468
- input=res.stdout,
469
- capture_output=True,
470
- check=True,
471
- )
472
- res = subprocess.run(
473
- [fstrmepslocal],
474
- input=res.stdout,
475
- capture_output=True,
476
- check=True,
477
- )
478
- res = subprocess.run(
479
- [fstminimizeencoded],
480
- input=res.stdout,
481
- capture_output=True,
482
- check=True,
483
- )
484
- out_f.write(res.stdout)
485
- except subprocess.CalledProcessError as e:
486
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
487
- os.remove(hlga_graph)
488
- raise
489
-
490
- return hlga_graph
491
-
492
-
493
- def create_HLa(
494
- kaldi_root: Path,
495
- fst_dir: Path,
496
- unique_label: str,
497
- h_graph: Path,
498
- l_graph: Path,
499
- disambig_in_words_file_int: Path,
500
- ) -> Path:
501
- hla_graph = fst_dir / f"HLa.{unique_label}.fst"
502
-
503
- if not hla_graph.exists():
504
- logger.info(f"Creating {hla_graph}")
505
-
506
- fsttablecompose = kaldi_root / "src/fstbin/fsttablecompose"
507
- fstdeterminizestar = kaldi_root / "src/fstbin/fstdeterminizestar"
508
- fstrmsymbols = kaldi_root / "src/fstbin/fstrmsymbols"
509
- fstrmepslocal = kaldi_root / "src/fstbin/fstrmepslocal"
510
- fstminimizeencoded = kaldi_root / "src/fstbin/fstminimizeencoded"
511
-
512
- try:
513
- with open(hla_graph, "wb") as out_f:
514
- res = subprocess.run(
515
- [
516
- fsttablecompose,
517
- h_graph,
518
- l_graph,
519
- ],
520
- capture_output=True,
521
- check=True,
522
- )
523
- res = subprocess.run(
524
- [fstdeterminizestar, "--use-log=true"],
525
- input=res.stdout,
526
- capture_output=True,
527
- check=True,
528
- )
529
- res = subprocess.run(
530
- [fstrmsymbols, disambig_in_words_file_int],
531
- input=res.stdout,
532
- capture_output=True,
533
- check=True,
534
- )
535
- res = subprocess.run(
536
- [fstrmepslocal],
537
- input=res.stdout,
538
- capture_output=True,
539
- check=True,
540
- )
541
- res = subprocess.run(
542
- [fstminimizeencoded],
543
- input=res.stdout,
544
- capture_output=True,
545
- check=True,
546
- )
547
- out_f.write(res.stdout)
548
- except subprocess.CalledProcessError as e:
549
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
550
- os.remove(hla_graph)
551
- raise
552
-
553
- return hla_graph
554
-
555
-
556
- def create_HLG(
557
- kaldi_root: Path,
558
- fst_dir: Path,
559
- unique_label: str,
560
- hlga_graph: Path,
561
- prefix: str = "HLG",
562
- ) -> Path:
563
- hlg_graph = fst_dir / f"{prefix}.{unique_label}.fst"
564
-
565
- if not hlg_graph.exists():
566
- logger.info(f"Creating {hlg_graph}")
567
-
568
- add_self_loop = script_dir / "add-self-loop-simple"
569
- kaldi_src = kaldi_root / "src"
570
- kaldi_lib = kaldi_src / "lib"
571
-
572
- try:
573
- if not add_self_loop.exists():
574
- fst_include = kaldi_root / "tools/openfst-1.6.7/include"
575
- add_self_loop_src = script_dir / "add-self-loop-simple.cc"
576
-
577
- subprocess.run(
578
- [
579
- "c++",
580
- f"-I{kaldi_src}",
581
- f"-I{fst_include}",
582
- f"-L{kaldi_lib}",
583
- add_self_loop_src,
584
- "-lkaldi-base",
585
- "-lkaldi-fstext",
586
- "-o",
587
- add_self_loop,
588
- ],
589
- check=True,
590
- )
591
-
592
- my_env = os.environ.copy()
593
- my_env["LD_LIBRARY_PATH"] = f"{kaldi_lib}:{my_env['LD_LIBRARY_PATH']}"
594
-
595
- subprocess.run(
596
- [
597
- add_self_loop,
598
- hlga_graph,
599
- hlg_graph,
600
- ],
601
- check=True,
602
- capture_output=True,
603
- env=my_env,
604
- )
605
- except subprocess.CalledProcessError as e:
606
- logger.error(f"cmd: {e.cmd}, err: {e.stderr.decode('utf-8')}")
607
- raise
608
-
609
- return hlg_graph
610
-
611
-
612
- def initalize_kaldi(cfg: KaldiInitializerConfig) -> Path:
613
- if cfg.fst_dir is None:
614
- cfg.fst_dir = osp.join(cfg.data_dir, "kaldi")
615
- if cfg.out_labels is None:
616
- cfg.out_labels = cfg.in_labels
617
-
618
- kaldi_root = Path(cfg.kaldi_root)
619
- data_dir = Path(cfg.data_dir)
620
- fst_dir = Path(cfg.fst_dir)
621
- fst_dir.mkdir(parents=True, exist_ok=True)
622
-
623
- arpa_base = osp.splitext(osp.basename(cfg.lm_arpa))[0]
624
- unique_label = f"{cfg.in_labels}.{arpa_base}"
625
-
626
- with open(data_dir / f"dict.{cfg.in_labels}.txt", "r") as f:
627
- vocab = Dictionary.load(f)
628
-
629
- in_units_file = create_units(fst_dir, cfg.in_labels, vocab)
630
-
631
- grammar_graph, out_words_file = create_G(
632
- kaldi_root, fst_dir, Path(cfg.lm_arpa), arpa_base
633
- )
634
-
635
- disambig_lexicon_file, disambig_L_in_units_file = create_lexicon(
636
- cfg, fst_dir, unique_label, in_units_file, out_words_file
637
- )
638
-
639
- h_graph, h_out_units_file, disambig_in_units_file_int = create_H(
640
- kaldi_root,
641
- fst_dir,
642
- disambig_L_in_units_file,
643
- cfg.in_labels,
644
- vocab,
645
- cfg.blank_symbol,
646
- cfg.silence_symbol,
647
- )
648
- lexicon_graph = create_L(
649
- kaldi_root,
650
- fst_dir,
651
- unique_label,
652
- disambig_lexicon_file,
653
- disambig_L_in_units_file,
654
- out_words_file,
655
- )
656
- lg_graph = create_LG(
657
- kaldi_root, fst_dir, unique_label, lexicon_graph, grammar_graph
658
- )
659
- hlga_graph = create_HLGa(
660
- kaldi_root, fst_dir, unique_label, h_graph, lg_graph, disambig_in_units_file_int
661
- )
662
- hlg_graph = create_HLG(kaldi_root, fst_dir, unique_label, hlga_graph)
663
-
664
- # for debugging
665
- # hla_graph = create_HLa(kaldi_root, fst_dir, unique_label, h_graph, lexicon_graph, disambig_in_units_file_int)
666
- # hl_graph = create_HLG(kaldi_root, fst_dir, unique_label, hla_graph, prefix="HL_looped")
667
- # create_HLG(kaldi_root, fst_dir, "phnc", h_graph, prefix="H_looped")
668
-
669
- return hlg_graph
670
-
671
-
672
- @hydra.main(config_path=config_path, config_name="kaldi_initializer")
673
- def cli_main(cfg: KaldiInitializerConfig) -> None:
674
- container = OmegaConf.to_container(cfg, resolve=True, enum_to_str=True)
675
- cfg = OmegaConf.create(container)
676
- OmegaConf.set_struct(cfg, True)
677
- initalize_kaldi(cfg)
678
-
679
-
680
- if __name__ == "__main__":
681
-
682
- logging.root.setLevel(logging.INFO)
683
- logging.basicConfig(level=logging.INFO)
684
-
685
- try:
686
- from hydra._internal.utils import ( # pylint: disable=import-outside-toplevel
687
- get_args,
688
- )
689
-
690
- cfg_name = get_args().config_name or "kaldi_initializer"
691
- except ImportError:
692
- logger.warning("Failed to get config name from hydra args")
693
- cfg_name = "kaldi_initializer"
694
-
695
- cs = ConfigStore.instance()
696
- cs.store(name=cfg_name, node=KaldiInitializerConfig)
697
-
698
- cli_main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/__init__.py DELETED
@@ -1,7 +0,0 @@
1
- import importlib
2
- import os
3
-
4
- for file in sorted(os.listdir(os.path.dirname(__file__))):
5
- if file.endswith(".py") and not file.startswith("_"):
6
- model_name = file[: file.find(".py")]
7
- importlib.import_module("examples.speech_recognition.models." + model_name)
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/vggtransformer.py DELETED
@@ -1,1020 +0,0 @@
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
- from collections.abc import Iterable
9
-
10
- import torch
11
- import torch.nn as nn
12
- from examples.speech_recognition.data.data_utils import lengths_to_encoder_padding_mask
13
- from fairseq import utils
14
- from fairseq.models import (
15
- FairseqEncoder,
16
- FairseqEncoderDecoderModel,
17
- FairseqEncoderModel,
18
- FairseqIncrementalDecoder,
19
- register_model,
20
- register_model_architecture,
21
- )
22
- from fairseq.modules import (
23
- LinearizedConvolution,
24
- TransformerDecoderLayer,
25
- TransformerEncoderLayer,
26
- VGGBlock,
27
- )
28
-
29
-
30
- @register_model("asr_vggtransformer")
31
- class VGGTransformerModel(FairseqEncoderDecoderModel):
32
- """
33
- Transformers with convolutional context for ASR
34
- https://arxiv.org/abs/1904.11660
35
- """
36
-
37
- def __init__(self, encoder, decoder):
38
- super().__init__(encoder, decoder)
39
-
40
- @staticmethod
41
- def add_args(parser):
42
- """Add model-specific arguments to the parser."""
43
- parser.add_argument(
44
- "--input-feat-per-channel",
45
- type=int,
46
- metavar="N",
47
- help="encoder input dimension per input channel",
48
- )
49
- parser.add_argument(
50
- "--vggblock-enc-config",
51
- type=str,
52
- metavar="EXPR",
53
- help="""
54
- an array of tuples each containing the configuration of one vggblock:
55
- [(out_channels,
56
- conv_kernel_size,
57
- pooling_kernel_size,
58
- num_conv_layers,
59
- use_layer_norm), ...])
60
- """,
61
- )
62
- parser.add_argument(
63
- "--transformer-enc-config",
64
- type=str,
65
- metavar="EXPR",
66
- help=""""
67
- a tuple containing the configuration of the encoder transformer layers
68
- configurations:
69
- [(input_dim,
70
- num_heads,
71
- ffn_dim,
72
- normalize_before,
73
- dropout,
74
- attention_dropout,
75
- relu_dropout), ...]')
76
- """,
77
- )
78
- parser.add_argument(
79
- "--enc-output-dim",
80
- type=int,
81
- metavar="N",
82
- help="""
83
- encoder output dimension, can be None. If specified, projecting the
84
- transformer output to the specified dimension""",
85
- )
86
- parser.add_argument(
87
- "--in-channels",
88
- type=int,
89
- metavar="N",
90
- help="number of encoder input channels",
91
- )
92
- parser.add_argument(
93
- "--tgt-embed-dim",
94
- type=int,
95
- metavar="N",
96
- help="embedding dimension of the decoder target tokens",
97
- )
98
- parser.add_argument(
99
- "--transformer-dec-config",
100
- type=str,
101
- metavar="EXPR",
102
- help="""
103
- a tuple containing the configuration of the decoder transformer layers
104
- configurations:
105
- [(input_dim,
106
- num_heads,
107
- ffn_dim,
108
- normalize_before,
109
- dropout,
110
- attention_dropout,
111
- relu_dropout), ...]
112
- """,
113
- )
114
- parser.add_argument(
115
- "--conv-dec-config",
116
- type=str,
117
- metavar="EXPR",
118
- help="""
119
- an array of tuples for the decoder 1-D convolution config
120
- [(out_channels, conv_kernel_size, use_layer_norm), ...]""",
121
- )
122
-
123
- @classmethod
124
- def build_encoder(cls, args, task):
125
- return VGGTransformerEncoder(
126
- input_feat_per_channel=args.input_feat_per_channel,
127
- vggblock_config=eval(args.vggblock_enc_config),
128
- transformer_config=eval(args.transformer_enc_config),
129
- encoder_output_dim=args.enc_output_dim,
130
- in_channels=args.in_channels,
131
- )
132
-
133
- @classmethod
134
- def build_decoder(cls, args, task):
135
- return TransformerDecoder(
136
- dictionary=task.target_dictionary,
137
- embed_dim=args.tgt_embed_dim,
138
- transformer_config=eval(args.transformer_dec_config),
139
- conv_config=eval(args.conv_dec_config),
140
- encoder_output_dim=args.enc_output_dim,
141
- )
142
-
143
- @classmethod
144
- def build_model(cls, args, task):
145
- """Build a new model instance."""
146
- # make sure that all args are properly defaulted
147
- # (in case there are any new ones)
148
- base_architecture(args)
149
-
150
- encoder = cls.build_encoder(args, task)
151
- decoder = cls.build_decoder(args, task)
152
- return cls(encoder, decoder)
153
-
154
- def get_normalized_probs(self, net_output, log_probs, sample=None):
155
- # net_output['encoder_out'] is a (B, T, D) tensor
156
- lprobs = super().get_normalized_probs(net_output, log_probs, sample)
157
- lprobs.batch_first = True
158
- return lprobs
159
-
160
-
161
- DEFAULT_ENC_VGGBLOCK_CONFIG = ((32, 3, 2, 2, False),) * 2
162
- DEFAULT_ENC_TRANSFORMER_CONFIG = ((256, 4, 1024, True, 0.2, 0.2, 0.2),) * 2
163
- # 256: embedding dimension
164
- # 4: number of heads
165
- # 1024: FFN
166
- # True: apply layerNorm before (dropout + resiaul) instead of after
167
- # 0.2 (dropout): dropout after MultiheadAttention and second FC
168
- # 0.2 (attention_dropout): dropout in MultiheadAttention
169
- # 0.2 (relu_dropout): dropout after ReLu
170
- DEFAULT_DEC_TRANSFORMER_CONFIG = ((256, 2, 1024, True, 0.2, 0.2, 0.2),) * 2
171
- DEFAULT_DEC_CONV_CONFIG = ((256, 3, True),) * 2
172
-
173
-
174
- # TODO: repace transformer encoder config from one liner
175
- # to explicit args to get rid of this transformation
176
- def prepare_transformer_encoder_params(
177
- input_dim,
178
- num_heads,
179
- ffn_dim,
180
- normalize_before,
181
- dropout,
182
- attention_dropout,
183
- relu_dropout,
184
- ):
185
- args = argparse.Namespace()
186
- args.encoder_embed_dim = input_dim
187
- args.encoder_attention_heads = num_heads
188
- args.attention_dropout = attention_dropout
189
- args.dropout = dropout
190
- args.activation_dropout = relu_dropout
191
- args.encoder_normalize_before = normalize_before
192
- args.encoder_ffn_embed_dim = ffn_dim
193
- return args
194
-
195
-
196
- def prepare_transformer_decoder_params(
197
- input_dim,
198
- num_heads,
199
- ffn_dim,
200
- normalize_before,
201
- dropout,
202
- attention_dropout,
203
- relu_dropout,
204
- ):
205
- args = argparse.Namespace()
206
- args.encoder_embed_dim = None
207
- args.decoder_embed_dim = input_dim
208
- args.decoder_attention_heads = num_heads
209
- args.attention_dropout = attention_dropout
210
- args.dropout = dropout
211
- args.activation_dropout = relu_dropout
212
- args.decoder_normalize_before = normalize_before
213
- args.decoder_ffn_embed_dim = ffn_dim
214
- return args
215
-
216
-
217
- class VGGTransformerEncoder(FairseqEncoder):
218
- """VGG + Transformer encoder"""
219
-
220
- def __init__(
221
- self,
222
- input_feat_per_channel,
223
- vggblock_config=DEFAULT_ENC_VGGBLOCK_CONFIG,
224
- transformer_config=DEFAULT_ENC_TRANSFORMER_CONFIG,
225
- encoder_output_dim=512,
226
- in_channels=1,
227
- transformer_context=None,
228
- transformer_sampling=None,
229
- ):
230
- """constructor for VGGTransformerEncoder
231
-
232
- Args:
233
- - input_feat_per_channel: feature dim (not including stacked,
234
- just base feature)
235
- - in_channel: # input channels (e.g., if stack 8 feature vector
236
- together, this is 8)
237
- - vggblock_config: configuration of vggblock, see comments on
238
- DEFAULT_ENC_VGGBLOCK_CONFIG
239
- - transformer_config: configuration of transformer layer, see comments
240
- on DEFAULT_ENC_TRANSFORMER_CONFIG
241
- - encoder_output_dim: final transformer output embedding dimension
242
- - transformer_context: (left, right) if set, self-attention will be focused
243
- on (t-left, t+right)
244
- - transformer_sampling: an iterable of int, must match with
245
- len(transformer_config), transformer_sampling[i] indicates sampling
246
- factor for i-th transformer layer, after multihead att and feedfoward
247
- part
248
- """
249
- super().__init__(None)
250
-
251
- self.num_vggblocks = 0
252
- if vggblock_config is not None:
253
- if not isinstance(vggblock_config, Iterable):
254
- raise ValueError("vggblock_config is not iterable")
255
- self.num_vggblocks = len(vggblock_config)
256
-
257
- self.conv_layers = nn.ModuleList()
258
- self.in_channels = in_channels
259
- self.input_dim = input_feat_per_channel
260
- self.pooling_kernel_sizes = []
261
-
262
- if vggblock_config is not None:
263
- for _, config in enumerate(vggblock_config):
264
- (
265
- out_channels,
266
- conv_kernel_size,
267
- pooling_kernel_size,
268
- num_conv_layers,
269
- layer_norm,
270
- ) = config
271
- self.conv_layers.append(
272
- VGGBlock(
273
- in_channels,
274
- out_channels,
275
- conv_kernel_size,
276
- pooling_kernel_size,
277
- num_conv_layers,
278
- input_dim=input_feat_per_channel,
279
- layer_norm=layer_norm,
280
- )
281
- )
282
- self.pooling_kernel_sizes.append(pooling_kernel_size)
283
- in_channels = out_channels
284
- input_feat_per_channel = self.conv_layers[-1].output_dim
285
-
286
- transformer_input_dim = self.infer_conv_output_dim(
287
- self.in_channels, self.input_dim
288
- )
289
- # transformer_input_dim is the output dimension of VGG part
290
-
291
- self.validate_transformer_config(transformer_config)
292
- self.transformer_context = self.parse_transformer_context(transformer_context)
293
- self.transformer_sampling = self.parse_transformer_sampling(
294
- transformer_sampling, len(transformer_config)
295
- )
296
-
297
- self.transformer_layers = nn.ModuleList()
298
-
299
- if transformer_input_dim != transformer_config[0][0]:
300
- self.transformer_layers.append(
301
- Linear(transformer_input_dim, transformer_config[0][0])
302
- )
303
- self.transformer_layers.append(
304
- TransformerEncoderLayer(
305
- prepare_transformer_encoder_params(*transformer_config[0])
306
- )
307
- )
308
-
309
- for i in range(1, len(transformer_config)):
310
- if transformer_config[i - 1][0] != transformer_config[i][0]:
311
- self.transformer_layers.append(
312
- Linear(transformer_config[i - 1][0], transformer_config[i][0])
313
- )
314
- self.transformer_layers.append(
315
- TransformerEncoderLayer(
316
- prepare_transformer_encoder_params(*transformer_config[i])
317
- )
318
- )
319
-
320
- self.encoder_output_dim = encoder_output_dim
321
- self.transformer_layers.extend(
322
- [
323
- Linear(transformer_config[-1][0], encoder_output_dim),
324
- LayerNorm(encoder_output_dim),
325
- ]
326
- )
327
-
328
- def forward(self, src_tokens, src_lengths, **kwargs):
329
- """
330
- src_tokens: padded tensor (B, T, C * feat)
331
- src_lengths: tensor of original lengths of input utterances (B,)
332
- """
333
- bsz, max_seq_len, _ = src_tokens.size()
334
- x = src_tokens.view(bsz, max_seq_len, self.in_channels, self.input_dim)
335
- x = x.transpose(1, 2).contiguous()
336
- # (B, C, T, feat)
337
-
338
- for layer_idx in range(len(self.conv_layers)):
339
- x = self.conv_layers[layer_idx](x)
340
-
341
- bsz, _, output_seq_len, _ = x.size()
342
-
343
- # (B, C, T, feat) -> (B, T, C, feat) -> (T, B, C, feat) -> (T, B, C * feat)
344
- x = x.transpose(1, 2).transpose(0, 1)
345
- x = x.contiguous().view(output_seq_len, bsz, -1)
346
-
347
- input_lengths = src_lengths.clone()
348
- for s in self.pooling_kernel_sizes:
349
- input_lengths = (input_lengths.float() / s).ceil().long()
350
-
351
- encoder_padding_mask, _ = lengths_to_encoder_padding_mask(
352
- input_lengths, batch_first=True
353
- )
354
- if not encoder_padding_mask.any():
355
- encoder_padding_mask = None
356
-
357
- subsampling_factor = int(max_seq_len * 1.0 / output_seq_len + 0.5)
358
- attn_mask = self.lengths_to_attn_mask(input_lengths, subsampling_factor)
359
-
360
- transformer_layer_idx = 0
361
-
362
- for layer_idx in range(len(self.transformer_layers)):
363
-
364
- if isinstance(self.transformer_layers[layer_idx], TransformerEncoderLayer):
365
- x = self.transformer_layers[layer_idx](
366
- x, encoder_padding_mask, attn_mask
367
- )
368
-
369
- if self.transformer_sampling[transformer_layer_idx] != 1:
370
- sampling_factor = self.transformer_sampling[transformer_layer_idx]
371
- x, encoder_padding_mask, attn_mask = self.slice(
372
- x, encoder_padding_mask, attn_mask, sampling_factor
373
- )
374
-
375
- transformer_layer_idx += 1
376
-
377
- else:
378
- x = self.transformer_layers[layer_idx](x)
379
-
380
- # encoder_padding_maks is a (T x B) tensor, its [t, b] elements indicate
381
- # whether encoder_output[t, b] is valid or not (valid=0, invalid=1)
382
-
383
- return {
384
- "encoder_out": x, # (T, B, C)
385
- "encoder_padding_mask": encoder_padding_mask.t()
386
- if encoder_padding_mask is not None
387
- else None,
388
- # (B, T) --> (T, B)
389
- }
390
-
391
- def infer_conv_output_dim(self, in_channels, input_dim):
392
- sample_seq_len = 200
393
- sample_bsz = 10
394
- x = torch.randn(sample_bsz, in_channels, sample_seq_len, input_dim)
395
- for i, _ in enumerate(self.conv_layers):
396
- x = self.conv_layers[i](x)
397
- x = x.transpose(1, 2)
398
- mb, seq = x.size()[:2]
399
- return x.contiguous().view(mb, seq, -1).size(-1)
400
-
401
- def validate_transformer_config(self, transformer_config):
402
- for config in transformer_config:
403
- input_dim, num_heads = config[:2]
404
- if input_dim % num_heads != 0:
405
- msg = (
406
- "ERROR in transformer config {}: ".format(config)
407
- + "input dimension {} ".format(input_dim)
408
- + "not dividable by number of heads {}".format(num_heads)
409
- )
410
- raise ValueError(msg)
411
-
412
- def parse_transformer_context(self, transformer_context):
413
- """
414
- transformer_context can be the following:
415
- - None; indicates no context is used, i.e.,
416
- transformer can access full context
417
- - a tuple/list of two int; indicates left and right context,
418
- any number <0 indicates infinite context
419
- * e.g., (5, 6) indicates that for query at x_t, transformer can
420
- access [t-5, t+6] (inclusive)
421
- * e.g., (-1, 6) indicates that for query at x_t, transformer can
422
- access [0, t+6] (inclusive)
423
- """
424
- if transformer_context is None:
425
- return None
426
-
427
- if not isinstance(transformer_context, Iterable):
428
- raise ValueError("transformer context must be Iterable if it is not None")
429
-
430
- if len(transformer_context) != 2:
431
- raise ValueError("transformer context must have length 2")
432
-
433
- left_context = transformer_context[0]
434
- if left_context < 0:
435
- left_context = None
436
-
437
- right_context = transformer_context[1]
438
- if right_context < 0:
439
- right_context = None
440
-
441
- if left_context is None and right_context is None:
442
- return None
443
-
444
- return (left_context, right_context)
445
-
446
- def parse_transformer_sampling(self, transformer_sampling, num_layers):
447
- """
448
- parsing transformer sampling configuration
449
-
450
- Args:
451
- - transformer_sampling, accepted input:
452
- * None, indicating no sampling
453
- * an Iterable with int (>0) as element
454
- - num_layers, expected number of transformer layers, must match with
455
- the length of transformer_sampling if it is not None
456
-
457
- Returns:
458
- - A tuple with length num_layers
459
- """
460
- if transformer_sampling is None:
461
- return (1,) * num_layers
462
-
463
- if not isinstance(transformer_sampling, Iterable):
464
- raise ValueError(
465
- "transformer_sampling must be an iterable if it is not None"
466
- )
467
-
468
- if len(transformer_sampling) != num_layers:
469
- raise ValueError(
470
- "transformer_sampling {} does not match with the number "
471
- "of layers {}".format(transformer_sampling, num_layers)
472
- )
473
-
474
- for layer, value in enumerate(transformer_sampling):
475
- if not isinstance(value, int):
476
- raise ValueError("Invalid value in transformer_sampling: ")
477
- if value < 1:
478
- raise ValueError(
479
- "{} layer's subsampling is {}.".format(layer, value)
480
- + " This is not allowed! "
481
- )
482
- return transformer_sampling
483
-
484
- def slice(self, embedding, padding_mask, attn_mask, sampling_factor):
485
- """
486
- embedding is a (T, B, D) tensor
487
- padding_mask is a (B, T) tensor or None
488
- attn_mask is a (T, T) tensor or None
489
- """
490
- embedding = embedding[::sampling_factor, :, :]
491
- if padding_mask is not None:
492
- padding_mask = padding_mask[:, ::sampling_factor]
493
- if attn_mask is not None:
494
- attn_mask = attn_mask[::sampling_factor, ::sampling_factor]
495
-
496
- return embedding, padding_mask, attn_mask
497
-
498
- def lengths_to_attn_mask(self, input_lengths, subsampling_factor=1):
499
- """
500
- create attention mask according to sequence lengths and transformer
501
- context
502
-
503
- Args:
504
- - input_lengths: (B, )-shape Int/Long tensor; input_lengths[b] is
505
- the length of b-th sequence
506
- - subsampling_factor: int
507
- * Note that the left_context and right_context is specified in
508
- the input frame-level while input to transformer may already
509
- go through subsampling (e.g., the use of striding in vggblock)
510
- we use subsampling_factor to scale the left/right context
511
-
512
- Return:
513
- - a (T, T) binary tensor or None, where T is max(input_lengths)
514
- * if self.transformer_context is None, None
515
- * if left_context is None,
516
- * attn_mask[t, t + right_context + 1:] = 1
517
- * others = 0
518
- * if right_context is None,
519
- * attn_mask[t, 0:t - left_context] = 1
520
- * others = 0
521
- * elsif
522
- * attn_mask[t, t - left_context: t + right_context + 1] = 0
523
- * others = 1
524
- """
525
- if self.transformer_context is None:
526
- return None
527
-
528
- maxT = torch.max(input_lengths).item()
529
- attn_mask = torch.zeros(maxT, maxT)
530
-
531
- left_context = self.transformer_context[0]
532
- right_context = self.transformer_context[1]
533
- if left_context is not None:
534
- left_context = math.ceil(self.transformer_context[0] / subsampling_factor)
535
- if right_context is not None:
536
- right_context = math.ceil(self.transformer_context[1] / subsampling_factor)
537
-
538
- for t in range(maxT):
539
- if left_context is not None:
540
- st = 0
541
- en = max(st, t - left_context)
542
- attn_mask[t, st:en] = 1
543
- if right_context is not None:
544
- st = t + right_context + 1
545
- st = min(st, maxT - 1)
546
- attn_mask[t, st:] = 1
547
-
548
- return attn_mask.to(input_lengths.device)
549
-
550
- def reorder_encoder_out(self, encoder_out, new_order):
551
- encoder_out["encoder_out"] = encoder_out["encoder_out"].index_select(
552
- 1, new_order
553
- )
554
- if encoder_out["encoder_padding_mask"] is not None:
555
- encoder_out["encoder_padding_mask"] = encoder_out[
556
- "encoder_padding_mask"
557
- ].index_select(1, new_order)
558
- return encoder_out
559
-
560
-
561
- class TransformerDecoder(FairseqIncrementalDecoder):
562
- """
563
- Transformer decoder consisting of *args.decoder_layers* layers. Each layer
564
- is a :class:`TransformerDecoderLayer`.
565
- Args:
566
- args (argparse.Namespace): parsed command-line arguments
567
- dictionary (~fairseq.data.Dictionary): decoding dictionary
568
- embed_tokens (torch.nn.Embedding): output embedding
569
- no_encoder_attn (bool, optional): whether to attend to encoder outputs.
570
- Default: ``False``
571
- left_pad (bool, optional): whether the input is left-padded. Default:
572
- ``False``
573
- """
574
-
575
- def __init__(
576
- self,
577
- dictionary,
578
- embed_dim=512,
579
- transformer_config=DEFAULT_ENC_TRANSFORMER_CONFIG,
580
- conv_config=DEFAULT_DEC_CONV_CONFIG,
581
- encoder_output_dim=512,
582
- ):
583
-
584
- super().__init__(dictionary)
585
- vocab_size = len(dictionary)
586
- self.padding_idx = dictionary.pad()
587
- self.embed_tokens = Embedding(vocab_size, embed_dim, self.padding_idx)
588
-
589
- self.conv_layers = nn.ModuleList()
590
- for i in range(len(conv_config)):
591
- out_channels, kernel_size, layer_norm = conv_config[i]
592
- if i == 0:
593
- conv_layer = LinearizedConv1d(
594
- embed_dim, out_channels, kernel_size, padding=kernel_size - 1
595
- )
596
- else:
597
- conv_layer = LinearizedConv1d(
598
- conv_config[i - 1][0],
599
- out_channels,
600
- kernel_size,
601
- padding=kernel_size - 1,
602
- )
603
- self.conv_layers.append(conv_layer)
604
- if layer_norm:
605
- self.conv_layers.append(nn.LayerNorm(out_channels))
606
- self.conv_layers.append(nn.ReLU())
607
-
608
- self.layers = nn.ModuleList()
609
- if conv_config[-1][0] != transformer_config[0][0]:
610
- self.layers.append(Linear(conv_config[-1][0], transformer_config[0][0]))
611
- self.layers.append(
612
- TransformerDecoderLayer(
613
- prepare_transformer_decoder_params(*transformer_config[0])
614
- )
615
- )
616
-
617
- for i in range(1, len(transformer_config)):
618
- if transformer_config[i - 1][0] != transformer_config[i][0]:
619
- self.layers.append(
620
- Linear(transformer_config[i - 1][0], transformer_config[i][0])
621
- )
622
- self.layers.append(
623
- TransformerDecoderLayer(
624
- prepare_transformer_decoder_params(*transformer_config[i])
625
- )
626
- )
627
- self.fc_out = Linear(transformer_config[-1][0], vocab_size)
628
-
629
- def forward(self, prev_output_tokens, encoder_out=None, incremental_state=None):
630
- """
631
- Args:
632
- prev_output_tokens (LongTensor): previous decoder outputs of shape
633
- `(batch, tgt_len)`, for input feeding/teacher forcing
634
- encoder_out (Tensor, optional): output from the encoder, used for
635
- encoder-side attention
636
- incremental_state (dict): dictionary used for storing state during
637
- :ref:`Incremental decoding`
638
- Returns:
639
- tuple:
640
- - the last decoder layer's output of shape `(batch, tgt_len,
641
- vocab)`
642
- - the last decoder layer's attention weights of shape `(batch,
643
- tgt_len, src_len)`
644
- """
645
- target_padding_mask = (
646
- (prev_output_tokens == self.padding_idx).to(prev_output_tokens.device)
647
- if incremental_state is None
648
- else None
649
- )
650
-
651
- if incremental_state is not None:
652
- prev_output_tokens = prev_output_tokens[:, -1:]
653
-
654
- # embed tokens
655
- x = self.embed_tokens(prev_output_tokens)
656
-
657
- # B x T x C -> T x B x C
658
- x = self._transpose_if_training(x, incremental_state)
659
-
660
- for layer in self.conv_layers:
661
- if isinstance(layer, LinearizedConvolution):
662
- x = layer(x, incremental_state)
663
- else:
664
- x = layer(x)
665
-
666
- # B x T x C -> T x B x C
667
- x = self._transpose_if_inference(x, incremental_state)
668
-
669
- # decoder layers
670
- for layer in self.layers:
671
- if isinstance(layer, TransformerDecoderLayer):
672
- x, *_ = layer(
673
- x,
674
- (encoder_out["encoder_out"] if encoder_out is not None else None),
675
- (
676
- encoder_out["encoder_padding_mask"].t()
677
- if encoder_out["encoder_padding_mask"] is not None
678
- else None
679
- ),
680
- incremental_state,
681
- self_attn_mask=(
682
- self.buffered_future_mask(x)
683
- if incremental_state is None
684
- else None
685
- ),
686
- self_attn_padding_mask=(
687
- target_padding_mask if incremental_state is None else None
688
- ),
689
- )
690
- else:
691
- x = layer(x)
692
-
693
- # T x B x C -> B x T x C
694
- x = x.transpose(0, 1)
695
-
696
- x = self.fc_out(x)
697
-
698
- return x, None
699
-
700
- def buffered_future_mask(self, tensor):
701
- dim = tensor.size(0)
702
- if (
703
- not hasattr(self, "_future_mask")
704
- or self._future_mask is None
705
- or self._future_mask.device != tensor.device
706
- ):
707
- self._future_mask = torch.triu(
708
- utils.fill_with_neg_inf(tensor.new(dim, dim)), 1
709
- )
710
- if self._future_mask.size(0) < dim:
711
- self._future_mask = torch.triu(
712
- utils.fill_with_neg_inf(self._future_mask.resize_(dim, dim)), 1
713
- )
714
- return self._future_mask[:dim, :dim]
715
-
716
- def _transpose_if_training(self, x, incremental_state):
717
- if incremental_state is None:
718
- x = x.transpose(0, 1)
719
- return x
720
-
721
- def _transpose_if_inference(self, x, incremental_state):
722
- if incremental_state:
723
- x = x.transpose(0, 1)
724
- return x
725
-
726
-
727
- @register_model("asr_vggtransformer_encoder")
728
- class VGGTransformerEncoderModel(FairseqEncoderModel):
729
- def __init__(self, encoder):
730
- super().__init__(encoder)
731
-
732
- @staticmethod
733
- def add_args(parser):
734
- """Add model-specific arguments to the parser."""
735
- parser.add_argument(
736
- "--input-feat-per-channel",
737
- type=int,
738
- metavar="N",
739
- help="encoder input dimension per input channel",
740
- )
741
- parser.add_argument(
742
- "--vggblock-enc-config",
743
- type=str,
744
- metavar="EXPR",
745
- help="""
746
- an array of tuples each containing the configuration of one vggblock
747
- [(out_channels, conv_kernel_size, pooling_kernel_size,num_conv_layers), ...]
748
- """,
749
- )
750
- parser.add_argument(
751
- "--transformer-enc-config",
752
- type=str,
753
- metavar="EXPR",
754
- help="""
755
- a tuple containing the configuration of the Transformer layers
756
- configurations:
757
- [(input_dim,
758
- num_heads,
759
- ffn_dim,
760
- normalize_before,
761
- dropout,
762
- attention_dropout,
763
- relu_dropout), ]""",
764
- )
765
- parser.add_argument(
766
- "--enc-output-dim",
767
- type=int,
768
- metavar="N",
769
- help="encoder output dimension, projecting the LSTM output",
770
- )
771
- parser.add_argument(
772
- "--in-channels",
773
- type=int,
774
- metavar="N",
775
- help="number of encoder input channels",
776
- )
777
- parser.add_argument(
778
- "--transformer-context",
779
- type=str,
780
- metavar="EXPR",
781
- help="""
782
- either None or a tuple of two ints, indicating left/right context a
783
- transformer can have access to""",
784
- )
785
- parser.add_argument(
786
- "--transformer-sampling",
787
- type=str,
788
- metavar="EXPR",
789
- help="""
790
- either None or a tuple of ints, indicating sampling factor in each layer""",
791
- )
792
-
793
- @classmethod
794
- def build_model(cls, args, task):
795
- """Build a new model instance."""
796
- base_architecture_enconly(args)
797
- encoder = VGGTransformerEncoderOnly(
798
- vocab_size=len(task.target_dictionary),
799
- input_feat_per_channel=args.input_feat_per_channel,
800
- vggblock_config=eval(args.vggblock_enc_config),
801
- transformer_config=eval(args.transformer_enc_config),
802
- encoder_output_dim=args.enc_output_dim,
803
- in_channels=args.in_channels,
804
- transformer_context=eval(args.transformer_context),
805
- transformer_sampling=eval(args.transformer_sampling),
806
- )
807
- return cls(encoder)
808
-
809
- def get_normalized_probs(self, net_output, log_probs, sample=None):
810
- # net_output['encoder_out'] is a (T, B, D) tensor
811
- lprobs = super().get_normalized_probs(net_output, log_probs, sample)
812
- # lprobs is a (T, B, D) tensor
813
- # we need to transoose to get (B, T, D) tensor
814
- lprobs = lprobs.transpose(0, 1).contiguous()
815
- lprobs.batch_first = True
816
- return lprobs
817
-
818
-
819
- class VGGTransformerEncoderOnly(VGGTransformerEncoder):
820
- def __init__(
821
- self,
822
- vocab_size,
823
- input_feat_per_channel,
824
- vggblock_config=DEFAULT_ENC_VGGBLOCK_CONFIG,
825
- transformer_config=DEFAULT_ENC_TRANSFORMER_CONFIG,
826
- encoder_output_dim=512,
827
- in_channels=1,
828
- transformer_context=None,
829
- transformer_sampling=None,
830
- ):
831
- super().__init__(
832
- input_feat_per_channel=input_feat_per_channel,
833
- vggblock_config=vggblock_config,
834
- transformer_config=transformer_config,
835
- encoder_output_dim=encoder_output_dim,
836
- in_channels=in_channels,
837
- transformer_context=transformer_context,
838
- transformer_sampling=transformer_sampling,
839
- )
840
- self.fc_out = Linear(self.encoder_output_dim, vocab_size)
841
-
842
- def forward(self, src_tokens, src_lengths, **kwargs):
843
- """
844
- src_tokens: padded tensor (B, T, C * feat)
845
- src_lengths: tensor of original lengths of input utterances (B,)
846
- """
847
-
848
- enc_out = super().forward(src_tokens, src_lengths)
849
- x = self.fc_out(enc_out["encoder_out"])
850
- # x = F.log_softmax(x, dim=-1)
851
- # Note: no need this line, because model.get_normalized_prob will call
852
- # log_softmax
853
- return {
854
- "encoder_out": x, # (T, B, C)
855
- "encoder_padding_mask": enc_out["encoder_padding_mask"], # (T, B)
856
- }
857
-
858
- def max_positions(self):
859
- """Maximum input length supported by the encoder."""
860
- return (1e6, 1e6) # an arbitrary large number
861
-
862
-
863
- def Embedding(num_embeddings, embedding_dim, padding_idx):
864
- m = nn.Embedding(num_embeddings, embedding_dim, padding_idx=padding_idx)
865
- # nn.init.uniform_(m.weight, -0.1, 0.1)
866
- # nn.init.constant_(m.weight[padding_idx], 0)
867
- return m
868
-
869
-
870
- def Linear(in_features, out_features, bias=True, dropout=0):
871
- """Linear layer (input: N x T x C)"""
872
- m = nn.Linear(in_features, out_features, bias=bias)
873
- # m.weight.data.uniform_(-0.1, 0.1)
874
- # if bias:
875
- # m.bias.data.uniform_(-0.1, 0.1)
876
- return m
877
-
878
-
879
- def LinearizedConv1d(in_channels, out_channels, kernel_size, dropout=0, **kwargs):
880
- """Weight-normalized Conv1d layer optimized for decoding"""
881
- m = LinearizedConvolution(in_channels, out_channels, kernel_size, **kwargs)
882
- std = math.sqrt((4 * (1.0 - dropout)) / (m.kernel_size[0] * in_channels))
883
- nn.init.normal_(m.weight, mean=0, std=std)
884
- nn.init.constant_(m.bias, 0)
885
- return nn.utils.weight_norm(m, dim=2)
886
-
887
-
888
- def LayerNorm(embedding_dim):
889
- m = nn.LayerNorm(embedding_dim)
890
- return m
891
-
892
-
893
- # seq2seq models
894
- def base_architecture(args):
895
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 40)
896
- args.vggblock_enc_config = getattr(
897
- args, "vggblock_enc_config", DEFAULT_ENC_VGGBLOCK_CONFIG
898
- )
899
- args.transformer_enc_config = getattr(
900
- args, "transformer_enc_config", DEFAULT_ENC_TRANSFORMER_CONFIG
901
- )
902
- args.enc_output_dim = getattr(args, "enc_output_dim", 512)
903
- args.in_channels = getattr(args, "in_channels", 1)
904
- args.tgt_embed_dim = getattr(args, "tgt_embed_dim", 128)
905
- args.transformer_dec_config = getattr(
906
- args, "transformer_dec_config", DEFAULT_ENC_TRANSFORMER_CONFIG
907
- )
908
- args.conv_dec_config = getattr(args, "conv_dec_config", DEFAULT_DEC_CONV_CONFIG)
909
- args.transformer_context = getattr(args, "transformer_context", "None")
910
-
911
-
912
- @register_model_architecture("asr_vggtransformer", "vggtransformer_1")
913
- def vggtransformer_1(args):
914
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
915
- args.vggblock_enc_config = getattr(
916
- args, "vggblock_enc_config", "[(64, 3, 2, 2, True), (128, 3, 2, 2, True)]"
917
- )
918
- args.transformer_enc_config = getattr(
919
- args,
920
- "transformer_enc_config",
921
- "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 14",
922
- )
923
- args.enc_output_dim = getattr(args, "enc_output_dim", 1024)
924
- args.tgt_embed_dim = getattr(args, "tgt_embed_dim", 128)
925
- args.conv_dec_config = getattr(args, "conv_dec_config", "((256, 3, True),) * 4")
926
- args.transformer_dec_config = getattr(
927
- args,
928
- "transformer_dec_config",
929
- "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 4",
930
- )
931
-
932
-
933
- @register_model_architecture("asr_vggtransformer", "vggtransformer_2")
934
- def vggtransformer_2(args):
935
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
936
- args.vggblock_enc_config = getattr(
937
- args, "vggblock_enc_config", "[(64, 3, 2, 2, True), (128, 3, 2, 2, True)]"
938
- )
939
- args.transformer_enc_config = getattr(
940
- args,
941
- "transformer_enc_config",
942
- "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 16",
943
- )
944
- args.enc_output_dim = getattr(args, "enc_output_dim", 1024)
945
- args.tgt_embed_dim = getattr(args, "tgt_embed_dim", 512)
946
- args.conv_dec_config = getattr(args, "conv_dec_config", "((256, 3, True),) * 4")
947
- args.transformer_dec_config = getattr(
948
- args,
949
- "transformer_dec_config",
950
- "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 6",
951
- )
952
-
953
-
954
- @register_model_architecture("asr_vggtransformer", "vggtransformer_base")
955
- def vggtransformer_base(args):
956
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
957
- args.vggblock_enc_config = getattr(
958
- args, "vggblock_enc_config", "[(64, 3, 2, 2, True), (128, 3, 2, 2, True)]"
959
- )
960
- args.transformer_enc_config = getattr(
961
- args, "transformer_enc_config", "((512, 8, 2048, True, 0.15, 0.15, 0.15),) * 12"
962
- )
963
-
964
- args.enc_output_dim = getattr(args, "enc_output_dim", 512)
965
- args.tgt_embed_dim = getattr(args, "tgt_embed_dim", 512)
966
- args.conv_dec_config = getattr(args, "conv_dec_config", "((256, 3, True),) * 4")
967
- args.transformer_dec_config = getattr(
968
- args, "transformer_dec_config", "((512, 8, 2048, True, 0.15, 0.15, 0.15),) * 6"
969
- )
970
- # Size estimations:
971
- # Encoder:
972
- # - vggblock param: 64*1*3*3 + 64*64*3*3 + 128*64*3*3 + 128*128*3 = 258K
973
- # Transformer:
974
- # - input dimension adapter: 2560 x 512 -> 1.31M
975
- # - transformer_layers (x12) --> 37.74M
976
- # * MultiheadAttention: 512*512*3 (in_proj) + 512*512 (out_proj) = 1.048M
977
- # * FFN weight: 512*2048*2 = 2.097M
978
- # - output dimension adapter: 512 x 512 -> 0.26 M
979
- # Decoder:
980
- # - LinearizedConv1d: 512 * 256 * 3 + 256 * 256 * 3 * 3
981
- # - transformer_layer: (x6) --> 25.16M
982
- # * MultiheadAttention (self-attention): 512*512*3 + 512*512 = 1.048M
983
- # * MultiheadAttention (encoder-attention): 512*512*3 + 512*512 = 1.048M
984
- # * FFN: 512*2048*2 = 2.097M
985
- # Final FC:
986
- # - FC: 512*5000 = 256K (assuming vocab size 5K)
987
- # In total:
988
- # ~65 M
989
-
990
-
991
- # CTC models
992
- def base_architecture_enconly(args):
993
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 40)
994
- args.vggblock_enc_config = getattr(
995
- args, "vggblock_enc_config", "[(32, 3, 2, 2, True)] * 2"
996
- )
997
- args.transformer_enc_config = getattr(
998
- args, "transformer_enc_config", "((256, 4, 1024, True, 0.2, 0.2, 0.2),) * 2"
999
- )
1000
- args.enc_output_dim = getattr(args, "enc_output_dim", 512)
1001
- args.in_channels = getattr(args, "in_channels", 1)
1002
- args.transformer_context = getattr(args, "transformer_context", "None")
1003
- args.transformer_sampling = getattr(args, "transformer_sampling", "None")
1004
-
1005
-
1006
- @register_model_architecture("asr_vggtransformer_encoder", "vggtransformer_enc_1")
1007
- def vggtransformer_enc_1(args):
1008
- # vggtransformer_1 is the same as vggtransformer_enc_big, except the number
1009
- # of layers is increased to 16
1010
- # keep it here for backward compatiablity purpose
1011
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
1012
- args.vggblock_enc_config = getattr(
1013
- args, "vggblock_enc_config", "[(64, 3, 2, 2, True), (128, 3, 2, 2, True)]"
1014
- )
1015
- args.transformer_enc_config = getattr(
1016
- args,
1017
- "transformer_enc_config",
1018
- "((1024, 16, 4096, True, 0.15, 0.15, 0.15),) * 16",
1019
- )
1020
- args.enc_output_dim = getattr(args, "enc_output_dim", 1024)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/models/w2l_conv_glu_enc.py DELETED
@@ -1,176 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- import math
9
-
10
- import torch
11
- import torch.nn as nn
12
- import torch.nn.functional as F
13
- from fairseq.models import (
14
- FairseqEncoder,
15
- FairseqEncoderModel,
16
- register_model,
17
- register_model_architecture,
18
- )
19
- from fairseq.modules.fairseq_dropout import FairseqDropout
20
-
21
- default_conv_enc_config = """[
22
- (400, 13, 170, 0.2),
23
- (440, 14, 0, 0.214),
24
- (484, 15, 0, 0.22898),
25
- (532, 16, 0, 0.2450086),
26
- (584, 17, 0, 0.262159202),
27
- (642, 18, 0, 0.28051034614),
28
- (706, 19, 0, 0.30014607037),
29
- (776, 20, 0, 0.321156295296),
30
- (852, 21, 0, 0.343637235966),
31
- (936, 22, 0, 0.367691842484),
32
- (1028, 23, 0, 0.393430271458),
33
- (1130, 24, 0, 0.42097039046),
34
- (1242, 25, 0, 0.450438317792),
35
- (1366, 26, 0, 0.481969000038),
36
- (1502, 27, 0, 0.51570683004),
37
- (1652, 28, 0, 0.551806308143),
38
- (1816, 29, 0, 0.590432749713),
39
- ]"""
40
-
41
-
42
- @register_model("asr_w2l_conv_glu_encoder")
43
- class W2lConvGluEncoderModel(FairseqEncoderModel):
44
- def __init__(self, encoder):
45
- super().__init__(encoder)
46
-
47
- @staticmethod
48
- def add_args(parser):
49
- """Add model-specific arguments to the parser."""
50
- parser.add_argument(
51
- "--input-feat-per-channel",
52
- type=int,
53
- metavar="N",
54
- help="encoder input dimension per input channel",
55
- )
56
- parser.add_argument(
57
- "--in-channels",
58
- type=int,
59
- metavar="N",
60
- help="number of encoder input channels",
61
- )
62
- parser.add_argument(
63
- "--conv-enc-config",
64
- type=str,
65
- metavar="EXPR",
66
- help="""
67
- an array of tuples each containing the configuration of one conv layer
68
- [(out_channels, kernel_size, padding, dropout), ...]
69
- """,
70
- )
71
-
72
- @classmethod
73
- def build_model(cls, args, task):
74
- """Build a new model instance."""
75
- conv_enc_config = getattr(args, "conv_enc_config", default_conv_enc_config)
76
- encoder = W2lConvGluEncoder(
77
- vocab_size=len(task.target_dictionary),
78
- input_feat_per_channel=args.input_feat_per_channel,
79
- in_channels=args.in_channels,
80
- conv_enc_config=eval(conv_enc_config),
81
- )
82
- return cls(encoder)
83
-
84
- def get_normalized_probs(self, net_output, log_probs, sample=None):
85
- lprobs = super().get_normalized_probs(net_output, log_probs, sample)
86
- lprobs.batch_first = False
87
- return lprobs
88
-
89
-
90
- class W2lConvGluEncoder(FairseqEncoder):
91
- def __init__(
92
- self, vocab_size, input_feat_per_channel, in_channels, conv_enc_config
93
- ):
94
- super().__init__(None)
95
-
96
- self.input_dim = input_feat_per_channel
97
- if in_channels != 1:
98
- raise ValueError("only 1 input channel is currently supported")
99
-
100
- self.conv_layers = nn.ModuleList()
101
- self.linear_layers = nn.ModuleList()
102
- self.dropouts = []
103
- cur_channels = input_feat_per_channel
104
-
105
- for out_channels, kernel_size, padding, dropout in conv_enc_config:
106
- layer = nn.Conv1d(cur_channels, out_channels, kernel_size, padding=padding)
107
- layer.weight.data.mul_(math.sqrt(3)) # match wav2letter init
108
- self.conv_layers.append(nn.utils.weight_norm(layer))
109
- self.dropouts.append(
110
- FairseqDropout(dropout, module_name=self.__class__.__name__)
111
- )
112
- if out_channels % 2 != 0:
113
- raise ValueError("odd # of out_channels is incompatible with GLU")
114
- cur_channels = out_channels // 2 # halved by GLU
115
-
116
- for out_channels in [2 * cur_channels, vocab_size]:
117
- layer = nn.Linear(cur_channels, out_channels)
118
- layer.weight.data.mul_(math.sqrt(3))
119
- self.linear_layers.append(nn.utils.weight_norm(layer))
120
- cur_channels = out_channels // 2
121
-
122
- def forward(self, src_tokens, src_lengths, **kwargs):
123
-
124
- """
125
- src_tokens: padded tensor (B, T, C * feat)
126
- src_lengths: tensor of original lengths of input utterances (B,)
127
- """
128
- B, T, _ = src_tokens.size()
129
- x = src_tokens.transpose(1, 2).contiguous() # (B, feat, T) assuming C == 1
130
-
131
- for layer_idx in range(len(self.conv_layers)):
132
- x = self.conv_layers[layer_idx](x)
133
- x = F.glu(x, dim=1)
134
- x = self.dropouts[layer_idx](x)
135
-
136
- x = x.transpose(1, 2).contiguous() # (B, T, 908)
137
- x = self.linear_layers[0](x)
138
- x = F.glu(x, dim=2)
139
- x = self.dropouts[-1](x)
140
- x = self.linear_layers[1](x)
141
-
142
- assert x.size(0) == B
143
- assert x.size(1) == T
144
-
145
- encoder_out = x.transpose(0, 1) # (T, B, vocab_size)
146
-
147
- # need to debug this -- find a simpler/elegant way in pytorch APIs
148
- encoder_padding_mask = (
149
- torch.arange(T).view(1, T).expand(B, -1).to(x.device)
150
- >= src_lengths.view(B, 1).expand(-1, T)
151
- ).t() # (B x T) -> (T x B)
152
-
153
- return {
154
- "encoder_out": encoder_out, # (T, B, vocab_size)
155
- "encoder_padding_mask": encoder_padding_mask, # (T, B)
156
- }
157
-
158
- def reorder_encoder_out(self, encoder_out, new_order):
159
- encoder_out["encoder_out"] = encoder_out["encoder_out"].index_select(
160
- 1, new_order
161
- )
162
- encoder_out["encoder_padding_mask"] = encoder_out[
163
- "encoder_padding_mask"
164
- ].index_select(1, new_order)
165
- return encoder_out
166
-
167
- def max_positions(self):
168
- """Maximum input length supported by the encoder."""
169
- return (1e6, 1e6) # an arbitrary large number
170
-
171
-
172
- @register_model_architecture("asr_w2l_conv_glu_encoder", "w2l_conv_glu_enc")
173
- def w2l_conv_glu_enc(args):
174
- args.input_feat_per_channel = getattr(args, "input_feat_per_channel", 80)
175
- args.in_channels = getattr(args, "in_channels", 1)
176
- args.conv_enc_config = getattr(args, "conv_enc_config", default_conv_enc_config)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/README.md DELETED
@@ -1,43 +0,0 @@
1
- # Flashlight Decoder
2
-
3
- This script runs decoding for pre-trained speech recognition models.
4
-
5
- ## Usage
6
-
7
- Assuming a few variables:
8
-
9
- ```bash
10
- checkpoint=<path-to-checkpoint>
11
- data=<path-to-data-directory>
12
- lm_model=<path-to-language-model>
13
- lexicon=<path-to-lexicon>
14
- ```
15
-
16
- Example usage for decoding a fine-tuned Wav2Vec model:
17
-
18
- ```bash
19
- python $FAIRSEQ_ROOT/examples/speech_recognition/new/infer.py --multirun \
20
- task=audio_pretraining \
21
- task.data=$data \
22
- task.labels=ltr \
23
- common_eval.path=$checkpoint \
24
- decoding.type=kenlm \
25
- decoding.lexicon=$lexicon \
26
- decoding.lmpath=$lm_model \
27
- dataset.gen_subset=dev_clean,dev_other,test_clean,test_other
28
- ```
29
-
30
- Example usage for using Ax to sweep WER parameters (requires `pip install hydra-ax-sweeper`):
31
-
32
- ```bash
33
- python $FAIRSEQ_ROOT/examples/speech_recognition/new/infer.py --multirun \
34
- hydra/sweeper=ax \
35
- task=audio_pretraining \
36
- task.data=$data \
37
- task.labels=ltr \
38
- common_eval.path=$checkpoint \
39
- decoding.type=kenlm \
40
- decoding.lexicon=$lexicon \
41
- decoding.lmpath=$lm_model \
42
- dataset.gen_subset=dev_other
43
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/__init__.py DELETED
File without changes
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/conf/hydra/sweeper/ax.yaml DELETED
@@ -1,29 +0,0 @@
1
- # @package hydra.sweeper
2
- _target_: hydra_plugins.hydra_ax_sweeper.ax_sweeper.AxSweeper
3
- max_batch_size: null
4
- ax_config:
5
- max_trials: 128
6
- early_stop:
7
- minimize: true
8
- max_epochs_without_improvement: 10
9
- epsilon: 0.025
10
- experiment:
11
- name: ${dataset.gen_subset}
12
- objective_name: wer
13
- minimize: true
14
- parameter_constraints: null
15
- outcome_constraints: null
16
- status_quo: null
17
- client:
18
- verbose_logging: false
19
- random_seed: null
20
- params:
21
- decoding.lmweight:
22
- type: range
23
- bounds: [0.0, 5.0]
24
- decoding.wordscore:
25
- type: range
26
- bounds: [-5.0, 5.0]
27
- decoding.silweight:
28
- type: range
29
- bounds: [ -8.0, 0.0 ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/conf/infer.yaml DELETED
@@ -1,25 +0,0 @@
1
- # @package _group_
2
-
3
- defaults:
4
- - task: null
5
- - model: null
6
-
7
- hydra:
8
- run:
9
- dir: ${common_eval.results_path}/${dataset.gen_subset}
10
- sweep:
11
- dir: /checkpoint/${env:USER}/${env:PREFIX}/${common_eval.results_path}
12
- subdir: ${dataset.gen_subset}
13
- common_eval:
14
- results_path: null
15
- path: null
16
- post_process: letter
17
- quiet: true
18
- dataset:
19
- max_tokens: 3000000
20
- gen_subset: test
21
- distributed_training:
22
- distributed_world_size: 1
23
- decoding:
24
- beam: 5
25
- type: viterbi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/__init__.py DELETED
File without changes
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/base_decoder.py DELETED
@@ -1,62 +0,0 @@
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 itertools as it
7
- from typing import Any, Dict, List
8
-
9
- import torch
10
- from fairseq.data.dictionary import Dictionary
11
- from fairseq.models.fairseq_model import FairseqModel
12
-
13
-
14
- class BaseDecoder:
15
- def __init__(self, tgt_dict: Dictionary) -> None:
16
- self.tgt_dict = tgt_dict
17
- self.vocab_size = len(tgt_dict)
18
-
19
- self.blank = (
20
- tgt_dict.index("<ctc_blank>")
21
- if "<ctc_blank>" in tgt_dict.indices
22
- else tgt_dict.bos()
23
- )
24
- if "<sep>" in tgt_dict.indices:
25
- self.silence = tgt_dict.index("<sep>")
26
- elif "|" in tgt_dict.indices:
27
- self.silence = tgt_dict.index("|")
28
- else:
29
- self.silence = tgt_dict.eos()
30
-
31
- def generate(
32
- self, models: List[FairseqModel], sample: Dict[str, Any], **unused
33
- ) -> List[List[Dict[str, torch.LongTensor]]]:
34
- encoder_input = {
35
- k: v for k, v in sample["net_input"].items() if k != "prev_output_tokens"
36
- }
37
- emissions = self.get_emissions(models, encoder_input)
38
- return self.decode(emissions)
39
-
40
- def get_emissions(
41
- self,
42
- models: List[FairseqModel],
43
- encoder_input: Dict[str, Any],
44
- ) -> torch.FloatTensor:
45
- model = models[0]
46
- encoder_out = model(**encoder_input)
47
- if hasattr(model, "get_logits"):
48
- emissions = model.get_logits(encoder_out)
49
- else:
50
- emissions = model.get_normalized_probs(encoder_out, log_probs=True)
51
- return emissions.transpose(0, 1).float().cpu().contiguous()
52
-
53
- def get_tokens(self, idxs: torch.IntTensor) -> torch.LongTensor:
54
- idxs = (g[0] for g in it.groupby(idxs))
55
- idxs = filter(lambda x: x != self.blank, idxs)
56
- return torch.LongTensor(list(idxs))
57
-
58
- def decode(
59
- self,
60
- emissions: torch.FloatTensor,
61
- ) -> List[List[Dict[str, torch.LongTensor]]]:
62
- raise NotImplementedError
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/decoder.py DELETED
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- from typing import Union
9
-
10
- from fairseq.data.dictionary import Dictionary
11
-
12
- from .base_decoder import BaseDecoder
13
- from .decoder_config import DecoderConfig, FlashlightDecoderConfig
14
-
15
-
16
- def Decoder(
17
- cfg: Union[DecoderConfig, FlashlightDecoderConfig], tgt_dict: Dictionary
18
- ) -> BaseDecoder:
19
-
20
- if cfg.type == "viterbi":
21
- from .viterbi_decoder import ViterbiDecoder
22
-
23
- return ViterbiDecoder(tgt_dict)
24
- if cfg.type == "kenlm":
25
- from .flashlight_decoder import KenLMDecoder
26
-
27
- return KenLMDecoder(cfg, tgt_dict)
28
- if cfg.type == "fairseqlm":
29
- from .flashlight_decoder import FairseqLMDecoder
30
-
31
- return FairseqLMDecoder(cfg, tgt_dict)
32
- raise NotImplementedError(f"Invalid decoder name: {cfg.name}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/decoder_config.py DELETED
@@ -1,69 +0,0 @@
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 math
7
- from dataclasses import dataclass, field
8
- from typing import Optional
9
-
10
- from fairseq.dataclass.configs import FairseqDataclass
11
- from fairseq.dataclass.constants import ChoiceEnum
12
- from omegaconf import MISSING
13
-
14
- DECODER_CHOICES = ChoiceEnum(["viterbi", "kenlm", "fairseqlm"])
15
-
16
-
17
- @dataclass
18
- class DecoderConfig(FairseqDataclass):
19
- type: DECODER_CHOICES = field(
20
- default="viterbi",
21
- metadata={"help": "The type of decoder to use"},
22
- )
23
-
24
-
25
- @dataclass
26
- class FlashlightDecoderConfig(FairseqDataclass):
27
- nbest: int = field(
28
- default=1,
29
- metadata={"help": "Number of decodings to return"},
30
- )
31
- unitlm: bool = field(
32
- default=False,
33
- metadata={"help": "If set, use unit language model"},
34
- )
35
- lmpath: str = field(
36
- default=MISSING,
37
- metadata={"help": "Language model for KenLM decoder"},
38
- )
39
- lexicon: Optional[str] = field(
40
- default=None,
41
- metadata={"help": "Lexicon for Flashlight decoder"},
42
- )
43
- beam: int = field(
44
- default=50,
45
- metadata={"help": "Number of beams to use for decoding"},
46
- )
47
- beamthreshold: float = field(
48
- default=50.0,
49
- metadata={"help": "Threshold for beam search decoding"},
50
- )
51
- beamsizetoken: Optional[int] = field(
52
- default=None, metadata={"help": "Beam size to use"}
53
- )
54
- wordscore: float = field(
55
- default=-1,
56
- metadata={"help": "Word score for KenLM decoder"},
57
- )
58
- unkweight: float = field(
59
- default=-math.inf,
60
- metadata={"help": "Unknown weight for KenLM decoder"},
61
- )
62
- silweight: float = field(
63
- default=0,
64
- metadata={"help": "Silence weight for KenLM decoder"},
65
- )
66
- lmweight: float = field(
67
- default=2,
68
- metadata={"help": "Weight for LM while interpolating score"},
69
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/flashlight_decoder.py DELETED
@@ -1,429 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- import gc
9
- import os.path as osp
10
- import warnings
11
- from collections import deque, namedtuple
12
- from typing import Any, Dict, List, Tuple
13
-
14
- import numpy as np
15
- import torch
16
- from fairseq import tasks
17
- from fairseq.data.dictionary import Dictionary
18
- from fairseq.dataclass.utils import convert_namespace_to_omegaconf
19
- from fairseq.models.fairseq_model import FairseqModel
20
- from fairseq.utils import apply_to_sample
21
- from omegaconf import OmegaConf, open_dict
22
-
23
- from .base_decoder import BaseDecoder
24
- from .decoder_config import FlashlightDecoderConfig
25
-
26
- try:
27
- from flashlight.lib.text.decoder import (
28
- LM,
29
- CriterionType,
30
- DecodeResult,
31
- KenLM,
32
- LexiconDecoder,
33
- LexiconDecoderOptions,
34
- LexiconFreeDecoder,
35
- LexiconFreeDecoderOptions,
36
- LMState,
37
- SmearingMode,
38
- Trie,
39
- )
40
- from flashlight.lib.text.dictionary import create_word_dict, load_words
41
- except ImportError:
42
- warnings.warn(
43
- "flashlight python bindings are required to use this functionality. "
44
- "Please install from "
45
- "https://github.com/facebookresearch/flashlight/tree/master/bindings/python"
46
- )
47
- LM = object
48
- LMState = object
49
-
50
-
51
- class KenLMDecoder(BaseDecoder):
52
- def __init__(self, cfg: FlashlightDecoderConfig, tgt_dict: Dictionary) -> None:
53
- super().__init__(tgt_dict)
54
-
55
- self.nbest = cfg.nbest
56
- self.unitlm = cfg.unitlm
57
-
58
- if cfg.lexicon:
59
- self.lexicon = load_words(cfg.lexicon)
60
- self.word_dict = create_word_dict(self.lexicon)
61
- self.unk_word = self.word_dict.get_index("<unk>")
62
-
63
- self.lm = KenLM(cfg.lmpath, self.word_dict)
64
- self.trie = Trie(self.vocab_size, self.silence)
65
-
66
- start_state = self.lm.start(False)
67
- for word, spellings in self.lexicon.items():
68
- word_idx = self.word_dict.get_index(word)
69
- _, score = self.lm.score(start_state, word_idx)
70
- for spelling in spellings:
71
- spelling_idxs = [tgt_dict.index(token) for token in spelling]
72
- assert (
73
- tgt_dict.unk() not in spelling_idxs
74
- ), f"{word} {spelling} {spelling_idxs}"
75
- self.trie.insert(spelling_idxs, word_idx, score)
76
- self.trie.smear(SmearingMode.MAX)
77
-
78
- self.decoder_opts = LexiconDecoderOptions(
79
- beam_size=cfg.beam,
80
- beam_size_token=cfg.beamsizetoken or len(tgt_dict),
81
- beam_threshold=cfg.beamthreshold,
82
- lm_weight=cfg.lmweight,
83
- word_score=cfg.wordscore,
84
- unk_score=cfg.unkweight,
85
- sil_score=cfg.silweight,
86
- log_add=False,
87
- criterion_type=CriterionType.CTC,
88
- )
89
-
90
- self.decoder = LexiconDecoder(
91
- self.decoder_opts,
92
- self.trie,
93
- self.lm,
94
- self.silence,
95
- self.blank,
96
- self.unk_word,
97
- [],
98
- self.unitlm,
99
- )
100
- else:
101
- assert self.unitlm, "Lexicon-free decoding requires unit LM"
102
-
103
- d = {w: [[w]] for w in tgt_dict.symbols}
104
- self.word_dict = create_word_dict(d)
105
- self.lm = KenLM(cfg.lmpath, self.word_dict)
106
- self.decoder_opts = LexiconFreeDecoderOptions(
107
- beam_size=cfg.beam,
108
- beam_size_token=cfg.beamsizetoken or len(tgt_dict),
109
- beam_threshold=cfg.beamthreshold,
110
- lm_weight=cfg.lmweight,
111
- sil_score=cfg.silweight,
112
- log_add=False,
113
- criterion_type=CriterionType.CTC,
114
- )
115
- self.decoder = LexiconFreeDecoder(
116
- self.decoder_opts, self.lm, self.silence, self.blank, []
117
- )
118
-
119
- def get_timesteps(self, token_idxs: List[int]) -> List[int]:
120
- """Returns frame numbers corresponding to every non-blank token.
121
-
122
- Parameters
123
- ----------
124
- token_idxs : List[int]
125
- IDs of decoded tokens.
126
-
127
- Returns
128
- -------
129
- List[int]
130
- Frame numbers corresponding to every non-blank token.
131
- """
132
- timesteps = []
133
- for i, token_idx in enumerate(token_idxs):
134
- if token_idx == self.blank:
135
- continue
136
- if i == 0 or token_idx != token_idxs[i-1]:
137
- timesteps.append(i)
138
- return timesteps
139
-
140
- def decode(
141
- self,
142
- emissions: torch.FloatTensor,
143
- ) -> List[List[Dict[str, torch.LongTensor]]]:
144
- B, T, N = emissions.size()
145
- hypos = []
146
- for b in range(B):
147
- emissions_ptr = emissions.data_ptr() + 4 * b * emissions.stride(0)
148
- results = self.decoder.decode(emissions_ptr, T, N)
149
-
150
- nbest_results = results[: self.nbest]
151
- hypos.append(
152
- [
153
- {
154
- "tokens": self.get_tokens(result.tokens),
155
- "score": result.score,
156
- "timesteps": self.get_timesteps(result.tokens),
157
- "words": [
158
- self.word_dict.get_entry(x) for x in result.words if x >= 0
159
- ],
160
- }
161
- for result in nbest_results
162
- ]
163
- )
164
- return hypos
165
-
166
-
167
- FairseqLMState = namedtuple(
168
- "FairseqLMState",
169
- [
170
- "prefix",
171
- "incremental_state",
172
- "probs",
173
- ],
174
- )
175
-
176
-
177
- class FairseqLM(LM):
178
- def __init__(self, dictionary: Dictionary, model: FairseqModel) -> None:
179
- super().__init__()
180
-
181
- self.dictionary = dictionary
182
- self.model = model
183
- self.unk = self.dictionary.unk()
184
-
185
- self.save_incremental = False # this currently does not work properly
186
- self.max_cache = 20_000
187
-
188
- if torch.cuda.is_available():
189
- model.cuda()
190
- model.eval()
191
- model.make_generation_fast_()
192
-
193
- self.states = {}
194
- self.stateq = deque()
195
-
196
- def start(self, start_with_nothing: bool) -> LMState:
197
- state = LMState()
198
- prefix = torch.LongTensor([[self.dictionary.eos()]])
199
- incremental_state = {} if self.save_incremental else None
200
- with torch.no_grad():
201
- res = self.model(prefix.cuda(), incremental_state=incremental_state)
202
- probs = self.model.get_normalized_probs(res, log_probs=True, sample=None)
203
-
204
- if incremental_state is not None:
205
- incremental_state = apply_to_sample(lambda x: x.cpu(), incremental_state)
206
- self.states[state] = FairseqLMState(
207
- prefix.numpy(), incremental_state, probs[0, -1].cpu().numpy()
208
- )
209
- self.stateq.append(state)
210
-
211
- return state
212
-
213
- def score(
214
- self,
215
- state: LMState,
216
- token_index: int,
217
- no_cache: bool = False,
218
- ) -> Tuple[LMState, int]:
219
- """
220
- Evaluate language model based on the current lm state and new word
221
- Parameters:
222
- -----------
223
- state: current lm state
224
- token_index: index of the word
225
- (can be lexicon index then you should store inside LM the
226
- mapping between indices of lexicon and lm, or lm index of a word)
227
- Returns:
228
- --------
229
- (LMState, float): pair of (new state, score for the current word)
230
- """
231
- curr_state = self.states[state]
232
-
233
- def trim_cache(targ_size: int) -> None:
234
- while len(self.stateq) > targ_size:
235
- rem_k = self.stateq.popleft()
236
- rem_st = self.states[rem_k]
237
- rem_st = FairseqLMState(rem_st.prefix, None, None)
238
- self.states[rem_k] = rem_st
239
-
240
- if curr_state.probs is None:
241
- new_incremental_state = (
242
- curr_state.incremental_state.copy()
243
- if curr_state.incremental_state is not None
244
- else None
245
- )
246
- with torch.no_grad():
247
- if new_incremental_state is not None:
248
- new_incremental_state = apply_to_sample(
249
- lambda x: x.cuda(), new_incremental_state
250
- )
251
- elif self.save_incremental:
252
- new_incremental_state = {}
253
-
254
- res = self.model(
255
- torch.from_numpy(curr_state.prefix).cuda(),
256
- incremental_state=new_incremental_state,
257
- )
258
- probs = self.model.get_normalized_probs(
259
- res, log_probs=True, sample=None
260
- )
261
-
262
- if new_incremental_state is not None:
263
- new_incremental_state = apply_to_sample(
264
- lambda x: x.cpu(), new_incremental_state
265
- )
266
-
267
- curr_state = FairseqLMState(
268
- curr_state.prefix, new_incremental_state, probs[0, -1].cpu().numpy()
269
- )
270
-
271
- if not no_cache:
272
- self.states[state] = curr_state
273
- self.stateq.append(state)
274
-
275
- score = curr_state.probs[token_index].item()
276
-
277
- trim_cache(self.max_cache)
278
-
279
- outstate = state.child(token_index)
280
- if outstate not in self.states and not no_cache:
281
- prefix = np.concatenate(
282
- [curr_state.prefix, torch.LongTensor([[token_index]])], -1
283
- )
284
- incr_state = curr_state.incremental_state
285
-
286
- self.states[outstate] = FairseqLMState(prefix, incr_state, None)
287
-
288
- if token_index == self.unk:
289
- score = float("-inf")
290
-
291
- return outstate, score
292
-
293
- def finish(self, state: LMState) -> Tuple[LMState, int]:
294
- """
295
- Evaluate eos for language model based on the current lm state
296
- Returns:
297
- --------
298
- (LMState, float): pair of (new state, score for the current word)
299
- """
300
- return self.score(state, self.dictionary.eos())
301
-
302
- def empty_cache(self) -> None:
303
- self.states = {}
304
- self.stateq = deque()
305
- gc.collect()
306
-
307
-
308
- class FairseqLMDecoder(BaseDecoder):
309
- def __init__(self, cfg: FlashlightDecoderConfig, tgt_dict: Dictionary) -> None:
310
- super().__init__(tgt_dict)
311
-
312
- self.nbest = cfg.nbest
313
- self.unitlm = cfg.unitlm
314
-
315
- self.lexicon = load_words(cfg.lexicon) if cfg.lexicon else None
316
- self.idx_to_wrd = {}
317
-
318
- checkpoint = torch.load(cfg.lmpath, map_location="cpu")
319
-
320
- if "cfg" in checkpoint and checkpoint["cfg"] is not None:
321
- lm_args = checkpoint["cfg"]
322
- else:
323
- lm_args = convert_namespace_to_omegaconf(checkpoint["args"])
324
-
325
- if not OmegaConf.is_dict(lm_args):
326
- lm_args = OmegaConf.create(lm_args)
327
-
328
- with open_dict(lm_args.task):
329
- lm_args.task.data = osp.dirname(cfg.lmpath)
330
-
331
- task = tasks.setup_task(lm_args.task)
332
- model = task.build_model(lm_args.model)
333
- model.load_state_dict(checkpoint["model"], strict=False)
334
-
335
- self.trie = Trie(self.vocab_size, self.silence)
336
-
337
- self.word_dict = task.dictionary
338
- self.unk_word = self.word_dict.unk()
339
- self.lm = FairseqLM(self.word_dict, model)
340
-
341
- if self.lexicon:
342
- start_state = self.lm.start(False)
343
- for i, (word, spellings) in enumerate(self.lexicon.items()):
344
- if self.unitlm:
345
- word_idx = i
346
- self.idx_to_wrd[i] = word
347
- score = 0
348
- else:
349
- word_idx = self.word_dict.index(word)
350
- _, score = self.lm.score(start_state, word_idx, no_cache=True)
351
-
352
- for spelling in spellings:
353
- spelling_idxs = [tgt_dict.index(token) for token in spelling]
354
- assert (
355
- tgt_dict.unk() not in spelling_idxs
356
- ), f"{spelling} {spelling_idxs}"
357
- self.trie.insert(spelling_idxs, word_idx, score)
358
- self.trie.smear(SmearingMode.MAX)
359
-
360
- self.decoder_opts = LexiconDecoderOptions(
361
- beam_size=cfg.beam,
362
- beam_size_token=cfg.beamsizetoken or len(tgt_dict),
363
- beam_threshold=cfg.beamthreshold,
364
- lm_weight=cfg.lmweight,
365
- word_score=cfg.wordscore,
366
- unk_score=cfg.unkweight,
367
- sil_score=cfg.silweight,
368
- log_add=False,
369
- criterion_type=CriterionType.CTC,
370
- )
371
-
372
- self.decoder = LexiconDecoder(
373
- self.decoder_opts,
374
- self.trie,
375
- self.lm,
376
- self.silence,
377
- self.blank,
378
- self.unk_word,
379
- [],
380
- self.unitlm,
381
- )
382
- else:
383
- assert self.unitlm, "Lexicon-free decoding requires unit LM"
384
-
385
- d = {w: [[w]] for w in tgt_dict.symbols}
386
- self.word_dict = create_word_dict(d)
387
- self.lm = KenLM(cfg.lmpath, self.word_dict)
388
- self.decoder_opts = LexiconFreeDecoderOptions(
389
- beam_size=cfg.beam,
390
- beam_size_token=cfg.beamsizetoken or len(tgt_dict),
391
- beam_threshold=cfg.beamthreshold,
392
- lm_weight=cfg.lmweight,
393
- sil_score=cfg.silweight,
394
- log_add=False,
395
- criterion_type=CriterionType.CTC,
396
- )
397
- self.decoder = LexiconFreeDecoder(
398
- self.decoder_opts, self.lm, self.silence, self.blank, []
399
- )
400
-
401
- def decode(
402
- self,
403
- emissions: torch.FloatTensor,
404
- ) -> List[List[Dict[str, torch.LongTensor]]]:
405
- B, T, N = emissions.size()
406
- hypos = []
407
-
408
- def make_hypo(result: DecodeResult) -> Dict[str, Any]:
409
- hypo = {
410
- "tokens": self.get_tokens(result.tokens),
411
- "score": result.score,
412
- }
413
- if self.lexicon:
414
- hypo["words"] = [
415
- self.idx_to_wrd[x] if self.unitlm else self.word_dict[x]
416
- for x in result.words
417
- if x >= 0
418
- ]
419
- return hypo
420
-
421
- for b in range(B):
422
- emissions_ptr = emissions.data_ptr() + 4 * b * emissions.stride(0)
423
- results = self.decoder.decode(emissions_ptr, T, N)
424
-
425
- nbest_results = results[: self.nbest]
426
- hypos.append([make_hypo(result) for result in nbest_results])
427
- self.lm.empty_cache()
428
-
429
- return hypos
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/decoders/viterbi_decoder.py DELETED
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- from typing import Dict, List
9
-
10
- import torch
11
-
12
- from .base_decoder import BaseDecoder
13
-
14
-
15
- class ViterbiDecoder(BaseDecoder):
16
- def decode(
17
- self,
18
- emissions: torch.FloatTensor,
19
- ) -> List[List[Dict[str, torch.LongTensor]]]:
20
- def get_pred(e):
21
- toks = e.argmax(dim=-1).unique_consecutive()
22
- return toks[toks != self.blank]
23
-
24
- return [[{"tokens": get_pred(x), "score": 0}] for x in emissions]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/new/infer.py DELETED
@@ -1,474 +0,0 @@
1
- #!/usr/bin/env python -u
2
- # Copyright (c) Facebook, Inc. and its affiliates.
3
- #
4
- # This source code is licensed under the MIT license found in the
5
- # LICENSE file in the root directory of this source tree.
6
-
7
- import ast
8
- import hashlib
9
- import logging
10
- import os
11
- import shutil
12
- import sys
13
- from dataclasses import dataclass, field, is_dataclass
14
- from pathlib import Path
15
- from typing import Any, Dict, List, Optional, Tuple, Union
16
-
17
- import editdistance
18
- import hydra
19
- import torch
20
- import torch.distributed as dist
21
- from examples.speech_recognition.new.decoders.decoder import Decoder
22
- from examples.speech_recognition.new.decoders.decoder_config import (
23
- DecoderConfig,
24
- FlashlightDecoderConfig,
25
- )
26
- from fairseq import checkpoint_utils, distributed_utils, progress_bar, tasks, utils
27
- from fairseq.data.data_utils import post_process
28
- from fairseq.dataclass.configs import (
29
- CheckpointConfig,
30
- CommonConfig,
31
- CommonEvalConfig,
32
- DatasetConfig,
33
- DistributedTrainingConfig,
34
- FairseqDataclass,
35
- )
36
- from fairseq.logging.meters import StopwatchMeter, TimeMeter
37
- from fairseq.logging.progress_bar import BaseProgressBar
38
- from fairseq.models.fairseq_model import FairseqModel
39
- from hydra.core.config_store import ConfigStore
40
- from omegaconf import OmegaConf
41
-
42
- logging.root.setLevel(logging.INFO)
43
- logging.basicConfig(level=logging.INFO)
44
- logger = logging.getLogger(__name__)
45
-
46
- config_path = Path(__file__).resolve().parent / "conf"
47
-
48
-
49
- @dataclass
50
- class DecodingConfig(DecoderConfig, FlashlightDecoderConfig):
51
- unique_wer_file: bool = field(
52
- default=False,
53
- metadata={"help": "If set, use a unique file for storing WER"},
54
- )
55
- results_path: Optional[str] = field(
56
- default=None,
57
- metadata={
58
- "help": "If set, write hypothesis and reference sentences into this directory"
59
- },
60
- )
61
-
62
-
63
- @dataclass
64
- class InferConfig(FairseqDataclass):
65
- task: Any = None
66
- decoding: DecodingConfig = DecodingConfig()
67
- common: CommonConfig = CommonConfig()
68
- common_eval: CommonEvalConfig = CommonEvalConfig()
69
- checkpoint: CheckpointConfig = CheckpointConfig()
70
- distributed_training: DistributedTrainingConfig = DistributedTrainingConfig()
71
- dataset: DatasetConfig = DatasetConfig()
72
- is_ax: bool = field(
73
- default=False,
74
- metadata={
75
- "help": "if true, assumes we are using ax for tuning and returns a tuple for ax to consume"
76
- },
77
- )
78
-
79
-
80
- def reset_logging():
81
- root = logging.getLogger()
82
- for handler in root.handlers:
83
- root.removeHandler(handler)
84
- root.setLevel(os.environ.get("LOGLEVEL", "INFO").upper())
85
- handler = logging.StreamHandler(sys.stdout)
86
- handler.setFormatter(
87
- logging.Formatter(
88
- fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
89
- datefmt="%Y-%m-%d %H:%M:%S",
90
- )
91
- )
92
- root.addHandler(handler)
93
-
94
-
95
- class InferenceProcessor:
96
- cfg: InferConfig
97
-
98
- def __init__(self, cfg: InferConfig) -> None:
99
- self.cfg = cfg
100
- self.task = tasks.setup_task(cfg.task)
101
-
102
- models, saved_cfg = self.load_model_ensemble()
103
- self.models = models
104
- self.saved_cfg = saved_cfg
105
- self.tgt_dict = self.task.target_dictionary
106
-
107
- self.task.load_dataset(
108
- self.cfg.dataset.gen_subset,
109
- task_cfg=saved_cfg.task,
110
- )
111
- self.generator = Decoder(cfg.decoding, self.tgt_dict)
112
- self.gen_timer = StopwatchMeter()
113
- self.wps_meter = TimeMeter()
114
- self.num_sentences = 0
115
- self.total_errors = 0
116
- self.total_length = 0
117
-
118
- self.hypo_words_file = None
119
- self.hypo_units_file = None
120
- self.ref_words_file = None
121
- self.ref_units_file = None
122
-
123
- self.progress_bar = self.build_progress_bar()
124
-
125
- def __enter__(self) -> "InferenceProcessor":
126
- if self.cfg.decoding.results_path is not None:
127
- self.hypo_words_file = self.get_res_file("hypo.word")
128
- self.hypo_units_file = self.get_res_file("hypo.units")
129
- self.ref_words_file = self.get_res_file("ref.word")
130
- self.ref_units_file = self.get_res_file("ref.units")
131
- return self
132
-
133
- def __exit__(self, *exc) -> bool:
134
- if self.cfg.decoding.results_path is not None:
135
- self.hypo_words_file.close()
136
- self.hypo_units_file.close()
137
- self.ref_words_file.close()
138
- self.ref_units_file.close()
139
- return False
140
-
141
- def __iter__(self) -> Any:
142
- for sample in self.progress_bar:
143
- if not self.cfg.common.cpu:
144
- sample = utils.move_to_cuda(sample)
145
-
146
- # Happens on the last batch.
147
- if "net_input" not in sample:
148
- continue
149
- yield sample
150
-
151
- def log(self, *args, **kwargs):
152
- self.progress_bar.log(*args, **kwargs)
153
-
154
- def print(self, *args, **kwargs):
155
- self.progress_bar.print(*args, **kwargs)
156
-
157
- def get_res_file(self, fname: str) -> None:
158
- fname = os.path.join(self.cfg.decoding.results_path, fname)
159
- if self.data_parallel_world_size > 1:
160
- fname = f"{fname}.{self.data_parallel_rank}"
161
- return open(fname, "w", buffering=1)
162
-
163
- def merge_shards(self) -> None:
164
- """Merges all shard files into shard 0, then removes shard suffix."""
165
-
166
- shard_id = self.data_parallel_rank
167
- num_shards = self.data_parallel_world_size
168
-
169
- if self.data_parallel_world_size > 1:
170
-
171
- def merge_shards_with_root(fname: str) -> None:
172
- fname = os.path.join(self.cfg.decoding.results_path, fname)
173
- logger.info("Merging %s on shard %d", fname, shard_id)
174
- base_fpath = Path(f"{fname}.0")
175
- with open(base_fpath, "a") as out_file:
176
- for s in range(1, num_shards):
177
- shard_fpath = Path(f"{fname}.{s}")
178
- with open(shard_fpath, "r") as in_file:
179
- for line in in_file:
180
- out_file.write(line)
181
- shard_fpath.unlink()
182
- shutil.move(f"{fname}.0", fname)
183
-
184
- dist.barrier() # ensure all shards finished writing
185
- if shard_id == (0 % num_shards):
186
- merge_shards_with_root("hypo.word")
187
- if shard_id == (1 % num_shards):
188
- merge_shards_with_root("hypo.units")
189
- if shard_id == (2 % num_shards):
190
- merge_shards_with_root("ref.word")
191
- if shard_id == (3 % num_shards):
192
- merge_shards_with_root("ref.units")
193
- dist.barrier()
194
-
195
- def optimize_model(self, model: FairseqModel) -> None:
196
- model.make_generation_fast_()
197
- if self.cfg.common.fp16:
198
- model.half()
199
- if not self.cfg.common.cpu:
200
- model.cuda()
201
-
202
- def load_model_ensemble(self) -> Tuple[List[FairseqModel], FairseqDataclass]:
203
- arg_overrides = ast.literal_eval(self.cfg.common_eval.model_overrides)
204
- models, saved_cfg = checkpoint_utils.load_model_ensemble(
205
- utils.split_paths(self.cfg.common_eval.path, separator="\\"),
206
- arg_overrides=arg_overrides,
207
- task=self.task,
208
- suffix=self.cfg.checkpoint.checkpoint_suffix,
209
- strict=(self.cfg.checkpoint.checkpoint_shard_count == 1),
210
- num_shards=self.cfg.checkpoint.checkpoint_shard_count,
211
- )
212
- for model in models:
213
- self.optimize_model(model)
214
- return models, saved_cfg
215
-
216
- def get_dataset_itr(self, disable_iterator_cache: bool = False) -> None:
217
- return self.task.get_batch_iterator(
218
- dataset=self.task.dataset(self.cfg.dataset.gen_subset),
219
- max_tokens=self.cfg.dataset.max_tokens,
220
- max_sentences=self.cfg.dataset.batch_size,
221
- max_positions=(sys.maxsize, sys.maxsize),
222
- ignore_invalid_inputs=self.cfg.dataset.skip_invalid_size_inputs_valid_test,
223
- required_batch_size_multiple=self.cfg.dataset.required_batch_size_multiple,
224
- seed=self.cfg.common.seed,
225
- num_shards=self.data_parallel_world_size,
226
- shard_id=self.data_parallel_rank,
227
- num_workers=self.cfg.dataset.num_workers,
228
- data_buffer_size=self.cfg.dataset.data_buffer_size,
229
- disable_iterator_cache=disable_iterator_cache,
230
- ).next_epoch_itr(shuffle=False)
231
-
232
- def build_progress_bar(
233
- self,
234
- epoch: Optional[int] = None,
235
- prefix: Optional[str] = None,
236
- default_log_format: str = "tqdm",
237
- ) -> BaseProgressBar:
238
- return progress_bar.progress_bar(
239
- iterator=self.get_dataset_itr(),
240
- log_format=self.cfg.common.log_format,
241
- log_interval=self.cfg.common.log_interval,
242
- epoch=epoch,
243
- prefix=prefix,
244
- tensorboard_logdir=self.cfg.common.tensorboard_logdir,
245
- default_log_format=default_log_format,
246
- )
247
-
248
- @property
249
- def data_parallel_world_size(self):
250
- if self.cfg.distributed_training.distributed_world_size == 1:
251
- return 1
252
- return distributed_utils.get_data_parallel_world_size()
253
-
254
- @property
255
- def data_parallel_rank(self):
256
- if self.cfg.distributed_training.distributed_world_size == 1:
257
- return 0
258
- return distributed_utils.get_data_parallel_rank()
259
-
260
- def process_sentence(
261
- self,
262
- sample: Dict[str, Any],
263
- hypo: Dict[str, Any],
264
- sid: int,
265
- batch_id: int,
266
- ) -> Tuple[int, int]:
267
- speaker = None # Speaker can't be parsed from dataset.
268
-
269
- if "target_label" in sample:
270
- toks = sample["target_label"]
271
- else:
272
- toks = sample["target"]
273
- toks = toks[batch_id, :]
274
-
275
- # Processes hypothesis.
276
- hyp_pieces = self.tgt_dict.string(hypo["tokens"].int().cpu())
277
- if "words" in hypo:
278
- hyp_words = " ".join(hypo["words"])
279
- else:
280
- hyp_words = post_process(hyp_pieces, self.cfg.common_eval.post_process)
281
-
282
- # Processes target.
283
- target_tokens = utils.strip_pad(toks, self.tgt_dict.pad())
284
- tgt_pieces = self.tgt_dict.string(target_tokens.int().cpu())
285
- tgt_words = post_process(tgt_pieces, self.cfg.common_eval.post_process)
286
-
287
- if self.cfg.decoding.results_path is not None:
288
- print(f"{hyp_pieces} ({speaker}-{sid})", file=self.hypo_units_file)
289
- print(f"{hyp_words} ({speaker}-{sid})", file=self.hypo_words_file)
290
- print(f"{tgt_pieces} ({speaker}-{sid})", file=self.ref_units_file)
291
- print(f"{tgt_words} ({speaker}-{sid})", file=self.ref_words_file)
292
-
293
- if not self.cfg.common_eval.quiet:
294
- logger.info(f"HYPO: {hyp_words}")
295
- logger.info(f"REF: {tgt_words}")
296
- logger.info("---------------------")
297
-
298
- hyp_words, tgt_words = hyp_words.split(), tgt_words.split()
299
-
300
- return editdistance.eval(hyp_words, tgt_words), len(tgt_words)
301
-
302
- def process_sample(self, sample: Dict[str, Any]) -> None:
303
- self.gen_timer.start()
304
- hypos = self.task.inference_step(
305
- generator=self.generator,
306
- models=self.models,
307
- sample=sample,
308
- )
309
- num_generated_tokens = sum(len(h[0]["tokens"]) for h in hypos)
310
- self.gen_timer.stop(num_generated_tokens)
311
- self.wps_meter.update(num_generated_tokens)
312
-
313
- for batch_id, sample_id in enumerate(sample["id"].tolist()):
314
- errs, length = self.process_sentence(
315
- sample=sample,
316
- sid=sample_id,
317
- batch_id=batch_id,
318
- hypo=hypos[batch_id][0],
319
- )
320
- self.total_errors += errs
321
- self.total_length += length
322
-
323
- self.log({"wps": round(self.wps_meter.avg)})
324
- if "nsentences" in sample:
325
- self.num_sentences += sample["nsentences"]
326
- else:
327
- self.num_sentences += sample["id"].numel()
328
-
329
- def log_generation_time(self) -> None:
330
- logger.info(
331
- "Processed %d sentences (%d tokens) in %.1fs %.2f "
332
- "sentences per second, %.2f tokens per second)",
333
- self.num_sentences,
334
- self.gen_timer.n,
335
- self.gen_timer.sum,
336
- self.num_sentences / (self.gen_timer.sum + 1e-6),
337
- 1.0 / (self.gen_timer.avg + 1e-6),
338
- )
339
-
340
-
341
- def parse_wer(wer_file: Path) -> float:
342
- with open(wer_file, "r") as f:
343
- return float(f.readline().strip().split(" ")[1])
344
-
345
-
346
- def get_wer_file(cfg: InferConfig) -> Path:
347
- """Hashes the decoding parameters to a unique file ID."""
348
- base_path = "wer"
349
- if cfg.decoding.results_path is not None:
350
- base_path = os.path.join(cfg.decoding.results_path, base_path)
351
-
352
- if cfg.decoding.unique_wer_file:
353
- yaml_str = OmegaConf.to_yaml(cfg.decoding)
354
- fid = int(hashlib.md5(yaml_str.encode("utf-8")).hexdigest(), 16)
355
- return Path(f"{base_path}.{fid % 1000000}")
356
- else:
357
- return Path(base_path)
358
-
359
-
360
- def main(cfg: InferConfig) -> float:
361
- """Entry point for main processing logic.
362
-
363
- Args:
364
- cfg: The inferance configuration to use.
365
- wer: Optional shared memory pointer for returning the WER. If not None,
366
- the final WER value will be written here instead of being returned.
367
-
368
- Returns:
369
- The final WER if `wer` is None, otherwise None.
370
- """
371
-
372
- yaml_str, wer_file = OmegaConf.to_yaml(cfg.decoding), get_wer_file(cfg)
373
-
374
- # Validates the provided configuration.
375
- if cfg.dataset.max_tokens is None and cfg.dataset.batch_size is None:
376
- cfg.dataset.max_tokens = 4000000
377
- if not cfg.common.cpu and not torch.cuda.is_available():
378
- raise ValueError("CUDA not found; set `cpu=True` to run without CUDA")
379
-
380
- logger.info(cfg.common_eval.path)
381
-
382
- with InferenceProcessor(cfg) as processor:
383
- for sample in processor:
384
- processor.process_sample(sample)
385
-
386
- processor.log_generation_time()
387
-
388
- if cfg.decoding.results_path is not None:
389
- processor.merge_shards()
390
-
391
- errs_t, leng_t = processor.total_errors, processor.total_length
392
-
393
- if cfg.common.cpu:
394
- logger.warning("Merging WER requires CUDA.")
395
- elif processor.data_parallel_world_size > 1:
396
- stats = torch.LongTensor([errs_t, leng_t]).cuda()
397
- dist.all_reduce(stats, op=dist.ReduceOp.SUM)
398
- errs_t, leng_t = stats[0].item(), stats[1].item()
399
-
400
- wer = errs_t * 100.0 / leng_t
401
-
402
- if distributed_utils.is_master(cfg.distributed_training):
403
- with open(wer_file, "w") as f:
404
- f.write(
405
- (
406
- f"WER: {wer}\n"
407
- f"err / num_ref_words = {errs_t} / {leng_t}\n\n"
408
- f"{yaml_str}"
409
- )
410
- )
411
-
412
- return wer
413
-
414
-
415
- @hydra.main(config_path=config_path, config_name="infer")
416
- def hydra_main(cfg: InferConfig) -> Union[float, Tuple[float, Optional[float]]]:
417
- container = OmegaConf.to_container(cfg, resolve=True, enum_to_str=True)
418
- cfg = OmegaConf.create(container)
419
- OmegaConf.set_struct(cfg, True)
420
-
421
- if cfg.common.reset_logging:
422
- reset_logging()
423
-
424
- utils.import_user_module(cfg.common)
425
-
426
- # logger.info("Config:\n%s", OmegaConf.to_yaml(cfg))
427
- wer = float("inf")
428
-
429
- try:
430
- if cfg.common.profile:
431
- with torch.cuda.profiler.profile():
432
- with torch.autograd.profiler.emit_nvtx():
433
- distributed_utils.call_main(cfg, main)
434
- else:
435
- distributed_utils.call_main(cfg, main)
436
-
437
- wer = parse_wer(get_wer_file(cfg))
438
- except BaseException as e: # pylint: disable=broad-except
439
- if not cfg.common.suppress_crashes:
440
- raise
441
- else:
442
- logger.error("Crashed! %s", str(e))
443
-
444
- logger.info("Word error rate: %.4f", wer)
445
- if cfg.is_ax:
446
- return wer, None
447
-
448
- return wer
449
-
450
-
451
- def cli_main() -> None:
452
- try:
453
- from hydra._internal.utils import ( # pylint: disable=import-outside-toplevel
454
- get_args,
455
- )
456
-
457
- cfg_name = get_args().config_name or "infer"
458
- except ImportError:
459
- logger.warning("Failed to get config name from hydra args")
460
- cfg_name = "infer"
461
-
462
- cs = ConfigStore.instance()
463
- cs.store(name=cfg_name, node=InferConfig)
464
-
465
- for k in InferConfig.__dataclass_fields__:
466
- if is_dataclass(InferConfig.__dataclass_fields__[k].type):
467
- v = InferConfig.__dataclass_fields__[k].default
468
- cs.store(name=k, node=v)
469
-
470
- hydra_main() # pylint: disable=no-value-for-parameter
471
-
472
-
473
- if __name__ == "__main__":
474
- cli_main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/tasks/__init__.py DELETED
@@ -1,7 +0,0 @@
1
- import importlib
2
- import os
3
-
4
- for file in sorted(os.listdir(os.path.dirname(__file__))):
5
- if file.endswith(".py") and not file.startswith("_"):
6
- task_name = file[: file.find(".py")]
7
- importlib.import_module("examples.speech_recognition.tasks." + task_name)
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/tasks/speech_recognition.py DELETED
@@ -1,157 +0,0 @@
1
- # Copyright (c) Facebook, Inc. and its affiliates.
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
-
6
- import json
7
- import os
8
- import re
9
- import sys
10
-
11
- import torch
12
- from examples.speech_recognition.data import AsrDataset
13
- from examples.speech_recognition.data.replabels import replabel_symbol
14
- from fairseq.data import Dictionary
15
- from fairseq.tasks import LegacyFairseqTask, register_task
16
-
17
-
18
- def get_asr_dataset_from_json(data_json_path, tgt_dict):
19
- """
20
- Parse data json and create dataset.
21
- See scripts/asr_prep_json.py which pack json from raw files
22
-
23
- Json example:
24
- {
25
- "utts": {
26
- "4771-29403-0025": {
27
- "input": {
28
- "length_ms": 170,
29
- "path": "/tmp/file1.flac"
30
- },
31
- "output": {
32
- "text": "HELLO \n",
33
- "token": "HE LLO",
34
- "tokenid": "4815, 861"
35
- }
36
- },
37
- "1564-142299-0096": {
38
- ...
39
- }
40
- }
41
- """
42
- if not os.path.isfile(data_json_path):
43
- raise FileNotFoundError("Dataset not found: {}".format(data_json_path))
44
- with open(data_json_path, "rb") as f:
45
- data_samples = json.load(f)["utts"]
46
- assert len(data_samples) != 0
47
- sorted_samples = sorted(
48
- data_samples.items(),
49
- key=lambda sample: int(sample[1]["input"]["length_ms"]),
50
- reverse=True,
51
- )
52
- aud_paths = [s[1]["input"]["path"] for s in sorted_samples]
53
- ids = [s[0] for s in sorted_samples]
54
- speakers = []
55
- for s in sorted_samples:
56
- m = re.search("(.+?)-(.+?)-(.+?)", s[0])
57
- speakers.append(m.group(1) + "_" + m.group(2))
58
- frame_sizes = [s[1]["input"]["length_ms"] for s in sorted_samples]
59
- tgt = [
60
- [int(i) for i in s[1]["output"]["tokenid"].split(", ")]
61
- for s in sorted_samples
62
- ]
63
- # append eos
64
- tgt = [[*t, tgt_dict.eos()] for t in tgt]
65
- return AsrDataset(aud_paths, frame_sizes, tgt, tgt_dict, ids, speakers)
66
-
67
-
68
- @register_task("speech_recognition")
69
- class SpeechRecognitionTask(LegacyFairseqTask):
70
- """
71
- Task for training speech recognition model.
72
- """
73
-
74
- @staticmethod
75
- def add_args(parser):
76
- """Add task-specific arguments to the parser."""
77
- parser.add_argument("data", help="path to data directory")
78
- parser.add_argument(
79
- "--silence-token", default="\u2581", help="token for silence (used by w2l)"
80
- )
81
- parser.add_argument(
82
- "--max-source-positions",
83
- default=sys.maxsize,
84
- type=int,
85
- metavar="N",
86
- help="max number of frames in the source sequence",
87
- )
88
- parser.add_argument(
89
- "--max-target-positions",
90
- default=1024,
91
- type=int,
92
- metavar="N",
93
- help="max number of tokens in the target sequence",
94
- )
95
-
96
- def __init__(self, args, tgt_dict):
97
- super().__init__(args)
98
- self.tgt_dict = tgt_dict
99
-
100
- @classmethod
101
- def setup_task(cls, args, **kwargs):
102
- """Setup the task (e.g., load dictionaries)."""
103
- dict_path = os.path.join(args.data, "dict.txt")
104
- if not os.path.isfile(dict_path):
105
- raise FileNotFoundError("Dict not found: {}".format(dict_path))
106
- tgt_dict = Dictionary.load(dict_path)
107
-
108
- if args.criterion == "ctc_loss":
109
- tgt_dict.add_symbol("<ctc_blank>")
110
- elif args.criterion == "asg_loss":
111
- for i in range(1, args.max_replabel + 1):
112
- tgt_dict.add_symbol(replabel_symbol(i))
113
-
114
- print("| dictionary: {} types".format(len(tgt_dict)))
115
- return cls(args, tgt_dict)
116
-
117
- def load_dataset(self, split, combine=False, **kwargs):
118
- """Load a given dataset split.
119
-
120
- Args:
121
- split (str): name of the split (e.g., train, valid, test)
122
- """
123
- data_json_path = os.path.join(self.args.data, "{}.json".format(split))
124
- self.datasets[split] = get_asr_dataset_from_json(data_json_path, self.tgt_dict)
125
-
126
- def build_generator(self, models, args, **unused):
127
- w2l_decoder = getattr(args, "w2l_decoder", None)
128
- if w2l_decoder == "viterbi":
129
- from examples.speech_recognition.w2l_decoder import W2lViterbiDecoder
130
-
131
- return W2lViterbiDecoder(args, self.target_dictionary)
132
- elif w2l_decoder == "kenlm":
133
- from examples.speech_recognition.w2l_decoder import W2lKenLMDecoder
134
-
135
- return W2lKenLMDecoder(args, self.target_dictionary)
136
- elif w2l_decoder == "fairseqlm":
137
- from examples.speech_recognition.w2l_decoder import W2lFairseqLMDecoder
138
-
139
- return W2lFairseqLMDecoder(args, self.target_dictionary)
140
- else:
141
- return super().build_generator(models, args)
142
-
143
- @property
144
- def target_dictionary(self):
145
- """Return the :class:`~fairseq.data.Dictionary` for the language
146
- model."""
147
- return self.tgt_dict
148
-
149
- @property
150
- def source_dictionary(self):
151
- """Return the source :class:`~fairseq.data.Dictionary` (if applicable
152
- for this task)."""
153
- return None
154
-
155
- def max_positions(self):
156
- """Return the max speech and sentence length allowed by the task."""
157
- return (self.args.max_source_positions, self.args.max_target_positions)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/utils/wer_utils.py DELETED
@@ -1,380 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- from __future__ import absolute_import, division, print_function, unicode_literals
9
-
10
- import re
11
- from collections import deque
12
- from enum import Enum
13
-
14
- import numpy as np
15
-
16
- """
17
- Utility modules for computation of Word Error Rate,
18
- Alignments, as well as more granular metrics like
19
- deletion, insersion and substitutions.
20
- """
21
-
22
-
23
- class Code(Enum):
24
- match = 1
25
- substitution = 2
26
- insertion = 3
27
- deletion = 4
28
-
29
-
30
- class Token(object):
31
- def __init__(self, lbl="", st=np.nan, en=np.nan):
32
- if np.isnan(st):
33
- self.label, self.start, self.end = "", 0.0, 0.0
34
- else:
35
- self.label, self.start, self.end = lbl, st, en
36
-
37
-
38
- class AlignmentResult(object):
39
- def __init__(self, refs, hyps, codes, score):
40
- self.refs = refs # std::deque<int>
41
- self.hyps = hyps # std::deque<int>
42
- self.codes = codes # std::deque<Code>
43
- self.score = score # float
44
-
45
-
46
- def coordinate_to_offset(row, col, ncols):
47
- return int(row * ncols + col)
48
-
49
-
50
- def offset_to_row(offset, ncols):
51
- return int(offset / ncols)
52
-
53
-
54
- def offset_to_col(offset, ncols):
55
- return int(offset % ncols)
56
-
57
-
58
- def trimWhitespace(str):
59
- return re.sub(" +", " ", re.sub(" *$", "", re.sub("^ *", "", str)))
60
-
61
-
62
- def str2toks(str):
63
- pieces = trimWhitespace(str).split(" ")
64
- toks = []
65
- for p in pieces:
66
- toks.append(Token(p, 0.0, 0.0))
67
- return toks
68
-
69
-
70
- class EditDistance(object):
71
- def __init__(self, time_mediated):
72
- self.time_mediated_ = time_mediated
73
- self.scores_ = np.nan # Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>
74
- self.backtraces_ = (
75
- np.nan
76
- ) # Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic> backtraces_;
77
- self.confusion_pairs_ = {}
78
-
79
- def cost(self, ref, hyp, code):
80
- if self.time_mediated_:
81
- if code == Code.match:
82
- return abs(ref.start - hyp.start) + abs(ref.end - hyp.end)
83
- elif code == Code.insertion:
84
- return hyp.end - hyp.start
85
- elif code == Code.deletion:
86
- return ref.end - ref.start
87
- else: # substitution
88
- return abs(ref.start - hyp.start) + abs(ref.end - hyp.end) + 0.1
89
- else:
90
- if code == Code.match:
91
- return 0
92
- elif code == Code.insertion or code == Code.deletion:
93
- return 3
94
- else: # substitution
95
- return 4
96
-
97
- def get_result(self, refs, hyps):
98
- res = AlignmentResult(refs=deque(), hyps=deque(), codes=deque(), score=np.nan)
99
-
100
- num_rows, num_cols = self.scores_.shape
101
- res.score = self.scores_[num_rows - 1, num_cols - 1]
102
-
103
- curr_offset = coordinate_to_offset(num_rows - 1, num_cols - 1, num_cols)
104
-
105
- while curr_offset != 0:
106
- curr_row = offset_to_row(curr_offset, num_cols)
107
- curr_col = offset_to_col(curr_offset, num_cols)
108
-
109
- prev_offset = self.backtraces_[curr_row, curr_col]
110
-
111
- prev_row = offset_to_row(prev_offset, num_cols)
112
- prev_col = offset_to_col(prev_offset, num_cols)
113
-
114
- res.refs.appendleft(curr_row - 1) # Note: this was .push_front() in C++
115
- res.hyps.appendleft(curr_col - 1)
116
- if curr_row - 1 == prev_row and curr_col == prev_col:
117
- res.codes.appendleft(Code.deletion)
118
- elif curr_row == prev_row and curr_col - 1 == prev_col:
119
- res.codes.appendleft(Code.insertion)
120
- else:
121
- # assert(curr_row - 1 == prev_row and curr_col - 1 == prev_col)
122
- ref_str = refs[res.refs[0]].label
123
- hyp_str = hyps[res.hyps[0]].label
124
-
125
- if ref_str == hyp_str:
126
- res.codes.appendleft(Code.match)
127
- else:
128
- res.codes.appendleft(Code.substitution)
129
-
130
- confusion_pair = "%s -> %s" % (ref_str, hyp_str)
131
- if confusion_pair not in self.confusion_pairs_:
132
- self.confusion_pairs_[confusion_pair] = 1
133
- else:
134
- self.confusion_pairs_[confusion_pair] += 1
135
-
136
- curr_offset = prev_offset
137
-
138
- return res
139
-
140
- def align(self, refs, hyps):
141
- if len(refs) == 0 and len(hyps) == 0:
142
- return np.nan
143
-
144
- # NOTE: we're not resetting the values in these matrices because every value
145
- # will be overridden in the loop below. If this assumption doesn't hold,
146
- # be sure to set all entries in self.scores_ and self.backtraces_ to 0.
147
- self.scores_ = np.zeros((len(refs) + 1, len(hyps) + 1))
148
- self.backtraces_ = np.zeros((len(refs) + 1, len(hyps) + 1))
149
-
150
- num_rows, num_cols = self.scores_.shape
151
-
152
- for i in range(num_rows):
153
- for j in range(num_cols):
154
- if i == 0 and j == 0:
155
- self.scores_[i, j] = 0.0
156
- self.backtraces_[i, j] = 0
157
- continue
158
-
159
- if i == 0:
160
- self.scores_[i, j] = self.scores_[i, j - 1] + self.cost(
161
- None, hyps[j - 1], Code.insertion
162
- )
163
- self.backtraces_[i, j] = coordinate_to_offset(i, j - 1, num_cols)
164
- continue
165
-
166
- if j == 0:
167
- self.scores_[i, j] = self.scores_[i - 1, j] + self.cost(
168
- refs[i - 1], None, Code.deletion
169
- )
170
- self.backtraces_[i, j] = coordinate_to_offset(i - 1, j, num_cols)
171
- continue
172
-
173
- # Below here both i and j are greater than 0
174
- ref = refs[i - 1]
175
- hyp = hyps[j - 1]
176
- best_score = self.scores_[i - 1, j - 1] + (
177
- self.cost(ref, hyp, Code.match)
178
- if (ref.label == hyp.label)
179
- else self.cost(ref, hyp, Code.substitution)
180
- )
181
-
182
- prev_row = i - 1
183
- prev_col = j - 1
184
- ins = self.scores_[i, j - 1] + self.cost(None, hyp, Code.insertion)
185
- if ins < best_score:
186
- best_score = ins
187
- prev_row = i
188
- prev_col = j - 1
189
-
190
- delt = self.scores_[i - 1, j] + self.cost(ref, None, Code.deletion)
191
- if delt < best_score:
192
- best_score = delt
193
- prev_row = i - 1
194
- prev_col = j
195
-
196
- self.scores_[i, j] = best_score
197
- self.backtraces_[i, j] = coordinate_to_offset(
198
- prev_row, prev_col, num_cols
199
- )
200
-
201
- return self.get_result(refs, hyps)
202
-
203
-
204
- class WERTransformer(object):
205
- def __init__(self, hyp_str, ref_str, verbose=True):
206
- self.ed_ = EditDistance(False)
207
- self.id2oracle_errs_ = {}
208
- self.utts_ = 0
209
- self.words_ = 0
210
- self.insertions_ = 0
211
- self.deletions_ = 0
212
- self.substitutions_ = 0
213
-
214
- self.process(["dummy_str", hyp_str, ref_str])
215
-
216
- if verbose:
217
- print("'%s' vs '%s'" % (hyp_str, ref_str))
218
- self.report_result()
219
-
220
- def process(self, input): # std::vector<std::string>&& input
221
- if len(input) < 3:
222
- print(
223
- "Input must be of the form <id> ... <hypo> <ref> , got ",
224
- len(input),
225
- " inputs:",
226
- )
227
- return None
228
-
229
- # Align
230
- # std::vector<Token> hyps;
231
- # std::vector<Token> refs;
232
-
233
- hyps = str2toks(input[-2])
234
- refs = str2toks(input[-1])
235
-
236
- alignment = self.ed_.align(refs, hyps)
237
- if alignment is None:
238
- print("Alignment is null")
239
- return np.nan
240
-
241
- # Tally errors
242
- ins = 0
243
- dels = 0
244
- subs = 0
245
- for code in alignment.codes:
246
- if code == Code.substitution:
247
- subs += 1
248
- elif code == Code.insertion:
249
- ins += 1
250
- elif code == Code.deletion:
251
- dels += 1
252
-
253
- # Output
254
- row = input
255
- row.append(str(len(refs)))
256
- row.append(str(ins))
257
- row.append(str(dels))
258
- row.append(str(subs))
259
- # print(row)
260
-
261
- # Accumulate
262
- kIdIndex = 0
263
- kNBestSep = "/"
264
-
265
- pieces = input[kIdIndex].split(kNBestSep)
266
-
267
- if len(pieces) == 0:
268
- print(
269
- "Error splitting ",
270
- input[kIdIndex],
271
- " on '",
272
- kNBestSep,
273
- "', got empty list",
274
- )
275
- return np.nan
276
-
277
- id = pieces[0]
278
- if id not in self.id2oracle_errs_:
279
- self.utts_ += 1
280
- self.words_ += len(refs)
281
- self.insertions_ += ins
282
- self.deletions_ += dels
283
- self.substitutions_ += subs
284
- self.id2oracle_errs_[id] = [ins, dels, subs]
285
- else:
286
- curr_err = ins + dels + subs
287
- prev_err = np.sum(self.id2oracle_errs_[id])
288
- if curr_err < prev_err:
289
- self.id2oracle_errs_[id] = [ins, dels, subs]
290
-
291
- return 0
292
-
293
- def report_result(self):
294
- # print("---------- Summary ---------------")
295
- if self.words_ == 0:
296
- print("No words counted")
297
- return
298
-
299
- # 1-best
300
- best_wer = (
301
- 100.0
302
- * (self.insertions_ + self.deletions_ + self.substitutions_)
303
- / self.words_
304
- )
305
-
306
- print(
307
- "\tWER = %0.2f%% (%i utts, %i words, %0.2f%% ins, "
308
- "%0.2f%% dels, %0.2f%% subs)"
309
- % (
310
- best_wer,
311
- self.utts_,
312
- self.words_,
313
- 100.0 * self.insertions_ / self.words_,
314
- 100.0 * self.deletions_ / self.words_,
315
- 100.0 * self.substitutions_ / self.words_,
316
- )
317
- )
318
-
319
- def wer(self):
320
- if self.words_ == 0:
321
- wer = np.nan
322
- else:
323
- wer = (
324
- 100.0
325
- * (self.insertions_ + self.deletions_ + self.substitutions_)
326
- / self.words_
327
- )
328
- return wer
329
-
330
- def stats(self):
331
- if self.words_ == 0:
332
- stats = {}
333
- else:
334
- wer = (
335
- 100.0
336
- * (self.insertions_ + self.deletions_ + self.substitutions_)
337
- / self.words_
338
- )
339
- stats = dict(
340
- {
341
- "wer": wer,
342
- "utts": self.utts_,
343
- "numwords": self.words_,
344
- "ins": self.insertions_,
345
- "dels": self.deletions_,
346
- "subs": self.substitutions_,
347
- "confusion_pairs": self.ed_.confusion_pairs_,
348
- }
349
- )
350
- return stats
351
-
352
-
353
- def calc_wer(hyp_str, ref_str):
354
- t = WERTransformer(hyp_str, ref_str, verbose=0)
355
- return t.wer()
356
-
357
-
358
- def calc_wer_stats(hyp_str, ref_str):
359
- t = WERTransformer(hyp_str, ref_str, verbose=0)
360
- return t.stats()
361
-
362
-
363
- def get_wer_alignment_codes(hyp_str, ref_str):
364
- """
365
- INPUT: hypothesis string, reference string
366
- OUTPUT: List of alignment codes (intermediate results from WER computation)
367
- """
368
- t = WERTransformer(hyp_str, ref_str, verbose=0)
369
- return t.ed_.align(str2toks(ref_str), str2toks(hyp_str)).codes
370
-
371
-
372
- def merge_counts(x, y):
373
- # Merge two hashes which have 'counts' as their values
374
- # This can be used for example to merge confusion pair counts
375
- # conf_pairs = merge_counts(conf_pairs, stats['confusion_pairs'])
376
- for k, v in y.items():
377
- if k not in x:
378
- x[k] = 0
379
- x[k] += v
380
- return x
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_recognition/w2l_decoder.py DELETED
@@ -1,491 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- # Copyright (c) Facebook, Inc. and its affiliates.
4
- #
5
- # This source code is licensed under the MIT license found in the
6
- # LICENSE file in the root directory of this source tree.
7
-
8
- """
9
- Flashlight decoders.
10
- """
11
-
12
- import gc
13
- import itertools as it
14
- import os.path as osp
15
- import warnings
16
- from collections import deque, namedtuple
17
- from typing import List
18
-
19
- import numpy as np
20
- import torch
21
- from examples.speech_recognition.data.replabels import unpack_replabels
22
- from fairseq import tasks
23
- from fairseq.dataclass.utils import convert_namespace_to_omegaconf
24
- from fairseq.utils import apply_to_sample
25
- from omegaconf import open_dict
26
-
27
- try:
28
- from flashlight.lib.sequence.criterion import CpuViterbiPath, get_data_ptr_as_bytes
29
- from flashlight.lib.text.decoder import (
30
- LM,
31
- CriterionType,
32
- KenLM,
33
- LexiconDecoder,
34
- LexiconDecoderOptions,
35
- LMState,
36
- SmearingMode,
37
- Trie,
38
- )
39
- from flashlight.lib.text.dictionary import create_word_dict, load_words
40
- except:
41
- warnings.warn(
42
- "flashlight python bindings are required to use this functionality. Please install from https://github.com/facebookresearch/flashlight/tree/master/bindings/python"
43
- )
44
- LM = object
45
- LMState = object
46
-
47
-
48
- class W2lDecoder(object):
49
- def __init__(self, args, tgt_dict):
50
- self.tgt_dict = tgt_dict
51
- self.vocab_size = len(tgt_dict)
52
- self.nbest = args.nbest
53
-
54
- # criterion-specific init
55
- self.criterion_type = CriterionType.CTC
56
- self.blank = (
57
- tgt_dict.index("<ctc_blank>")
58
- if "<ctc_blank>" in tgt_dict.indices
59
- else tgt_dict.bos()
60
- )
61
- if "<sep>" in tgt_dict.indices:
62
- self.silence = tgt_dict.index("<sep>")
63
- elif "|" in tgt_dict.indices:
64
- self.silence = tgt_dict.index("|")
65
- else:
66
- self.silence = tgt_dict.eos()
67
- self.asg_transitions = None
68
-
69
- def generate(self, models, sample, **unused):
70
- """Generate a batch of inferences."""
71
- # model.forward normally channels prev_output_tokens into the decoder
72
- # separately, but SequenceGenerator directly calls model.encoder
73
- encoder_input = {
74
- k: v for k, v in sample["net_input"].items() if k != "prev_output_tokens"
75
- }
76
- emissions = self.get_emissions(models, encoder_input)
77
- return self.decode(emissions)
78
-
79
- def get_emissions(self, models, encoder_input):
80
- """Run encoder and normalize emissions"""
81
- model = models[0]
82
- encoder_out = model(**encoder_input)
83
- if hasattr(model, "get_logits"):
84
- emissions = model.get_logits(encoder_out) # no need to normalize emissions
85
- else:
86
- emissions = model.get_normalized_probs(encoder_out, log_probs=True)
87
- return emissions.transpose(0, 1).float().cpu().contiguous()
88
-
89
- def get_tokens(self, idxs):
90
- """Normalize tokens by handling CTC blank, ASG replabels, etc."""
91
- idxs = (g[0] for g in it.groupby(idxs))
92
- idxs = filter(lambda x: x != self.blank, idxs)
93
- return torch.LongTensor(list(idxs))
94
-
95
-
96
- class W2lViterbiDecoder(W2lDecoder):
97
- def __init__(self, args, tgt_dict):
98
- super().__init__(args, tgt_dict)
99
-
100
- def decode(self, emissions):
101
- B, T, N = emissions.size()
102
- hypos = []
103
- if self.asg_transitions is None:
104
- transitions = torch.FloatTensor(N, N).zero_()
105
- else:
106
- transitions = torch.FloatTensor(self.asg_transitions).view(N, N)
107
- viterbi_path = torch.IntTensor(B, T)
108
- workspace = torch.ByteTensor(CpuViterbiPath.get_workspace_size(B, T, N))
109
- CpuViterbiPath.compute(
110
- B,
111
- T,
112
- N,
113
- get_data_ptr_as_bytes(emissions),
114
- get_data_ptr_as_bytes(transitions),
115
- get_data_ptr_as_bytes(viterbi_path),
116
- get_data_ptr_as_bytes(workspace),
117
- )
118
- return [
119
- [{"tokens": self.get_tokens(viterbi_path[b].tolist()), "score": 0}]
120
- for b in range(B)
121
- ]
122
-
123
-
124
- class W2lKenLMDecoder(W2lDecoder):
125
- def __init__(self, args, tgt_dict):
126
- super().__init__(args, tgt_dict)
127
-
128
- self.unit_lm = getattr(args, "unit_lm", False)
129
-
130
- if args.lexicon:
131
- self.lexicon = load_words(args.lexicon)
132
- self.word_dict = create_word_dict(self.lexicon)
133
- self.unk_word = self.word_dict.get_index("<unk>")
134
-
135
- self.lm = KenLM(args.kenlm_model, self.word_dict)
136
- self.trie = Trie(self.vocab_size, self.silence)
137
-
138
- start_state = self.lm.start(False)
139
- for i, (word, spellings) in enumerate(self.lexicon.items()):
140
- word_idx = self.word_dict.get_index(word)
141
- _, score = self.lm.score(start_state, word_idx)
142
- for spelling in spellings:
143
- spelling_idxs = [tgt_dict.index(token) for token in spelling]
144
- assert (
145
- tgt_dict.unk() not in spelling_idxs
146
- ), f"{spelling} {spelling_idxs}"
147
- self.trie.insert(spelling_idxs, word_idx, score)
148
- self.trie.smear(SmearingMode.MAX)
149
-
150
- self.decoder_opts = LexiconDecoderOptions(
151
- beam_size=args.beam,
152
- beam_size_token=int(getattr(args, "beam_size_token", len(tgt_dict))),
153
- beam_threshold=args.beam_threshold,
154
- lm_weight=args.lm_weight,
155
- word_score=args.word_score,
156
- unk_score=args.unk_weight,
157
- sil_score=args.sil_weight,
158
- log_add=False,
159
- criterion_type=self.criterion_type,
160
- )
161
-
162
- if self.asg_transitions is None:
163
- N = 768
164
- # self.asg_transitions = torch.FloatTensor(N, N).zero_()
165
- self.asg_transitions = []
166
-
167
- self.decoder = LexiconDecoder(
168
- self.decoder_opts,
169
- self.trie,
170
- self.lm,
171
- self.silence,
172
- self.blank,
173
- self.unk_word,
174
- self.asg_transitions,
175
- self.unit_lm,
176
- )
177
- else:
178
- assert args.unit_lm, "lexicon free decoding can only be done with a unit language model"
179
- from flashlight.lib.text.decoder import (
180
- LexiconFreeDecoder,
181
- LexiconFreeDecoderOptions,
182
- )
183
-
184
- d = {w: [[w]] for w in tgt_dict.symbols}
185
- self.word_dict = create_word_dict(d)
186
- self.lm = KenLM(args.kenlm_model, self.word_dict)
187
- self.decoder_opts = LexiconFreeDecoderOptions(
188
- beam_size=args.beam,
189
- beam_size_token=int(getattr(args, "beam_size_token", len(tgt_dict))),
190
- beam_threshold=args.beam_threshold,
191
- lm_weight=args.lm_weight,
192
- sil_score=args.sil_weight,
193
- log_add=False,
194
- criterion_type=self.criterion_type,
195
- )
196
- self.decoder = LexiconFreeDecoder(
197
- self.decoder_opts, self.lm, self.silence, self.blank, []
198
- )
199
-
200
- def get_timesteps(self, token_idxs: List[int]) -> List[int]:
201
- """Returns frame numbers corresponding to every non-blank token.
202
-
203
- Parameters
204
- ----------
205
- token_idxs : List[int]
206
- IDs of decoded tokens.
207
-
208
- Returns
209
- -------
210
- List[int]
211
- Frame numbers corresponding to every non-blank token.
212
- """
213
- timesteps = []
214
- for i, token_idx in enumerate(token_idxs):
215
- if token_idx == self.blank:
216
- continue
217
- if i == 0 or token_idx != token_idxs[i-1]:
218
- timesteps.append(i)
219
- return timesteps
220
-
221
- def decode(self, emissions):
222
- B, T, N = emissions.size()
223
- hypos = []
224
- for b in range(B):
225
- emissions_ptr = emissions.data_ptr() + 4 * b * emissions.stride(0)
226
- results = self.decoder.decode(emissions_ptr, T, N)
227
-
228
- nbest_results = results[: self.nbest]
229
- hypos.append(
230
- [
231
- {
232
- "tokens": self.get_tokens(result.tokens),
233
- "score": result.score,
234
- "timesteps": self.get_timesteps(result.tokens),
235
- "words": [
236
- self.word_dict.get_entry(x) for x in result.words if x >= 0
237
- ],
238
- }
239
- for result in nbest_results
240
- ]
241
- )
242
- return hypos
243
-
244
-
245
- FairseqLMState = namedtuple("FairseqLMState", ["prefix", "incremental_state", "probs"])
246
-
247
-
248
- class FairseqLM(LM):
249
- def __init__(self, dictionary, model):
250
- LM.__init__(self)
251
- self.dictionary = dictionary
252
- self.model = model
253
- self.unk = self.dictionary.unk()
254
-
255
- self.save_incremental = False # this currently does not work properly
256
- self.max_cache = 20_000
257
-
258
- model.cuda()
259
- model.eval()
260
- model.make_generation_fast_()
261
-
262
- self.states = {}
263
- self.stateq = deque()
264
-
265
- def start(self, start_with_nothing):
266
- state = LMState()
267
- prefix = torch.LongTensor([[self.dictionary.eos()]])
268
- incremental_state = {} if self.save_incremental else None
269
- with torch.no_grad():
270
- res = self.model(prefix.cuda(), incremental_state=incremental_state)
271
- probs = self.model.get_normalized_probs(res, log_probs=True, sample=None)
272
-
273
- if incremental_state is not None:
274
- incremental_state = apply_to_sample(lambda x: x.cpu(), incremental_state)
275
- self.states[state] = FairseqLMState(
276
- prefix.numpy(), incremental_state, probs[0, -1].cpu().numpy()
277
- )
278
- self.stateq.append(state)
279
-
280
- return state
281
-
282
- def score(self, state: LMState, token_index: int, no_cache: bool = False):
283
- """
284
- Evaluate language model based on the current lm state and new word
285
- Parameters:
286
- -----------
287
- state: current lm state
288
- token_index: index of the word
289
- (can be lexicon index then you should store inside LM the
290
- mapping between indices of lexicon and lm, or lm index of a word)
291
-
292
- Returns:
293
- --------
294
- (LMState, float): pair of (new state, score for the current word)
295
- """
296
- curr_state = self.states[state]
297
-
298
- def trim_cache(targ_size):
299
- while len(self.stateq) > targ_size:
300
- rem_k = self.stateq.popleft()
301
- rem_st = self.states[rem_k]
302
- rem_st = FairseqLMState(rem_st.prefix, None, None)
303
- self.states[rem_k] = rem_st
304
-
305
- if curr_state.probs is None:
306
- new_incremental_state = (
307
- curr_state.incremental_state.copy()
308
- if curr_state.incremental_state is not None
309
- else None
310
- )
311
- with torch.no_grad():
312
- if new_incremental_state is not None:
313
- new_incremental_state = apply_to_sample(
314
- lambda x: x.cuda(), new_incremental_state
315
- )
316
- elif self.save_incremental:
317
- new_incremental_state = {}
318
-
319
- res = self.model(
320
- torch.from_numpy(curr_state.prefix).cuda(),
321
- incremental_state=new_incremental_state,
322
- )
323
- probs = self.model.get_normalized_probs(
324
- res, log_probs=True, sample=None
325
- )
326
-
327
- if new_incremental_state is not None:
328
- new_incremental_state = apply_to_sample(
329
- lambda x: x.cpu(), new_incremental_state
330
- )
331
-
332
- curr_state = FairseqLMState(
333
- curr_state.prefix, new_incremental_state, probs[0, -1].cpu().numpy()
334
- )
335
-
336
- if not no_cache:
337
- self.states[state] = curr_state
338
- self.stateq.append(state)
339
-
340
- score = curr_state.probs[token_index].item()
341
-
342
- trim_cache(self.max_cache)
343
-
344
- outstate = state.child(token_index)
345
- if outstate not in self.states and not no_cache:
346
- prefix = np.concatenate(
347
- [curr_state.prefix, torch.LongTensor([[token_index]])], -1
348
- )
349
- incr_state = curr_state.incremental_state
350
-
351
- self.states[outstate] = FairseqLMState(prefix, incr_state, None)
352
-
353
- if token_index == self.unk:
354
- score = float("-inf")
355
-
356
- return outstate, score
357
-
358
- def finish(self, state: LMState):
359
- """
360
- Evaluate eos for language model based on the current lm state
361
-
362
- Returns:
363
- --------
364
- (LMState, float): pair of (new state, score for the current word)
365
- """
366
- return self.score(state, self.dictionary.eos())
367
-
368
- def empty_cache(self):
369
- self.states = {}
370
- self.stateq = deque()
371
- gc.collect()
372
-
373
-
374
- class W2lFairseqLMDecoder(W2lDecoder):
375
- def __init__(self, args, tgt_dict):
376
- super().__init__(args, tgt_dict)
377
-
378
- self.unit_lm = getattr(args, "unit_lm", False)
379
-
380
- self.lexicon = load_words(args.lexicon) if args.lexicon else None
381
- self.idx_to_wrd = {}
382
-
383
- checkpoint = torch.load(args.kenlm_model, map_location="cpu")
384
-
385
- if "cfg" in checkpoint and checkpoint["cfg"] is not None:
386
- lm_args = checkpoint["cfg"]
387
- else:
388
- lm_args = convert_namespace_to_omegaconf(checkpoint["args"])
389
-
390
- with open_dict(lm_args.task):
391
- lm_args.task.data = osp.dirname(args.kenlm_model)
392
-
393
- task = tasks.setup_task(lm_args.task)
394
- model = task.build_model(lm_args.model)
395
- model.load_state_dict(checkpoint["model"], strict=False)
396
-
397
- self.trie = Trie(self.vocab_size, self.silence)
398
-
399
- self.word_dict = task.dictionary
400
- self.unk_word = self.word_dict.unk()
401
- self.lm = FairseqLM(self.word_dict, model)
402
-
403
- if self.lexicon:
404
- start_state = self.lm.start(False)
405
- for i, (word, spellings) in enumerate(self.lexicon.items()):
406
- if self.unit_lm:
407
- word_idx = i
408
- self.idx_to_wrd[i] = word
409
- score = 0
410
- else:
411
- word_idx = self.word_dict.index(word)
412
- _, score = self.lm.score(start_state, word_idx, no_cache=True)
413
-
414
- for spelling in spellings:
415
- spelling_idxs = [tgt_dict.index(token) for token in spelling]
416
- assert (
417
- tgt_dict.unk() not in spelling_idxs
418
- ), f"{spelling} {spelling_idxs}"
419
- self.trie.insert(spelling_idxs, word_idx, score)
420
- self.trie.smear(SmearingMode.MAX)
421
-
422
- self.decoder_opts = LexiconDecoderOptions(
423
- beam_size=args.beam,
424
- beam_size_token=int(getattr(args, "beam_size_token", len(tgt_dict))),
425
- beam_threshold=args.beam_threshold,
426
- lm_weight=args.lm_weight,
427
- word_score=args.word_score,
428
- unk_score=args.unk_weight,
429
- sil_score=args.sil_weight,
430
- log_add=False,
431
- criterion_type=self.criterion_type,
432
- )
433
-
434
- self.decoder = LexiconDecoder(
435
- self.decoder_opts,
436
- self.trie,
437
- self.lm,
438
- self.silence,
439
- self.blank,
440
- self.unk_word,
441
- [],
442
- self.unit_lm,
443
- )
444
- else:
445
- assert args.unit_lm, "lexicon free decoding can only be done with a unit language model"
446
- from flashlight.lib.text.decoder import (
447
- LexiconFreeDecoder,
448
- LexiconFreeDecoderOptions,
449
- )
450
-
451
- d = {w: [[w]] for w in tgt_dict.symbols}
452
- self.word_dict = create_word_dict(d)
453
- self.lm = KenLM(args.kenlm_model, self.word_dict)
454
- self.decoder_opts = LexiconFreeDecoderOptions(
455
- beam_size=args.beam,
456
- beam_size_token=int(getattr(args, "beam_size_token", len(tgt_dict))),
457
- beam_threshold=args.beam_threshold,
458
- lm_weight=args.lm_weight,
459
- sil_score=args.sil_weight,
460
- log_add=False,
461
- criterion_type=self.criterion_type,
462
- )
463
- self.decoder = LexiconFreeDecoder(
464
- self.decoder_opts, self.lm, self.silence, self.blank, []
465
- )
466
-
467
- def decode(self, emissions):
468
- B, T, N = emissions.size()
469
- hypos = []
470
-
471
- def idx_to_word(idx):
472
- if self.unit_lm:
473
- return self.idx_to_wrd[idx]
474
- else:
475
- return self.word_dict[idx]
476
-
477
- def make_hypo(result):
478
- hypo = {"tokens": self.get_tokens(result.tokens), "score": result.score}
479
- if self.lexicon:
480
- hypo["words"] = [idx_to_word(x) for x in result.words if x >= 0]
481
- return hypo
482
-
483
- for b in range(B):
484
- emissions_ptr = emissions.data_ptr() + 4 * b * emissions.stride(0)
485
- results = self.decoder.decode(emissions_ptr, T, N)
486
-
487
- nbest_results = results[: self.nbest]
488
- hypos.append([make_hypo(result) for result in nbest_results])
489
- self.lm.empty_cache()
490
-
491
- return hypos
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/README.md DELETED
@@ -1,38 +0,0 @@
1
- Speech Synthesis (S^2)
2
- ===
3
- [https://arxiv.org/abs/2109.06912](https://arxiv.org/abs/2109.06912)
4
-
5
- Speech synthesis with fairseq.
6
-
7
- ## Features
8
-
9
- - Autoregressive and non-autoregressive models
10
- - Multi-speaker synthesis
11
- - Audio preprocessing (denoising, VAD, etc.) for less curated data
12
- - Automatic metrics for model development
13
- - Similar data configuration as [S2T](../speech_to_text/README.md)
14
-
15
-
16
- ## Examples
17
- - [Single-speaker synthesis on LJSpeech](docs/ljspeech_example.md)
18
- - [Multi-speaker synthesis on VCTK](docs/vctk_example.md)
19
- - [Multi-speaker synthesis on Common Voice](docs/common_voice_example.md)
20
-
21
-
22
- ## Citation
23
- Please cite as:
24
- ```
25
- @article{wang2021fairseqs2,
26
- title={fairseq S\^{} 2: A Scalable and Integrable Speech Synthesis Toolkit},
27
- author={Wang, Changhan and Hsu, Wei-Ning and Adi, Yossi and Polyak, Adam and Lee, Ann and Chen, Peng-Jen and Gu, Jiatao and Pino, Juan},
28
- journal={arXiv preprint arXiv:2109.06912},
29
- year={2021}
30
- }
31
-
32
- @inproceedings{ott2019fairseq,
33
- title = {fairseq: A Fast, Extensible Toolkit for Sequence Modeling},
34
- author = {Myle Ott and Sergey Edunov and Alexei Baevski and Angela Fan and Sam Gross and Nathan Ng and David Grangier and Michael Auli},
35
- booktitle = {Proceedings of NAACL-HLT 2019: Demonstrations},
36
- year = {2019},
37
- }
38
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/__init__.py DELETED
@@ -1,4 +0,0 @@
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.
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/data_utils.py DELETED
@@ -1,347 +0,0 @@
1
- # Copyright (c) Facebook, Inc. and its affiliates.
2
- #
3
- # This source code is licensed under the MIT license found in the
4
- # LICENSE file in the root directory of this source tree.
5
-
6
- import io
7
- import os
8
- import tempfile
9
- import zipfile
10
- from dataclasses import dataclass
11
- from itertools import groupby
12
- from pathlib import Path
13
- from typing import Dict, List, Optional
14
-
15
- import numpy as np
16
- import torch
17
- import torch.nn.functional as F
18
- from examples.speech_to_text.data_utils import load_tsv_to_dicts
19
- from fairseq.data.audio.audio_utils import (
20
- TTSMelScale,
21
- TTSSpectrogram,
22
- is_npy_data,
23
- parse_path,
24
- read_from_stored_zip,
25
- )
26
- from tqdm import tqdm
27
-
28
-
29
- def trim_or_pad_to_target_length(
30
- data_1d_or_2d: np.ndarray, target_length: int
31
- ) -> np.ndarray:
32
- assert len(data_1d_or_2d.shape) in {1, 2}
33
- delta = data_1d_or_2d.shape[0] - target_length
34
- if delta >= 0: # trim if being longer
35
- data_1d_or_2d = data_1d_or_2d[: target_length]
36
- else: # pad if being shorter
37
- if len(data_1d_or_2d.shape) == 1:
38
- data_1d_or_2d = np.concatenate(
39
- [data_1d_or_2d, np.zeros(-delta)], axis=0
40
- )
41
- else:
42
- data_1d_or_2d = np.concatenate(
43
- [data_1d_or_2d, np.zeros((-delta, data_1d_or_2d.shape[1]))],
44
- axis=0
45
- )
46
- return data_1d_or_2d
47
-
48
-
49
- def extract_logmel_spectrogram(
50
- waveform: torch.Tensor, sample_rate: int,
51
- output_path: Optional[Path] = None, win_length: int = 1024,
52
- hop_length: int = 256, n_fft: int = 1024,
53
- win_fn: callable = torch.hann_window, n_mels: int = 80,
54
- f_min: float = 0., f_max: float = 8000, eps: float = 1e-5,
55
- overwrite: bool = False, target_length: Optional[int] = None
56
- ):
57
- if output_path is not None and output_path.is_file() and not overwrite:
58
- return
59
-
60
- spectrogram_transform = TTSSpectrogram(
61
- n_fft=n_fft, win_length=win_length, hop_length=hop_length,
62
- window_fn=win_fn
63
- )
64
- mel_scale_transform = TTSMelScale(
65
- n_mels=n_mels, sample_rate=sample_rate, f_min=f_min, f_max=f_max,
66
- n_stft=n_fft // 2 + 1
67
- )
68
- spectrogram = spectrogram_transform(waveform)
69
- mel_spec = mel_scale_transform(spectrogram)
70
- logmel_spec = torch.clamp(mel_spec, min=eps).log()
71
- assert len(logmel_spec.shape) == 3 and logmel_spec.shape[0] == 1
72
- logmel_spec = logmel_spec.squeeze().t() # D x T -> T x D
73
- if target_length is not None:
74
- logmel_spec = trim_or_pad_to_target_length(logmel_spec, target_length)
75
-
76
- if output_path is not None:
77
- np.save(output_path.as_posix(), logmel_spec)
78
- else:
79
- return logmel_spec
80
-
81
-
82
- def extract_pitch(
83
- waveform: torch.Tensor, sample_rate: int,
84
- output_path: Optional[Path] = None, hop_length: int = 256,
85
- log_scale: bool = True, phoneme_durations: Optional[List[int]] = None
86
- ):
87
- if output_path is not None and output_path.is_file():
88
- return
89
-
90
- try:
91
- import pyworld
92
- except ImportError:
93
- raise ImportError("Please install PyWORLD: pip install pyworld")
94
-
95
- _waveform = waveform.squeeze(0).double().numpy()
96
- pitch, t = pyworld.dio(
97
- _waveform, sample_rate, frame_period=hop_length / sample_rate * 1000
98
- )
99
- pitch = pyworld.stonemask(_waveform, pitch, t, sample_rate)
100
-
101
- if phoneme_durations is not None:
102
- pitch = trim_or_pad_to_target_length(pitch, sum(phoneme_durations))
103
- try:
104
- from scipy.interpolate import interp1d
105
- except ImportError:
106
- raise ImportError("Please install SciPy: pip install scipy")
107
- nonzero_ids = np.where(pitch != 0)[0]
108
- if len(nonzero_ids) == 0:
109
- print((f"{output_path} has all empty values in the pitch contour"))
110
- return
111
- elif len(nonzero_ids) == 1:
112
- print((f"{output_path} has only one non-zero values in the pitch contour"))
113
- return
114
- else:
115
- interp_fn = interp1d(
116
- nonzero_ids,
117
- pitch[nonzero_ids],
118
- fill_value=(pitch[nonzero_ids[0]], pitch[nonzero_ids[-1]]),
119
- bounds_error=False,
120
- )
121
- pitch = interp_fn(np.arange(0, len(pitch)))
122
- d_cumsum = np.cumsum(np.concatenate([np.array([0]), phoneme_durations]))
123
- pitch = np.array(
124
- [
125
- np.mean(pitch[d_cumsum[i-1]: d_cumsum[i]])
126
- for i in range(1, len(d_cumsum))
127
- ]
128
- )
129
- assert len(pitch) == len(phoneme_durations)
130
-
131
- if log_scale:
132
- pitch = np.log(pitch + 1)
133
-
134
- if output_path is not None:
135
- np.save(output_path.as_posix(), pitch)
136
- else:
137
- return pitch
138
-
139
-
140
- def extract_energy(
141
- waveform: torch.Tensor, output_path: Optional[Path] = None,
142
- hop_length: int = 256, n_fft: int = 1024, log_scale: bool = True,
143
- phoneme_durations: Optional[List[int]] = None
144
- ):
145
- if output_path is not None and output_path.is_file():
146
- return
147
-
148
- assert len(waveform.shape) == 2 and waveform.shape[0] == 1
149
- waveform = waveform.view(1, 1, waveform.shape[1])
150
- waveform = F.pad(
151
- waveform.unsqueeze(1), [n_fft // 2, n_fft // 2, 0, 0],
152
- mode="reflect"
153
- )
154
- waveform = waveform.squeeze(1)
155
-
156
- fourier_basis = np.fft.fft(np.eye(n_fft))
157
- cutoff = int((n_fft / 2 + 1))
158
- fourier_basis = np.vstack(
159
- [np.real(fourier_basis[:cutoff, :]),
160
- np.imag(fourier_basis[:cutoff, :])]
161
- )
162
-
163
- forward_basis = torch.FloatTensor(fourier_basis[:, None, :])
164
- forward_transform = F.conv1d(
165
- waveform, forward_basis, stride=hop_length, padding=0
166
- )
167
-
168
- real_part = forward_transform[:, :cutoff, :]
169
- imag_part = forward_transform[:, cutoff:, :]
170
- magnitude = torch.sqrt(real_part ** 2 + imag_part ** 2)
171
- energy = torch.norm(magnitude, dim=1).squeeze(0).numpy()
172
-
173
- if phoneme_durations is not None:
174
- energy = trim_or_pad_to_target_length(energy, sum(phoneme_durations))
175
- d_cumsum = np.cumsum(np.concatenate([np.array([0]), phoneme_durations]))
176
- energy = np.array(
177
- [
178
- np.mean(energy[d_cumsum[i - 1]: d_cumsum[i]])
179
- for i in range(1, len(d_cumsum))
180
- ]
181
- )
182
- assert len(energy) == len(phoneme_durations)
183
-
184
- if log_scale:
185
- energy = np.log(energy + 1)
186
-
187
- if output_path is not None:
188
- np.save(output_path.as_posix(), energy)
189
- else:
190
- return energy
191
-
192
-
193
- def get_global_cmvn(feature_root: Path, output_path: Optional[Path] = None):
194
- mean_x, mean_x2, n_frames = None, None, 0
195
- feature_paths = feature_root.glob("*.npy")
196
- for p in tqdm(feature_paths):
197
- with open(p, 'rb') as f:
198
- frames = np.load(f).squeeze()
199
-
200
- n_frames += frames.shape[0]
201
-
202
- cur_mean_x = frames.sum(axis=0)
203
- if mean_x is None:
204
- mean_x = cur_mean_x
205
- else:
206
- mean_x += cur_mean_x
207
-
208
- cur_mean_x2 = (frames ** 2).sum(axis=0)
209
- if mean_x2 is None:
210
- mean_x2 = cur_mean_x2
211
- else:
212
- mean_x2 += cur_mean_x2
213
-
214
- mean_x /= n_frames
215
- mean_x2 /= n_frames
216
- var_x = mean_x2 - mean_x ** 2
217
- std_x = np.sqrt(np.maximum(var_x, 1e-10))
218
-
219
- if output_path is not None:
220
- with open(output_path, 'wb') as f:
221
- np.savez(f, mean=mean_x, std=std_x)
222
- else:
223
- return {"mean": mean_x, "std": std_x}
224
-
225
-
226
- def ipa_phonemize(text, lang="en-us", use_g2p=False):
227
- if use_g2p:
228
- assert lang == "en-us", "g2pE phonemizer only works for en-us"
229
- try:
230
- from g2p_en import G2p
231
- g2p = G2p()
232
- return " ".join("|" if p == " " else p for p in g2p(text))
233
- except ImportError:
234
- raise ImportError(
235
- "Please install phonemizer: pip install g2p_en"
236
- )
237
- else:
238
- try:
239
- from phonemizer import phonemize
240
- from phonemizer.separator import Separator
241
- return phonemize(
242
- text, backend='espeak', language=lang,
243
- separator=Separator(word="| ", phone=" ")
244
- )
245
- except ImportError:
246
- raise ImportError(
247
- "Please install phonemizer: pip install phonemizer"
248
- )
249
-
250
-
251
- @dataclass
252
- class ForceAlignmentInfo(object):
253
- tokens: List[str]
254
- frame_durations: List[int]
255
- start_sec: Optional[float]
256
- end_sec: Optional[float]
257
-
258
-
259
- def get_mfa_alignment_by_sample_id(
260
- textgrid_zip_path: str, sample_id: str, sample_rate: int,
261
- hop_length: int, silence_phones: List[str] = ("sil", "sp", "spn")
262
- ) -> ForceAlignmentInfo:
263
- try:
264
- import tgt
265
- except ImportError:
266
- raise ImportError("Please install TextGridTools: pip install tgt")
267
-
268
- filename = f"{sample_id}.TextGrid"
269
- out_root = Path(tempfile.gettempdir())
270
- tgt_path = out_root / filename
271
- with zipfile.ZipFile(textgrid_zip_path) as f_zip:
272
- f_zip.extract(filename, path=out_root)
273
- textgrid = tgt.io.read_textgrid(tgt_path.as_posix())
274
- os.remove(tgt_path)
275
-
276
- phones, frame_durations = [], []
277
- start_sec, end_sec, end_idx = 0, 0, 0
278
- for t in textgrid.get_tier_by_name("phones")._objects:
279
- s, e, p = t.start_time, t.end_time, t.text
280
- # Trim leading silences
281
- if len(phones) == 0:
282
- if p in silence_phones:
283
- continue
284
- else:
285
- start_sec = s
286
- phones.append(p)
287
- if p not in silence_phones:
288
- end_sec = e
289
- end_idx = len(phones)
290
- r = sample_rate / hop_length
291
- frame_durations.append(int(np.round(e * r) - np.round(s * r)))
292
- # Trim tailing silences
293
- phones = phones[:end_idx]
294
- frame_durations = frame_durations[:end_idx]
295
-
296
- return ForceAlignmentInfo(
297
- tokens=phones, frame_durations=frame_durations, start_sec=start_sec,
298
- end_sec=end_sec
299
- )
300
-
301
-
302
- def get_mfa_alignment(
303
- textgrid_zip_path: str, sample_ids: List[str], sample_rate: int,
304
- hop_length: int
305
- ) -> Dict[str, ForceAlignmentInfo]:
306
- return {
307
- i: get_mfa_alignment_by_sample_id(
308
- textgrid_zip_path, i, sample_rate, hop_length
309
- ) for i in tqdm(sample_ids)
310
- }
311
-
312
-
313
- def get_unit_alignment(
314
- id_to_unit_tsv_path: str, sample_ids: List[str]
315
- ) -> Dict[str, ForceAlignmentInfo]:
316
- id_to_units = {
317
- e["id"]: e["units"] for e in load_tsv_to_dicts(id_to_unit_tsv_path)
318
- }
319
- id_to_units = {i: id_to_units[i].split() for i in sample_ids}
320
- id_to_units_collapsed = {
321
- i: [uu for uu, _ in groupby(u)] for i, u in id_to_units.items()
322
- }
323
- id_to_durations = {
324
- i: [len(list(g)) for _, g in groupby(u)] for i, u in id_to_units.items()
325
- }
326
-
327
- return {
328
- i: ForceAlignmentInfo(
329
- tokens=id_to_units_collapsed[i], frame_durations=id_to_durations[i],
330
- start_sec=None, end_sec=None
331
- )
332
- for i in sample_ids
333
- }
334
-
335
-
336
- def get_feature_value_min_max(feature_paths: List[str]):
337
- v_min, v_max = 1e-8, -1e-8
338
- for p in tqdm(feature_paths):
339
- _path, slice_ptr = parse_path(p)
340
- assert len(slice_ptr) == 2
341
- byte_data = read_from_stored_zip(_path, slice_ptr[0], slice_ptr[1])
342
- assert is_npy_data(byte_data)
343
- path_or_fp = io.BytesIO(byte_data)
344
- features = np.load(path_or_fp).squeeze()
345
- v_min = min(v_min, features.min().item())
346
- v_max = max(v_max, features.max().item())
347
- return v_min, v_max
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/common_voice_example.md DELETED
@@ -1,67 +0,0 @@
1
- [[Back]](..)
2
-
3
- # Common Voice
4
-
5
- [Common Voice](https://commonvoice.mozilla.org/en/datasets) is a public domain speech corpus with 11.2K hours of read
6
- speech in 76 languages (the latest version 7.0). We provide examples for building
7
- [Transformer](https://arxiv.org/abs/1809.08895) models on this dataset.
8
-
9
-
10
- ## Data preparation
11
- [Download](https://commonvoice.mozilla.org/en/datasets) and unpack Common Voice v4 to a path `${DATA_ROOT}/${LANG_ID}`.
12
- Create splits and generate audio manifests with
13
- ```bash
14
- python -m examples.speech_synthesis.preprocessing.get_common_voice_audio_manifest \
15
- --data-root ${DATA_ROOT} \
16
- --lang ${LANG_ID} \
17
- --output-manifest-root ${AUDIO_MANIFEST_ROOT} --convert-to-wav
18
- ```
19
-
20
- To denoise audio and trim leading/trailing silence using signal processing based VAD, run
21
- ```bash
22
- for SPLIT in dev test train; do
23
- python -m examples.speech_synthesis.preprocessing.denoise_and_vad_audio \
24
- --audio-manifest ${AUDIO_MANIFEST_ROOT}/${SPLIT}.audio.tsv \
25
- --output-dir ${PROCESSED_DATA_ROOT} \
26
- --denoise --vad --vad-agg-level 2
27
- done
28
- ```
29
-
30
- which generates a new audio TSV manifest under `${PROCESSED_DATA_ROOT}` with updated path to the processed audio and
31
- a new column for SNR.
32
-
33
- To do filtering by CER, follow the [Automatic Evaluation](../docs/ljspeech_example.md#automatic-evaluation) section to
34
- run ASR model (add `--eval-target` to `get_eval_manifest` for evaluation on the reference audio; add `--err-unit char`
35
- to `eval_asr` to compute CER instead of WER). The example-level CER is saved to
36
- `${EVAL_OUTPUT_ROOT}/uer_cer.${SPLIT}.tsv`.
37
-
38
- Then, extract log-Mel spectrograms, generate feature manifest and create data configuration YAML with
39
- ```bash
40
- python -m examples.speech_synthesis.preprocessing.get_feature_manifest \
41
- --audio-manifest-root ${AUDIO_MANIFEST_ROOT} \
42
- --output-root ${FEATURE_MANIFEST_ROOT} \
43
- --ipa-vocab --lang ${LANG_ID} \
44
- --snr-threshold 15 \
45
- --cer-threshold 0.1 --cer-tsv-path ${EVAL_OUTPUT_ROOT}/uer_cer.${SPLIT}.tsv
46
- ```
47
- where we use phoneme inputs (`--ipa-vocab`) as example. For sample filtering, we set the SNR and CER threshold
48
- to 15 and 10%, respectively.
49
-
50
-
51
- ## Training
52
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#transformer).)
53
-
54
-
55
- ## Inference
56
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#inference).)
57
-
58
- ## Automatic Evaluation
59
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#automatic-evaluation).)
60
-
61
- ## Results
62
-
63
- | Language | Speakers | --arch | Params | Test MCD | Model |
64
- |---|---|---|---|---|---|
65
- | English | 200 | tts_transformer | 54M | 3.8 | [Download](https://dl.fbaipublicfiles.com/fairseq/s2/cv4_en200_transformer_phn.tar) |
66
-
67
- [[Back]](..)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/ljspeech_example.md DELETED
@@ -1,137 +0,0 @@
1
- [[Back]](..)
2
-
3
- # LJSpeech
4
-
5
- [LJSpeech](https://keithito.com/LJ-Speech-Dataset) is a public domain TTS
6
- corpus with around 24 hours of English speech sampled at 22.05kHz. We provide examples for building
7
- [Transformer](https://arxiv.org/abs/1809.08895) and [FastSpeech 2](https://arxiv.org/abs/2006.04558)
8
- models on this dataset.
9
-
10
-
11
- ## Data preparation
12
-
13
- Download data, create splits and generate audio manifests with
14
- ```bash
15
- python -m examples.speech_synthesis.preprocessing.get_ljspeech_audio_manifest \
16
- --output-data-root ${AUDIO_DATA_ROOT} \
17
- --output-manifest-root ${AUDIO_MANIFEST_ROOT}
18
- ```
19
-
20
- Then, extract log-Mel spectrograms, generate feature manifest and create data configuration YAML with
21
- ```bash
22
- python -m examples.speech_synthesis.preprocessing.get_feature_manifest \
23
- --audio-manifest-root ${AUDIO_MANIFEST_ROOT} \
24
- --output-root ${FEATURE_MANIFEST_ROOT} \
25
- --ipa-vocab --use-g2p
26
- ```
27
- where we use phoneme inputs (`--ipa-vocab --use-g2p`) as example.
28
-
29
- FastSpeech 2 additionally requires frame durations, pitch and energy as auxiliary training targets.
30
- Add `--add-fastspeech-targets` to include these fields in the feature manifests. We get frame durations either from
31
- phoneme-level force-alignment or frame-level pseudo-text unit sequence. They should be pre-computed and specified via:
32
- - `--textgrid-zip ${TEXT_GRID_ZIP_PATH}` for a ZIP file, inside which there is one
33
- [TextGrid](https://www.fon.hum.uva.nl/praat/manual/TextGrid.html) file per sample to provide force-alignment info.
34
- - `--id-to-units-tsv ${ID_TO_UNIT_TSV}` for a TSV file, where there are 2 columns for sample ID and
35
- space-delimited pseudo-text unit sequence, respectively.
36
-
37
- For your convenience, we provide pre-computed
38
- [force-alignment](https://dl.fbaipublicfiles.com/fairseq/s2/ljspeech_mfa.zip) from
39
- [Montreal Forced Aligner](https://github.com/MontrealCorpusTools/Montreal-Forced-Aligner) and
40
- [pseudo-text units](s3://dl.fbaipublicfiles.com/fairseq/s2/ljspeech_hubert.tsv) from
41
- [HuBERT](https://github.com/pytorch/fairseq/tree/main/examples/hubert). You can also generate them by yourself using
42
- a different software or model.
43
-
44
-
45
- ## Training
46
- #### Transformer
47
- ```bash
48
- fairseq-train ${FEATURE_MANIFEST_ROOT} --save-dir ${SAVE_DIR} \
49
- --config-yaml config.yaml --train-subset train --valid-subset dev \
50
- --num-workers 4 --max-tokens 30000 --max-update 200000 \
51
- --task text_to_speech --criterion tacotron2 --arch tts_transformer \
52
- --clip-norm 5.0 --n-frames-per-step 4 --bce-pos-weight 5.0 \
53
- --dropout 0.1 --attention-dropout 0.1 --activation-dropout 0.1 \
54
- --encoder-normalize-before --decoder-normalize-before \
55
- --optimizer adam --lr 2e-3 --lr-scheduler inverse_sqrt --warmup-updates 4000 \
56
- --seed 1 --update-freq 8 --eval-inference --best-checkpoint-metric mcd_loss
57
- ```
58
- where `SAVE_DIR` is the checkpoint root path. We set `--update-freq 8` to simulate 8 GPUs with 1 GPU. You may want to
59
- update it accordingly when using more than 1 GPU.
60
-
61
- #### FastSpeech2
62
- ```bash
63
- fairseq-train ${FEATURE_MANIFEST_ROOT} --save-dir ${SAVE_DIR} \
64
- --config-yaml config.yaml --train-subset train --valid-subset dev \
65
- --num-workers 4 --max-sentences 6 --max-update 200000 \
66
- --task text_to_speech --criterion fastspeech2 --arch fastspeech2 \
67
- --clip-norm 5.0 --n-frames-per-step 1 \
68
- --dropout 0.1 --attention-dropout 0.1 \
69
- --optimizer adam --lr 5e-4 --lr-scheduler inverse_sqrt --warmup-updates 4000 \
70
- --seed 1 --update-freq 8 --eval-inference --best-checkpoint-metric mcd_loss
71
- ```
72
-
73
-
74
- ## Inference
75
- Average the last 5 checkpoints, generate the test split spectrogram and waveform using the default Griffin-Lim vocoder:
76
- ```bash
77
- SPLIT=test
78
- CHECKPOINT_NAME=avg_last_5
79
- CHECKPOINT_PATH=${SAVE_DIR}/checkpoint_${CHECKPOINT_NAME}.pt
80
- python scripts/average_checkpoints.py --inputs ${SAVE_DIR} \
81
- --num-epoch-checkpoints 5 \
82
- --output ${CHECKPOINT_PATH}
83
-
84
- python -m examples.speech_synthesis.generate_waveform ${FEATURE_MANIFEST_ROOT} \
85
- --config-yaml config.yaml --gen-subset ${SPLIT} --task text_to_speech \
86
- --path ${CHECKPOINT_PATH} --max-tokens 50000 --spec-bwd-max-iter 32 \
87
- --dump-waveforms
88
- ```
89
- which dumps files (waveform, feature, attention plot, etc.) to `${SAVE_DIR}/generate-${CHECKPOINT_NAME}-${SPLIT}`. To
90
- re-synthesize target waveforms for automatic evaluation, add `--dump-target`.
91
-
92
- ## Automatic Evaluation
93
- To start with, generate the manifest for synthetic speech, which will be taken as inputs by evaluation scripts.
94
- ```bash
95
- python -m examples.speech_synthesis.evaluation.get_eval_manifest \
96
- --generation-root ${SAVE_DIR}/generate-${CHECKPOINT_NAME}-${SPLIT} \
97
- --audio-manifest ${AUDIO_MANIFEST_ROOT}/${SPLIT}.audio.tsv \
98
- --output-path ${EVAL_OUTPUT_ROOT}/eval.tsv \
99
- --vocoder griffin_lim --sample-rate 22050 --audio-format flac \
100
- --use-resynthesized-target
101
- ```
102
- Speech recognition (ASR) models usually operate at lower sample rates (e.g. 16kHz). For the WER/CER metric,
103
- you may need to resample the audios accordingly --- add `--output-sample-rate 16000` for `generate_waveform.py` and
104
- use `--sample-rate 16000` for `get_eval_manifest.py`.
105
-
106
-
107
- #### WER/CER metric
108
- We use wav2vec 2.0 ASR model as example. [Download](https://github.com/pytorch/fairseq/tree/main/examples/wav2vec)
109
- the model checkpoint and dictionary, then compute WER/CER with
110
- ```bash
111
- python -m examples.speech_synthesis.evaluation.eval_asr \
112
- --audio-header syn --text-header text --err-unit char --split ${SPLIT} \
113
- --w2v-ckpt ${WAV2VEC2_CHECKPOINT_PATH} --w2v-dict-dir ${WAV2VEC2_DICT_DIR} \
114
- --raw-manifest ${EVAL_OUTPUT_ROOT}/eval_16khz.tsv --asr-dir ${EVAL_OUTPUT_ROOT}/asr
115
- ```
116
-
117
- #### MCD/MSD metric
118
- ```bash
119
- python -m examples.speech_synthesis.evaluation.eval_sp \
120
- ${EVAL_OUTPUT_ROOT}/eval.tsv --mcd --msd
121
- ```
122
-
123
- #### F0 metrics
124
- ```bash
125
- python -m examples.speech_synthesis.evaluation.eval_f0 \
126
- ${EVAL_OUTPUT_ROOT}/eval.tsv --gpe --vde --ffe
127
- ```
128
-
129
-
130
- ## Results
131
-
132
- | --arch | Params | Test MCD | Model |
133
- |---|---|---|---|
134
- | tts_transformer | 54M | 3.8 | [Download](https://dl.fbaipublicfiles.com/fairseq/s2/ljspeech_transformer_phn.tar) |
135
- | fastspeech2 | 41M | 3.8 | [Download](https://dl.fbaipublicfiles.com/fairseq/s2/ljspeech_fastspeech2_phn.tar) |
136
-
137
- [[Back]](..)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/docs/vctk_example.md DELETED
@@ -1,61 +0,0 @@
1
- [[Back]](..)
2
-
3
- # VCTK
4
-
5
- [VCTK](https://datashare.ed.ac.uk/handle/10283/3443) is an open English speech corpus. We provide examples
6
- for building [Transformer](https://arxiv.org/abs/1809.08895) models on this dataset.
7
-
8
-
9
- ## Data preparation
10
- Download data, create splits and generate audio manifests with
11
- ```bash
12
- python -m examples.speech_synthesis.preprocessing.get_vctk_audio_manifest \
13
- --output-data-root ${AUDIO_DATA_ROOT} \
14
- --output-manifest-root ${AUDIO_MANIFEST_ROOT}
15
- ```
16
-
17
- To denoise audio and trim leading/trailing silence using signal processing based VAD, run
18
- ```bash
19
- for SPLIT in dev test train; do
20
- python -m examples.speech_synthesis.preprocessing.denoise_and_vad_audio \
21
- --audio-manifest ${AUDIO_MANIFEST_ROOT}/${SPLIT}.audio.tsv \
22
- --output-dir ${PROCESSED_DATA_ROOT} \
23
- --denoise --vad --vad-agg-level 3
24
- done
25
- ```
26
- which generates a new audio TSV manifest under `${PROCESSED_DATA_ROOT}` with updated path to the processed audio and
27
- a new column for SNR.
28
-
29
- To do filtering by CER, follow the [Automatic Evaluation](../docs/ljspeech_example.md#automatic-evaluation) section to
30
- run ASR model (add `--eval-target` to `get_eval_manifest` for evaluation on the reference audio; add `--err-unit char`
31
- to `eval_asr` to compute CER instead of WER). The example-level CER is saved to
32
- `${EVAL_OUTPUT_ROOT}/uer_cer.${SPLIT}.tsv`.
33
-
34
- Then, extract log-Mel spectrograms, generate feature manifest and create data configuration YAML with
35
- ```bash
36
- python -m examples.speech_synthesis.preprocessing.get_feature_manifest \
37
- --audio-manifest-root ${PROCESSED_DATA_ROOT} \
38
- --output-root ${FEATURE_MANIFEST_ROOT} \
39
- --ipa-vocab --use-g2p \
40
- --snr-threshold 15 \
41
- --cer-threshold 0.1 --cer-tsv-path ${EVAL_OUTPUT_ROOT}/uer_cer.${SPLIT}.tsv
42
- ```
43
- where we use phoneme inputs (`--ipa-vocab --use-g2p`) as example. For sample filtering, we set the SNR and CER threshold
44
- to 15 and 10%, respectively.
45
-
46
- ## Training
47
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#transformer).)
48
-
49
- ## Inference
50
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#inference).)
51
-
52
- ## Automatic Evaluation
53
- (Please refer to [the LJSpeech example](../docs/ljspeech_example.md#automatic-evaluation).)
54
-
55
- ## Results
56
-
57
- | --arch | Params | Test MCD | Model |
58
- |---|---|---|---|
59
- | tts_transformer | 54M | 3.4 | [Download](https://dl.fbaipublicfiles.com/fairseq/s2/vctk_transformer_phn.tar) |
60
-
61
- [[Back]](..)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/__init__.py DELETED
@@ -1,4 +0,0 @@
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.
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_asr.py DELETED
@@ -1,128 +0,0 @@
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 re
8
- import shutil
9
- import subprocess
10
- from pathlib import Path
11
-
12
- import editdistance
13
- import soundfile as sf
14
- from examples.speech_to_text.data_utils import load_tsv_to_dicts
15
-
16
-
17
- def preprocess_text(text):
18
- text = "|".join(re.sub(r"[^A-Z' ]", " ", text.upper()).split())
19
- text = " ".join(text)
20
- return text
21
-
22
-
23
- def prepare_w2v_data(
24
- dict_dir, sample_rate, label, audio_paths, texts, split, data_dir
25
- ):
26
- data_dir.mkdir(parents=True, exist_ok=True)
27
- shutil.copyfile(
28
- dict_dir / f"dict.{label}.txt",
29
- data_dir / f"dict.{label}.txt"
30
- )
31
- with open(data_dir / f"{split}.tsv", "w") as f:
32
- f.write("/\n")
33
- for audio_path in audio_paths:
34
- wav, sr = sf.read(audio_path)
35
- assert sr == sample_rate, f"{sr} != sample_rate"
36
- nsample = len(wav)
37
- f.write(f"{audio_path}\t{nsample}\n")
38
- with open(data_dir / f"{split}.{label}", "w") as f:
39
- for text in texts:
40
- text = preprocess_text(text)
41
- f.write(f"{text}\n")
42
-
43
-
44
- def run_asr(asr_dir, split, w2v_ckpt, w2v_label, res_dir):
45
- """
46
- results will be saved at
47
- {res_dir}/{ref,hypo}.word-{w2v_ckpt.filename}-{split}.txt
48
- """
49
- cmd = ["python", "-m", "examples.speech_recognition.infer"]
50
- cmd += [str(asr_dir.resolve())]
51
- cmd += ["--task", "audio_finetuning", "--nbest", "1", "--quiet"]
52
- cmd += ["--w2l-decoder", "viterbi", "--criterion", "ctc"]
53
- cmd += ["--post-process", "letter", "--max-tokens", "4000000"]
54
- cmd += ["--path", str(w2v_ckpt.resolve()), "--labels", w2v_label]
55
- cmd += ["--gen-subset", split, "--results-path", str(res_dir.resolve())]
56
-
57
- print(f"running cmd:\n{' '.join(cmd)}")
58
- subprocess.run(cmd, check=True)
59
-
60
-
61
- def compute_error_rate(hyp_wrd_path, ref_wrd_path, unit="word"):
62
- """each line is "<text> (None-<index>)" """
63
- tokenize_line = {
64
- "word": lambda x: re.sub(r" \(.*\)$", "", x.rstrip()).split(),
65
- "char": lambda x: list(re.sub(r" \(.*\)$", "", x.rstrip()))
66
- }.get(unit)
67
- if tokenize_line is None:
68
- raise ValueError(f"{unit} not supported")
69
-
70
- inds = [int(re.sub(r"\D*(\d*)\D*", r"\1", line))
71
- for line in open(hyp_wrd_path)]
72
- hyps = [tokenize_line(line) for line in open(hyp_wrd_path)]
73
- refs = [tokenize_line(line) for line in open(ref_wrd_path)]
74
- assert(len(hyps) == len(refs))
75
- err_rates = [
76
- editdistance.eval(hyp, ref) / len(ref) for hyp, ref in zip(hyps, refs)
77
- ]
78
- ind_to_err_rates = {i: e for i, e in zip(inds, err_rates)}
79
- return ind_to_err_rates
80
-
81
-
82
- def main(args):
83
- samples = load_tsv_to_dicts(args.raw_manifest)
84
- ids = [
85
- sample[args.id_header] if args.id_header else "" for sample in samples
86
- ]
87
- audio_paths = [sample[args.audio_header] for sample in samples]
88
- texts = [sample[args.text_header] for sample in samples]
89
-
90
- prepare_w2v_data(
91
- args.w2v_dict_dir,
92
- args.w2v_sample_rate,
93
- args.w2v_label,
94
- audio_paths,
95
- texts,
96
- args.split,
97
- args.asr_dir
98
- )
99
- run_asr(args.asr_dir, args.split, args.w2v_ckpt, args.w2v_label, args.asr_dir)
100
- ind_to_err_rates = compute_error_rate(
101
- args.asr_dir / f"hypo.word-{args.w2v_ckpt.name}-{args.split}.txt",
102
- args.asr_dir / f"ref.word-{args.w2v_ckpt.name}-{args.split}.txt",
103
- args.err_unit,
104
- )
105
-
106
- uer_path = args.asr_dir / f"uer_{args.err_unit}.{args.split}.tsv"
107
- with open(uer_path, "w") as f:
108
- f.write("id\taudio\tuer\n")
109
- for ind, (id_, audio_path) in enumerate(zip(ids, audio_paths)):
110
- f.write(f"{id_}\t{audio_path}\t{ind_to_err_rates[ind]:.4f}\n")
111
-
112
-
113
- if __name__ == "__main__":
114
- parser = argparse.ArgumentParser()
115
- parser.add_argument("--raw-manifest", required=True, type=Path)
116
- parser.add_argument("--asr-dir", required=True, type=Path)
117
- parser.add_argument("--id-header", default="id", type=str)
118
- parser.add_argument("--audio-header", default="audio", type=str)
119
- parser.add_argument("--text-header", default="src_text", type=str)
120
- parser.add_argument("--split", default="raw", type=str)
121
- parser.add_argument("--w2v-ckpt", required=True, type=Path)
122
- parser.add_argument("--w2v-dict-dir", required=True, type=Path)
123
- parser.add_argument("--w2v-sample-rate", default=16000, type=int)
124
- parser.add_argument("--w2v-label", default="ltr", type=str)
125
- parser.add_argument("--err-unit", default="word", type=str)
126
- args = parser.parse_args()
127
-
128
- main(args)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_f0.py DELETED
@@ -1,267 +0,0 @@
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
- Signal processing-based evaluation using waveforms
8
- """
9
- import os.path as op
10
-
11
- import numpy as np
12
- import torchaudio
13
- import tqdm
14
- from examples.speech_synthesis.evaluation.eval_sp import load_eval_spec
15
- from examples.speech_synthesis.utils import (
16
- f0_frame_error,
17
- gross_pitch_error,
18
- voicing_decision_error,
19
- )
20
- from tabulate import tabulate
21
-
22
-
23
- def difference_function(x, n, tau_max):
24
- """
25
- Compute difference function of data x. This solution is implemented directly
26
- with Numpy fft.
27
-
28
-
29
- :param x: audio data
30
- :param n: length of data
31
- :param tau_max: integration window size
32
- :return: difference function
33
- :rtype: list
34
- """
35
-
36
- x = np.array(x, np.float64)
37
- w = x.size
38
- tau_max = min(tau_max, w)
39
- x_cumsum = np.concatenate((np.array([0.]), (x * x).cumsum()))
40
- size = w + tau_max
41
- p2 = (size // 32).bit_length()
42
- nice_numbers = (16, 18, 20, 24, 25, 27, 30, 32)
43
- size_pad = min(x * 2 ** p2 for x in nice_numbers if x * 2 ** p2 >= size)
44
- fc = np.fft.rfft(x, size_pad)
45
- conv = np.fft.irfft(fc * fc.conjugate())[:tau_max]
46
- return x_cumsum[w:w - tau_max:-1] + x_cumsum[w] - x_cumsum[:tau_max] - \
47
- 2 * conv
48
-
49
-
50
- def cumulative_mean_normalized_difference_function(df, n):
51
- """
52
- Compute cumulative mean normalized difference function (CMND).
53
-
54
- :param df: Difference function
55
- :param n: length of data
56
- :return: cumulative mean normalized difference function
57
- :rtype: list
58
- """
59
-
60
- # scipy method
61
- cmn_df = df[1:] * range(1, n) / np.cumsum(df[1:]).astype(float)
62
- return np.insert(cmn_df, 0, 1)
63
-
64
-
65
- def get_pitch(cmdf, tau_min, tau_max, harmo_th=0.1):
66
- """
67
- Return fundamental period of a frame based on CMND function.
68
-
69
- :param cmdf: Cumulative Mean Normalized Difference function
70
- :param tau_min: minimum period for speech
71
- :param tau_max: maximum period for speech
72
- :param harmo_th: harmonicity threshold to determine if it is necessary to
73
- compute pitch frequency
74
- :return: fundamental period if there is values under threshold, 0 otherwise
75
- :rtype: float
76
- """
77
- tau = tau_min
78
- while tau < tau_max:
79
- if cmdf[tau] < harmo_th:
80
- while tau + 1 < tau_max and cmdf[tau + 1] < cmdf[tau]:
81
- tau += 1
82
- return tau
83
- tau += 1
84
-
85
- return 0 # if unvoiced
86
-
87
-
88
- def compute_yin(sig, sr, w_len=512, w_step=256, f0_min=100, f0_max=500,
89
- harmo_thresh=0.1):
90
- """
91
-
92
- Compute the Yin Algorithm. Return fundamental frequency and harmonic rate.
93
-
94
- https://github.com/NVIDIA/mellotron adaption of
95
- https://github.com/patriceguyot/Yin
96
-
97
- :param sig: Audio signal (list of float)
98
- :param sr: sampling rate (int)
99
- :param w_len: size of the analysis window (samples)
100
- :param w_step: size of the lag between two consecutives windows (samples)
101
- :param f0_min: Minimum fundamental frequency that can be detected (hertz)
102
- :param f0_max: Maximum fundamental frequency that can be detected (hertz)
103
- :param harmo_thresh: Threshold of detection. The yalgorithmù return the
104
- first minimum of the CMND function below this threshold.
105
-
106
- :returns:
107
-
108
- * pitches: list of fundamental frequencies,
109
- * harmonic_rates: list of harmonic rate values for each fundamental
110
- frequency value (= confidence value)
111
- * argmins: minimums of the Cumulative Mean Normalized DifferenceFunction
112
- * times: list of time of each estimation
113
- :rtype: tuple
114
- """
115
-
116
- tau_min = int(sr / f0_max)
117
- tau_max = int(sr / f0_min)
118
-
119
- # time values for each analysis window
120
- time_scale = range(0, len(sig) - w_len, w_step)
121
- times = [t/float(sr) for t in time_scale]
122
- frames = [sig[t:t + w_len] for t in time_scale]
123
-
124
- pitches = [0.0] * len(time_scale)
125
- harmonic_rates = [0.0] * len(time_scale)
126
- argmins = [0.0] * len(time_scale)
127
-
128
- for i, frame in enumerate(frames):
129
- # Compute YIN
130
- df = difference_function(frame, w_len, tau_max)
131
- cm_df = cumulative_mean_normalized_difference_function(df, tau_max)
132
- p = get_pitch(cm_df, tau_min, tau_max, harmo_thresh)
133
-
134
- # Get results
135
- if np.argmin(cm_df) > tau_min:
136
- argmins[i] = float(sr / np.argmin(cm_df))
137
- if p != 0: # A pitch was found
138
- pitches[i] = float(sr / p)
139
- harmonic_rates[i] = cm_df[p]
140
- else: # No pitch, but we compute a value of the harmonic rate
141
- harmonic_rates[i] = min(cm_df)
142
-
143
- return pitches, harmonic_rates, argmins, times
144
-
145
-
146
- def extract_f0(samples):
147
- f0_samples = []
148
- for sample in tqdm.tqdm(samples):
149
- if not op.isfile(sample["ref"]) or not op.isfile(sample["syn"]):
150
- f0_samples.append(None)
151
- continue
152
-
153
- # assume single channel
154
- yref, sr = torchaudio.load(sample["ref"])
155
- ysyn, _sr = torchaudio.load(sample["syn"])
156
- yref, ysyn = yref[0], ysyn[0]
157
- assert sr == _sr, f"{sr} != {_sr}"
158
-
159
- yref_f0 = compute_yin(yref, sr)
160
- ysyn_f0 = compute_yin(ysyn, sr)
161
-
162
- f0_samples += [
163
- {
164
- "ref": yref_f0,
165
- "syn": ysyn_f0
166
- }
167
- ]
168
-
169
- return f0_samples
170
-
171
-
172
- def eval_f0_error(samples, distortion_fn):
173
- results = []
174
- for sample in tqdm.tqdm(samples):
175
- if sample is None:
176
- results.append(None)
177
- continue
178
- # assume single channel
179
- yref_f, _, _, yref_t = sample["ref"]
180
- ysyn_f, _, _, ysyn_t = sample["syn"]
181
-
182
- yref_f = np.array(yref_f)
183
- yref_t = np.array(yref_t)
184
- ysyn_f = np.array(ysyn_f)
185
- ysyn_t = np.array(ysyn_t)
186
-
187
- distortion = distortion_fn(yref_t, yref_f, ysyn_t, ysyn_f)
188
- results.append((distortion.item(),
189
- len(yref_f),
190
- len(ysyn_f)
191
- ))
192
- return results
193
-
194
-
195
- def eval_gross_pitch_error(samples):
196
- return eval_f0_error(samples, gross_pitch_error)
197
-
198
-
199
- def eval_voicing_decision_error(samples):
200
- return eval_f0_error(samples, voicing_decision_error)
201
-
202
-
203
- def eval_f0_frame_error(samples):
204
- return eval_f0_error(samples, f0_frame_error)
205
-
206
-
207
- def print_results(results, show_bin):
208
- results = np.array(list(filter(lambda x: x is not None, results)))
209
-
210
- np.set_printoptions(precision=3)
211
-
212
- def _print_result(results):
213
- res = {
214
- "nutt": len(results),
215
- "error": results[:, 0].mean(),
216
- "std": results[:, 0].std(),
217
- "dur_ref": int(results[:, 1].sum()),
218
- "dur_syn": int(results[:, 2].sum()),
219
- }
220
- print(tabulate([res.values()], res.keys(), floatfmt=".4f"))
221
-
222
- print(">>>> ALL")
223
- _print_result(results)
224
-
225
- if show_bin:
226
- edges = [0, 200, 400, 600, 800, 1000, 2000, 4000]
227
- for i in range(1, len(edges)):
228
- mask = np.logical_and(results[:, 1] >= edges[i-1],
229
- results[:, 1] < edges[i])
230
- if not mask.any():
231
- continue
232
- bin_results = results[mask]
233
- print(f">>>> ({edges[i-1]}, {edges[i]})")
234
- _print_result(bin_results)
235
-
236
-
237
- def main(eval_f0, gpe, vde, ffe, show_bin):
238
- samples = load_eval_spec(eval_f0)
239
- if gpe or vde or ffe:
240
- f0_samples = extract_f0(samples)
241
-
242
- if gpe:
243
- print("===== Evaluate Gross Pitch Error =====")
244
- results = eval_gross_pitch_error(f0_samples)
245
- print_results(results, show_bin)
246
- if vde:
247
- print("===== Evaluate Voicing Decision Error =====")
248
- results = eval_voicing_decision_error(f0_samples)
249
- print_results(results, show_bin)
250
- if ffe:
251
- print("===== Evaluate F0 Frame Error =====")
252
- results = eval_f0_frame_error(f0_samples)
253
- print_results(results, show_bin)
254
-
255
-
256
- if __name__ == "__main__":
257
- import argparse
258
-
259
- parser = argparse.ArgumentParser()
260
- parser.add_argument("eval_f0")
261
- parser.add_argument("--gpe", action="store_true")
262
- parser.add_argument("--vde", action="store_true")
263
- parser.add_argument("--ffe", action="store_true")
264
- parser.add_argument("--show-bin", action="store_true")
265
- args = parser.parse_args()
266
-
267
- main(args.eval_f0, args.gpe, args.vde, args.ffe, args.show_bin)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/eval_sp.py DELETED
@@ -1,130 +0,0 @@
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
- """
8
- Signal processing-based evaluation using waveforms
9
- """
10
-
11
- import csv
12
- import os.path as op
13
-
14
- import numpy as np
15
- import torch
16
- import torchaudio
17
- import tqdm
18
- from examples.speech_synthesis.utils import batch_mel_spectral_distortion
19
- from fairseq.tasks.text_to_speech import batch_mel_cepstral_distortion
20
- from tabulate import tabulate
21
-
22
-
23
- def load_eval_spec(path):
24
- with open(path) as f:
25
- reader = csv.DictReader(f, delimiter='\t')
26
- samples = list(reader)
27
- return samples
28
-
29
-
30
- def eval_distortion(samples, distortion_fn, device="cuda"):
31
- nmiss = 0
32
- results = []
33
- for sample in tqdm.tqdm(samples):
34
- if not op.isfile(sample["ref"]) or not op.isfile(sample["syn"]):
35
- nmiss += 1
36
- results.append(None)
37
- continue
38
- # assume single channel
39
- yref, sr = torchaudio.load(sample["ref"])
40
- ysyn, _sr = torchaudio.load(sample["syn"])
41
- yref, ysyn = yref[0].to(device), ysyn[0].to(device)
42
- assert sr == _sr, f"{sr} != {_sr}"
43
-
44
- distortion, extra = distortion_fn([yref], [ysyn], sr, None)[0]
45
- _, _, _, _, _, pathmap = extra
46
- nins = torch.sum(pathmap.sum(dim=1) - 1) # extra frames in syn
47
- ndel = torch.sum(pathmap.sum(dim=0) - 1) # missing frames from syn
48
- results.append(
49
- (distortion.item(), # path distortion
50
- pathmap.size(0), # yref num frames
51
- pathmap.size(1), # ysyn num frames
52
- pathmap.sum().item(), # path length
53
- nins.item(), # insertion
54
- ndel.item(), # deletion
55
- )
56
- )
57
- return results
58
-
59
-
60
- def eval_mel_cepstral_distortion(samples, device="cuda"):
61
- return eval_distortion(samples, batch_mel_cepstral_distortion, device)
62
-
63
-
64
- def eval_mel_spectral_distortion(samples, device="cuda"):
65
- return eval_distortion(samples, batch_mel_spectral_distortion, device)
66
-
67
-
68
- def print_results(results, show_bin):
69
- results = np.array(list(filter(lambda x: x is not None, results)))
70
-
71
- np.set_printoptions(precision=3)
72
-
73
- def _print_result(results):
74
- dist, dur_ref, dur_syn, dur_ali, nins, ndel = results.sum(axis=0)
75
- res = {
76
- "nutt": len(results),
77
- "dist": dist,
78
- "dur_ref": int(dur_ref),
79
- "dur_syn": int(dur_syn),
80
- "dur_ali": int(dur_ali),
81
- "dist_per_ref_frm": dist/dur_ref,
82
- "dist_per_syn_frm": dist/dur_syn,
83
- "dist_per_ali_frm": dist/dur_ali,
84
- "ins": nins/dur_ref,
85
- "del": ndel/dur_ref,
86
- }
87
- print(tabulate(
88
- [res.values()],
89
- res.keys(),
90
- floatfmt=".4f"
91
- ))
92
-
93
- print(">>>> ALL")
94
- _print_result(results)
95
-
96
- if show_bin:
97
- edges = [0, 200, 400, 600, 800, 1000, 2000, 4000]
98
- for i in range(1, len(edges)):
99
- mask = np.logical_and(results[:, 1] >= edges[i-1],
100
- results[:, 1] < edges[i])
101
- if not mask.any():
102
- continue
103
- bin_results = results[mask]
104
- print(f">>>> ({edges[i-1]}, {edges[i]})")
105
- _print_result(bin_results)
106
-
107
-
108
- def main(eval_spec, mcd, msd, show_bin):
109
- samples = load_eval_spec(eval_spec)
110
- device = "cpu"
111
- if mcd:
112
- print("===== Evaluate Mean Cepstral Distortion =====")
113
- results = eval_mel_cepstral_distortion(samples, device)
114
- print_results(results, show_bin)
115
- if msd:
116
- print("===== Evaluate Mean Spectral Distortion =====")
117
- results = eval_mel_spectral_distortion(samples, device)
118
- print_results(results, show_bin)
119
-
120
-
121
- if __name__ == "__main__":
122
- import argparse
123
- parser = argparse.ArgumentParser()
124
- parser.add_argument("eval_spec")
125
- parser.add_argument("--mcd", action="store_true")
126
- parser.add_argument("--msd", action="store_true")
127
- parser.add_argument("--show-bin", action="store_true")
128
- args = parser.parse_args()
129
-
130
- main(args.eval_spec, args.mcd, args.msd, args.show_bin)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/evaluation/get_eval_manifest.py DELETED
@@ -1,64 +0,0 @@
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 csv
8
- from pathlib import Path
9
-
10
-
11
- def main(args):
12
- """
13
- `uid syn ref text`
14
- """
15
- in_root = Path(args.generation_root).resolve()
16
- ext = args.audio_format
17
- with open(args.audio_manifest) as f, open(args.output_path, "w") as f_out:
18
- reader = csv.DictReader(
19
- f, delimiter="\t", quotechar=None, doublequote=False,
20
- lineterminator="\n", quoting=csv.QUOTE_NONE
21
- )
22
- header = ["id", "syn", "ref", "text", "speaker"]
23
- f_out.write("\t".join(header) + "\n")
24
- for row in reader:
25
- dir_name = f"{ext}_{args.sample_rate}hz_{args.vocoder}"
26
- id_ = row["id"]
27
- syn = (in_root / dir_name / f"{id_}.{ext}").as_posix()
28
- ref = row["audio"]
29
- if args.use_resynthesized_target:
30
- ref = (in_root / f"{dir_name}_tgt" / f"{id_}.{ext}").as_posix()
31
- if args.eval_target:
32
- syn = row["audio"]
33
- sample = [id_, syn, ref, row["tgt_text"], row["speaker"]]
34
- f_out.write("\t".join(sample) + "\n")
35
- print(f"wrote evaluation file to {args.output_path}")
36
-
37
-
38
- if __name__ == "__main__":
39
- import argparse
40
- parser = argparse.ArgumentParser()
41
- parser.add_argument(
42
- "--generation-root", help="output directory for generate_waveform.py"
43
- )
44
- parser.add_argument(
45
- "--audio-manifest",
46
- help="used to determine the original utterance ID and text"
47
- )
48
- parser.add_argument(
49
- "--output-path", help="path to output evaluation spec file"
50
- )
51
- parser.add_argument(
52
- "--use-resynthesized-target", action="store_true",
53
- help="use resynthesized reference instead of the original audio"
54
- )
55
- parser.add_argument(
56
- "--eval-target", action="store_true",
57
- help="evaluate reference instead of model prediction"
58
- )
59
- parser.add_argument("--vocoder", type=str, default="griffin_lim")
60
- parser.add_argument("--sample-rate", type=int, default=22_050)
61
- parser.add_argument("--audio-format", type=str, default="wav")
62
- args = parser.parse_args()
63
-
64
- main(args)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
audio/safeear/safeear_repo/fairseq_ours/examples/speech_synthesis/generate_waveform.py DELETED
@@ -1,191 +0,0 @@
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 logging
8
- import sys
9
- from pathlib import Path
10
-
11
- import matplotlib.pyplot as plt
12
- import numpy as np
13
- import soundfile as sf
14
- import torch
15
- import torchaudio
16
- from fairseq import checkpoint_utils, options, tasks, utils
17
- from fairseq.data.audio.text_to_speech_dataset import TextToSpeechDataset
18
- from fairseq.logging import progress_bar
19
- from fairseq.tasks.text_to_speech import plot_tts_output
20
-
21
- logging.basicConfig()
22
- logging.root.setLevel(logging.INFO)
23
- logging.basicConfig(level=logging.INFO)
24
- logger = logging.getLogger(__name__)
25
-
26
-
27
- def make_parser():
28
- parser = options.get_speech_generation_parser()
29
- parser.add_argument("--dump-features", action="store_true")
30
- parser.add_argument("--dump-waveforms", action="store_true")
31
- parser.add_argument("--dump-attentions", action="store_true")
32
- parser.add_argument("--dump-eos-probs", action="store_true")
33
- parser.add_argument("--dump-plots", action="store_true")
34
- parser.add_argument("--dump-target", action="store_true")
35
- parser.add_argument("--output-sample-rate", default=22050, type=int)
36
- parser.add_argument("--teacher-forcing", action="store_true")
37
- parser.add_argument(
38
- "--audio-format", type=str, default="wav", choices=["wav", "flac"]
39
- )
40
- return parser
41
-
42
-
43
- def postprocess_results(
44
- dataset: TextToSpeechDataset, sample, hypos, resample_fn, dump_target
45
- ):
46
- def to_np(x):
47
- return None if x is None else x.detach().cpu().numpy()
48
-
49
- sample_ids = [dataset.ids[i] for i in sample["id"].tolist()]
50
- texts = sample["src_texts"] if "src_texts" in sample else [""] * len(hypos)
51
- attns = [to_np(hypo["attn"]) for hypo in hypos]
52
- eos_probs = [to_np(hypo.get("eos_prob", None)) for hypo in hypos]
53
- feat_preds = [to_np(hypo["feature"]) for hypo in hypos]
54
- wave_preds = [to_np(resample_fn(h["waveform"])) for h in hypos]
55
- if dump_target:
56
- feat_targs = [to_np(hypo["targ_feature"]) for hypo in hypos]
57
- wave_targs = [to_np(resample_fn(h["targ_waveform"])) for h in hypos]
58
- else:
59
- feat_targs = [None for _ in hypos]
60
- wave_targs = [None for _ in hypos]
61
-
62
- return zip(sample_ids, texts, attns, eos_probs, feat_preds, wave_preds,
63
- feat_targs, wave_targs)
64
-
65
-
66
- def dump_result(
67
- is_na_model,
68
- args,
69
- vocoder,
70
- sample_id,
71
- text,
72
- attn,
73
- eos_prob,
74
- feat_pred,
75
- wave_pred,
76
- feat_targ,
77
- wave_targ,
78
- ):
79
- sample_rate = args.output_sample_rate
80
- out_root = Path(args.results_path)
81
- if args.dump_features:
82
- feat_dir = out_root / "feat"
83
- feat_dir.mkdir(exist_ok=True, parents=True)
84
- np.save(feat_dir / f"{sample_id}.npy", feat_pred)
85
- if args.dump_target:
86
- feat_tgt_dir = out_root / "feat_tgt"
87
- feat_tgt_dir.mkdir(exist_ok=True, parents=True)
88
- np.save(feat_tgt_dir / f"{sample_id}.npy", feat_targ)
89
- if args.dump_attentions:
90
- attn_dir = out_root / "attn"
91
- attn_dir.mkdir(exist_ok=True, parents=True)
92
- np.save(attn_dir / f"{sample_id}.npy", attn.numpy())
93
- if args.dump_eos_probs and not is_na_model:
94
- eos_dir = out_root / "eos"
95
- eos_dir.mkdir(exist_ok=True, parents=True)
96
- np.save(eos_dir / f"{sample_id}.npy", eos_prob)
97
-
98
- if args.dump_plots:
99
- images = [feat_pred.T] if is_na_model else [feat_pred.T, attn]
100
- names = ["output"] if is_na_model else ["output", "alignment"]
101
- if feat_targ is not None:
102
- images = [feat_targ.T] + images
103
- names = [f"target (idx={sample_id})"] + names
104
- if is_na_model:
105
- plot_tts_output(images, names, attn, "alignment", suptitle=text)
106
- else:
107
- plot_tts_output(images, names, eos_prob, "eos prob", suptitle=text)
108
- plot_dir = out_root / "plot"
109
- plot_dir.mkdir(exist_ok=True, parents=True)
110
- plt.savefig(plot_dir / f"{sample_id}.png")
111
- plt.close()
112
-
113
- if args.dump_waveforms:
114
- ext = args.audio_format
115
- if wave_pred is not None:
116
- wav_dir = out_root / f"{ext}_{sample_rate}hz_{vocoder}"
117
- wav_dir.mkdir(exist_ok=True, parents=True)
118
- sf.write(wav_dir / f"{sample_id}.{ext}", wave_pred, sample_rate)
119
- if args.dump_target and wave_targ is not None:
120
- wav_tgt_dir = out_root / f"{ext}_{sample_rate}hz_{vocoder}_tgt"
121
- wav_tgt_dir.mkdir(exist_ok=True, parents=True)
122
- sf.write(wav_tgt_dir / f"{sample_id}.{ext}", wave_targ, sample_rate)
123
-
124
-
125
- def main(args):
126
- assert(args.dump_features or args.dump_waveforms or args.dump_attentions
127
- or args.dump_eos_probs or args.dump_plots)
128
- if args.max_tokens is None and args.batch_size is None:
129
- args.max_tokens = 8000
130
- logger.info(args)
131
-
132
- use_cuda = torch.cuda.is_available() and not args.cpu
133
- task = tasks.setup_task(args)
134
- models, saved_cfg, task = checkpoint_utils.load_model_ensemble_and_task(
135
- [args.path],
136
- task=task,
137
- arg_overrides=ast.literal_eval(args.model_overrides),
138
- )
139
- model = models[0].cuda() if use_cuda else models[0]
140
- # use the original n_frames_per_step
141
- task.args.n_frames_per_step = saved_cfg.task.n_frames_per_step
142
- task.load_dataset(args.gen_subset, task_cfg=saved_cfg.task)
143
-
144
- data_cfg = task.data_cfg
145
- sample_rate = data_cfg.config.get("features", {}).get("sample_rate", 22050)
146
- resample_fn = {
147
- False: lambda x: x,
148
- True: lambda x: torchaudio.sox_effects.apply_effects_tensor(
149
- x.detach().cpu().unsqueeze(0), sample_rate,
150
- [['rate', str(args.output_sample_rate)]]
151
- )[0].squeeze(0)
152
- }.get(args.output_sample_rate != sample_rate)
153
- if args.output_sample_rate != sample_rate:
154
- logger.info(f"resampling to {args.output_sample_rate}Hz")
155
-
156
- generator = task.build_generator([model], args)
157
- itr = task.get_batch_iterator(
158
- dataset=task.dataset(args.gen_subset),
159
- max_tokens=args.max_tokens,
160
- max_sentences=args.batch_size,
161
- max_positions=(sys.maxsize, sys.maxsize),
162
- ignore_invalid_inputs=args.skip_invalid_size_inputs_valid_test,
163
- required_batch_size_multiple=args.required_batch_size_multiple,
164
- num_shards=args.num_shards,
165
- shard_id=args.shard_id,
166
- num_workers=args.num_workers,
167
- data_buffer_size=args.data_buffer_size,
168
- ).next_epoch_itr(shuffle=False)
169
-
170
- Path(args.results_path).mkdir(exist_ok=True, parents=True)
171
- is_na_model = getattr(model, "NON_AUTOREGRESSIVE", False)
172
- dataset = task.dataset(args.gen_subset)
173
- vocoder = task.args.vocoder
174
- with progress_bar.build_progress_bar(args, itr) as t:
175
- for sample in t:
176
- sample = utils.move_to_cuda(sample) if use_cuda else sample
177
- hypos = generator.generate(model, sample, has_targ=args.dump_target)
178
- for result in postprocess_results(
179
- dataset, sample, hypos, resample_fn, args.dump_target
180
- ):
181
- dump_result(is_na_model, args, vocoder, *result)
182
-
183
-
184
- def cli_main():
185
- parser = make_parser()
186
- args = options.parse_args_and_arch(parser)
187
- main(args)
188
-
189
-
190
- if __name__ == "__main__":
191
- cli_main()