cristinae guasch commited on
Commit
29fe1ea
·
1 Parent(s): fc9a8a7

self-contained-inference (#1)

Browse files

- fix: make ConMamba-small-ca self-contained for inference (df1804fb060f9af7e0f673c3283e90629c8717e8)


Co-authored-by: ferran <guasch@users.noreply.huggingface.co>

conmambamamba_debug_catalan_small_1k_unigram_inference.yaml DELETED
@@ -1,386 +0,0 @@
1
- # ############################################################################
2
- # Model: E2E ASR with Transformer
3
- # Encoder: ConMamba Encoder
4
- # Decoder: Mamba Decoder + (CTC/ATT joint) beamsearch + TransformerLM
5
- # Tokens: unigram
6
- # losses: CTC + KLdiv (Label Smoothing loss)
7
- # Training: Librispeech 960h
8
- # Authors: Xilin Jiang
9
- # ############################################################################
10
- # Seed needs to be set at top of yaml, before objects with parameters are made
11
-
12
- seed: 7780
13
- __set_seed: !apply:torch.manual_seed [!ref <seed>]
14
- project: Mamba-ASR
15
- experiment: conmambamamba_S_S2S_catalan_1k_unigram
16
- output_folder: !ref /gpfs/projects/bsc88/speech/TTS/outputs/checkpoints/Mamba_ASR/catalan/<experiment>/<seed>
17
- output_wer_folder: !ref <output_folder>/
18
- save_folder: !ref <output_folder>/save
19
- train_log: !ref <output_folder>/train_log.txt
20
-
21
- # Language model (LM) pretraining
22
- # NB: To avoid mismatch, the speech recognizer must be trained with the same
23
- # tokenizer used for LM training. Here, we download everything from the
24
- # speechbrain HuggingFace repository. However, a local path pointing to a
25
- # directory containing the lm.ckpt and tokenizer.ckpt may also be specified
26
- # instead. E.g if you want to use your own LM / tokenizer.
27
- pretrained_lm_tokenizer_path: /gpfs/projects/bsc88/speech/TTS/repos/Mamba-ASR/catalan/lm
28
-
29
- # Data files
30
- # If RIRS_NOISES dir exists in /localscratch/xxx_corpus/RIRS_NOISES
31
- # then data_folder_rirs should be /localscratch/xxx_corpus
32
- # otherwise the dataset will automatically be downloaded
33
- # data_folder_rirs: !ref <data_folder>
34
- train_splits: ["train-clean-100"]
35
- dev_splits: ["dev-clean"]
36
- test_splits: ["test-clean"]
37
- skip_prep: True
38
- train_csv: /gpfs/projects/bsc88/speech/TTS/repos/Mamba-ASR/catalan/catalan_train_merged_duration.csv
39
- valid_csv: /gpfs/projects/bsc88/speech/TTS/repos/Mamba-ASR/catalan/catalan_dev_2h.csv
40
- test_csv:
41
- - /gpfs/projects/bsc88/speech/TTS/repos/Mamba-ASR/catalan/catalan_test_merged_duration.csv
42
-
43
- skip_train: True
44
- no_lm: True
45
-
46
- # Training parameters
47
- # To make Transformers converge, the global bath size should be large enough.
48
- # The global batch size is computed as batch_size * n_gpus * grad_accumulation_factor.
49
- # Empirically, we found that this value should be >= 128.
50
- # Please, set your parameters accordingly.
51
- number_of_epochs: 110 # En original debemos ponerle minimo 110
52
- batch_size: 30 # This works for 2x GPUs with 32GB
53
- ctc_weight: 0.6 #0.3
54
- grad_accumulation_factor: 1 #1 valor normal
55
- max_grad_norm: 5.0
56
- loss_reduction: 'batchmean'
57
- sorting: random
58
- num_workers: 8 #8 old version
59
- precision: bf16 # bf16, fp16 or fp32
60
- avg_checkpoints: 10 # Number of checkpoints to average for evaluation
61
-
62
- # stages related parameters
63
- # stage_one_epochs: 90
64
- lr_adam: 0.001 #0.001
65
- # lr_sgd: 0.000025
66
-
67
- # Feature parameters
68
- sample_rate: 16000
69
- n_fft: 400
70
- n_mels: 80
71
-
72
- # This setup works well for V100 32GB GPU, adapts it to your needs.
73
- # Or turn it off (but training speed will decrease)
74
- dynamic_batching: False #True
75
- max_batch_length_train: 900 #900 old version
76
- max_batch_length_val: 100 #100 we reduce it as the beam is much wider (VRAM)
77
- num_bucket: 200
78
- shuffle: True # if true re-creates batches at each epoch shuffling examples.
79
- batch_ordering: random
80
- max_batch_ex: 256 #128 old version
81
-
82
- dynamic_batch_sampler_train:
83
- max_batch_length: !ref <max_batch_length_train>
84
- num_buckets: !ref <num_bucket>
85
- shuffle: !ref <shuffle>
86
- batch_ordering: !ref <batch_ordering>
87
- max_batch_ex: !ref <max_batch_ex>
88
-
89
- dynamic_batch_sampler_valid:
90
- max_batch_length: !ref <max_batch_length_val>
91
- num_buckets: !ref <num_bucket>
92
- shuffle: !ref <shuffle>
93
- batch_ordering: !ref <batch_ordering>
94
- max_batch_ex: !ref <max_batch_ex>
95
-
96
- # Dataloader options
97
- train_dataloader_opts:
98
- batch_size: !ref <batch_size>
99
- shuffle: True
100
- num_workers: !ref <num_workers>
101
-
102
- valid_dataloader_opts:
103
- batch_size: 1
104
-
105
- test_dataloader_opts:
106
- batch_size: 1
107
-
108
- ####################### Model parameters ###########################
109
- # Transformer
110
- d_model: 144
111
- nhead: 4
112
- num_encoder_layers: 12
113
- num_decoder_layers: 4
114
- d_ffn: 1024
115
- transformer_dropout: 0.1
116
- activation: !name:torch.nn.GELU
117
- output_neurons: 1000
118
-
119
- # Outputs
120
- blank_index: 0
121
- label_smoothing: 0.1 #0.0
122
- pad_index: 0
123
- bos_index: 1
124
- eos_index: 2
125
-
126
- # Decoding parameters
127
- min_decode_ratio: 0.0
128
- max_decode_ratio: 1.0
129
- valid_search_interval: 15 #Cambiar esto para ver el WER
130
- valid_beam_size: 10
131
- test_beam_size: 24 #66
132
- lm_weight: 0.30 #0.60
133
- ctc_weight_decode: 0.70 #0.40
134
-
135
- # Mamba parameters
136
-
137
- d_state: 16
138
- expand: 2
139
- d_conv: 4
140
- bidirectional: True
141
- mamba_config:
142
- d_state: !ref <d_state>
143
- expand: !ref <expand>
144
- d_conv: !ref <d_conv>
145
- bidirectional: !ref <bidirectional>
146
-
147
- ############################## models ################################
148
-
149
- CNN: !new:speechbrain.lobes.models.convolution.ConvolutionFrontEnd
150
- input_shape: (8, 10, 80)
151
- num_blocks: 2
152
- num_layers_per_block: 1
153
- out_channels: (64, 32)
154
- kernel_sizes: (3, 3)
155
- strides: (2, 2)
156
- residuals: (False, False)
157
-
158
- Transformer: !new:modules.TransformerASR.TransformerASR # yamllint disable-line rule:line-length
159
- input_size: 640
160
- tgt_vocab: !ref <output_neurons>
161
- d_model: !ref <d_model>
162
- nhead: !ref <nhead> # unused
163
- num_encoder_layers: !ref <num_encoder_layers>
164
- num_decoder_layers: !ref <num_decoder_layers>
165
- d_ffn: !ref <d_ffn>
166
- dropout: !ref <transformer_dropout>
167
- activation: !ref <activation>
168
- encoder_module: conmamba
169
- decoder_module: mamba
170
- attention_type: RelPosMHAXL # unused
171
- normalize_before: True
172
- causal: False
173
- mamba_config: !ref <mamba_config>
174
-
175
- # This is the TransformerLM that is used according to the Huggingface repository
176
- # Visit the HuggingFace model corresponding to the pretrained_lm_tokenizer_path
177
- # For more details about the model!
178
- # NB: It has to match the pre-trained TransformerLM!!
179
- lm_model: !new:speechbrain.lobes.models.transformer.TransformerLM.TransformerLM # yamllint disable-line rule:line-length
180
- vocab: !ref <output_neurons>
181
- d_model: 768
182
- nhead: 12
183
- num_encoder_layers: 12
184
- num_decoder_layers: 0
185
- d_ffn: 3072
186
- dropout: 0.0
187
- activation: !name:torch.nn.GELU
188
- normalize_before: False
189
-
190
-
191
-
192
- tokenizer: !new:sentencepiece.SentencePieceProcessor
193
-
194
- ctc_lin: !new:speechbrain.nnet.linear.Linear
195
- input_size: !ref <d_model>
196
- n_neurons: !ref <output_neurons>
197
-
198
- seq_lin: !new:speechbrain.nnet.linear.Linear
199
- input_size: !ref <d_model>
200
- n_neurons: !ref <output_neurons>
201
-
202
- normalize: !new:speechbrain.processing.features.InputNormalization
203
- norm_type: global
204
- update_until_epoch: 4
205
-
206
- modules:
207
- CNN: !ref <CNN>
208
- Transformer: !ref <Transformer>
209
- seq_lin: !ref <seq_lin>
210
- ctc_lin: !ref <ctc_lin>
211
- normalize: !ref <normalize>
212
-
213
- model: !new:torch.nn.ModuleList
214
- - [!ref <CNN>, !ref <Transformer>, !ref <seq_lin>, !ref <ctc_lin>]
215
-
216
- # define two optimizers here for two-stage training
217
- Adam: !name:torch.optim.Adam
218
- lr: !ref <lr_adam>
219
- betas: (0.9, 0.98)
220
- eps: 0.000000001
221
-
222
- # Scorer
223
- ctc_scorer: !new:speechbrain.decoders.scorer.CTCScorer
224
- eos_index: !ref <eos_index>
225
- blank_index: !ref <blank_index>
226
- ctc_fc: !ref <ctc_lin>
227
-
228
-
229
- transformerlm_scorer: !new:speechbrain.decoders.scorer.TransformerLMScorer
230
- language_model: !ref <lm_model>
231
- temperature: 1.15
232
-
233
- scorer_test_search: !new:speechbrain.decoders.scorer.ScorerBuilder
234
- full_scorers: [!ref <transformerlm_scorer>, !ref <ctc_scorer>]
235
- weights:
236
- ctc: !ref <ctc_weight_decode>
237
- transformerlm: !ref <lm_weight>
238
-
239
- scorer_valid_search: !new:speechbrain.decoders.scorer.ScorerBuilder
240
- full_scorers: [!ref <ctc_scorer>]
241
- weights:
242
- ctc: !ref <ctc_weight_decode>
243
-
244
- valid_search: !new:speechbrain.decoders.S2STransformerBeamSearcher
245
- modules: [!ref <Transformer>, !ref <seq_lin>]
246
- bos_index: !ref <bos_index>
247
- eos_index: !ref <eos_index>
248
- min_decode_ratio: !ref <min_decode_ratio>
249
- max_decode_ratio: !ref <max_decode_ratio>
250
- beam_size: !ref <valid_beam_size>
251
- using_eos_threshold: False
252
- length_normalization: True
253
- scorer: !ref <scorer_valid_search>
254
-
255
- test_search: !new:speechbrain.decoders.S2STransformerBeamSearcher
256
- modules: [!ref <Transformer>, !ref <seq_lin>]
257
- bos_index: !ref <bos_index>
258
- eos_index: !ref <eos_index>
259
- min_decode_ratio: !ref <min_decode_ratio>
260
- max_decode_ratio: !ref <max_decode_ratio>
261
- beam_size: !ref <test_beam_size>
262
- temperature: 1.15
263
- using_eos_threshold: False
264
- length_normalization: True
265
- scorer: !ref <scorer_test_search>
266
-
267
-
268
- log_softmax: !new:torch.nn.LogSoftmax
269
- dim: -1
270
-
271
- ctc_cost: !name:speechbrain.nnet.losses.ctc_loss
272
- blank_index: !ref <blank_index>
273
- reduction: !ref <loss_reduction>
274
-
275
- seq_cost: !name:speechbrain.nnet.losses.kldiv_loss
276
- label_smoothing: !ref <label_smoothing>
277
- reduction: !ref <loss_reduction>
278
-
279
- noam_annealing: !new:speechbrain.nnet.schedulers.NoamScheduler
280
- lr_initial: !ref <lr_adam>
281
- n_warmup_steps: !ref 25000 // <grad_accumulation_factor>
282
-
283
- checkpointer: !new:speechbrain.utils.checkpoints.Checkpointer
284
- checkpoints_dir: !ref <save_folder>
285
- recoverables:
286
- model: !ref <model>
287
- noam_scheduler: !ref <noam_annealing>
288
- normalizer: !ref <normalize>
289
- counter: !ref <epoch_counter>
290
-
291
- epoch_counter: !new:speechbrain.utils.epoch_loop.EpochCounter
292
- limit: !ref <number_of_epochs>
293
-
294
- # Speed perturbation
295
- speed_changes: [95, 100, 105] # List of speed changes for time-stretching
296
-
297
- speed_perturb: !new:speechbrain.augment.time_domain.SpeedPerturb
298
- orig_freq: !ref <sample_rate>
299
- speeds: !ref <speed_changes>
300
-
301
- # Time Drop
302
- time_drop_length_low: 15 # Min length for temporal chunk to drop in spectrogram
303
- time_drop_length_high: 25 # Max length for temporal chunk to drop in spectrogram
304
- time_drop_count_low: 2 #4 Min number of chunks to drop in time in the spectrogram
305
- time_drop_count_high: 2 #4 Max number of chunks to drop in time in the spectrogram
306
- time_drop_replace: "mean" # Method of dropping chunks
307
-
308
- time_drop: !new:speechbrain.augment.freq_domain.SpectrogramDrop
309
- drop_length_low: !ref <time_drop_length_low>
310
- drop_length_high: !ref <time_drop_length_high>
311
- drop_count_low: !ref <time_drop_count_low>
312
- drop_count_high: !ref <time_drop_count_high>
313
- replace: !ref <time_drop_replace>
314
- dim: 1
315
-
316
- # Frequency Drop
317
- freq_drop_length_low: 10 # Min length for chunks to drop in frequency in the spectrogram
318
- freq_drop_length_high: 20 # Max length for chunks to drop in frequency in the spectrogram
319
- freq_drop_count_low: 2 #4 Min number of chunks to drop in frequency in the spectrogram
320
- freq_drop_count_high: 2 #4 Max number of chunks to drop in frequency in the spectrogram
321
- freq_drop_replace: "mean" # Method of dropping chunks
322
-
323
- freq_drop: !new:speechbrain.augment.freq_domain.SpectrogramDrop
324
- drop_length_low: !ref <freq_drop_length_low>
325
- drop_length_high: !ref <freq_drop_length_high>
326
- drop_count_low: !ref <freq_drop_count_low>
327
- drop_count_high: !ref <freq_drop_count_high>
328
- replace: !ref <freq_drop_replace>
329
- dim: 2
330
-
331
- # Time warp
332
- time_warp_window: 5 # Length of time warping window
333
- time_warp_mode: "bicubic" # Time warping method
334
-
335
- time_warp: !new:speechbrain.augment.freq_domain.Warping
336
- warp_window: !ref <time_warp_window>
337
- warp_mode: !ref <time_warp_mode>
338
- dim: 1
339
-
340
- fea_augment: !new:speechbrain.augment.augmenter.Augmenter
341
- parallel_augment: False
342
- concat_original: False
343
- repeat_augment: 1
344
- shuffle_augmentations: False
345
- min_augmentations: 3
346
- max_augmentations: 3
347
- augment_prob: 1.0
348
- augmentations: [
349
- !ref <time_drop>,
350
- !ref <freq_drop>,
351
- !ref <time_warp>]
352
-
353
- compute_features: !new:speechbrain.lobes.features.Fbank
354
- sample_rate: !ref <sample_rate>
355
- n_fft: !ref <n_fft>
356
- n_mels: !ref <n_mels>
357
-
358
- train_logger: !new:speechbrain.utils.train_logger.FileTrainLogger
359
- save_file: !ref <train_log>
360
-
361
- error_rate_computer: !name:speechbrain.utils.metric_stats.ErrorRateStats
362
- acc_computer: !name:speechbrain.utils.Accuracy.AccuracyStats
363
-
364
- # The pretrainer allows a mapping between pretrained files and instances that
365
- # are declared in the yaml. E.g here, we will download the file lm.ckpt
366
- # and it will be loaded into "lm" which is pointing to the <lm_model> defined
367
- # before.
368
- pretrainer: !new:speechbrain.utils.parameter_transfer.Pretrainer
369
- collect_in: !ref <save_folder>
370
- loadables:
371
- lm: !ref <lm_model>
372
- tokenizer: !ref <tokenizer>
373
- paths:
374
- lm: !ref <pretrained_lm_tokenizer_path>/lm.ckpt
375
- tokenizer: !ref <pretrained_lm_tokenizer_path>/tokenizer.ckpt
376
-
377
- use_wandb: False #False
378
- resume: False
379
- wandb_logger: !name:speechbrain.utils.train_logger.WandBLogger
380
- initializer: !name:wandb.init
381
- entity: BSC
382
- project: !ref <project>
383
- name: !ref <experiment>
384
- dir: !ref <save_folder>
385
- reinit: true
386
- resume: !ref <resume>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inference.yaml ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ConMamba-small-ca standalone inference configuration.
2
+ # Decoding uses the LM-free beam search validated in ASR Arena.
3
+
4
+ seed: 7780
5
+ __set_seed: !apply:torch.manual_seed [!ref <seed>]
6
+
7
+ # The inference entry point overrides model_dir with the repository directory,
8
+ # so these assets resolve correctly even when invoked from another directory.
9
+ model_dir: .
10
+ model_checkpoint: !ref <model_dir>/model.ckpt
11
+ tokenizer_checkpoint: !ref <model_dir>/tokenizer.ckpt
12
+ normalizer_checkpoint: !ref <model_dir>/normalizer.ckpt
13
+
14
+ # Audio features
15
+ sample_rate: 16000
16
+ n_fft: 400
17
+ n_mels: 80
18
+
19
+ # Model architecture
20
+ d_model: 144
21
+ nhead: 4
22
+ num_encoder_layers: 12
23
+ num_decoder_layers: 4
24
+ d_ffn: 1024
25
+ transformer_dropout: 0.1
26
+ activation: !name:torch.nn.GELU
27
+ output_neurons: 1000
28
+
29
+ # Token indices
30
+ blank_index: 0
31
+ pad_index: 0
32
+ bos_index: 1
33
+ eos_index: 2
34
+
35
+ # LM-free decoding parameters
36
+ min_decode_ratio: 0.0
37
+ max_decode_ratio: 1.0
38
+ beam_size: 10
39
+ ctc_weight_decode: 0.70
40
+
41
+ # Mamba parameters
42
+ d_state: 16
43
+ expand: 2
44
+ d_conv: 4
45
+ bidirectional: True
46
+ mamba_config:
47
+ d_state: !ref <d_state>
48
+ expand: !ref <expand>
49
+ d_conv: !ref <d_conv>
50
+ bidirectional: !ref <bidirectional>
51
+
52
+ # Acoustic model
53
+ CNN: !new:speechbrain.lobes.models.convolution.ConvolutionFrontEnd
54
+ input_shape: (8, 10, 80)
55
+ num_blocks: 2
56
+ num_layers_per_block: 1
57
+ out_channels: (64, 32)
58
+ kernel_sizes: (3, 3)
59
+ strides: (2, 2)
60
+ residuals: (False, False)
61
+
62
+ Transformer: !new:modules.TransformerASR.TransformerASR
63
+ input_size: 640
64
+ tgt_vocab: !ref <output_neurons>
65
+ d_model: !ref <d_model>
66
+ nhead: !ref <nhead>
67
+ num_encoder_layers: !ref <num_encoder_layers>
68
+ num_decoder_layers: !ref <num_decoder_layers>
69
+ d_ffn: !ref <d_ffn>
70
+ dropout: !ref <transformer_dropout>
71
+ activation: !ref <activation>
72
+ encoder_module: conmamba
73
+ decoder_module: mamba
74
+ attention_type: RelPosMHAXL
75
+ normalize_before: True
76
+ causal: False
77
+ mamba_config: !ref <mamba_config>
78
+
79
+ tokenizer: !new:sentencepiece.SentencePieceProcessor
80
+
81
+ ctc_lin: !new:speechbrain.nnet.linear.Linear
82
+ input_size: !ref <d_model>
83
+ n_neurons: !ref <output_neurons>
84
+
85
+ seq_lin: !new:speechbrain.nnet.linear.Linear
86
+ input_size: !ref <d_model>
87
+ n_neurons: !ref <output_neurons>
88
+
89
+ normalize: !new:speechbrain.processing.features.InputNormalization
90
+ norm_type: global
91
+ update_until_epoch: 4
92
+
93
+ modules:
94
+ CNN: !ref <CNN>
95
+ Transformer: !ref <Transformer>
96
+ seq_lin: !ref <seq_lin>
97
+ ctc_lin: !ref <ctc_lin>
98
+ normalize: !ref <normalize>
99
+
100
+ model: !new:torch.nn.ModuleList
101
+ - [!ref <CNN>, !ref <Transformer>, !ref <seq_lin>, !ref <ctc_lin>]
102
+
103
+ # CTC/attention beam search, without an external language model
104
+ ctc_scorer: !new:speechbrain.decoders.scorer.CTCScorer
105
+ eos_index: !ref <eos_index>
106
+ blank_index: !ref <blank_index>
107
+ ctc_fc: !ref <ctc_lin>
108
+
109
+ search_scorer: !new:speechbrain.decoders.scorer.ScorerBuilder
110
+ full_scorers: [!ref <ctc_scorer>]
111
+ weights:
112
+ ctc: !ref <ctc_weight_decode>
113
+
114
+ beam_search: !new:speechbrain.decoders.S2STransformerBeamSearcher
115
+ modules: [!ref <Transformer>, !ref <seq_lin>]
116
+ bos_index: !ref <bos_index>
117
+ eos_index: !ref <eos_index>
118
+ min_decode_ratio: !ref <min_decode_ratio>
119
+ max_decode_ratio: !ref <max_decode_ratio>
120
+ beam_size: !ref <beam_size>
121
+ using_eos_threshold: False
122
+ length_normalization: True
123
+ scorer: !ref <search_scorer>
124
+
125
+ log_softmax: !new:torch.nn.LogSoftmax
126
+ dim: -1
127
+
128
+ compute_features: !new:speechbrain.lobes.features.Fbank
129
+ sample_rate: !ref <sample_rate>
130
+ n_fft: !ref <n_fft>
131
+ n_mels: !ref <n_mels>
model.ckpt CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:be1a4250fe4f60d7bf5308bdc3f06430be7d195bff3f6dcbe8f483df1b4285b4
3
  size 52989622
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb075002ffd3258f9cb3ad7f127c55d8bcf61f5cc3ba5a28a7b9573d38f07b60
3
  size 52989622
modules/Conformer.py ADDED
@@ -0,0 +1,1094 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Conformer implementation.
2
+
3
+ Authors
4
+ -------
5
+ * Jianyuan Zhong 2020
6
+ * Samuele Cornell 2021
7
+ * Sylvain de Langen 2023
8
+ """
9
+
10
+ import warnings
11
+ from dataclasses import dataclass
12
+ from typing import List, Optional
13
+
14
+ import torch
15
+ import torch.nn as nn
16
+ import torch.nn.functional as F
17
+
18
+ import speechbrain as sb
19
+ from speechbrain.nnet.activations import Swish
20
+ from speechbrain.nnet.attention import (
21
+ MultiheadAttention,
22
+ PositionalwiseFeedForward,
23
+ RelPosMHAXL,
24
+ )
25
+ from speechbrain.nnet.hypermixing import HyperMixing
26
+ from speechbrain.nnet.normalization import LayerNorm
27
+ from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
28
+
29
+
30
+ @dataclass
31
+ class ConformerEncoderLayerStreamingContext:
32
+ """Streaming metadata and state for a `ConformerEncoderLayer`.
33
+
34
+ The multi-head attention and Dynamic Chunk Convolution require to save some
35
+ left context that gets inserted as left padding.
36
+
37
+ See :class:`.ConvolutionModule` documentation for further details.
38
+ """
39
+
40
+ mha_left_context_size: int
41
+ """For this layer, specifies how many frames of inputs should be saved.
42
+ Usually, the same value is used across all layers, but this can be modified.
43
+ """
44
+
45
+ mha_left_context: Optional[torch.Tensor] = None
46
+ """Left context to insert at the left of the current chunk as inputs to the
47
+ multi-head attention. It can be `None` (if we're dealing with the first
48
+ chunk) or `<= mha_left_context_size` because for the first few chunks, not
49
+ enough left context may be available to pad.
50
+ """
51
+
52
+ dcconv_left_context: Optional[torch.Tensor] = None
53
+ """Left context to insert at the left of the convolution according to the
54
+ Dynamic Chunk Convolution method.
55
+
56
+ Unlike `mha_left_context`, here the amount of frames to keep is fixed and
57
+ inferred from the kernel size of the convolution module.
58
+ """
59
+
60
+
61
+ @dataclass
62
+ class ConformerEncoderStreamingContext:
63
+ """Streaming metadata and state for a `ConformerEncoder`."""
64
+
65
+ dynchunktrain_config: DynChunkTrainConfig
66
+ """Dynamic Chunk Training configuration holding chunk size and context size
67
+ information."""
68
+
69
+ layers: List[ConformerEncoderLayerStreamingContext]
70
+ """Streaming metadata and state for each layer of the encoder."""
71
+
72
+
73
+ class ConvolutionModule(nn.Module):
74
+ """This is an implementation of convolution module in Conformer.
75
+
76
+ Arguments
77
+ ---------
78
+ input_size : int
79
+ The expected size of the input embedding dimension.
80
+ kernel_size: int, optional
81
+ Kernel size of non-bottleneck convolutional layer.
82
+ bias: bool, optional
83
+ Whether to use bias in the non-bottleneck conv layer.
84
+ activation: torch.nn.Module
85
+ Activation function used after non-bottleneck conv layer.
86
+ dropout: float, optional
87
+ Dropout rate.
88
+ causal: bool, optional
89
+ Whether the convolution should be causal or not.
90
+ dilation: int, optional
91
+ Dilation factor for the non bottleneck conv layer.
92
+
93
+ Example
94
+ -------
95
+ >>> import torch
96
+ >>> x = torch.rand((8, 60, 512))
97
+ >>> net = ConvolutionModule(512, 3)
98
+ >>> output = net(x)
99
+ >>> output.shape
100
+ torch.Size([8, 60, 512])
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ input_size,
106
+ kernel_size=31,
107
+ bias=True,
108
+ activation=Swish,
109
+ dropout=0.0,
110
+ causal=False,
111
+ dilation=1,
112
+ ):
113
+ super().__init__()
114
+
115
+ self.kernel_size = kernel_size
116
+ self.causal = causal
117
+ self.dilation = dilation
118
+
119
+ if self.causal:
120
+ self.padding = (kernel_size - 1) * 2 ** (dilation - 1)
121
+ else:
122
+ self.padding = (kernel_size - 1) * 2 ** (dilation - 1) // 2
123
+
124
+ self.layer_norm = nn.LayerNorm(input_size)
125
+ self.bottleneck = nn.Sequential(
126
+ # pointwise
127
+ nn.Conv1d(
128
+ input_size, 2 * input_size, kernel_size=1, stride=1, bias=bias
129
+ ),
130
+ nn.GLU(dim=1),
131
+ )
132
+ # depthwise
133
+ self.conv = nn.Conv1d(
134
+ input_size,
135
+ input_size,
136
+ kernel_size=kernel_size,
137
+ stride=1,
138
+ padding=self.padding,
139
+ dilation=dilation,
140
+ groups=input_size,
141
+ bias=bias,
142
+ )
143
+
144
+ # BatchNorm in the original Conformer replaced with a LayerNorm due to
145
+ # https://github.com/speechbrain/speechbrain/pull/1329
146
+ # see discussion
147
+ # https://github.com/speechbrain/speechbrain/pull/933#issuecomment-1033367884
148
+
149
+ self.after_conv = nn.Sequential(
150
+ nn.LayerNorm(input_size),
151
+ activation(),
152
+ # pointwise
153
+ nn.Linear(input_size, input_size, bias=bias),
154
+ nn.Dropout(dropout),
155
+ )
156
+
157
+ def forward(
158
+ self,
159
+ x: torch.Tensor,
160
+ mask: Optional[torch.Tensor] = None,
161
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
162
+ ):
163
+ """Applies the convolution to an input tensor `x`.
164
+
165
+ Arguments
166
+ ---------
167
+ x: torch.Tensor
168
+ Input tensor to the convolution module.
169
+ mask: torch.Tensor, optional
170
+ Mask to be applied over the output of the convolution using
171
+ `masked_fill_`, if specified.
172
+ dynchunktrain_config: DynChunkTrainConfig, optional
173
+ If specified, makes the module support Dynamic Chunk Convolution
174
+ (DCConv) as implemented by
175
+ `Dynamic Chunk Convolution for Unified Streaming and Non-Streaming Conformer ASR <https://www.amazon.science/publications/dynamic-chunk-convolution-for-unified-streaming-and-non-streaming-conformer-asr>`_.
176
+ This allows masking future frames while preserving better accuracy
177
+ than a fully causal convolution, at a small speed cost.
178
+ This should only be used for training (or, if you know what you're
179
+ doing, for masked evaluation at inference time), as the forward
180
+ streaming function should be used at inference time.
181
+
182
+ Returns
183
+ -------
184
+ out: torch.Tensor
185
+ The output tensor.
186
+ """
187
+
188
+ if dynchunktrain_config is not None:
189
+ # chances are chunking+causal is unintended; i don't know where it
190
+ # may make sense, but if it does to you, feel free to implement it.
191
+ assert (
192
+ not self.causal
193
+ ), "Chunked convolution not supported with causal padding"
194
+
195
+ assert (
196
+ self.dilation == 1
197
+ ), "Current DynChunkTrain logic does not support dilation != 1"
198
+
199
+ # in a causal convolution, which is not the case here, an output
200
+ # frame would never be able to depend on a input frame from any
201
+ # point in the future.
202
+
203
+ # but with the dynamic chunk convolution, we instead use a "normal"
204
+ # convolution but where, for any output frame, the future beyond the
205
+ # "current" chunk gets masked.
206
+ # see the paper linked in the documentation for details.
207
+
208
+ chunk_size = dynchunktrain_config.chunk_size
209
+ batch_size = x.shape[0]
210
+
211
+ # determine the amount of padding we need to insert at the right of
212
+ # the last chunk so that all chunks end up with the same size.
213
+ if x.shape[1] % chunk_size != 0:
214
+ final_right_padding = chunk_size - (x.shape[1] % chunk_size)
215
+ else:
216
+ final_right_padding = 0
217
+
218
+ # -> [batch_size, t, in_channels]
219
+ out = self.layer_norm(x)
220
+
221
+ # -> [batch_size, in_channels, t] for the CNN
222
+ out = out.transpose(1, 2)
223
+
224
+ # -> [batch_size, in_channels, t] (pointwise)
225
+ out = self.bottleneck(out)
226
+
227
+ # -> [batch_size, in_channels, lc+t+final_right_padding]
228
+ out = F.pad(out, (self.padding, final_right_padding), value=0)
229
+
230
+ # now, make chunks with left context.
231
+ # as a recap to what the above padding and this unfold do, consider
232
+ # each a/b/c letter represents a frame as part of chunks a, b, c.
233
+ # consider a chunk size of 4 and a kernel size of 5 (padding=2):
234
+ #
235
+ # input seq: 00aaaabbbbcc00
236
+ # chunk #1: 00aaaa
237
+ # chunk #2: aabbbb
238
+ # chunk #3: bbcc00
239
+ #
240
+ # a few remarks here:
241
+ # - the left padding gets inserted early so that the unfold logic
242
+ # works trivially
243
+ # - the right 0-padding got inserted as the number of time steps
244
+ # could not be evenly split in `chunk_size` chunks
245
+
246
+ # -> [batch_size, in_channels, num_chunks, lc+chunk_size]
247
+ out = out.unfold(2, size=chunk_size + self.padding, step=chunk_size)
248
+
249
+ # as we manually disable padding in the convolution below, we insert
250
+ # right 0-padding to the chunks, e.g. reusing the above example:
251
+ #
252
+ # chunk #1: 00aaaa00
253
+ # chunk #2: aabbbb00
254
+ # chunk #3: bbcc0000
255
+
256
+ # -> [batch_size, in_channels, num_chunks, lc+chunk_size+rpad]
257
+ out = F.pad(out, (0, self.padding), value=0)
258
+
259
+ # the transpose+flatten effectively flattens chunks into the batch
260
+ # dimension to be processed into the time-wise convolution. the
261
+ # chunks will later on be unflattened.
262
+
263
+ # -> [batch_size, num_chunks, in_channels, lc+chunk_size+rpad]
264
+ out = out.transpose(1, 2)
265
+
266
+ # -> [batch_size * num_chunks, in_channels, lc+chunk_size+rpad]
267
+ out = out.flatten(start_dim=0, end_dim=1)
268
+
269
+ # TODO: experiment around reflect padding, which is difficult
270
+ # because small chunks have too little time steps to reflect from
271
+
272
+ # let's keep backwards compat by pointing at the weights from the
273
+ # already declared Conv1d.
274
+ #
275
+ # still reusing the above example, the convolution will be applied,
276
+ # with the padding truncated on both ends. the following example
277
+ # shows the letter corresponding to the input frame on which the
278
+ # convolution was centered.
279
+ #
280
+ # as you can see, the sum of lengths of all chunks is equal to our
281
+ # input sequence length + `final_right_padding`.
282
+ #
283
+ # chunk #1: aaaa
284
+ # chunk #2: bbbb
285
+ # chunk #3: cc00
286
+
287
+ # -> [batch_size * num_chunks, out_channels, chunk_size]
288
+ out = F.conv1d(
289
+ out,
290
+ weight=self.conv.weight,
291
+ bias=self.conv.bias,
292
+ stride=self.conv.stride,
293
+ padding=0,
294
+ dilation=self.conv.dilation,
295
+ groups=self.conv.groups,
296
+ )
297
+
298
+ # -> [batch_size * num_chunks, chunk_size, out_channels]
299
+ out = out.transpose(1, 2)
300
+
301
+ out = self.after_conv(out)
302
+
303
+ # -> [batch_size, num_chunks, chunk_size, out_channels]
304
+ out = torch.unflatten(out, dim=0, sizes=(batch_size, -1))
305
+
306
+ # -> [batch_size, t + final_right_padding, out_channels]
307
+ out = torch.flatten(out, start_dim=1, end_dim=2)
308
+
309
+ # -> [batch_size, t, out_channels]
310
+ if final_right_padding > 0:
311
+ out = out[:, :-final_right_padding, :]
312
+ else:
313
+ out = self.layer_norm(x)
314
+ out = out.transpose(1, 2)
315
+ out = self.bottleneck(out)
316
+ out = self.conv(out)
317
+
318
+ if self.causal:
319
+ # chomp
320
+ out = out[..., : -self.padding]
321
+
322
+ out = out.transpose(1, 2)
323
+ out = self.after_conv(out)
324
+
325
+ if mask is not None:
326
+ out.masked_fill_(mask, 0.0)
327
+
328
+ return out
329
+
330
+
331
+ class ConformerEncoderLayer(nn.Module):
332
+ """This is an implementation of Conformer encoder layer.
333
+
334
+ Arguments
335
+ ---------
336
+ d_model : int
337
+ The expected size of the input embedding.
338
+ d_ffn : int
339
+ Hidden size of self-attention Feed Forward layer.
340
+ nhead : int
341
+ Number of attention heads.
342
+ kernel_size : int, optional
343
+ Kernel size of convolution model.
344
+ kdim : int, optional
345
+ Dimension of the key.
346
+ vdim : int, optional
347
+ Dimension of the value.
348
+ activation: torch.nn.Module
349
+ Activation function used in each Conformer layer.
350
+ bias : bool, optional
351
+ Whether convolution module.
352
+ dropout : int, optional
353
+ Dropout for the encoder.
354
+ causal : bool, optional
355
+ Whether the convolutions should be causal or not.
356
+ attention_type : str, optional
357
+ type of attention layer, e.g. regularMHA for regular MultiHeadAttention.
358
+
359
+ Example
360
+ -------
361
+ >>> import torch
362
+ >>> x = torch.rand((8, 60, 512))
363
+ >>> pos_embs = torch.rand((1, 2*60-1, 512))
364
+ >>> net = ConformerEncoderLayer(d_ffn=512, nhead=8, d_model=512, kernel_size=3)
365
+ >>> output = net(x, pos_embs=pos_embs)
366
+ >>> output[0].shape
367
+ torch.Size([8, 60, 512])
368
+ """
369
+
370
+ def __init__(
371
+ self,
372
+ d_model,
373
+ d_ffn,
374
+ nhead,
375
+ kernel_size=31,
376
+ kdim=None,
377
+ vdim=None,
378
+ activation=Swish,
379
+ bias=True,
380
+ dropout=0.0,
381
+ causal=False,
382
+ attention_type="RelPosMHAXL",
383
+ ):
384
+ super().__init__()
385
+
386
+ if attention_type == "regularMHA":
387
+ self.mha_layer = MultiheadAttention(
388
+ nhead=nhead,
389
+ d_model=d_model,
390
+ dropout=dropout,
391
+ kdim=kdim,
392
+ vdim=vdim,
393
+ )
394
+ elif attention_type == "RelPosMHAXL":
395
+ # transformerXL style positional encoding
396
+ self.mha_layer = RelPosMHAXL(
397
+ num_heads=nhead,
398
+ embed_dim=d_model,
399
+ dropout=dropout,
400
+ mask_pos_future=causal,
401
+ )
402
+ elif attention_type == "hypermixing":
403
+ self.mha_layer = HyperMixing(
404
+ input_output_dim=d_model,
405
+ hypernet_size=d_ffn,
406
+ tied=False,
407
+ num_heads=nhead,
408
+ fix_tm_hidden_size=False,
409
+ )
410
+
411
+ self.convolution_module = ConvolutionModule(
412
+ d_model, kernel_size, bias, activation, dropout, causal=causal
413
+ )
414
+
415
+ self.ffn_module1 = nn.Sequential(
416
+ nn.LayerNorm(d_model),
417
+ PositionalwiseFeedForward(
418
+ d_ffn=d_ffn,
419
+ input_size=d_model,
420
+ dropout=dropout,
421
+ activation=activation,
422
+ ),
423
+ nn.Dropout(dropout),
424
+ )
425
+
426
+ self.ffn_module2 = nn.Sequential(
427
+ nn.LayerNorm(d_model),
428
+ PositionalwiseFeedForward(
429
+ d_ffn=d_ffn,
430
+ input_size=d_model,
431
+ dropout=dropout,
432
+ activation=activation,
433
+ ),
434
+ nn.Dropout(dropout),
435
+ )
436
+
437
+ self.norm1 = LayerNorm(d_model)
438
+ self.norm2 = LayerNorm(d_model)
439
+ self.drop = nn.Dropout(dropout)
440
+
441
+ def forward(
442
+ self,
443
+ x,
444
+ src_mask: Optional[torch.Tensor] = None,
445
+ src_key_padding_mask: Optional[torch.Tensor] = None,
446
+ pos_embs: torch.Tensor = None,
447
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
448
+ ):
449
+ """
450
+ Arguments
451
+ ----------
452
+ src : torch.Tensor
453
+ The sequence to the encoder layer.
454
+ src_mask : torch.Tensor, optional
455
+ The mask for the src sequence.
456
+ src_key_padding_mask : torch.Tensor, optional
457
+ The mask for the src keys per batch.
458
+ pos_embs: torch.Tensor, torch.nn.Module, optional
459
+ Module or tensor containing the input sequence positional embeddings
460
+ dynchunktrain_config: Optional[DynChunkTrainConfig]
461
+ Dynamic Chunk Training configuration object for streaming,
462
+ specifically involved here to apply Dynamic Chunk Convolution to
463
+ the convolution module.
464
+ """
465
+ conv_mask: Optional[torch.Tensor] = None
466
+ if src_key_padding_mask is not None:
467
+ conv_mask = src_key_padding_mask.unsqueeze(-1)
468
+ # ffn module
469
+ x = x + 0.5 * self.ffn_module1(x)
470
+ # multi-head attention module
471
+ skip = x
472
+ x = self.norm1(x)
473
+
474
+ x, self_attn = self.mha_layer(
475
+ x,
476
+ x,
477
+ x,
478
+ attn_mask=src_mask,
479
+ key_padding_mask=src_key_padding_mask,
480
+ pos_embs=pos_embs,
481
+ )
482
+ x = x + skip
483
+ # convolution module
484
+ x = x + self.convolution_module(
485
+ x, conv_mask, dynchunktrain_config=dynchunktrain_config
486
+ )
487
+ # ffn module
488
+ x = self.norm2(x + 0.5 * self.ffn_module2(x))
489
+ return x, self_attn
490
+
491
+ def forward_streaming(
492
+ self,
493
+ x,
494
+ context: ConformerEncoderLayerStreamingContext,
495
+ pos_embs: torch.Tensor = None,
496
+ ):
497
+ """Conformer layer streaming forward (typically for
498
+ DynamicChunkTraining-trained models), which is to be used at inference
499
+ time. Relies on a mutable context object as initialized by
500
+ `make_streaming_context` that should be used across chunks.
501
+ Invoked by `ConformerEncoder.forward_streaming`.
502
+
503
+ Arguments
504
+ ---------
505
+ x : torch.Tensor
506
+ Input tensor for this layer. Batching is supported as long as you
507
+ keep the context consistent.
508
+ context : ConformerEncoderStreamingContext
509
+ Mutable streaming context; the same object should be passed across
510
+ calls.
511
+ pos_embs : torch.Tensor, optional
512
+ Positional embeddings, if used.
513
+
514
+ Returns
515
+ -------
516
+ x : torch.Tensor
517
+ Output tensor.
518
+ self_attn : list
519
+ List of self attention values.
520
+ """
521
+
522
+ orig_len = x.shape[-2]
523
+ # ffn module
524
+ x = x + 0.5 * self.ffn_module1(x)
525
+
526
+ # TODO: make the approach for MHA left context more efficient.
527
+ # currently, this saves the inputs to the MHA.
528
+ # the naive approach is suboptimal in a few ways, namely that the
529
+ # outputs for this left padding is being re-computed even though we
530
+ # discard them immediately after.
531
+
532
+ # left pad `x` with our MHA left context
533
+ if context.mha_left_context is not None:
534
+ x = torch.cat((context.mha_left_context, x), dim=1)
535
+
536
+ # compute new MHA left context for the next call to our function
537
+ if context.mha_left_context_size > 0:
538
+ context.mha_left_context = x[
539
+ ..., -context.mha_left_context_size :, :
540
+ ]
541
+
542
+ # multi-head attention module
543
+ skip = x
544
+ x = self.norm1(x)
545
+
546
+ x, self_attn = self.mha_layer(
547
+ x,
548
+ x,
549
+ x,
550
+ attn_mask=None,
551
+ key_padding_mask=None,
552
+ pos_embs=pos_embs,
553
+ )
554
+ x = x + skip
555
+
556
+ # truncate outputs corresponding to the MHA left context (we only care
557
+ # about our chunk's outputs); see above to-do
558
+ x = x[..., -orig_len:, :]
559
+
560
+ if context.dcconv_left_context is not None:
561
+ x = torch.cat((context.dcconv_left_context, x), dim=1)
562
+
563
+ # compute new DCConv left context for the next call to our function
564
+ context.dcconv_left_context = x[
565
+ ..., -self.convolution_module.padding :, :
566
+ ]
567
+
568
+ # convolution module
569
+ x = x + self.convolution_module(x)
570
+
571
+ # truncate outputs corresponding to the DCConv left context
572
+ x = x[..., -orig_len:, :]
573
+
574
+ # ffn module
575
+ x = self.norm2(x + 0.5 * self.ffn_module2(x))
576
+ return x, self_attn
577
+
578
+ def make_streaming_context(self, mha_left_context_size: int):
579
+ """Creates a blank streaming context for this encoding layer.
580
+
581
+ Arguments
582
+ ---------
583
+ mha_left_context_size : int
584
+ How many left frames should be saved and used as left context to the
585
+ current chunk when streaming
586
+
587
+ Returns
588
+ -------
589
+ ConformerEncoderLayerStreamingContext
590
+ """
591
+ return ConformerEncoderLayerStreamingContext(
592
+ mha_left_context_size=mha_left_context_size
593
+ )
594
+
595
+
596
+ class ConformerEncoder(nn.Module):
597
+ """This class implements the Conformer encoder.
598
+
599
+ Arguments
600
+ ---------
601
+ num_layers : int
602
+ Number of layers.
603
+ d_model : int
604
+ Embedding dimension size.
605
+ d_ffn : int
606
+ Hidden size of self-attention Feed Forward layer.
607
+ nhead : int
608
+ Number of attention heads.
609
+ kernel_size : int, optional
610
+ Kernel size of convolution model.
611
+ kdim : int, optional
612
+ Dimension of the key.
613
+ vdim : int, optional
614
+ Dimension of the value.
615
+ activation: torch.nn.Module
616
+ Activation function used in each Confomer layer.
617
+ bias : bool, optional
618
+ Whether convolution module.
619
+ dropout : int, optional
620
+ Dropout for the encoder.
621
+ causal: bool, optional
622
+ Whether the convolutions should be causal or not.
623
+ attention_type: str, optional
624
+ type of attention layer, e.g. regularMHA for regular MultiHeadAttention.
625
+
626
+
627
+ Example
628
+ -------
629
+ >>> import torch
630
+ >>> x = torch.rand((8, 60, 512))
631
+ >>> pos_emb = torch.rand((1, 2*60-1, 512))
632
+ >>> net = ConformerEncoder(1, 512, 512, 8)
633
+ >>> output, _ = net(x, pos_embs=pos_emb)
634
+ >>> output.shape
635
+ torch.Size([8, 60, 512])
636
+ """
637
+
638
+ def __init__(
639
+ self,
640
+ num_layers,
641
+ d_model,
642
+ d_ffn,
643
+ nhead,
644
+ kernel_size=31,
645
+ kdim=None,
646
+ vdim=None,
647
+ activation=Swish,
648
+ bias=True,
649
+ dropout=0.0,
650
+ causal=False,
651
+ attention_type="RelPosMHAXL",
652
+ ):
653
+ super().__init__()
654
+
655
+ self.layers = torch.nn.ModuleList(
656
+ [
657
+ ConformerEncoderLayer(
658
+ d_ffn=d_ffn,
659
+ nhead=nhead,
660
+ d_model=d_model,
661
+ kdim=kdim,
662
+ vdim=vdim,
663
+ dropout=dropout,
664
+ activation=activation,
665
+ kernel_size=kernel_size,
666
+ bias=bias,
667
+ causal=causal,
668
+ attention_type=attention_type,
669
+ )
670
+ for i in range(num_layers)
671
+ ]
672
+ )
673
+ self.norm = LayerNorm(d_model, eps=1e-6)
674
+ self.attention_type = attention_type
675
+
676
+ def forward(
677
+ self,
678
+ src,
679
+ src_mask: Optional[torch.Tensor] = None,
680
+ src_key_padding_mask: Optional[torch.Tensor] = None,
681
+ pos_embs: Optional[torch.Tensor] = None,
682
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
683
+ ):
684
+ """
685
+ Arguments
686
+ ----------
687
+ src : torch.Tensor
688
+ The sequence to the encoder layer.
689
+ src_mask : torch.Tensor, optional
690
+ The mask for the src sequence.
691
+ src_key_padding_mask : torch.Tensor, optional
692
+ The mask for the src keys per batch.
693
+ pos_embs: torch.Tensor, torch.nn.Module,
694
+ Module or tensor containing the input sequence positional embeddings
695
+ If custom pos_embs are given it needs to have the shape (1, 2*S-1, E)
696
+ where S is the sequence length, and E is the embedding dimension.
697
+ dynchunktrain_config: Optional[DynChunkTrainConfig]
698
+ Dynamic Chunk Training configuration object for streaming,
699
+ specifically involved here to apply Dynamic Chunk Convolution to the
700
+ convolution module.
701
+ """
702
+ if self.attention_type == "RelPosMHAXL":
703
+ if pos_embs is None:
704
+ raise ValueError(
705
+ "The chosen attention type for the Conformer is RelPosMHAXL. For this attention type, the positional embeddings are mandatory"
706
+ )
707
+
708
+ output = src
709
+ attention_lst = []
710
+ for enc_layer in self.layers:
711
+ output, attention = enc_layer(
712
+ output,
713
+ src_mask=src_mask,
714
+ src_key_padding_mask=src_key_padding_mask,
715
+ pos_embs=pos_embs,
716
+ dynchunktrain_config=dynchunktrain_config,
717
+ )
718
+ attention_lst.append(attention)
719
+ output = self.norm(output)
720
+
721
+ return output, attention_lst
722
+
723
+ def forward_streaming(
724
+ self,
725
+ src: torch.Tensor,
726
+ context: ConformerEncoderStreamingContext,
727
+ pos_embs: Optional[torch.Tensor] = None,
728
+ ):
729
+ """Conformer streaming forward (typically for
730
+ DynamicChunkTraining-trained models), which is to be used at inference
731
+ time. Relies on a mutable context object as initialized by
732
+ `make_streaming_context` that should be used across chunks.
733
+
734
+ Arguments
735
+ ---------
736
+ src : torch.Tensor
737
+ Input tensor. Batching is supported as long as you keep the context
738
+ consistent.
739
+ context : ConformerEncoderStreamingContext
740
+ Mutable streaming context; the same object should be passed across
741
+ calls.
742
+ pos_embs : torch.Tensor, optional
743
+ Positional embeddings, if used.
744
+
745
+ Returns
746
+ -------
747
+ output : torch.Tensor
748
+ The output of the streaming conformer.
749
+ attention_lst : list
750
+ The attention values.
751
+ """
752
+
753
+ if self.attention_type == "RelPosMHAXL":
754
+ if pos_embs is None:
755
+ raise ValueError(
756
+ "The chosen attention type for the Conformer is RelPosMHAXL. For this attention type, the positional embeddings are mandatory"
757
+ )
758
+
759
+ output = src
760
+ attention_lst = []
761
+ for i, enc_layer in enumerate(self.layers):
762
+ output, attention = enc_layer.forward_streaming(
763
+ output, pos_embs=pos_embs, context=context.layers[i]
764
+ )
765
+ attention_lst.append(attention)
766
+ output = self.norm(output)
767
+
768
+ return output, attention_lst
769
+
770
+ def make_streaming_context(self, dynchunktrain_config: DynChunkTrainConfig):
771
+ """Creates a blank streaming context for the encoder.
772
+
773
+ Arguments
774
+ ---------
775
+ dynchunktrain_config: Optional[DynChunkTrainConfig]
776
+ Dynamic Chunk Training configuration object for streaming
777
+
778
+ Returns
779
+ -------
780
+ ConformerEncoderStreamingContext
781
+ """
782
+ return ConformerEncoderStreamingContext(
783
+ dynchunktrain_config=dynchunktrain_config,
784
+ layers=[
785
+ layer.make_streaming_context(
786
+ mha_left_context_size=dynchunktrain_config.left_context_size_frames()
787
+ )
788
+ for layer in self.layers
789
+ ],
790
+ )
791
+
792
+
793
+ class ConformerDecoderLayer(nn.Module):
794
+ """This is an implementation of Conformer encoder layer.
795
+
796
+ Arguments
797
+ ---------
798
+ d_model : int
799
+ The expected size of the input embedding.
800
+ d_ffn : int
801
+ Hidden size of self-attention Feed Forward layer.
802
+ nhead : int
803
+ Number of attention heads.
804
+ kernel_size : int, optional
805
+ Kernel size of convolution model.
806
+ kdim : int, optional
807
+ Dimension of the key.
808
+ vdim : int, optional
809
+ Dimension of the value.
810
+ activation : torch.nn.Module, optional
811
+ Activation function used in each Conformer layer.
812
+ bias : bool, optional
813
+ Whether convolution module.
814
+ dropout : int, optional
815
+ Dropout for the encoder.
816
+ causal : bool, optional
817
+ Whether the convolutions should be causal or not.
818
+ attention_type : str, optional
819
+ type of attention layer, e.g. regularMHA for regular MultiHeadAttention.
820
+
821
+ Example
822
+ -------
823
+ >>> import torch
824
+ >>> x = torch.rand((8, 60, 512))
825
+ >>> pos_embs = torch.rand((1, 2*60-1, 512))
826
+ >>> net = ConformerEncoderLayer(d_ffn=512, nhead=8, d_model=512, kernel_size=3)
827
+ >>> output = net(x, pos_embs=pos_embs)
828
+ >>> output[0].shape
829
+ torch.Size([8, 60, 512])
830
+ """
831
+
832
+ def __init__(
833
+ self,
834
+ d_model,
835
+ d_ffn,
836
+ nhead,
837
+ kernel_size,
838
+ kdim=None,
839
+ vdim=None,
840
+ activation=Swish,
841
+ bias=True,
842
+ dropout=0.0,
843
+ causal=True,
844
+ attention_type="RelPosMHAXL",
845
+ ):
846
+ super().__init__()
847
+
848
+ if not causal:
849
+ warnings.warn(
850
+ "Decoder is not causal, in most applications it should be causal, you have been warned !"
851
+ )
852
+
853
+ if attention_type == "regularMHA":
854
+ self.mha_layer = MultiheadAttention(
855
+ nhead=nhead,
856
+ d_model=d_model,
857
+ dropout=dropout,
858
+ kdim=kdim,
859
+ vdim=vdim,
860
+ )
861
+ elif attention_type == "RelPosMHAXL":
862
+ # transformerXL style positional encoding
863
+ self.mha_layer = RelPosMHAXL(
864
+ num_heads=nhead,
865
+ embed_dim=d_model,
866
+ dropout=dropout,
867
+ mask_pos_future=causal,
868
+ )
869
+
870
+ self.convolution_module = ConvolutionModule(
871
+ d_model, kernel_size, bias, activation, dropout, causal=causal
872
+ )
873
+
874
+ self.ffn_module1 = nn.Sequential(
875
+ nn.LayerNorm(d_model),
876
+ PositionalwiseFeedForward(
877
+ d_ffn=d_ffn,
878
+ input_size=d_model,
879
+ dropout=dropout,
880
+ activation=activation,
881
+ ),
882
+ nn.Dropout(dropout),
883
+ )
884
+
885
+ self.ffn_module2 = nn.Sequential(
886
+ nn.LayerNorm(d_model),
887
+ PositionalwiseFeedForward(
888
+ d_ffn=d_ffn,
889
+ input_size=d_model,
890
+ dropout=dropout,
891
+ activation=activation,
892
+ ),
893
+ nn.Dropout(dropout),
894
+ )
895
+
896
+ self.norm1 = LayerNorm(d_model)
897
+ self.norm2 = LayerNorm(d_model)
898
+ self.drop = nn.Dropout(dropout)
899
+
900
+ def forward(
901
+ self,
902
+ tgt,
903
+ memory,
904
+ tgt_mask=None,
905
+ memory_mask=None,
906
+ tgt_key_padding_mask=None,
907
+ memory_key_padding_mask=None,
908
+ pos_embs_tgt=None,
909
+ pos_embs_src=None,
910
+ ):
911
+ """
912
+ Arguments
913
+ ---------
914
+ tgt: torch.Tensor
915
+ The sequence to the decoder layer.
916
+ memory: torch.Tensor
917
+ The sequence from the last layer of the encoder.
918
+ tgt_mask: torch.Tensor, optional, optional
919
+ The mask for the tgt sequence.
920
+ memory_mask: torch.Tensor, optional
921
+ The mask for the memory sequence.
922
+ tgt_key_padding_mask: torch.Tensor, optional
923
+ The mask for the tgt keys per batch.
924
+ memory_key_padding_mask: torch.Tensor, optional
925
+ The mask for the memory keys per batch.
926
+ pos_embs_tgt: torch.Tensor, torch.nn.Module, optional
927
+ Module or tensor containing the target sequence positional embeddings for each attention layer.
928
+ pos_embs_src: torch.Tensor, torch.nn.Module, optional
929
+ Module or tensor containing the source sequence positional embeddings for each attention layer.
930
+
931
+ Returns
932
+ -------
933
+ x: torch.Tensor
934
+ The output tensor
935
+ self_attn : torch.Tensor
936
+ self_attn : torch.Tensor
937
+ The self attention tensor
938
+ """
939
+ # ffn module
940
+ tgt = tgt + 0.5 * self.ffn_module1(tgt)
941
+ # multi-head attention module
942
+ skip = tgt
943
+ x = self.norm1(tgt)
944
+ x, self_attn = self.mha_layer(
945
+ x,
946
+ memory,
947
+ memory,
948
+ attn_mask=memory_mask,
949
+ key_padding_mask=memory_key_padding_mask,
950
+ pos_embs=pos_embs_src,
951
+ )
952
+ x = x + skip
953
+ # convolution module
954
+ x = x + self.convolution_module(x)
955
+ # ffn module
956
+ x = self.norm2(x + 0.5 * self.ffn_module2(x))
957
+ return x, self_attn, self_attn
958
+
959
+
960
+ class ConformerDecoder(nn.Module):
961
+ """This class implements the Transformer decoder.
962
+
963
+ Arguments
964
+ ---------
965
+ num_layers: int
966
+ Number of layers.
967
+ nhead: int
968
+ Number of attention heads.
969
+ d_ffn: int
970
+ Hidden size of self-attention Feed Forward layer.
971
+ d_model: int
972
+ Embedding dimension size.
973
+ kdim: int, optional
974
+ Dimension for key.
975
+ vdim: int, optional
976
+ Dimension for value.
977
+ dropout: float, optional
978
+ Dropout rate.
979
+ activation: torch.nn.Module, optional
980
+ Activation function used after non-bottleneck conv layer.
981
+ kernel_size : int, optional
982
+ Kernel size of convolutional layer.
983
+ bias : bool, optional
984
+ Whether convolution module.
985
+ causal: bool, optional
986
+ Whether the convolutions should be causal or not.
987
+ attention_type: str, optional
988
+ type of attention layer, e.g. regularMHA for regular MultiHeadAttention.
989
+
990
+
991
+ Example
992
+ -------
993
+ >>> src = torch.rand((8, 60, 512))
994
+ >>> tgt = torch.rand((8, 60, 512))
995
+ >>> net = ConformerDecoder(1, 8, 1024, 512, attention_type="regularMHA")
996
+ >>> output, _, _ = net(tgt, src)
997
+ >>> output.shape
998
+ torch.Size([8, 60, 512])
999
+ """
1000
+
1001
+ def __init__(
1002
+ self,
1003
+ num_layers,
1004
+ nhead,
1005
+ d_ffn,
1006
+ d_model,
1007
+ kdim=None,
1008
+ vdim=None,
1009
+ dropout=0.0,
1010
+ activation=Swish,
1011
+ kernel_size=3,
1012
+ bias=True,
1013
+ causal=True,
1014
+ attention_type="RelPosMHAXL",
1015
+ ):
1016
+ super().__init__()
1017
+ self.layers = torch.nn.ModuleList(
1018
+ [
1019
+ ConformerDecoderLayer(
1020
+ d_ffn=d_ffn,
1021
+ nhead=nhead,
1022
+ d_model=d_model,
1023
+ kdim=kdim,
1024
+ vdim=vdim,
1025
+ dropout=dropout,
1026
+ activation=activation,
1027
+ kernel_size=kernel_size,
1028
+ bias=bias,
1029
+ causal=causal,
1030
+ attention_type=attention_type,
1031
+ )
1032
+ for _ in range(num_layers)
1033
+ ]
1034
+ )
1035
+ self.norm = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
1036
+
1037
+ def forward(
1038
+ self,
1039
+ tgt,
1040
+ memory,
1041
+ tgt_mask=None,
1042
+ memory_mask=None,
1043
+ tgt_key_padding_mask=None,
1044
+ memory_key_padding_mask=None,
1045
+ pos_embs_tgt=None,
1046
+ pos_embs_src=None,
1047
+ ):
1048
+ """
1049
+ Arguments
1050
+ ---------
1051
+ tgt: torch.Tensor
1052
+ The sequence to the decoder layer.
1053
+ memory: torch.Tensor
1054
+ The sequence from the last layer of the encoder.
1055
+ tgt_mask: torch.Tensor, optional, optional
1056
+ The mask for the tgt sequence.
1057
+ memory_mask: torch.Tensor, optional
1058
+ The mask for the memory sequence.
1059
+ tgt_key_padding_mask : torch.Tensor, optional
1060
+ The mask for the tgt keys per batch.
1061
+ memory_key_padding_mask : torch.Tensor, optional
1062
+ The mask for the memory keys per batch.
1063
+ pos_embs_tgt: torch.Tensor, torch.nn.Module, optional
1064
+ Module or tensor containing the target sequence positional embeddings for each attention layer.
1065
+ pos_embs_src: torch.Tensor, torch.nn.Module, optional
1066
+ Module or tensor containing the source sequence positional embeddings for each attention layer.
1067
+
1068
+ Returns
1069
+ -------
1070
+ output: torch.Tensor
1071
+ Conformer decoder output.
1072
+ self_attns : list
1073
+ Location of self attentions.
1074
+ multihead_attns : list
1075
+ Location of multihead attentions.
1076
+ """
1077
+ output = tgt
1078
+ self_attns, multihead_attns = [], []
1079
+ for dec_layer in self.layers:
1080
+ output, self_attn, multihead_attn = dec_layer(
1081
+ output,
1082
+ memory,
1083
+ tgt_mask=tgt_mask,
1084
+ memory_mask=memory_mask,
1085
+ tgt_key_padding_mask=tgt_key_padding_mask,
1086
+ memory_key_padding_mask=memory_key_padding_mask,
1087
+ pos_embs_tgt=pos_embs_tgt,
1088
+ pos_embs_src=pos_embs_src,
1089
+ )
1090
+ self_attns.append(self_attn)
1091
+ multihead_attns.append(multihead_attn)
1092
+ output = self.norm(output)
1093
+
1094
+ return output, self_attns, multihead_attns
modules/Conmamba.py ADDED
@@ -0,0 +1,607 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ConMamba encoder and Mamba decoder implementation.
2
+
3
+ Authors
4
+ -------
5
+ * Xilin Jiang 2024
6
+ """
7
+
8
+ import warnings
9
+ from dataclasses import dataclass
10
+ from typing import List, Optional
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ import torch.nn.functional as F
15
+
16
+ import speechbrain as sb
17
+ from speechbrain.nnet.activations import Swish
18
+ from speechbrain.nnet.attention import (
19
+ MultiheadAttention,
20
+ PositionalwiseFeedForward,
21
+ RelPosMHAXL,
22
+ )
23
+ from speechbrain.nnet.hypermixing import HyperMixing
24
+ from speechbrain.nnet.normalization import LayerNorm
25
+ from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
26
+
27
+ # Mamba
28
+ from mamba_ssm import Mamba
29
+ from modules.mamba.bimamba import Mamba as BiMamba
30
+
31
+
32
+ class ConvolutionModule(nn.Module):
33
+ """This is an implementation of convolution module in Conmamba.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ input_size,
39
+ kernel_size=31,
40
+ bias=True,
41
+ activation=Swish,
42
+ dropout=0.0,
43
+ causal=False,
44
+ dilation=1,
45
+ ):
46
+ super().__init__()
47
+
48
+ self.kernel_size = kernel_size
49
+ self.causal = causal
50
+ self.dilation = dilation
51
+
52
+ if self.causal:
53
+ self.padding = (kernel_size - 1) * 2 ** (dilation - 1)
54
+ else:
55
+ self.padding = (kernel_size - 1) * 2 ** (dilation - 1) // 2
56
+
57
+ self.layer_norm = nn.LayerNorm(input_size)
58
+ self.bottleneck = nn.Sequential(
59
+ # pointwise
60
+ nn.Conv1d(
61
+ input_size, 2 * input_size, kernel_size=1, stride=1, bias=bias
62
+ ),
63
+ nn.GLU(dim=1),
64
+ )
65
+ # depthwise
66
+ self.conv = nn.Conv1d(
67
+ input_size,
68
+ input_size,
69
+ kernel_size=kernel_size,
70
+ stride=1,
71
+ padding=self.padding,
72
+ dilation=dilation,
73
+ groups=input_size,
74
+ bias=bias,
75
+ )
76
+
77
+ # BatchNorm in the original Conformer replaced with a LayerNorm due to
78
+ # https://github.com/speechbrain/speechbrain/pull/1329
79
+ # see discussion
80
+ # https://github.com/speechbrain/speechbrain/pull/933#issuecomment-1033367884
81
+
82
+ self.after_conv = nn.Sequential(
83
+ nn.LayerNorm(input_size),
84
+ activation(),
85
+ # pointwise
86
+ nn.Linear(input_size, input_size, bias=bias),
87
+ nn.Dropout(dropout),
88
+ )
89
+
90
+ def forward(
91
+ self,
92
+ x: torch.Tensor,
93
+ mask: Optional[torch.Tensor] = None,
94
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
95
+ ):
96
+ """Applies the convolution to an input tensor `x`.
97
+ """
98
+
99
+ if dynchunktrain_config is not None:
100
+ # chances are chunking+causal is unintended; i don't know where it
101
+ # may make sense, but if it does to you, feel free to implement it.
102
+ assert (
103
+ not self.causal
104
+ ), "Chunked convolution not supported with causal padding"
105
+
106
+ assert (
107
+ self.dilation == 1
108
+ ), "Current DynChunkTrain logic does not support dilation != 1"
109
+
110
+ # in a causal convolution, which is not the case here, an output
111
+ # frame would never be able to depend on a input frame from any
112
+ # point in the future.
113
+
114
+ # but with the dynamic chunk convolution, we instead use a "normal"
115
+ # convolution but where, for any output frame, the future beyond the
116
+ # "current" chunk gets masked.
117
+ # see the paper linked in the documentation for details.
118
+
119
+ chunk_size = dynchunktrain_config.chunk_size
120
+ batch_size = x.shape[0]
121
+
122
+ # determine the amount of padding we need to insert at the right of
123
+ # the last chunk so that all chunks end up with the same size.
124
+ if x.shape[1] % chunk_size != 0:
125
+ final_right_padding = chunk_size - (x.shape[1] % chunk_size)
126
+ else:
127
+ final_right_padding = 0
128
+
129
+ # -> [batch_size, t, in_channels]
130
+ out = self.layer_norm(x)
131
+
132
+ # -> [batch_size, in_channels, t] for the CNN
133
+ out = out.transpose(1, 2)
134
+
135
+ # -> [batch_size, in_channels, t] (pointwise)
136
+ out = self.bottleneck(out)
137
+
138
+ # -> [batch_size, in_channels, lc+t+final_right_padding]
139
+ out = F.pad(out, (self.padding, final_right_padding), value=0)
140
+
141
+ # now, make chunks with left context.
142
+ # as a recap to what the above padding and this unfold do, consider
143
+ # each a/b/c letter represents a frame as part of chunks a, b, c.
144
+ # consider a chunk size of 4 and a kernel size of 5 (padding=2):
145
+ #
146
+ # input seq: 00aaaabbbbcc00
147
+ # chunk #1: 00aaaa
148
+ # chunk #2: aabbbb
149
+ # chunk #3: bbcc00
150
+ #
151
+ # a few remarks here:
152
+ # - the left padding gets inserted early so that the unfold logic
153
+ # works trivially
154
+ # - the right 0-padding got inserted as the number of time steps
155
+ # could not be evenly split in `chunk_size` chunks
156
+
157
+ # -> [batch_size, in_channels, num_chunks, lc+chunk_size]
158
+ out = out.unfold(2, size=chunk_size + self.padding, step=chunk_size)
159
+
160
+ # as we manually disable padding in the convolution below, we insert
161
+ # right 0-padding to the chunks, e.g. reusing the above example:
162
+ #
163
+ # chunk #1: 00aaaa00
164
+ # chunk #2: aabbbb00
165
+ # chunk #3: bbcc0000
166
+
167
+ # -> [batch_size, in_channels, num_chunks, lc+chunk_size+rpad]
168
+ out = F.pad(out, (0, self.padding), value=0)
169
+
170
+ # the transpose+flatten effectively flattens chunks into the batch
171
+ # dimension to be processed into the time-wise convolution. the
172
+ # chunks will later on be unflattened.
173
+
174
+ # -> [batch_size, num_chunks, in_channels, lc+chunk_size+rpad]
175
+ out = out.transpose(1, 2)
176
+
177
+ # -> [batch_size * num_chunks, in_channels, lc+chunk_size+rpad]
178
+ out = out.flatten(start_dim=0, end_dim=1)
179
+
180
+ # TODO: experiment around reflect padding, which is difficult
181
+ # because small chunks have too little time steps to reflect from
182
+
183
+ # let's keep backwards compat by pointing at the weights from the
184
+ # already declared Conv1d.
185
+ #
186
+ # still reusing the above example, the convolution will be applied,
187
+ # with the padding truncated on both ends. the following example
188
+ # shows the letter corresponding to the input frame on which the
189
+ # convolution was centered.
190
+ #
191
+ # as you can see, the sum of lengths of all chunks is equal to our
192
+ # input sequence length + `final_right_padding`.
193
+ #
194
+ # chunk #1: aaaa
195
+ # chunk #2: bbbb
196
+ # chunk #3: cc00
197
+
198
+ # -> [batch_size * num_chunks, out_channels, chunk_size]
199
+ out = F.conv1d(
200
+ out,
201
+ weight=self.conv.weight,
202
+ bias=self.conv.bias,
203
+ stride=self.conv.stride,
204
+ padding=0,
205
+ dilation=self.conv.dilation,
206
+ groups=self.conv.groups,
207
+ )
208
+
209
+ # -> [batch_size * num_chunks, chunk_size, out_channels]
210
+ out = out.transpose(1, 2)
211
+
212
+ out = self.after_conv(out)
213
+
214
+ # -> [batch_size, num_chunks, chunk_size, out_channels]
215
+ out = torch.unflatten(out, dim=0, sizes=(batch_size, -1))
216
+
217
+ # -> [batch_size, t + final_right_padding, out_channels]
218
+ out = torch.flatten(out, start_dim=1, end_dim=2)
219
+
220
+ # -> [batch_size, t, out_channels]
221
+ if final_right_padding > 0:
222
+ out = out[:, :-final_right_padding, :]
223
+ else:
224
+ out = self.layer_norm(x)
225
+ out = out.transpose(1, 2)
226
+ out = self.bottleneck(out)
227
+ out = self.conv(out)
228
+
229
+ if self.causal:
230
+ # chomp
231
+ out = out[..., : -self.padding]
232
+
233
+ out = out.transpose(1, 2)
234
+ out = self.after_conv(out)
235
+
236
+ if mask is not None:
237
+ out.masked_fill_(mask, 0.0)
238
+
239
+ return out
240
+
241
+
242
+ class ConmambaEncoderLayer(nn.Module):
243
+ """This is an implementation of Conmamba encoder layer.
244
+ """
245
+
246
+ def __init__(
247
+ self,
248
+ d_model,
249
+ d_ffn,
250
+ kernel_size=31,
251
+ activation=Swish,
252
+ bias=True,
253
+ dropout=0.0,
254
+ causal=False,
255
+ mamba_config=None
256
+ ):
257
+ super().__init__()
258
+ assert mamba_config != None
259
+
260
+ bidirectional = mamba_config.pop('bidirectional')
261
+ if causal or (not bidirectional):
262
+ self.mamba = Mamba(
263
+ d_model=d_model,
264
+ **mamba_config
265
+ )
266
+ else:
267
+ self.mamba = BiMamba(
268
+ d_model=d_model,
269
+ bimamba_type='v2',
270
+ **mamba_config
271
+ )
272
+ mamba_config['bidirectional'] = bidirectional
273
+
274
+ self.convolution_module = ConvolutionModule(
275
+ d_model, kernel_size, bias, activation, dropout, causal=causal
276
+ )
277
+
278
+ self.ffn_module1 = nn.Sequential(
279
+ nn.LayerNorm(d_model),
280
+ PositionalwiseFeedForward(
281
+ d_ffn=d_ffn,
282
+ input_size=d_model,
283
+ dropout=dropout,
284
+ activation=activation,
285
+ ),
286
+ nn.Dropout(dropout),
287
+ )
288
+
289
+ self.ffn_module2 = nn.Sequential(
290
+ nn.LayerNorm(d_model),
291
+ PositionalwiseFeedForward(
292
+ d_ffn=d_ffn,
293
+ input_size=d_model,
294
+ dropout=dropout,
295
+ activation=activation,
296
+ ),
297
+ nn.Dropout(dropout),
298
+ )
299
+
300
+ self.norm1 = LayerNorm(d_model)
301
+ self.norm2 = LayerNorm(d_model)
302
+ self.drop = nn.Dropout(dropout)
303
+
304
+ def forward(
305
+ self,
306
+ x,
307
+ src_mask: Optional[torch.Tensor] = None,
308
+ src_key_padding_mask: Optional[torch.Tensor] = None,
309
+ pos_embs: torch.Tensor = None,
310
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
311
+ ):
312
+ conv_mask: Optional[torch.Tensor] = None
313
+ if src_key_padding_mask is not None:
314
+ conv_mask = src_key_padding_mask.unsqueeze(-1)
315
+
316
+ conv_mask = None
317
+
318
+ # ffn module
319
+ x = x + 0.5 * self.ffn_module1(x)
320
+ # mamba module
321
+ skip = x
322
+ x = self.norm1(x)
323
+ x = self.mamba(x)
324
+ x = x + skip
325
+ # convolution module
326
+ x = x + self.convolution_module(
327
+ x, conv_mask, dynchunktrain_config=dynchunktrain_config
328
+ )
329
+ # ffn module
330
+ x = self.norm2(x + 0.5 * self.ffn_module2(x))
331
+ return x
332
+
333
+
334
+ class ConmambaEncoder(nn.Module):
335
+ """This class implements the Conmamba encoder.
336
+ """
337
+
338
+ def __init__(
339
+ self,
340
+ num_layers,
341
+ d_model,
342
+ d_ffn,
343
+ kernel_size=31,
344
+ activation=Swish,
345
+ bias=True,
346
+ dropout=0.0,
347
+ causal=False,
348
+ mamba_config=None
349
+ ):
350
+ super().__init__()
351
+ print(f'dropout={str(dropout)} is not used in Mamba.')
352
+
353
+ self.layers = torch.nn.ModuleList(
354
+ [
355
+ ConmambaEncoderLayer(
356
+ d_model=d_model,
357
+ d_ffn=d_ffn,
358
+ dropout=dropout,
359
+ activation=activation,
360
+ kernel_size=kernel_size,
361
+ bias=bias,
362
+ causal=causal,
363
+ mamba_config=mamba_config,
364
+ )
365
+ for i in range(num_layers)
366
+ ]
367
+ )
368
+ self.norm = LayerNorm(d_model, eps=1e-6)
369
+
370
+ def forward(
371
+ self,
372
+ src,
373
+ src_mask: Optional[torch.Tensor] = None,
374
+ src_key_padding_mask: Optional[torch.Tensor] = None,
375
+ pos_embs: Optional[torch.Tensor] = None,
376
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
377
+ ):
378
+ """
379
+ Arguments
380
+ ----------
381
+ src : torch.Tensor
382
+ The sequence to the encoder layer.
383
+ src_mask : torch.Tensor, optional
384
+ The mask for the src sequence.
385
+ src_key_padding_mask : torch.Tensor, optional
386
+ The mask for the src keys per batch.
387
+ pos_embs: torch.Tensor, torch.nn.Module,
388
+ Module or tensor containing the input sequence positional embeddings
389
+ If custom pos_embs are given it needs to have the shape (1, 2*S-1, E)
390
+ where S is the sequence length, and E is the embedding dimension.
391
+ dynchunktrain_config: Optional[DynChunkTrainConfig]
392
+ Dynamic Chunk Training configuration object for streaming,
393
+ specifically involved here to apply Dynamic Chunk Convolution to the
394
+ convolution module.
395
+ """
396
+
397
+ output = src
398
+ for enc_layer in self.layers:
399
+ output = enc_layer(
400
+ output,
401
+ src_mask=src_mask,
402
+ src_key_padding_mask=src_key_padding_mask,
403
+ pos_embs=pos_embs,
404
+ dynchunktrain_config=dynchunktrain_config,
405
+ )
406
+ output = self.norm(output)
407
+
408
+ return output, None
409
+
410
+
411
+ class MambaDecoderLayer(nn.Module):
412
+ """This class implements the Mamba decoder layer.
413
+ """
414
+
415
+ def __init__(
416
+ self,
417
+ d_model,
418
+ d_ffn,
419
+ activation=nn.ReLU,
420
+ dropout=0.0,
421
+ normalize_before=False,
422
+ mamba_config=None
423
+ ):
424
+ super().__init__()
425
+
426
+ assert mamba_config != None
427
+
428
+ bidirectional = mamba_config.pop('bidirectional')
429
+
430
+ self.self_mamba = Mamba(
431
+ d_model=d_model,
432
+ **mamba_config
433
+ )
434
+
435
+ self.cross_mamba = Mamba(
436
+ d_model=d_model,
437
+ **mamba_config
438
+ )
439
+
440
+ mamba_config['bidirectional'] = bidirectional
441
+
442
+ self.pos_ffn = sb.nnet.attention.PositionalwiseFeedForward(
443
+ d_ffn=d_ffn,
444
+ input_size=d_model,
445
+ dropout=dropout,
446
+ activation=activation,
447
+ )
448
+
449
+ # normalization layers
450
+ self.norm1 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
451
+ self.norm2 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
452
+ self.norm3 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
453
+ self.dropout1 = torch.nn.Dropout(dropout)
454
+ self.dropout2 = torch.nn.Dropout(dropout)
455
+ self.dropout3 = torch.nn.Dropout(dropout)
456
+
457
+ self.normalize_before = normalize_before
458
+
459
+ def forward(
460
+ self,
461
+ tgt,
462
+ memory,
463
+ tgt_mask=None,
464
+ memory_mask=None,
465
+ tgt_key_padding_mask=None,
466
+ memory_key_padding_mask=None,
467
+ pos_embs_tgt=None,
468
+ pos_embs_src=None,
469
+ ):
470
+ """
471
+ Arguments
472
+ ----------
473
+ tgt: torch.Tensor
474
+ The sequence to the decoder layer (required).
475
+ memory: torch.Tensor
476
+ The sequence from the last layer of the encoder (required).
477
+ tgt_mask: torch.Tensor
478
+ The mask for the tgt sequence (optional).
479
+ memory_mask: torch.Tensor
480
+ The mask for the memory sequence (optional).
481
+ tgt_key_padding_mask: torch.Tensor
482
+ The mask for the tgt keys per batch (optional).
483
+ memory_key_padding_mask: torch.Tensor
484
+ The mask for the memory keys per batch (optional).
485
+ pos_embs_tgt: torch.Tensor
486
+ The positional embeddings for the target (optional).
487
+ pos_embs_src: torch.Tensor
488
+ The positional embeddings for the source (optional).
489
+ """
490
+ if self.normalize_before:
491
+ tgt1 = self.norm1(tgt)
492
+ else:
493
+ tgt1 = tgt
494
+
495
+ # Mamba over the target sequence
496
+ tgt2 = self.self_mamba(tgt1)
497
+
498
+ # add & norm
499
+ tgt = tgt + self.dropout1(tgt2)
500
+ if not self.normalize_before:
501
+ tgt = self.norm1(tgt)
502
+
503
+ if self.normalize_before:
504
+ tgt1 = self.norm2(tgt)
505
+ else:
506
+ tgt1 = tgt
507
+
508
+ # Mamba over key=value + query
509
+ # and only take the last len(query) tokens
510
+ tgt2 = self.cross_mamba(torch.cat([memory, tgt1], dim=1))[:, -tgt1.shape[1]:]
511
+
512
+ # add & norm
513
+ tgt = tgt + self.dropout2(tgt2)
514
+ if not self.normalize_before:
515
+ tgt = self.norm2(tgt)
516
+
517
+ if self.normalize_before:
518
+ tgt1 = self.norm3(tgt)
519
+ else:
520
+ tgt1 = tgt
521
+
522
+ tgt2 = self.pos_ffn(tgt1)
523
+
524
+ # add & norm
525
+ tgt = tgt + self.dropout3(tgt2)
526
+ if not self.normalize_before:
527
+ tgt = self.norm3(tgt)
528
+
529
+ return tgt, None, None
530
+
531
+
532
+ class MambaDecoder(nn.Module):
533
+ """This class implements the Mamba decoder.
534
+ """
535
+
536
+ def __init__(
537
+ self,
538
+ num_layers,
539
+ d_model,
540
+ d_ffn,
541
+ activation=nn.ReLU,
542
+ dropout=0.0,
543
+ normalize_before=False,
544
+ mamba_config=None
545
+ ):
546
+ super().__init__()
547
+ self.layers = torch.nn.ModuleList(
548
+ [
549
+ MambaDecoderLayer(
550
+ d_model=d_model,
551
+ d_ffn=d_ffn,
552
+ activation=activation,
553
+ dropout=dropout,
554
+ normalize_before=normalize_before,
555
+ mamba_config=mamba_config
556
+ )
557
+ for _ in range(num_layers)
558
+ ]
559
+ )
560
+ self.norm = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
561
+
562
+ def forward(
563
+ self,
564
+ tgt,
565
+ memory,
566
+ tgt_mask=None,
567
+ memory_mask=None,
568
+ tgt_key_padding_mask=None,
569
+ memory_key_padding_mask=None,
570
+ pos_embs_tgt=None,
571
+ pos_embs_src=None,
572
+ ):
573
+ """
574
+ Arguments
575
+ ----------
576
+ tgt : torch.Tensor
577
+ The sequence to the decoder layer (required).
578
+ memory : torch.Tensor
579
+ The sequence from the last layer of the encoder (required).
580
+ tgt_mask : torch.Tensor
581
+ The mask for the tgt sequence (optional).
582
+ memory_mask : torch.Tensor
583
+ The mask for the memory sequence (optional).
584
+ tgt_key_padding_mask : torch.Tensor
585
+ The mask for the tgt keys per batch (optional).
586
+ memory_key_padding_mask : torch.Tensor
587
+ The mask for the memory keys per batch (optional).
588
+ pos_embs_tgt : torch.Tensor
589
+ The positional embeddings for the target (optional).
590
+ pos_embs_src : torch.Tensor
591
+ The positional embeddings for the source (optional).
592
+ """
593
+ output = tgt
594
+ for dec_layer in self.layers:
595
+ output, _, _ = dec_layer(
596
+ output,
597
+ memory,
598
+ tgt_mask=tgt_mask,
599
+ memory_mask=memory_mask,
600
+ tgt_key_padding_mask=tgt_key_padding_mask,
601
+ memory_key_padding_mask=memory_key_padding_mask,
602
+ pos_embs_tgt=pos_embs_tgt,
603
+ pos_embs_src=pos_embs_src,
604
+ )
605
+ output = self.norm(output)
606
+
607
+ return output, [None], [None]
modules/MambaASR.py ADDED
@@ -0,0 +1,588 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Added ConMamba and Mamba
2
+
3
+ Authors
4
+ * Xilin Jiang 2024
5
+ """
6
+
7
+ """Transformer for ASR in the SpeechBrain style.
8
+
9
+ Authors
10
+ * Jianyuan Zhong 2020
11
+ * Titouan Parcollet 2024
12
+ * Luca Della Libera 2024
13
+ """
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Any, Optional
17
+
18
+ import torch # noqa 42
19
+ from torch import nn
20
+
21
+ from speechbrain.dataio.dataio import length_to_mask
22
+ from modules.Transformer import (
23
+ NormalizedEmbedding,
24
+ TransformerInterface,
25
+ get_key_padding_mask,
26
+ get_lookahead_mask,
27
+ )
28
+ from speechbrain.nnet.activations import Swish
29
+ from speechbrain.nnet.containers import ModuleList
30
+ from speechbrain.nnet.linear import Linear
31
+ from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
32
+
33
+
34
+ @dataclass
35
+ class TransformerASRStreamingContext:
36
+ """Streaming metadata and state for a `TransformerASR` instance."""
37
+
38
+ dynchunktrain_config: DynChunkTrainConfig
39
+ """Dynamic Chunk Training configuration holding chunk size and context size
40
+ information."""
41
+
42
+ encoder_context: Any
43
+ """Opaque encoder context information. It is constructed by the encoder's
44
+ `make_streaming_context` method and is passed to the encoder when using
45
+ `encode_streaming`.
46
+ """
47
+
48
+
49
+ def make_transformer_src_mask(
50
+ src: torch.Tensor,
51
+ causal: bool = False,
52
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
53
+ ) -> Optional[torch.Tensor]:
54
+ """Prepare the source transformer mask that restricts which frames can
55
+ attend to which frames depending on causal or other simple restricted
56
+ attention methods.
57
+
58
+ Arguments
59
+ ---------
60
+ src: torch.Tensor
61
+ The source tensor to build a mask from. The contents of the tensor are
62
+ not actually used currently; only its shape and other metadata (e.g.
63
+ device).
64
+ causal: bool
65
+ Whether strict causality shall be used. Frames will not be able to
66
+ attend to any future frame.
67
+ dynchunktrain_config: DynChunkTrainConfig, optional
68
+ Dynamic Chunk Training configuration. This implements a simple form of
69
+ chunkwise attention. Incompatible with `causal`.
70
+
71
+ Returns
72
+ -------
73
+ torch.Tensor
74
+ A boolean mask Tensor of shape (timesteps, timesteps).
75
+ """
76
+ if causal:
77
+ assert dynchunktrain_config is None
78
+ return get_lookahead_mask(src)
79
+
80
+ if dynchunktrain_config is None:
81
+ return
82
+
83
+ # The following is not really the sole source used to implement this,
84
+ # but it helps introduce the concept.
85
+ # ref: Unified Streaming and Non-streaming Two-pass End-to-end Model for Speech Recognition
86
+ # https://arxiv.org/pdf/2012.05481.pdf
87
+ timesteps = src.size(1)
88
+
89
+ # Mask the future at the right of each chunk
90
+ chunk_size = dynchunktrain_config.chunk_size
91
+ num_chunks = timesteps // chunk_size
92
+ timestep_idx = torch.arange(timesteps, device=src.device)
93
+ mask_idx = torch.arange(
94
+ chunk_size, chunk_size * (num_chunks + 2), chunk_size, device=src.device
95
+ ).repeat_interleave(chunk_size)[:timesteps]
96
+ src_mask = timestep_idx[None] >= mask_idx[:, None]
97
+
98
+ # Mask the past at the left of each chunk (accounting for left context)
99
+ # only relevant if using left context
100
+ if not dynchunktrain_config.is_infinite_left_context():
101
+ num_left_chunks = dynchunktrain_config.left_context_size
102
+ mask_idx -= chunk_size * (num_left_chunks + 1)
103
+ src_mask += timestep_idx[None] < mask_idx[:, None]
104
+
105
+ return src_mask
106
+
107
+
108
+ def make_transformer_src_tgt_masks(
109
+ src,
110
+ tgt=None,
111
+ wav_len=None,
112
+ pad_idx=0,
113
+ causal: bool = False,
114
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
115
+ ):
116
+ """This function generates masks for training the transformer model,
117
+ opinionated for an ASR context with encoding masks and, optionally, decoding
118
+ masks (if specifying `tgt`).
119
+
120
+ Arguments
121
+ ---------
122
+ src : torch.Tensor
123
+ The sequence to the encoder (required).
124
+ tgt : torch.Tensor
125
+ The sequence to the decoder.
126
+ wav_len : torch.Tensor
127
+ The lengths of the inputs.
128
+ pad_idx : int
129
+ The index for <pad> token (default=0).
130
+ causal: bool
131
+ Whether strict causality shall be used. See `make_asr_src_mask`
132
+ dynchunktrain_config: DynChunkTrainConfig, optional
133
+ Dynamic Chunk Training configuration. See `make_asr_src_mask`
134
+
135
+ Returns
136
+ -------
137
+ src_key_padding_mask : torch.Tensor
138
+ Key padding mask for ignoring padding
139
+ tgt_key_padding_mask : torch.Tensor
140
+ Key padding mask for ignoring padding
141
+ src_mask : torch.Tensor
142
+ Mask for ignoring invalid (e.g. future) timesteps
143
+ tgt_mask : torch.Tensor
144
+ Mask for ignoring invalid (e.g. future) timesteps
145
+ """
146
+ src_key_padding_mask = None
147
+
148
+ # mask out audio beyond the length of audio for each batch
149
+ if wav_len is not None:
150
+ abs_len = torch.round(wav_len * src.shape[1])
151
+ src_key_padding_mask = ~length_to_mask(abs_len).bool()
152
+
153
+ # mask out the source
154
+ src_mask = make_transformer_src_mask(
155
+ src, causal=causal, dynchunktrain_config=dynchunktrain_config
156
+ )
157
+
158
+ # If no decoder in the transformer...
159
+ if tgt is not None:
160
+ tgt_key_padding_mask = get_key_padding_mask(tgt, pad_idx=pad_idx)
161
+ tgt_mask = get_lookahead_mask(tgt)
162
+ else:
163
+ tgt_key_padding_mask = None
164
+ tgt_mask = None
165
+
166
+ return src_key_padding_mask, tgt_key_padding_mask, src_mask, tgt_mask
167
+
168
+
169
+ class MambaASR(TransformerInterface):
170
+ def __init__(
171
+ self,
172
+ tgt_vocab,
173
+ input_size,
174
+ d_model=512,
175
+ num_encoder_layers=6,
176
+ num_decoder_layers=6,
177
+ d_ffn=2048,
178
+ activation=nn.ReLU,
179
+ normalize_before=False,
180
+ kernel_size: Optional[int] = 31,
181
+ bias: Optional[bool] = True,
182
+ encoder_module: Optional[str] = "conmamba",
183
+ decoder_module: Optional[str] = "mamba",
184
+ max_length: Optional[int] = 2500,
185
+ causal: Optional[bool] = False,
186
+ csgu_linear_units: Optional[int] = 3072,
187
+ gate_activation: Optional[nn.Module] = nn.Identity,
188
+ use_linear_after_conv: Optional[bool] = False,
189
+ mamba_config=None
190
+ ):
191
+ super().__init__(
192
+ d_model=d_model,
193
+ num_encoder_layers=num_encoder_layers,
194
+ num_decoder_layers=num_decoder_layers,
195
+ d_ffn=d_ffn,
196
+ activation=activation,
197
+ normalize_before=normalize_before,
198
+ kernel_size=kernel_size,
199
+ bias=bias,
200
+ encoder_module=encoder_module,
201
+ decoder_module=decoder_module,
202
+ max_length=max_length,
203
+ causal=causal,
204
+ csgu_linear_units=csgu_linear_units,
205
+ gate_activation=gate_activation,
206
+ use_linear_after_conv=use_linear_after_conv,
207
+ mamba_config=mamba_config
208
+ )
209
+
210
+ self.custom_src_module = ModuleList(
211
+ Linear(
212
+ input_size=input_size,
213
+ n_neurons=d_model,
214
+ bias=True,
215
+ combine_dims=False,
216
+ ),
217
+ )
218
+
219
+ self.num_decoder_layers = num_decoder_layers
220
+ if num_decoder_layers > 0:
221
+ self.custom_tgt_module = ModuleList(
222
+ NormalizedEmbedding(d_model, tgt_vocab)
223
+ )
224
+
225
+ # reset parameters using xavier_normal_
226
+ self._init_params()
227
+
228
+ def forward(self, src, tgt, wav_len=None, pad_idx=0):
229
+ """
230
+ Arguments
231
+ ----------
232
+ src : torch.Tensor
233
+ The sequence to the encoder.
234
+ tgt : torch.Tensor
235
+ The sequence to the decoder.
236
+ wav_len: torch.Tensor, optional
237
+ Torch Tensor of shape (batch, ) containing the relative length to padded length for each example.
238
+ pad_idx : int, optional
239
+ The index for <pad> token (default=0).
240
+ """
241
+
242
+ # reshape the src vector to [Batch, Time, Fea] is a 4d vector is given
243
+ if src.ndim == 4:
244
+ bz, t, ch1, ch2 = src.shape
245
+ src = src.reshape(bz, t, ch1 * ch2)
246
+
247
+ (
248
+ src_key_padding_mask,
249
+ tgt_key_padding_mask,
250
+ src_mask,
251
+ tgt_mask,
252
+ ) = make_transformer_src_tgt_masks(
253
+ src, tgt, wav_len, causal=self.causal, pad_idx=pad_idx
254
+ )
255
+
256
+ src = self.custom_src_module(src)
257
+ # add pos encoding to queries if are sinusoidal ones else
258
+ if self.attention_type == "hypermixing":
259
+ pos_embs_encoder = None
260
+ elif self.attention_type == "RelPosMHAXL":
261
+ pos_embs_encoder = self.positional_encoding(src)
262
+ elif self.positional_encoding_type == "fixed_abs_sine":
263
+ src = src + self.positional_encoding(src) # add the encodings here
264
+ pos_embs_encoder = None
265
+
266
+ encoder_out, _ = self.encoder(
267
+ src=src,
268
+ src_mask=src_mask,
269
+ src_key_padding_mask=src_key_padding_mask,
270
+ pos_embs=pos_embs_encoder,
271
+ )
272
+
273
+ if self.num_decoder_layers > 0:
274
+ tgt = self.custom_tgt_module(tgt)
275
+
276
+ if self.attention_type == "RelPosMHAXL":
277
+ tgt = tgt + self.positional_encoding_decoder(tgt)
278
+ pos_embs_encoder = None # self.positional_encoding(src)
279
+ pos_embs_target = None
280
+ elif (
281
+ self.positional_encoding_type == "fixed_abs_sine"
282
+ or self.attention_type == "hypermixing"
283
+ ):
284
+ tgt = tgt + self.positional_encoding(tgt)
285
+ pos_embs_target = None
286
+ pos_embs_encoder = None
287
+
288
+ decoder_out, _, _ = self.decoder(
289
+ tgt=tgt,
290
+ memory=encoder_out,
291
+ memory_mask=None,
292
+ tgt_mask=tgt_mask,
293
+ tgt_key_padding_mask=tgt_key_padding_mask,
294
+ memory_key_padding_mask=src_key_padding_mask,
295
+ pos_embs_tgt=pos_embs_target,
296
+ pos_embs_src=pos_embs_encoder,
297
+ )
298
+
299
+ else:
300
+ decoder_out = None
301
+
302
+ return encoder_out, decoder_out
303
+
304
+ @torch.no_grad()
305
+ def decode(self, tgt, encoder_out, enc_len=None):
306
+ """This method implements a decoding step for the transformer model.
307
+
308
+ Arguments
309
+ ---------
310
+ tgt : torch.Tensor
311
+ The sequence to the decoder.
312
+ encoder_out : torch.Tensor
313
+ Hidden output of the encoder.
314
+ enc_len : torch.LongTensor
315
+ The actual length of encoder states.
316
+
317
+ Returns
318
+ -------
319
+ prediction
320
+ """
321
+ tgt_mask = get_lookahead_mask(tgt)
322
+ src_key_padding_mask = None
323
+ if enc_len is not None:
324
+ src_key_padding_mask = (1 - length_to_mask(enc_len)).bool()
325
+
326
+ if self.num_decoder_layers > 0:
327
+ tgt = self.custom_tgt_module(tgt)
328
+ if self.attention_type == "RelPosMHAXL":
329
+ tgt = tgt + self.positional_encoding_decoder(tgt)
330
+ pos_embs_encoder = None # self.positional_encoding(src)
331
+ pos_embs_target = None
332
+ elif (
333
+ self.positional_encoding_type == "fixed_abs_sine"
334
+ or self.attention_type == "hypermixing"
335
+ ):
336
+ tgt = tgt + self.positional_encoding(tgt) # add the encodings here
337
+ pos_embs_target = None
338
+ pos_embs_encoder = None
339
+
340
+
341
+ prediction, self_attns, multihead_attns = self.decoder(
342
+ tgt,
343
+ encoder_out,
344
+ tgt_mask=tgt_mask,
345
+ memory_key_padding_mask=src_key_padding_mask,
346
+ pos_embs_tgt=pos_embs_target,
347
+ pos_embs_src=pos_embs_encoder,
348
+ )
349
+ return prediction, multihead_attns[-1]
350
+
351
+ def encode(
352
+ self,
353
+ src,
354
+ wav_len=None,
355
+ pad_idx=0,
356
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
357
+ ):
358
+ """
359
+ Encoder forward pass
360
+
361
+ Arguments
362
+ ---------
363
+ src : torch.Tensor
364
+ The sequence to the encoder.
365
+ wav_len : torch.Tensor, optional
366
+ Torch Tensor of shape (batch, ) containing the relative length to padded length for each example.
367
+ pad_idx : int
368
+ The index used for padding.
369
+ dynchunktrain_config : DynChunkTrainConfig
370
+ Dynamic chunking config.
371
+
372
+ Returns
373
+ -------
374
+ encoder_out : torch.Tensor
375
+ """
376
+ # reshape the src vector to [Batch, Time, Fea] if a 4d vector is given
377
+ if src.dim() == 4:
378
+ bz, t, ch1, ch2 = src.shape
379
+ src = src.reshape(bz, t, ch1 * ch2)
380
+
381
+ (
382
+ src_key_padding_mask,
383
+ _,
384
+ src_mask,
385
+ _,
386
+ ) = make_transformer_src_tgt_masks(
387
+ src,
388
+ None,
389
+ wav_len,
390
+ pad_idx=pad_idx,
391
+ causal=self.causal,
392
+ dynchunktrain_config=dynchunktrain_config,
393
+ )
394
+
395
+ src = self.custom_src_module(src)
396
+ if self.attention_type == "hypermixing":
397
+ pos_embs_source = None
398
+ elif self.attention_type == "RelPosMHAXL":
399
+ pos_embs_source = self.positional_encoding(src)
400
+ elif self.positional_encoding_type == "fixed_abs_sine":
401
+ src = src + self.positional_encoding(src)
402
+ pos_embs_source = None
403
+
404
+ encoder_out, _ = self.encoder(
405
+ src=src,
406
+ src_mask=src_mask,
407
+ src_key_padding_mask=src_key_padding_mask,
408
+ pos_embs=pos_embs_source,
409
+ dynchunktrain_config=dynchunktrain_config,
410
+ )
411
+
412
+ return encoder_out
413
+
414
+ def encode_streaming(self, src, context: TransformerASRStreamingContext):
415
+ """
416
+ Streaming encoder forward pass
417
+
418
+ Arguments
419
+ ---------
420
+ src : torch.Tensor
421
+ The sequence (chunk) to the encoder.
422
+ context : TransformerASRStreamingContext
423
+ Mutable reference to the streaming context. This holds the state
424
+ needed to persist across chunk inferences and can be built using
425
+ `make_streaming_context`. This will get mutated by this function.
426
+
427
+ Returns
428
+ -------
429
+ Encoder output for this chunk.
430
+
431
+ Example
432
+ -------
433
+ >>> import torch
434
+ >>> from speechbrain.lobes.models.transformer.TransformerASR import TransformerASR
435
+ >>> from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
436
+ >>> net = TransformerASR(
437
+ ... tgt_vocab=100,
438
+ ... input_size=64,
439
+ ... d_model=64,
440
+ ... nhead=8,
441
+ ... num_encoder_layers=1,
442
+ ... num_decoder_layers=0,
443
+ ... d_ffn=128,
444
+ ... attention_type="RelPosMHAXL",
445
+ ... positional_encoding=None,
446
+ ... encoder_module="conformer",
447
+ ... normalize_before=True,
448
+ ... causal=False,
449
+ ... )
450
+ >>> ctx = net.make_streaming_context(DynChunkTrainConfig(16, 1))
451
+ >>> src1 = torch.rand([8, 16, 64])
452
+ >>> src2 = torch.rand([8, 16, 64])
453
+ >>> out1 = net.encode_streaming(src1, ctx)
454
+ >>> out1.shape
455
+ torch.Size([8, 16, 64])
456
+ >>> ctx.encoder_context.layers[0].mha_left_context.shape
457
+ torch.Size([8, 16, 64])
458
+ >>> out2 = net.encode_streaming(src2, ctx)
459
+ >>> out2.shape
460
+ torch.Size([8, 16, 64])
461
+ >>> ctx.encoder_context.layers[0].mha_left_context.shape
462
+ torch.Size([8, 16, 64])
463
+ >>> combined_out = torch.concat((out1, out2), dim=1)
464
+ >>> combined_out.shape
465
+ torch.Size([8, 32, 64])
466
+ """
467
+
468
+ if src.dim() == 4:
469
+ bz, t, ch1, ch2 = src.shape
470
+ src = src.reshape(bz, t, ch1 * ch2)
471
+
472
+ # HACK: our problem here is that the positional_encoding is computed
473
+ # against the size of our source tensor, but we only know how many left
474
+ # context frames we're injecting to the encoder within the encoder
475
+ # context.
476
+ # so this workaround does just that.
477
+ #
478
+ # i'm not sure how this would be best refactored, but an option would be
479
+ # to let the encoder get the pos embedding itself and have a way to
480
+ # cache it.
481
+ #
482
+ # additionally, positional encoding functions take in a whole source
483
+ # tensor just to get its attributes (size, device, type) but this is
484
+ # sort of silly for the embeddings that don't need one.
485
+ # so we craft a dummy empty (uninitialized) tensor to help...
486
+ known_left_context = context.encoder_context.layers[0].mha_left_context
487
+ if known_left_context is None:
488
+ pos_encoding_dummy = src
489
+ else:
490
+ target_shape = list(src.shape)
491
+ target_shape[-2] += known_left_context.shape[-2]
492
+ pos_encoding_dummy = torch.empty(size=target_shape).to(src)
493
+
494
+ src = self.custom_src_module(src)
495
+ if self.attention_type == "RelPosMHAXL":
496
+ pos_embs_source = self.positional_encoding(pos_encoding_dummy)
497
+
498
+ elif self.positional_encoding_type == "fixed_abs_sine":
499
+ src = src + self.positional_encoding(pos_encoding_dummy)
500
+ pos_embs_source = None
501
+
502
+ encoder_out, _ = self.encoder.forward_streaming(
503
+ src=src, pos_embs=pos_embs_source, context=context.encoder_context
504
+ )
505
+ return encoder_out
506
+
507
+ def make_streaming_context(
508
+ self, dynchunktrain_config: DynChunkTrainConfig, encoder_kwargs={}
509
+ ):
510
+ """Creates a blank streaming context for this transformer and its
511
+ encoder.
512
+
513
+ Arguments
514
+ ---------
515
+ dynchunktrain_config : DynChunkTrainConfig
516
+ Runtime chunkwise attention configuration.
517
+ encoder_kwargs : dict
518
+ Parameters to be forward to the encoder's `make_streaming_context`.
519
+ Metadata required for the encoder could differ depending on the
520
+ encoder.
521
+
522
+ Returns
523
+ -------
524
+ TransformerASRStreamingContext
525
+ """
526
+ return TransformerASRStreamingContext(
527
+ dynchunktrain_config=dynchunktrain_config,
528
+ encoder_context=self.encoder.make_streaming_context(
529
+ dynchunktrain_config,
530
+ **encoder_kwargs,
531
+ ),
532
+ )
533
+
534
+ def _init_params(self):
535
+ for p in self.parameters():
536
+ if p.dim() > 1:
537
+ torch.nn.init.xavier_normal_(p)
538
+
539
+
540
+ class EncoderWrapper(nn.Module):
541
+ """This is a wrapper of any ASR transformer encoder. By default, the
542
+ TransformerASR .forward() function encodes and decodes. With this wrapper
543
+ the .forward() function becomes .encode() only.
544
+
545
+ Important: The TransformerASR class must contain a .encode() function.
546
+
547
+ Arguments
548
+ ---------
549
+ transformer : sb.lobes.models.TransformerInterface
550
+ A Transformer instance that contains a .encode() function.
551
+ *args : tuple
552
+ **kwargs : dict
553
+ Arguments to forward to parent class.
554
+
555
+ Example
556
+ -------
557
+ >>> src = torch.rand([8, 120, 512])
558
+ >>> tgt = torch.randint(0, 720, [8, 120])
559
+ >>> net = TransformerASR(
560
+ ... 720, 512, 512, 8, 1, 1, 1024, activation=torch.nn.GELU
561
+ ... )
562
+ >>> encoder = EncoderWrapper(net)
563
+ >>> enc_out = encoder(src)
564
+ >>> enc_out.shape
565
+ torch.Size([8, 120, 512])
566
+ """
567
+
568
+ def __init__(self, transformer, *args, **kwargs):
569
+ super().__init__(*args, **kwargs)
570
+ self.transformer = transformer
571
+ self.make_streaming_context = self.transformer.make_streaming_context
572
+
573
+ def forward(self, x, wav_lens=None, pad_idx=0, **kwargs):
574
+ """Processes the input tensor x and returns an output tensor."""
575
+ x = self.transformer.encode(x, wav_lens, pad_idx, **kwargs)
576
+ return x
577
+
578
+ def forward_streaming(self, x, context):
579
+ """Processes the input audio chunk tensor `x`, using and updating the
580
+ mutable encoder `context`"""
581
+ x = self.transformer.encode_streaming(x, context)
582
+ return x
583
+
584
+ def make_streaming_context(self, *args, **kwargs):
585
+ """Initializes a streaming context. Forwards all arguments to the
586
+ underlying transformer. See :meth:`speechbrain.lobes.models.transformer.TransformerASR.make_streaming_context`.
587
+ """
588
+ return self.transformer.make_streaming_context(*args, **kwargs)
modules/Transformer.py ADDED
@@ -0,0 +1,1085 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Added ConMamba and Mamba
2
+
3
+ Authors
4
+ * Xilin Jiang 2024
5
+ """
6
+
7
+ """Transformer implementation in the SpeechBrain style.
8
+
9
+ Authors
10
+ * Jianyuan Zhong 2020
11
+ * Samuele Cornell 2021
12
+ """
13
+
14
+ import math
15
+ from typing import Optional
16
+
17
+ import numpy as np
18
+ import torch
19
+ import torch.nn as nn
20
+
21
+ import speechbrain as sb
22
+ from speechbrain.nnet.activations import Swish
23
+ from speechbrain.nnet.attention import RelPosEncXL
24
+ from speechbrain.nnet.CNN import Conv1d
25
+
26
+ from modules.Conformer import ConformerEncoder
27
+ from modules.Conmamba import ConmambaEncoder, MambaDecoder
28
+
29
+
30
+ class TransformerInterface(nn.Module):
31
+ """This is an interface for transformer model.
32
+ Users can modify the attributes and define the forward function as
33
+ needed according to their own tasks.
34
+ The architecture is based on the paper "Attention Is All You Need":
35
+ https://arxiv.org/pdf/1706.03762.pdf
36
+
37
+ Arguments
38
+ ---------
39
+ d_model: int
40
+ The number of expected features in the encoder/decoder inputs (default=512).
41
+ nhead: int
42
+ The number of heads in the multi-head attention models (default=8).
43
+ num_encoder_layers: int, optional
44
+ The number of encoder layers in1ì the encoder.
45
+ num_decoder_layers: int, optional
46
+ The number of decoder layers in the decoder.
47
+ d_ffn: int, optional
48
+ The dimension of the feedforward network model hidden layer.
49
+ dropout: int, optional
50
+ The dropout value.
51
+ activation: torch.nn.Module, optional
52
+ The activation function for Feed-Forward Network layer,
53
+ e.g., relu or gelu or swish.
54
+ custom_src_module: torch.nn.Module, optional
55
+ Module that processes the src features to expected feature dim.
56
+ custom_tgt_module: torch.nn.Module, optional
57
+ Module that processes the src features to expected feature dim.
58
+ positional_encoding: str, optional
59
+ Type of positional encoding used. e.g. 'fixed_abs_sine' for fixed absolute positional encodings.
60
+ normalize_before: bool, optional
61
+ Whether normalization should be applied before or after MHA or FFN in Transformer layers.
62
+ Defaults to True as this was shown to lead to better performance and training stability.
63
+ kernel_size: int, optional
64
+ Kernel size in convolutional layers when Conformer is used.
65
+ bias: bool, optional
66
+ Whether to use bias in Conformer convolutional layers.
67
+ encoder_module: str, optional
68
+ Choose between Branchformer, Conformer, ConMamba, and Transformer for the encoder.
69
+ decoder_module: str, optional
70
+ Choose between Mamba and Transformer for the decoder.
71
+ conformer_activation: torch.nn.Module, optional
72
+ Activation module used after Conformer convolutional layers. E.g. Swish, ReLU etc. it has to be a torch Module.
73
+ branchformer_activation: torch.nn.Module, optional
74
+ Activation module used within the Branchformer Encoder. E.g. Swish, ReLU etc. it has to be a torch Module.
75
+ attention_type: str, optional
76
+ Type of attention layer used in all Transformer or Conformer layers.
77
+ e.g. regularMHA or RelPosMHA.
78
+ max_length: int, optional
79
+ Max length for the target and source sequence in input.
80
+ Used for positional encodings.
81
+ causal: bool, optional
82
+ Whether the encoder should be causal or not (the decoder is always causal).
83
+ If causal the Conformer convolutional layer is causal.
84
+ encoder_kdim: int, optional
85
+ Dimension of the key for the encoder.
86
+ encoder_vdim: int, optional
87
+ Dimension of the value for the encoder.
88
+ decoder_kdim: int, optional
89
+ Dimension of the key for the decoder.
90
+ decoder_vdim: int, optional
91
+ Dimension of the value for the decoder.
92
+ csgu_linear_units: int, optional
93
+ Number of neurons in the hidden linear units of the CSGU Module.
94
+ -> Branchformer
95
+ gate_activation: torch.nn.Module, optional
96
+ Activation function used at the gate of the CSGU module.
97
+ -> Branchformer
98
+ use_linear_after_conv: bool, optional
99
+ If True, will apply a linear transformation of size input_size//2.
100
+ -> Branchformer
101
+ mamba_config: dict, optional
102
+ Mamba parameters if encoder_module or decoder_module is Mamba or ConMamba
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ d_model=512,
108
+ nhead=8,
109
+ num_encoder_layers=6,
110
+ num_decoder_layers=6,
111
+ d_ffn=2048,
112
+ dropout=0.1,
113
+ activation=nn.ReLU,
114
+ custom_src_module=None,
115
+ custom_tgt_module=None,
116
+ positional_encoding="fixed_abs_sine",
117
+ normalize_before=True,
118
+ kernel_size: Optional[int] = 31,
119
+ bias: Optional[bool] = True,
120
+ encoder_module: Optional[str] = "transformer",
121
+ decoder_module: Optional[str] = "transformer",
122
+ conformer_activation: Optional[nn.Module] = Swish,
123
+ branchformer_activation: Optional[nn.Module] = nn.GELU,
124
+ attention_type: Optional[str] = "regularMHA",
125
+ max_length: Optional[int] = 2500,
126
+ causal: Optional[bool] = False,
127
+ encoder_kdim: Optional[int] = None,
128
+ encoder_vdim: Optional[int] = None,
129
+ decoder_kdim: Optional[int] = None,
130
+ decoder_vdim: Optional[int] = None,
131
+ csgu_linear_units: Optional[int] = 3072,
132
+ gate_activation: Optional[nn.Module] = nn.Identity,
133
+ use_linear_after_conv: Optional[bool] = False,
134
+ mamba_config=None
135
+ ):
136
+ super().__init__()
137
+ self.causal = causal
138
+ self.attention_type = attention_type
139
+ self.positional_encoding_type = positional_encoding
140
+ self.encoder_kdim = encoder_kdim
141
+ self.encoder_vdim = encoder_vdim
142
+ self.decoder_kdim = decoder_kdim
143
+ self.decoder_vdim = decoder_vdim
144
+
145
+ assert attention_type in ["regularMHA", "RelPosMHAXL", "hypermixing"]
146
+ assert positional_encoding in ["fixed_abs_sine", None]
147
+
148
+ assert (
149
+ num_encoder_layers + num_decoder_layers > 0
150
+ ), "number of encoder layers and number of decoder layers cannot both be 0!"
151
+
152
+ if positional_encoding == "fixed_abs_sine":
153
+ self.positional_encoding = PositionalEncoding(d_model, max_length)
154
+ elif positional_encoding is None:
155
+ pass
156
+ # no positional encodings
157
+
158
+ # overrides any other pos_embedding
159
+ if attention_type == "RelPosMHAXL":
160
+ self.positional_encoding = RelPosEncXL(d_model)
161
+ self.positional_encoding_decoder = PositionalEncoding(
162
+ d_model, max_length
163
+ )
164
+
165
+ # initialize the encoder
166
+ if num_encoder_layers > 0:
167
+ if custom_src_module is not None:
168
+ self.custom_src_module = custom_src_module(d_model)
169
+ if encoder_module == "transformer":
170
+ self.encoder = TransformerEncoder(
171
+ nhead=nhead,
172
+ num_layers=num_encoder_layers,
173
+ d_ffn=d_ffn,
174
+ d_model=d_model,
175
+ dropout=dropout,
176
+ activation=activation,
177
+ normalize_before=normalize_before,
178
+ causal=self.causal,
179
+ attention_type=self.attention_type,
180
+ kdim=self.encoder_kdim,
181
+ vdim=self.encoder_vdim,
182
+ )
183
+ elif encoder_module == "conformer":
184
+ self.encoder = ConformerEncoder(
185
+ nhead=nhead,
186
+ num_layers=num_encoder_layers,
187
+ d_ffn=d_ffn,
188
+ d_model=d_model,
189
+ dropout=dropout,
190
+ activation=conformer_activation,
191
+ kernel_size=kernel_size,
192
+ bias=bias,
193
+ causal=self.causal,
194
+ attention_type=self.attention_type,
195
+ )
196
+ assert (
197
+ normalize_before
198
+ ), "normalize_before must be True for Conformer"
199
+
200
+ assert (
201
+ conformer_activation is not None
202
+ ), "conformer_activation must not be None"
203
+ elif encoder_module == "branchformer":
204
+ self.encoder = BranchformerEncoder(
205
+ nhead=nhead,
206
+ num_layers=num_encoder_layers,
207
+ d_model=d_model,
208
+ dropout=dropout,
209
+ activation=branchformer_activation,
210
+ kernel_size=kernel_size,
211
+ attention_type=self.attention_type,
212
+ csgu_linear_units=csgu_linear_units,
213
+ gate_activation=gate_activation,
214
+ use_linear_after_conv=use_linear_after_conv,
215
+ )
216
+ elif encoder_module == "conmamba":
217
+ self.encoder = ConmambaEncoder(
218
+ num_layers=num_encoder_layers,
219
+ d_model=d_model,
220
+ d_ffn=d_ffn,
221
+ dropout=dropout,
222
+ activation=branchformer_activation,
223
+ kernel_size=kernel_size,
224
+ bias=bias,
225
+ causal=self.causal,
226
+ mamba_config=mamba_config
227
+ )
228
+ assert (
229
+ normalize_before
230
+ ), "normalize_before must be True for Conmamba"
231
+
232
+ assert (
233
+ conformer_activation is not None
234
+ ), "conformer_activation must not be None"
235
+
236
+ # initialize the decoder
237
+ if num_decoder_layers > 0:
238
+ if custom_tgt_module is not None:
239
+ self.custom_tgt_module = custom_tgt_module(d_model)
240
+ if decoder_module == 'transformer':
241
+ self.decoder = TransformerDecoder(
242
+ num_layers=num_decoder_layers,
243
+ nhead=nhead,
244
+ d_ffn=d_ffn,
245
+ d_model=d_model,
246
+ dropout=dropout,
247
+ activation=activation,
248
+ normalize_before=normalize_before,
249
+ causal=True,
250
+ attention_type="regularMHA", # always use regular attention in decoder
251
+ kdim=self.decoder_kdim,
252
+ vdim=self.decoder_vdim,
253
+ )
254
+ elif decoder_module in ['mamba']:
255
+ self.decoder = MambaDecoder(
256
+ num_layers=num_decoder_layers,
257
+ d_ffn=d_ffn,
258
+ d_model=d_model,
259
+ activation=activation,
260
+ dropout=dropout,
261
+ normalize_before=normalize_before,
262
+ mamba_config=mamba_config
263
+ )
264
+ else:
265
+ raise NotImplementedError(decoder_module)
266
+
267
+ def forward(self, **kwags):
268
+ """Users should modify this function according to their own tasks."""
269
+ raise NotImplementedError
270
+
271
+
272
+ class PositionalEncoding(nn.Module):
273
+ """This class implements the absolute sinusoidal positional encoding function.
274
+ PE(pos, 2i) = sin(pos/(10000^(2i/dmodel)))
275
+ PE(pos, 2i+1) = cos(pos/(10000^(2i/dmodel)))
276
+
277
+ Arguments
278
+ ---------
279
+ input_size: int
280
+ Embedding dimension.
281
+ max_len : int, optional
282
+ Max length of the input sequences (default 2500).
283
+
284
+ Example
285
+ -------
286
+ >>> a = torch.rand((8, 120, 512))
287
+ >>> enc = PositionalEncoding(input_size=a.shape[-1])
288
+ >>> b = enc(a)
289
+ >>> b.shape
290
+ torch.Size([1, 120, 512])
291
+ """
292
+
293
+ def __init__(self, input_size, max_len=2500):
294
+ super().__init__()
295
+ if input_size % 2 != 0:
296
+ raise ValueError(
297
+ f"Cannot use sin/cos positional encoding with odd channels (got channels={input_size})"
298
+ )
299
+ self.max_len = max_len
300
+ pe = torch.zeros(self.max_len, input_size, requires_grad=False)
301
+ positions = torch.arange(0, self.max_len).unsqueeze(1).float()
302
+ denominator = torch.exp(
303
+ torch.arange(0, input_size, 2).float()
304
+ * -(math.log(10000.0) / input_size)
305
+ )
306
+
307
+ pe[:, 0::2] = torch.sin(positions * denominator)
308
+ pe[:, 1::2] = torch.cos(positions * denominator)
309
+ pe = pe.unsqueeze(0)
310
+ self.register_buffer("pe", pe)
311
+
312
+ def forward(self, x):
313
+ """
314
+ Arguments
315
+ ---------
316
+ x : torch.Tensor
317
+ Input feature shape (batch, time, fea)
318
+
319
+ Returns
320
+ -------
321
+ The positional encoding.
322
+ """
323
+ return self.pe[:, : x.size(1)].clone().detach()
324
+
325
+
326
+ class TransformerEncoderLayer(nn.Module):
327
+ """This is an implementation of self-attention encoder layer.
328
+
329
+ Arguments
330
+ ---------
331
+ d_ffn: int, optional
332
+ The dimension of the feedforward network model hidden layer.
333
+ nhead: int
334
+ The number of heads in the multi-head attention models (default=8).
335
+ d_model: int
336
+ The number of expected features in the encoder/decoder inputs (default=512).
337
+ kdim: int, optional
338
+ Dimension of the key.
339
+ vdim: int, optional
340
+ Dimension of the value.
341
+ dropout: int, optional
342
+ The dropout value.
343
+ activation: torch.nn.Module, optional
344
+ The activation function for Feed-Forward Network layer,
345
+ e.g., relu or gelu or swish.
346
+ normalize_before: bool, optional
347
+ Whether normalization should be applied before or after MHA or FFN in Transformer layers.
348
+ Defaults to True as this was shown to lead to better performance and training stability.
349
+ attention_type: str, optional
350
+ Type of attention layer used in all Transformer or Conformer layers.
351
+ e.g. regularMHA or RelPosMHA.
352
+ ffn_type: str
353
+ type of ffn: regularFFN/1dcnn
354
+ ffn_cnn_kernel_size_list: list of int
355
+ kernel size of 2 1d-convs if ffn_type is 1dcnn
356
+ causal: bool, optional
357
+ Whether the encoder should be causal or not (the decoder is always causal).
358
+ If causal the Conformer convolutional layer is causal.
359
+
360
+ Example
361
+ -------
362
+ >>> import torch
363
+ >>> x = torch.rand((8, 60, 512))
364
+ >>> net = TransformerEncoderLayer(512, 8, d_model=512)
365
+ >>> output = net(x)
366
+ >>> output[0].shape
367
+ torch.Size([8, 60, 512])
368
+ """
369
+
370
+ def __init__(
371
+ self,
372
+ d_ffn,
373
+ nhead,
374
+ d_model,
375
+ kdim=None,
376
+ vdim=None,
377
+ dropout=0.0,
378
+ activation=nn.ReLU,
379
+ normalize_before=False,
380
+ attention_type="regularMHA",
381
+ ffn_type="regularFFN",
382
+ ffn_cnn_kernel_size_list=[3, 3],
383
+ causal=False,
384
+ ):
385
+ super().__init__()
386
+
387
+ if attention_type == "regularMHA":
388
+ self.self_att = sb.nnet.attention.MultiheadAttention(
389
+ nhead=nhead,
390
+ d_model=d_model,
391
+ dropout=dropout,
392
+ kdim=kdim,
393
+ vdim=vdim,
394
+ )
395
+
396
+ elif attention_type == "RelPosMHAXL":
397
+ self.self_att = sb.nnet.attention.RelPosMHAXL(
398
+ d_model, nhead, dropout, mask_pos_future=causal
399
+ )
400
+ elif attention_type == "hypermixing":
401
+ self.self_att = sb.nnet.hypermixing.HyperMixing(
402
+ input_output_dim=d_model,
403
+ hypernet_size=d_ffn,
404
+ tied=False,
405
+ num_heads=nhead,
406
+ fix_tm_hidden_size=False,
407
+ )
408
+
409
+ if ffn_type == "regularFFN":
410
+ self.pos_ffn = sb.nnet.attention.PositionalwiseFeedForward(
411
+ d_ffn=d_ffn,
412
+ input_size=d_model,
413
+ dropout=dropout,
414
+ activation=activation,
415
+ )
416
+ elif ffn_type == "1dcnn":
417
+ self.pos_ffn = nn.Sequential(
418
+ Conv1d(
419
+ in_channels=d_model,
420
+ out_channels=d_ffn,
421
+ kernel_size=ffn_cnn_kernel_size_list[0],
422
+ padding="causal" if causal else "same",
423
+ ),
424
+ nn.ReLU(),
425
+ Conv1d(
426
+ in_channels=d_ffn,
427
+ out_channels=d_model,
428
+ kernel_size=ffn_cnn_kernel_size_list[1],
429
+ padding="causal" if causal else "same",
430
+ ),
431
+ )
432
+
433
+ self.norm1 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
434
+ self.norm2 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
435
+ self.dropout1 = torch.nn.Dropout(dropout)
436
+ self.dropout2 = torch.nn.Dropout(dropout)
437
+
438
+ self.normalize_before = normalize_before
439
+ self.pos_ffn_type = ffn_type
440
+
441
+ def forward(
442
+ self,
443
+ src,
444
+ src_mask: Optional[torch.Tensor] = None,
445
+ src_key_padding_mask: Optional[torch.Tensor] = None,
446
+ pos_embs: Optional[torch.Tensor] = None,
447
+ ):
448
+ """
449
+ Arguments
450
+ ---------
451
+ src : torch.Tensor
452
+ The sequence to the encoder layer.
453
+ src_mask : torch.Tensor
454
+ The mask for the src query for each example in the batch.
455
+ src_key_padding_mask : torch.Tensor, optional
456
+ The mask for the src keys for each example in the batch.
457
+ pos_embs: torch.Tensor, optional
458
+ The positional embeddings tensor.
459
+
460
+ Returns
461
+ -------
462
+ output : torch.Tensor
463
+ The output of the transformer encoder layer.
464
+ """
465
+
466
+ if self.normalize_before:
467
+ src1 = self.norm1(src)
468
+ else:
469
+ src1 = src
470
+
471
+ output, self_attn = self.self_att(
472
+ src1,
473
+ src1,
474
+ src1,
475
+ attn_mask=src_mask,
476
+ key_padding_mask=src_key_padding_mask,
477
+ pos_embs=pos_embs,
478
+ )
479
+
480
+ # add & norm
481
+ src = src + self.dropout1(output)
482
+ if not self.normalize_before:
483
+ src = self.norm1(src)
484
+
485
+ if self.normalize_before:
486
+ src1 = self.norm2(src)
487
+ else:
488
+ src1 = src
489
+ output = self.pos_ffn(src1)
490
+
491
+ # add & norm
492
+ output = src + self.dropout2(output)
493
+ if not self.normalize_before:
494
+ output = self.norm2(output)
495
+ return output, self_attn
496
+
497
+
498
+ class TransformerEncoder(nn.Module):
499
+ """This class implements the transformer encoder.
500
+
501
+ Arguments
502
+ ---------
503
+ num_layers : int
504
+ Number of transformer layers to include.
505
+ nhead : int
506
+ Number of attention heads.
507
+ d_ffn : int
508
+ Hidden size of self-attention Feed Forward layer.
509
+ input_shape : tuple
510
+ Expected shape of the input.
511
+ d_model : int
512
+ The dimension of the input embedding.
513
+ kdim : int
514
+ Dimension for key (Optional).
515
+ vdim : int
516
+ Dimension for value (Optional).
517
+ dropout : float
518
+ Dropout for the encoder (Optional).
519
+ activation: torch.nn.Module, optional
520
+ The activation function for Feed-Forward Network layer,
521
+ e.g., relu or gelu or swish.
522
+ normalize_before: bool, optional
523
+ Whether normalization should be applied before or after MHA or FFN in Transformer layers.
524
+ Defaults to True as this was shown to lead to better performance and training stability.
525
+ causal: bool, optional
526
+ Whether the encoder should be causal or not (the decoder is always causal).
527
+ If causal the Conformer convolutional layer is causal.
528
+ layerdrop_prob: float
529
+ The probability to drop an entire layer
530
+ attention_type: str, optional
531
+ Type of attention layer used in all Transformer or Conformer layers.
532
+ e.g. regularMHA or RelPosMHA.
533
+ ffn_type: str
534
+ type of ffn: regularFFN/1dcnn
535
+ ffn_cnn_kernel_size_list: list of int
536
+ conv kernel size of 2 1d-convs if ffn_type is 1dcnn
537
+
538
+ Example
539
+ -------
540
+ >>> import torch
541
+ >>> x = torch.rand((8, 60, 512))
542
+ >>> net = TransformerEncoder(1, 8, 512, d_model=512)
543
+ >>> output, _ = net(x)
544
+ >>> output.shape
545
+ torch.Size([8, 60, 512])
546
+ """
547
+
548
+ def __init__(
549
+ self,
550
+ num_layers,
551
+ nhead,
552
+ d_ffn,
553
+ input_shape=None,
554
+ d_model=None,
555
+ kdim=None,
556
+ vdim=None,
557
+ dropout=0.0,
558
+ activation=nn.ReLU,
559
+ normalize_before=False,
560
+ causal=False,
561
+ layerdrop_prob=0.0,
562
+ attention_type="regularMHA",
563
+ ffn_type="regularFFN",
564
+ ffn_cnn_kernel_size_list=[3, 3],
565
+ ):
566
+ super().__init__()
567
+
568
+ self.layers = torch.nn.ModuleList(
569
+ [
570
+ TransformerEncoderLayer(
571
+ d_ffn=d_ffn,
572
+ nhead=nhead,
573
+ d_model=d_model,
574
+ kdim=kdim,
575
+ vdim=vdim,
576
+ dropout=dropout,
577
+ activation=activation,
578
+ normalize_before=normalize_before,
579
+ causal=causal,
580
+ attention_type=attention_type,
581
+ ffn_type=ffn_type,
582
+ ffn_cnn_kernel_size_list=ffn_cnn_kernel_size_list,
583
+ )
584
+ for i in range(num_layers)
585
+ ]
586
+ )
587
+ self.norm = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
588
+ self.layerdrop_prob = layerdrop_prob
589
+ self.rng = np.random.default_rng()
590
+
591
+ def forward(
592
+ self,
593
+ src,
594
+ src_mask: Optional[torch.Tensor] = None,
595
+ src_key_padding_mask: Optional[torch.Tensor] = None,
596
+ pos_embs: Optional[torch.Tensor] = None,
597
+ dynchunktrain_config=None,
598
+ ):
599
+ """
600
+ Arguments
601
+ ---------
602
+ src : torch.Tensor
603
+ The sequence to the encoder layer (required).
604
+ src_mask : torch.Tensor
605
+ The mask for the src sequence (optional).
606
+ src_key_padding_mask : torch.Tensor
607
+ The mask for the src keys per batch (optional).
608
+ pos_embs : torch.Tensor
609
+ The positional embedding tensor
610
+ dynchunktrain_config : config
611
+ Not supported for this encoder.
612
+
613
+ Returns
614
+ -------
615
+ output : torch.Tensor
616
+ The output of the transformer.
617
+ attention_lst : list
618
+ The attention values.
619
+ """
620
+ assert (
621
+ dynchunktrain_config is None
622
+ ), "Dynamic Chunk Training unsupported for this encoder"
623
+
624
+ output = src
625
+ if self.layerdrop_prob > 0.0:
626
+ keep_probs = self.rng.random(len(self.layers))
627
+ else:
628
+ keep_probs = None
629
+ attention_lst = []
630
+ for i, enc_layer in enumerate(self.layers):
631
+ if (
632
+ not self.training
633
+ or self.layerdrop_prob == 0.0
634
+ or keep_probs[i] > self.layerdrop_prob
635
+ ):
636
+ output, attention = enc_layer(
637
+ output,
638
+ src_mask=src_mask,
639
+ src_key_padding_mask=src_key_padding_mask,
640
+ pos_embs=pos_embs,
641
+ )
642
+
643
+ attention_lst.append(attention)
644
+ output = self.norm(output)
645
+ return output, attention_lst
646
+
647
+
648
+ class TransformerDecoderLayer(nn.Module):
649
+ """This class implements the self-attention decoder layer.
650
+
651
+ Arguments
652
+ ---------
653
+ d_ffn : int
654
+ Hidden size of self-attention Feed Forward layer.
655
+ nhead : int
656
+ Number of attention heads.
657
+ d_model : int
658
+ Dimension of the model.
659
+ kdim : int
660
+ Dimension for key (optional).
661
+ vdim : int
662
+ Dimension for value (optional).
663
+ dropout : float
664
+ Dropout for the decoder (optional).
665
+ activation : Callable
666
+ Function to use between layers, default nn.ReLU
667
+ normalize_before : bool
668
+ Whether to normalize before layers.
669
+ attention_type : str
670
+ Type of attention to use, "regularMHA" or "RelPosMHAXL"
671
+ causal : bool
672
+ Whether to mask future positions.
673
+
674
+ Example
675
+ -------
676
+ >>> src = torch.rand((8, 60, 512))
677
+ >>> tgt = torch.rand((8, 60, 512))
678
+ >>> net = TransformerDecoderLayer(1024, 8, d_model=512)
679
+ >>> output, self_attn, multihead_attn = net(src, tgt)
680
+ >>> output.shape
681
+ torch.Size([8, 60, 512])
682
+ """
683
+
684
+ def __init__(
685
+ self,
686
+ d_ffn,
687
+ nhead,
688
+ d_model,
689
+ kdim=None,
690
+ vdim=None,
691
+ dropout=0.0,
692
+ activation=nn.ReLU,
693
+ normalize_before=False,
694
+ attention_type="regularMHA",
695
+ causal=None,
696
+ ):
697
+ super().__init__()
698
+ self.nhead = nhead
699
+
700
+ if attention_type == "regularMHA":
701
+ self.self_attn = sb.nnet.attention.MultiheadAttention(
702
+ nhead=nhead,
703
+ d_model=d_model,
704
+ kdim=kdim,
705
+ vdim=vdim,
706
+ dropout=dropout,
707
+ )
708
+ self.multihead_attn = sb.nnet.attention.MultiheadAttention(
709
+ nhead=nhead,
710
+ d_model=d_model,
711
+ kdim=kdim,
712
+ vdim=vdim,
713
+ dropout=dropout,
714
+ )
715
+
716
+ elif attention_type == "RelPosMHAXL":
717
+ self.self_attn = sb.nnet.attention.RelPosMHAXL(
718
+ d_model, nhead, dropout, mask_pos_future=causal
719
+ )
720
+ self.multihead_attn = sb.nnet.attention.RelPosMHAXL(
721
+ d_model, nhead, dropout, mask_pos_future=causal
722
+ )
723
+
724
+ self.pos_ffn = sb.nnet.attention.PositionalwiseFeedForward(
725
+ d_ffn=d_ffn,
726
+ input_size=d_model,
727
+ dropout=dropout,
728
+ activation=activation,
729
+ )
730
+
731
+ # normalization layers
732
+ self.norm1 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
733
+ self.norm2 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
734
+ self.norm3 = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
735
+ self.dropout1 = torch.nn.Dropout(dropout)
736
+ self.dropout2 = torch.nn.Dropout(dropout)
737
+ self.dropout3 = torch.nn.Dropout(dropout)
738
+
739
+ self.normalize_before = normalize_before
740
+
741
+ def forward(
742
+ self,
743
+ tgt,
744
+ memory,
745
+ tgt_mask=None,
746
+ memory_mask=None,
747
+ tgt_key_padding_mask=None,
748
+ memory_key_padding_mask=None,
749
+ pos_embs_tgt=None,
750
+ pos_embs_src=None,
751
+ ):
752
+ """
753
+ Arguments
754
+ ----------
755
+ tgt: torch.Tensor
756
+ The sequence to the decoder layer (required).
757
+ memory: torch.Tensor
758
+ The sequence from the last layer of the encoder (required).
759
+ tgt_mask: torch.Tensor
760
+ The mask for the tgt sequence (optional).
761
+ memory_mask: torch.Tensor
762
+ The mask for the memory sequence (optional).
763
+ tgt_key_padding_mask: torch.Tensor
764
+ The mask for the tgt keys per batch (optional).
765
+ memory_key_padding_mask: torch.Tensor
766
+ The mask for the memory keys per batch (optional).
767
+ pos_embs_tgt: torch.Tensor
768
+ The positional embeddings for the target (optional).
769
+ pos_embs_src: torch.Tensor
770
+ The positional embeddings for the source (optional).
771
+ """
772
+ if self.normalize_before:
773
+ tgt1 = self.norm1(tgt)
774
+ else:
775
+ tgt1 = tgt
776
+
777
+ # self-attention over the target sequence
778
+ tgt2, self_attn = self.self_attn(
779
+ query=tgt1,
780
+ key=tgt1,
781
+ value=tgt1,
782
+ attn_mask=tgt_mask,
783
+ key_padding_mask=tgt_key_padding_mask,
784
+ pos_embs=pos_embs_tgt,
785
+ )
786
+
787
+ # add & norm
788
+ tgt = tgt + self.dropout1(tgt2)
789
+ if not self.normalize_before:
790
+ tgt = self.norm1(tgt)
791
+
792
+ if self.normalize_before:
793
+ tgt1 = self.norm2(tgt)
794
+ else:
795
+ tgt1 = tgt
796
+
797
+ # multi-head attention over the target sequence and encoder states
798
+
799
+ tgt2, multihead_attention = self.multihead_attn(
800
+ query=tgt1,
801
+ key=memory,
802
+ value=memory,
803
+ attn_mask=memory_mask,
804
+ key_padding_mask=memory_key_padding_mask,
805
+ pos_embs=pos_embs_src,
806
+ )
807
+
808
+ # add & norm
809
+ tgt = tgt + self.dropout2(tgt2)
810
+ if not self.normalize_before:
811
+ tgt = self.norm2(tgt)
812
+
813
+ if self.normalize_before:
814
+ tgt1 = self.norm3(tgt)
815
+ else:
816
+ tgt1 = tgt
817
+
818
+ tgt2 = self.pos_ffn(tgt1)
819
+
820
+ # add & norm
821
+ tgt = tgt + self.dropout3(tgt2)
822
+ if not self.normalize_before:
823
+ tgt = self.norm3(tgt)
824
+
825
+ return tgt, self_attn, multihead_attention
826
+
827
+
828
+ class TransformerDecoder(nn.Module):
829
+ """This class implements the Transformer decoder.
830
+
831
+ Arguments
832
+ ---------
833
+ num_layers : int
834
+ Number of transformer layers for the decoder.
835
+ nhead : int
836
+ Number of attention heads.
837
+ d_ffn : int
838
+ Hidden size of self-attention Feed Forward layer.
839
+ d_model : int
840
+ Dimension of the model.
841
+ kdim : int, optional
842
+ Dimension for key (Optional).
843
+ vdim : int, optional
844
+ Dimension for value (Optional).
845
+ dropout : float, optional
846
+ Dropout for the decoder (Optional).
847
+ activation : Callable
848
+ The function to apply between layers, default nn.ReLU
849
+ normalize_before : bool
850
+ Whether to normalize before layers.
851
+ causal : bool
852
+ Whether to allow future information in decoding.
853
+ attention_type : str
854
+ Type of attention to use, "regularMHA" or "RelPosMHAXL"
855
+
856
+ Example
857
+ -------
858
+ >>> src = torch.rand((8, 60, 512))
859
+ >>> tgt = torch.rand((8, 60, 512))
860
+ >>> net = TransformerDecoder(1, 8, 1024, d_model=512)
861
+ >>> output, _, _ = net(src, tgt)
862
+ >>> output.shape
863
+ torch.Size([8, 60, 512])
864
+ """
865
+
866
+ def __init__(
867
+ self,
868
+ num_layers,
869
+ nhead,
870
+ d_ffn,
871
+ d_model,
872
+ kdim=None,
873
+ vdim=None,
874
+ dropout=0.0,
875
+ activation=nn.ReLU,
876
+ normalize_before=False,
877
+ causal=False,
878
+ attention_type="regularMHA",
879
+ ):
880
+ super().__init__()
881
+ self.layers = torch.nn.ModuleList(
882
+ [
883
+ TransformerDecoderLayer(
884
+ d_ffn=d_ffn,
885
+ nhead=nhead,
886
+ d_model=d_model,
887
+ kdim=kdim,
888
+ vdim=vdim,
889
+ dropout=dropout,
890
+ activation=activation,
891
+ normalize_before=normalize_before,
892
+ causal=causal,
893
+ attention_type=attention_type,
894
+ )
895
+ for _ in range(num_layers)
896
+ ]
897
+ )
898
+ self.norm = sb.nnet.normalization.LayerNorm(d_model, eps=1e-6)
899
+
900
+ def forward(
901
+ self,
902
+ tgt,
903
+ memory,
904
+ tgt_mask=None,
905
+ memory_mask=None,
906
+ tgt_key_padding_mask=None,
907
+ memory_key_padding_mask=None,
908
+ pos_embs_tgt=None,
909
+ pos_embs_src=None,
910
+ ):
911
+ """
912
+ Arguments
913
+ ----------
914
+ tgt : torch.Tensor
915
+ The sequence to the decoder layer (required).
916
+ memory : torch.Tensor
917
+ The sequence from the last layer of the encoder (required).
918
+ tgt_mask : torch.Tensor
919
+ The mask for the tgt sequence (optional).
920
+ memory_mask : torch.Tensor
921
+ The mask for the memory sequence (optional).
922
+ tgt_key_padding_mask : torch.Tensor
923
+ The mask for the tgt keys per batch (optional).
924
+ memory_key_padding_mask : torch.Tensor
925
+ The mask for the memory keys per batch (optional).
926
+ pos_embs_tgt : torch.Tensor
927
+ The positional embeddings for the target (optional).
928
+ pos_embs_src : torch.Tensor
929
+ The positional embeddings for the source (optional).
930
+ """
931
+ output = tgt
932
+ self_attns, multihead_attns = [], []
933
+ for dec_layer in self.layers:
934
+ output, self_attn, multihead_attn = dec_layer(
935
+ output,
936
+ memory,
937
+ tgt_mask=tgt_mask,
938
+ memory_mask=memory_mask,
939
+ tgt_key_padding_mask=tgt_key_padding_mask,
940
+ memory_key_padding_mask=memory_key_padding_mask,
941
+ pos_embs_tgt=pos_embs_tgt,
942
+ pos_embs_src=pos_embs_src,
943
+ )
944
+ self_attns.append(self_attn)
945
+ multihead_attns.append(multihead_attn)
946
+ output = self.norm(output)
947
+
948
+ return output, self_attns, multihead_attns
949
+
950
+
951
+ class NormalizedEmbedding(nn.Module):
952
+ """This class implements the normalized embedding layer for the transformer.
953
+ Since the dot product of the self-attention is always normalized by sqrt(d_model)
954
+ and the final linear projection for prediction shares weight with the embedding layer,
955
+ we multiply the output of the embedding by sqrt(d_model).
956
+
957
+ Arguments
958
+ ---------
959
+ d_model: int
960
+ The number of expected features in the encoder/decoder inputs (default=512).
961
+ vocab: int
962
+ The vocab size.
963
+
964
+ Example
965
+ -------
966
+ >>> emb = NormalizedEmbedding(512, 1000)
967
+ >>> trg = torch.randint(0, 999, (8, 50))
968
+ >>> emb_fea = emb(trg)
969
+ """
970
+
971
+ def __init__(self, d_model, vocab):
972
+ super().__init__()
973
+ self.emb = sb.nnet.embedding.Embedding(
974
+ num_embeddings=vocab, embedding_dim=d_model, blank_id=0
975
+ )
976
+ self.d_model = d_model
977
+
978
+ def forward(self, x):
979
+ """Processes the input tensor x and returns an output tensor."""
980
+ return self.emb(x) * math.sqrt(self.d_model)
981
+
982
+
983
+ def get_key_padding_mask(padded_input, pad_idx):
984
+ """Creates a binary mask to prevent attention to padded locations.
985
+ We suggest using ``get_mask_from_lengths`` instead of this function.
986
+
987
+ Arguments
988
+ ---------
989
+ padded_input: torch.Tensor
990
+ Padded input.
991
+ pad_idx: int
992
+ idx for padding element.
993
+
994
+ Returns
995
+ -------
996
+ key_padded_mask: torch.Tensor
997
+ Binary mask to prevent attention to padding.
998
+
999
+ Example
1000
+ -------
1001
+ >>> a = torch.LongTensor([[1,1,0], [2,3,0], [4,5,0]])
1002
+ >>> get_key_padding_mask(a, pad_idx=0)
1003
+ tensor([[False, False, True],
1004
+ [False, False, True],
1005
+ [False, False, True]])
1006
+ """
1007
+ if len(padded_input.shape) == 4:
1008
+ bz, time, ch1, ch2 = padded_input.shape
1009
+ padded_input = padded_input.reshape(bz, time, ch1 * ch2)
1010
+
1011
+ key_padded_mask = padded_input.eq(pad_idx).to(padded_input.device)
1012
+
1013
+ # if the input is more than 2d, mask the locations where they are silence
1014
+ # across all channels
1015
+ if len(padded_input.shape) > 2:
1016
+ key_padded_mask = key_padded_mask.float().prod(dim=-1).bool()
1017
+ return key_padded_mask.detach()
1018
+
1019
+ return key_padded_mask.detach()
1020
+
1021
+
1022
+ def get_lookahead_mask(padded_input):
1023
+ """Creates a binary mask for each sequence which masks future frames.
1024
+
1025
+ Arguments
1026
+ ---------
1027
+ padded_input: torch.Tensor
1028
+ Padded input tensor.
1029
+
1030
+ Returns
1031
+ -------
1032
+ mask : torch.Tensor
1033
+ Binary mask for masking future frames.
1034
+
1035
+ Example
1036
+ -------
1037
+ >>> a = torch.LongTensor([[1,1,0], [2,3,0], [4,5,0]])
1038
+ >>> get_lookahead_mask(a)
1039
+ tensor([[0., -inf, -inf],
1040
+ [0., 0., -inf],
1041
+ [0., 0., 0.]])
1042
+ """
1043
+ seq_len = padded_input.shape[1]
1044
+ mask = (
1045
+ torch.triu(torch.ones((seq_len, seq_len), device=padded_input.device))
1046
+ == 1
1047
+ ).transpose(0, 1)
1048
+ mask = (
1049
+ mask.float()
1050
+ .masked_fill(mask == 0, float("-inf"))
1051
+ .masked_fill(mask == 1, float(0.0))
1052
+ )
1053
+ return mask.detach().to(padded_input.device)
1054
+
1055
+
1056
+ def get_mask_from_lengths(lengths, max_len=None):
1057
+ """Creates a binary mask from sequence lengths
1058
+
1059
+ Arguments
1060
+ ---------
1061
+ lengths: torch.Tensor
1062
+ A tensor of sequence lengths
1063
+ max_len: int (Optional)
1064
+ Maximum sequence length, defaults to None.
1065
+
1066
+ Returns
1067
+ -------
1068
+ mask: torch.Tensor
1069
+ the mask where padded elements are set to True.
1070
+ Then one can use tensor.masked_fill_(mask, 0) for the masking.
1071
+
1072
+ Example
1073
+ -------
1074
+ >>> lengths = torch.tensor([3, 2, 4])
1075
+ >>> get_mask_from_lengths(lengths)
1076
+ tensor([[False, False, False, True],
1077
+ [False, False, True, True],
1078
+ [False, False, False, False]])
1079
+ """
1080
+ if max_len is None:
1081
+ max_len = torch.max(lengths).item()
1082
+ seq_range = torch.arange(
1083
+ max_len, device=lengths.device, dtype=lengths.dtype
1084
+ )
1085
+ return ~(seq_range.unsqueeze(0) < lengths.unsqueeze(1))
modules/TransformerASR.py ADDED
@@ -0,0 +1,682 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Added ConMamba and Mamba
2
+
3
+ Authors
4
+ * Xilin Jiang 2024
5
+ """
6
+
7
+ """Transformer for ASR in the SpeechBrain style.
8
+
9
+ Authors
10
+ * Jianyuan Zhong 2020
11
+ * Titouan Parcollet 2024
12
+ * Luca Della Libera 2024
13
+ """
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Any, Optional
17
+
18
+ import torch # noqa 42
19
+ from torch import nn
20
+
21
+ from speechbrain.dataio.dataio import length_to_mask
22
+ from modules.Transformer import (
23
+ NormalizedEmbedding,
24
+ TransformerInterface,
25
+ get_key_padding_mask,
26
+ get_lookahead_mask,
27
+ )
28
+ from speechbrain.nnet.activations import Swish
29
+ from speechbrain.nnet.containers import ModuleList
30
+ from speechbrain.nnet.linear import Linear
31
+ from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
32
+
33
+
34
+ @dataclass
35
+ class TransformerASRStreamingContext:
36
+ """Streaming metadata and state for a `TransformerASR` instance."""
37
+
38
+ dynchunktrain_config: DynChunkTrainConfig
39
+ """Dynamic Chunk Training configuration holding chunk size and context size
40
+ information."""
41
+
42
+ encoder_context: Any
43
+ """Opaque encoder context information. It is constructed by the encoder's
44
+ `make_streaming_context` method and is passed to the encoder when using
45
+ `encode_streaming`.
46
+ """
47
+
48
+
49
+ def make_transformer_src_mask(
50
+ src: torch.Tensor,
51
+ causal: bool = False,
52
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
53
+ ) -> Optional[torch.Tensor]:
54
+ """Prepare the source transformer mask that restricts which frames can
55
+ attend to which frames depending on causal or other simple restricted
56
+ attention methods.
57
+
58
+ Arguments
59
+ ---------
60
+ src: torch.Tensor
61
+ The source tensor to build a mask from. The contents of the tensor are
62
+ not actually used currently; only its shape and other metadata (e.g.
63
+ device).
64
+ causal: bool
65
+ Whether strict causality shall be used. Frames will not be able to
66
+ attend to any future frame.
67
+ dynchunktrain_config: DynChunkTrainConfig, optional
68
+ Dynamic Chunk Training configuration. This implements a simple form of
69
+ chunkwise attention. Incompatible with `causal`.
70
+
71
+ Returns
72
+ -------
73
+ torch.Tensor
74
+ A boolean mask Tensor of shape (timesteps, timesteps).
75
+ """
76
+ if causal:
77
+ assert dynchunktrain_config is None
78
+ return get_lookahead_mask(src)
79
+
80
+ if dynchunktrain_config is None:
81
+ return
82
+
83
+ # The following is not really the sole source used to implement this,
84
+ # but it helps introduce the concept.
85
+ # ref: Unified Streaming and Non-streaming Two-pass End-to-end Model for Speech Recognition
86
+ # https://arxiv.org/pdf/2012.05481.pdf
87
+ timesteps = src.size(1)
88
+
89
+ # Mask the future at the right of each chunk
90
+ chunk_size = dynchunktrain_config.chunk_size
91
+ num_chunks = timesteps // chunk_size
92
+ timestep_idx = torch.arange(timesteps, device=src.device)
93
+ mask_idx = torch.arange(
94
+ chunk_size, chunk_size * (num_chunks + 2), chunk_size, device=src.device
95
+ ).repeat_interleave(chunk_size)[:timesteps]
96
+ src_mask = timestep_idx[None] >= mask_idx[:, None]
97
+
98
+ # Mask the past at the left of each chunk (accounting for left context)
99
+ # only relevant if using left context
100
+ if not dynchunktrain_config.is_infinite_left_context():
101
+ num_left_chunks = dynchunktrain_config.left_context_size
102
+ mask_idx -= chunk_size * (num_left_chunks + 1)
103
+ src_mask += timestep_idx[None] < mask_idx[:, None]
104
+
105
+ return src_mask
106
+
107
+
108
+ def make_transformer_src_tgt_masks(
109
+ src,
110
+ tgt=None,
111
+ wav_len=None,
112
+ pad_idx=0,
113
+ causal: bool = False,
114
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
115
+ ):
116
+ """This function generates masks for training the transformer model,
117
+ opinionated for an ASR context with encoding masks and, optionally, decoding
118
+ masks (if specifying `tgt`).
119
+
120
+ Arguments
121
+ ---------
122
+ src : torch.Tensor
123
+ The sequence to the encoder (required).
124
+ tgt : torch.Tensor
125
+ The sequence to the decoder.
126
+ wav_len : torch.Tensor
127
+ The lengths of the inputs.
128
+ pad_idx : int
129
+ The index for <pad> token (default=0).
130
+ causal: bool
131
+ Whether strict causality shall be used. See `make_asr_src_mask`
132
+ dynchunktrain_config: DynChunkTrainConfig, optional
133
+ Dynamic Chunk Training configuration. See `make_asr_src_mask`
134
+
135
+ Returns
136
+ -------
137
+ src_key_padding_mask : torch.Tensor
138
+ Key padding mask for ignoring padding
139
+ tgt_key_padding_mask : torch.Tensor
140
+ Key padding mask for ignoring padding
141
+ src_mask : torch.Tensor
142
+ Mask for ignoring invalid (e.g. future) timesteps
143
+ tgt_mask : torch.Tensor
144
+ Mask for ignoring invalid (e.g. future) timesteps
145
+ """
146
+ src_key_padding_mask = None
147
+
148
+ # mask out audio beyond the length of audio for each batch
149
+ if wav_len is not None:
150
+ abs_len = torch.round(wav_len * src.shape[1])
151
+ src_key_padding_mask = ~length_to_mask(abs_len).bool()
152
+
153
+ # mask out the source
154
+ src_mask = make_transformer_src_mask(
155
+ src, causal=causal, dynchunktrain_config=dynchunktrain_config
156
+ )
157
+
158
+ # If no decoder in the transformer...
159
+ if tgt is not None:
160
+ tgt_key_padding_mask = get_key_padding_mask(tgt, pad_idx=pad_idx)
161
+ tgt_mask = get_lookahead_mask(tgt)
162
+ else:
163
+ tgt_key_padding_mask = None
164
+ tgt_mask = None
165
+
166
+ return src_key_padding_mask, tgt_key_padding_mask, src_mask, tgt_mask
167
+
168
+
169
+ class TransformerASR(TransformerInterface):
170
+ """This is an implementation of transformer model for ASR.
171
+
172
+ The architecture is based on the paper "Attention Is All You Need":
173
+ https://arxiv.org/pdf/1706.03762.pdf
174
+
175
+ Arguments
176
+ ---------
177
+ tgt_vocab: int
178
+ Size of vocabulary.
179
+ input_size: int
180
+ Input feature size.
181
+ d_model : int, optional
182
+ Embedding dimension size.
183
+ (default=512).
184
+ nhead : int, optional
185
+ The number of heads in the multi-head attention models (default=8).
186
+ num_encoder_layers : int, optional
187
+ The number of sub-encoder-layers in the encoder (default=6).
188
+ num_decoder_layers : int, optional
189
+ The number of sub-decoder-layers in the decoder (default=6).
190
+ d_ffn : int, optional
191
+ The dimension of the feedforward network model (default=2048).
192
+ dropout : int, optional
193
+ The dropout value (default=0.1).
194
+ activation : torch.nn.Module, optional
195
+ The activation function of FFN layers.
196
+ Recommended: relu or gelu (default=relu).
197
+ positional_encoding: str, optional
198
+ Type of positional encoding used. e.g. 'fixed_abs_sine' for fixed absolute positional encodings.
199
+ normalize_before: bool, optional
200
+ Whether normalization should be applied before or after MHA or FFN in Transformer layers.
201
+ Defaults to True as this was shown to lead to better performance and training stability.
202
+ kernel_size: int, optional
203
+ Kernel size in convolutional layers when Conformer is used.
204
+ bias: bool, optional
205
+ Whether to use bias in Conformer convolutional layers.
206
+ encoder_module: str, optional
207
+ Choose between Branchformer, Conformer, ConMamba, and Transformer for the encoder.
208
+ decoder_module: str, optional
209
+ Choose between Mamba and Transformer for the decoder.
210
+ decoder_module: str, optional
211
+ Choose between Transformer and Mamba for the decoder.
212
+ conformer_activation: torch.nn.Module, optional
213
+ Activation module used after Conformer convolutional layers. E.g. Swish, ReLU etc. it has to be a torch Module.
214
+ branchformer_activation: torch.nn.Module, optional
215
+ Activation module used within the Branchformer Encoder. E.g. Swish, ReLU etc. it has to be a torch Module.
216
+ attention_type: str, optional
217
+ Type of attention layer used in all Transformer or Conformer layers.
218
+ e.g. regularMHA or RelPosMHA.
219
+ max_length: int, optional
220
+ Max length for the target and source sequence in input.
221
+ Used for positional encodings.
222
+ causal: bool, optional
223
+ Whether the encoder should be causal or not (the decoder is always causal).
224
+ If causal the Conformer convolutional layer is causal.
225
+ csgu_linear_units: int, optional
226
+ Number of neurons in the hidden linear units of the CSGU Module.
227
+ -> Branchformer
228
+ gate_activation: torch.nn.Module, optional
229
+ Activation function used at the gate of the CSGU module.
230
+ -> Branchformer
231
+ use_linear_after_conv: bool, optional
232
+ If True, will apply a linear transformation of size input_size//2.
233
+ -> Branchformer
234
+ mamba_config: dict, optional
235
+ Mamba parameters if encoder_module or decoder_module is Mamba or ConMamba
236
+
237
+ Example
238
+ -------
239
+ >>> src = torch.rand([8, 120, 512])
240
+ >>> tgt = torch.randint(0, 720, [8, 120])
241
+ >>> net = TransformerASR(
242
+ ... 720, 512, 512, 8, 1, 1, 1024, activation=torch.nn.GELU
243
+ ... )
244
+ >>> enc_out, dec_out = net.forward(src, tgt)
245
+ >>> enc_out.shape
246
+ torch.Size([8, 120, 512])
247
+ >>> dec_out.shape
248
+ torch.Size([8, 120, 512])
249
+ """
250
+
251
+ def __init__(
252
+ self,
253
+ tgt_vocab,
254
+ input_size,
255
+ d_model=512,
256
+ nhead=8,
257
+ num_encoder_layers=6,
258
+ num_decoder_layers=6,
259
+ d_ffn=2048,
260
+ dropout=0.1,
261
+ activation=nn.ReLU,
262
+ positional_encoding="fixed_abs_sine",
263
+ normalize_before=False,
264
+ kernel_size: Optional[int] = 31,
265
+ bias: Optional[bool] = True,
266
+ encoder_module: Optional[str] = "transformer",
267
+ decoder_module: Optional[str] = "transformer",
268
+ conformer_activation: Optional[nn.Module] = Swish,
269
+ branchformer_activation: Optional[nn.Module] = nn.GELU,
270
+ attention_type: Optional[str] = "regularMHA",
271
+ max_length: Optional[int] = 2500,
272
+ causal: Optional[bool] = True,
273
+ csgu_linear_units: Optional[int] = 3072,
274
+ gate_activation: Optional[nn.Module] = nn.Identity,
275
+ use_linear_after_conv: Optional[bool] = False,
276
+ mamba_config=None
277
+ ):
278
+ super().__init__(
279
+ d_model=d_model,
280
+ nhead=nhead,
281
+ num_encoder_layers=num_encoder_layers,
282
+ num_decoder_layers=num_decoder_layers,
283
+ d_ffn=d_ffn,
284
+ dropout=dropout,
285
+ activation=activation,
286
+ positional_encoding=positional_encoding,
287
+ normalize_before=normalize_before,
288
+ kernel_size=kernel_size,
289
+ bias=bias,
290
+ encoder_module=encoder_module,
291
+ decoder_module=decoder_module,
292
+ conformer_activation=conformer_activation,
293
+ branchformer_activation=branchformer_activation,
294
+ attention_type=attention_type,
295
+ max_length=max_length,
296
+ causal=causal,
297
+ csgu_linear_units=csgu_linear_units,
298
+ gate_activation=gate_activation,
299
+ use_linear_after_conv=use_linear_after_conv,
300
+ mamba_config=mamba_config
301
+ )
302
+
303
+ self.custom_src_module = ModuleList(
304
+ Linear(
305
+ input_size=input_size,
306
+ n_neurons=d_model,
307
+ bias=True,
308
+ combine_dims=False,
309
+ ),
310
+ torch.nn.Dropout(dropout),
311
+ )
312
+
313
+ self.num_decoder_layers = num_decoder_layers
314
+ if num_decoder_layers > 0:
315
+ self.custom_tgt_module = ModuleList(
316
+ NormalizedEmbedding(d_model, tgt_vocab)
317
+ )
318
+
319
+ # reset parameters using xavier_normal_
320
+ self._init_params()
321
+
322
+ def forward(self, src, tgt, wav_len=None, pad_idx=0):
323
+ """
324
+ Arguments
325
+ ----------
326
+ src : torch.Tensor
327
+ The sequence to the encoder.
328
+ tgt : torch.Tensor
329
+ The sequence to the decoder.
330
+ wav_len: torch.Tensor, optional
331
+ Torch Tensor of shape (batch, ) containing the relative length to padded length for each example.
332
+ pad_idx : int, optional
333
+ The index for <pad> token (default=0).
334
+ """
335
+
336
+ # reshape the src vector to [Batch, Time, Fea] is a 4d vector is given
337
+ if src.ndim == 4:
338
+ bz, t, ch1, ch2 = src.shape
339
+ src = src.reshape(bz, t, ch1 * ch2)
340
+
341
+ (
342
+ src_key_padding_mask,
343
+ tgt_key_padding_mask,
344
+ src_mask,
345
+ tgt_mask,
346
+ ) = make_transformer_src_tgt_masks(
347
+ src, tgt, wav_len, causal=self.causal, pad_idx=pad_idx
348
+ )
349
+
350
+ src = self.custom_src_module(src)
351
+ # add pos encoding to queries if are sinusoidal ones else
352
+ if self.attention_type == "hypermixing":
353
+ pos_embs_encoder = None
354
+ elif self.attention_type == "RelPosMHAXL":
355
+ pos_embs_encoder = self.positional_encoding(src)
356
+ elif self.positional_encoding_type == "fixed_abs_sine":
357
+ src = src + self.positional_encoding(src) # add the encodings here
358
+ pos_embs_encoder = None
359
+
360
+ encoder_out, _ = self.encoder(
361
+ src=src,
362
+ src_mask=src_mask,
363
+ src_key_padding_mask=src_key_padding_mask,
364
+ pos_embs=pos_embs_encoder,
365
+ )
366
+
367
+ if self.num_decoder_layers > 0:
368
+ tgt = self.custom_tgt_module(tgt)
369
+
370
+ if self.attention_type == "RelPosMHAXL":
371
+ tgt = tgt + self.positional_encoding_decoder(tgt)
372
+ pos_embs_encoder = None # self.positional_encoding(src)
373
+ pos_embs_target = None
374
+ elif (
375
+ self.positional_encoding_type == "fixed_abs_sine"
376
+ or self.attention_type == "hypermixing"
377
+ ):
378
+ tgt = tgt + self.positional_encoding(tgt)
379
+ pos_embs_target = None
380
+ pos_embs_encoder = None
381
+
382
+ decoder_out, _, _ = self.decoder(
383
+ tgt=tgt,
384
+ memory=encoder_out,
385
+ memory_mask=None,
386
+ tgt_mask=tgt_mask,
387
+ tgt_key_padding_mask=tgt_key_padding_mask,
388
+ memory_key_padding_mask=src_key_padding_mask,
389
+ pos_embs_tgt=pos_embs_target,
390
+ pos_embs_src=pos_embs_encoder,
391
+ )
392
+
393
+ else:
394
+ decoder_out = None
395
+
396
+ return encoder_out, decoder_out
397
+
398
+ @torch.no_grad()
399
+ def decode(self, tgt, encoder_out, enc_len=None):
400
+ """This method implements a decoding step for the transformer model.
401
+
402
+ Arguments
403
+ ---------
404
+ tgt : torch.Tensor
405
+ The sequence to the decoder.
406
+ encoder_out : torch.Tensor
407
+ Hidden output of the encoder.
408
+ enc_len : torch.LongTensor
409
+ The actual length of encoder states.
410
+
411
+ Returns
412
+ -------
413
+ prediction
414
+ """
415
+ tgt_mask = get_lookahead_mask(tgt)
416
+ src_key_padding_mask = None
417
+ if enc_len is not None:
418
+ src_key_padding_mask = (1 - length_to_mask(enc_len)).bool()
419
+
420
+ if self.num_decoder_layers > 0:
421
+ tgt = self.custom_tgt_module(tgt)
422
+ if self.attention_type == "RelPosMHAXL":
423
+ tgt = tgt + self.positional_encoding_decoder(tgt)
424
+ pos_embs_encoder = None # self.positional_encoding(src)
425
+ pos_embs_target = None
426
+ elif (
427
+ self.positional_encoding_type == "fixed_abs_sine"
428
+ or self.attention_type == "hypermixing"
429
+ ):
430
+ tgt = tgt + self.positional_encoding(tgt) # add the encodings here
431
+ pos_embs_target = None
432
+ pos_embs_encoder = None
433
+
434
+
435
+ prediction, self_attns, multihead_attns = self.decoder(
436
+ tgt,
437
+ encoder_out,
438
+ tgt_mask=tgt_mask,
439
+ memory_key_padding_mask=src_key_padding_mask,
440
+ pos_embs_tgt=pos_embs_target,
441
+ pos_embs_src=pos_embs_encoder,
442
+ )
443
+ return prediction, multihead_attns[-1]
444
+
445
+ def encode(
446
+ self,
447
+ src,
448
+ wav_len=None,
449
+ pad_idx=0,
450
+ dynchunktrain_config: Optional[DynChunkTrainConfig] = None,
451
+ ):
452
+ """
453
+ Encoder forward pass
454
+
455
+ Arguments
456
+ ---------
457
+ src : torch.Tensor
458
+ The sequence to the encoder.
459
+ wav_len : torch.Tensor, optional
460
+ Torch Tensor of shape (batch, ) containing the relative length to padded length for each example.
461
+ pad_idx : int
462
+ The index used for padding.
463
+ dynchunktrain_config : DynChunkTrainConfig
464
+ Dynamic chunking config.
465
+
466
+ Returns
467
+ -------
468
+ encoder_out : torch.Tensor
469
+ """
470
+ # reshape the src vector to [Batch, Time, Fea] if a 4d vector is given
471
+ if src.dim() == 4:
472
+ bz, t, ch1, ch2 = src.shape
473
+ src = src.reshape(bz, t, ch1 * ch2)
474
+
475
+ (
476
+ src_key_padding_mask,
477
+ _,
478
+ src_mask,
479
+ _,
480
+ ) = make_transformer_src_tgt_masks(
481
+ src,
482
+ None,
483
+ wav_len,
484
+ pad_idx=pad_idx,
485
+ causal=self.causal,
486
+ dynchunktrain_config=dynchunktrain_config,
487
+ )
488
+
489
+ src = self.custom_src_module(src)
490
+ if self.attention_type == "hypermixing":
491
+ pos_embs_source = None
492
+ elif self.attention_type == "RelPosMHAXL":
493
+ pos_embs_source = self.positional_encoding(src)
494
+ elif self.positional_encoding_type == "fixed_abs_sine":
495
+ src = src + self.positional_encoding(src)
496
+ pos_embs_source = None
497
+
498
+ encoder_out, _ = self.encoder(
499
+ src=src,
500
+ src_mask=src_mask,
501
+ src_key_padding_mask=src_key_padding_mask,
502
+ pos_embs=pos_embs_source,
503
+ dynchunktrain_config=dynchunktrain_config,
504
+ )
505
+
506
+ return encoder_out
507
+
508
+ def encode_streaming(self, src, context: TransformerASRStreamingContext):
509
+ """
510
+ Streaming encoder forward pass
511
+
512
+ Arguments
513
+ ---------
514
+ src : torch.Tensor
515
+ The sequence (chunk) to the encoder.
516
+ context : TransformerASRStreamingContext
517
+ Mutable reference to the streaming context. This holds the state
518
+ needed to persist across chunk inferences and can be built using
519
+ `make_streaming_context`. This will get mutated by this function.
520
+
521
+ Returns
522
+ -------
523
+ Encoder output for this chunk.
524
+
525
+ Example
526
+ -------
527
+ >>> import torch
528
+ >>> from speechbrain.lobes.models.transformer.TransformerASR import TransformerASR
529
+ >>> from speechbrain.utils.dynamic_chunk_training import DynChunkTrainConfig
530
+ >>> net = TransformerASR(
531
+ ... tgt_vocab=100,
532
+ ... input_size=64,
533
+ ... d_model=64,
534
+ ... nhead=8,
535
+ ... num_encoder_layers=1,
536
+ ... num_decoder_layers=0,
537
+ ... d_ffn=128,
538
+ ... attention_type="RelPosMHAXL",
539
+ ... positional_encoding=None,
540
+ ... encoder_module="conformer",
541
+ ... normalize_before=True,
542
+ ... causal=False,
543
+ ... )
544
+ >>> ctx = net.make_streaming_context(DynChunkTrainConfig(16, 1))
545
+ >>> src1 = torch.rand([8, 16, 64])
546
+ >>> src2 = torch.rand([8, 16, 64])
547
+ >>> out1 = net.encode_streaming(src1, ctx)
548
+ >>> out1.shape
549
+ torch.Size([8, 16, 64])
550
+ >>> ctx.encoder_context.layers[0].mha_left_context.shape
551
+ torch.Size([8, 16, 64])
552
+ >>> out2 = net.encode_streaming(src2, ctx)
553
+ >>> out2.shape
554
+ torch.Size([8, 16, 64])
555
+ >>> ctx.encoder_context.layers[0].mha_left_context.shape
556
+ torch.Size([8, 16, 64])
557
+ >>> combined_out = torch.concat((out1, out2), dim=1)
558
+ >>> combined_out.shape
559
+ torch.Size([8, 32, 64])
560
+ """
561
+
562
+ if src.dim() == 4:
563
+ bz, t, ch1, ch2 = src.shape
564
+ src = src.reshape(bz, t, ch1 * ch2)
565
+
566
+ # HACK: our problem here is that the positional_encoding is computed
567
+ # against the size of our source tensor, but we only know how many left
568
+ # context frames we're injecting to the encoder within the encoder
569
+ # context.
570
+ # so this workaround does just that.
571
+ #
572
+ # i'm not sure how this would be best refactored, but an option would be
573
+ # to let the encoder get the pos embedding itself and have a way to
574
+ # cache it.
575
+ #
576
+ # additionally, positional encoding functions take in a whole source
577
+ # tensor just to get its attributes (size, device, type) but this is
578
+ # sort of silly for the embeddings that don't need one.
579
+ # so we craft a dummy empty (uninitialized) tensor to help...
580
+ known_left_context = context.encoder_context.layers[0].mha_left_context
581
+ if known_left_context is None:
582
+ pos_encoding_dummy = src
583
+ else:
584
+ target_shape = list(src.shape)
585
+ target_shape[-2] += known_left_context.shape[-2]
586
+ pos_encoding_dummy = torch.empty(size=target_shape).to(src)
587
+
588
+ src = self.custom_src_module(src)
589
+ if self.attention_type == "RelPosMHAXL":
590
+ pos_embs_source = self.positional_encoding(pos_encoding_dummy)
591
+
592
+ elif self.positional_encoding_type == "fixed_abs_sine":
593
+ src = src + self.positional_encoding(pos_encoding_dummy)
594
+ pos_embs_source = None
595
+
596
+ encoder_out, _ = self.encoder.forward_streaming(
597
+ src=src, pos_embs=pos_embs_source, context=context.encoder_context
598
+ )
599
+ return encoder_out
600
+
601
+ def make_streaming_context(
602
+ self, dynchunktrain_config: DynChunkTrainConfig, encoder_kwargs={}
603
+ ):
604
+ """Creates a blank streaming context for this transformer and its
605
+ encoder.
606
+
607
+ Arguments
608
+ ---------
609
+ dynchunktrain_config : DynChunkTrainConfig
610
+ Runtime chunkwise attention configuration.
611
+ encoder_kwargs : dict
612
+ Parameters to be forward to the encoder's `make_streaming_context`.
613
+ Metadata required for the encoder could differ depending on the
614
+ encoder.
615
+
616
+ Returns
617
+ -------
618
+ TransformerASRStreamingContext
619
+ """
620
+ return TransformerASRStreamingContext(
621
+ dynchunktrain_config=dynchunktrain_config,
622
+ encoder_context=self.encoder.make_streaming_context(
623
+ dynchunktrain_config,
624
+ **encoder_kwargs,
625
+ ),
626
+ )
627
+
628
+ def _init_params(self):
629
+ for p in self.parameters():
630
+ if p.dim() > 1:
631
+ torch.nn.init.xavier_normal_(p)
632
+
633
+
634
+ class EncoderWrapper(nn.Module):
635
+ """This is a wrapper of any ASR transformer encoder. By default, the
636
+ TransformerASR .forward() function encodes and decodes. With this wrapper
637
+ the .forward() function becomes .encode() only.
638
+
639
+ Important: The TransformerASR class must contain a .encode() function.
640
+
641
+ Arguments
642
+ ---------
643
+ transformer : sb.lobes.models.TransformerInterface
644
+ A Transformer instance that contains a .encode() function.
645
+ *args : tuple
646
+ **kwargs : dict
647
+ Arguments to forward to parent class.
648
+
649
+ Example
650
+ -------
651
+ >>> src = torch.rand([8, 120, 512])
652
+ >>> tgt = torch.randint(0, 720, [8, 120])
653
+ >>> net = TransformerASR(
654
+ ... 720, 512, 512, 8, 1, 1, 1024, activation=torch.nn.GELU
655
+ ... )
656
+ >>> encoder = EncoderWrapper(net)
657
+ >>> enc_out = encoder(src)
658
+ >>> enc_out.shape
659
+ torch.Size([8, 120, 512])
660
+ """
661
+
662
+ def __init__(self, transformer, *args, **kwargs):
663
+ super().__init__(*args, **kwargs)
664
+ self.transformer = transformer
665
+ self.make_streaming_context = self.transformer.make_streaming_context
666
+
667
+ def forward(self, x, wav_lens=None, pad_idx=0, **kwargs):
668
+ """Processes the input tensor x and returns an output tensor."""
669
+ x = self.transformer.encode(x, wav_lens, pad_idx, **kwargs)
670
+ return x
671
+
672
+ def forward_streaming(self, x, context):
673
+ """Processes the input audio chunk tensor `x`, using and updating the
674
+ mutable encoder `context`"""
675
+ x = self.transformer.encode_streaming(x, context)
676
+ return x
677
+
678
+ def make_streaming_context(self, *args, **kwargs):
679
+ """Initializes a streaming context. Forwards all arguments to the
680
+ underlying transformer. See :meth:`speechbrain.lobes.models.transformer.TransformerASR.make_streaming_context`.
681
+ """
682
+ return self.transformer.make_streaming_context(*args, **kwargs)
modules/__init__.py ADDED
File without changes
modules/mamba/__init__.py ADDED
File without changes
modules/mamba/bimamba.py ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Copied and modified from
3
+ https://github.com/hustvl/Vim/blob/main/mamba-1p1p1/mamba_ssm/modules/mamba_simple.py
4
+ '''
5
+
6
+ # Copyright (c) 2023, Tri Dao, Albert Gu.
7
+
8
+ import math
9
+ from typing import Optional
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+ import torch.nn.functional as F
14
+ from torch import Tensor
15
+
16
+ from einops import rearrange, repeat
17
+
18
+ try:
19
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
20
+ except ImportError:
21
+ causal_conv1d_fn, causal_conv1d_update = None
22
+
23
+ try:
24
+ from modules.mamba.selective_scan_interface import selective_scan_fn, mamba_inner_fn, bimamba_inner_fn, mamba_inner_fn_no_out_proj
25
+ except ImportError:
26
+ selective_scan_fn, mamba_inner_fn, bimamba_inner_fn, mamba_inner_fn_no_out_proj = None, None, None, None, None
27
+
28
+ try:
29
+ from mamba_ssm.ops.triton.selective_state_update import selective_state_update
30
+ except ImportError:
31
+ selective_state_update = None
32
+
33
+ try:
34
+ from mamba_ssm.ops.triton.layernorm import RMSNorm, layer_norm_fn, rms_norm_fn
35
+ except ImportError:
36
+ RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
37
+
38
+
39
+ class Mamba(nn.Module):
40
+ def __init__(
41
+ self,
42
+ d_model,
43
+ d_state=16,
44
+ d_conv=4,
45
+ expand=2,
46
+ dt_rank="auto",
47
+ dt_min=0.001,
48
+ dt_max=0.1,
49
+ dt_init="random",
50
+ dt_scale=1.0,
51
+ dt_init_floor=1e-4,
52
+ conv_bias=True,
53
+ bias=False,
54
+ use_fast_path=True, # Fused kernel options
55
+ layer_idx=None,
56
+ device=None,
57
+ dtype=None,
58
+ bimamba_type="none",
59
+ if_devide_out=True, # False
60
+ init_layer_scale=None,
61
+ ):
62
+ factory_kwargs = {"device": device, "dtype": dtype}
63
+ super().__init__()
64
+ self.d_model = d_model
65
+ self.d_state = d_state
66
+ self.d_conv = d_conv
67
+ self.expand = expand
68
+ self.d_inner = int(self.expand * self.d_model)
69
+ self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == "auto" else dt_rank
70
+ self.use_fast_path = use_fast_path
71
+ self.layer_idx = layer_idx
72
+ self.bimamba_type = bimamba_type
73
+ self.if_devide_out = if_devide_out
74
+
75
+ assert bimamba_type == 'v2'
76
+
77
+ self.init_layer_scale = init_layer_scale
78
+ if init_layer_scale is not None:
79
+ self.gamma = nn.Parameter(init_layer_scale * torch.ones((d_model)), requires_grad=True)
80
+
81
+ self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias, **factory_kwargs)
82
+
83
+ self.conv1d = nn.Conv1d(
84
+ in_channels=self.d_inner,
85
+ out_channels=self.d_inner,
86
+ bias=conv_bias,
87
+ kernel_size=d_conv,
88
+ groups=self.d_inner,
89
+ padding=d_conv - 1,
90
+ **factory_kwargs,
91
+ )
92
+
93
+ self.activation = "silu"
94
+ self.act = nn.SiLU()
95
+
96
+ self.x_proj = nn.Linear(
97
+ self.d_inner, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs
98
+ )
99
+ self.dt_proj = nn.Linear(self.dt_rank, self.d_inner, bias=True, **factory_kwargs)
100
+
101
+ # Initialize special dt projection to preserve variance at initialization
102
+ dt_init_std = self.dt_rank**-0.5 * dt_scale
103
+ if dt_init == "constant":
104
+ nn.init.constant_(self.dt_proj.weight, dt_init_std)
105
+ elif dt_init == "random":
106
+ nn.init.uniform_(self.dt_proj.weight, -dt_init_std, dt_init_std)
107
+ else:
108
+ raise NotImplementedError
109
+
110
+ # Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max
111
+ dt = torch.exp(
112
+ torch.rand(self.d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
113
+ + math.log(dt_min)
114
+ ).clamp(min=dt_init_floor)
115
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
116
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
117
+ with torch.no_grad():
118
+ self.dt_proj.bias.copy_(inv_dt)
119
+ # Our initialization would set all Linear.bias to zero, need to mark this one as _no_reinit
120
+ self.dt_proj.bias._no_reinit = True
121
+
122
+ # S4D real initialization
123
+ A = repeat(
124
+ torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
125
+ "n -> d n",
126
+ d=self.d_inner,
127
+ ).contiguous()
128
+ A_log = torch.log(A) # Keep A_log in fp32
129
+ self.A_log = nn.Parameter(A_log)
130
+ self.A_log._no_weight_decay = True
131
+
132
+ # D "skip" parameter
133
+ self.D = nn.Parameter(torch.ones(self.d_inner, device=device)) # Keep in fp32
134
+ self.D._no_weight_decay = True
135
+
136
+ # bidirectional
137
+ if bimamba_type == "v1":
138
+ A_b = repeat(
139
+ torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
140
+ "n -> d n",
141
+ d=self.d_inner,
142
+ ).contiguous()
143
+ A_b_log = torch.log(A_b) # Keep A_b_log in fp32
144
+ self.A_b_log = nn.Parameter(A_b_log)
145
+ self.A_b_log._no_weight_decay = True
146
+ elif bimamba_type == "v2":
147
+ A_b = repeat(
148
+ torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
149
+ "n -> d n",
150
+ d=self.d_inner,
151
+ ).contiguous()
152
+ A_b_log = torch.log(A_b) # Keep A_b_log in fp32
153
+ self.A_b_log = nn.Parameter(A_b_log)
154
+ self.A_b_log._no_weight_decay = True
155
+
156
+ self.conv1d_b = nn.Conv1d(
157
+ in_channels=self.d_inner,
158
+ out_channels=self.d_inner,
159
+ bias=conv_bias,
160
+ kernel_size=d_conv,
161
+ groups=self.d_inner,
162
+ padding=d_conv - 1,
163
+ **factory_kwargs,
164
+ )
165
+
166
+ self.x_proj_b = nn.Linear(
167
+ self.d_inner, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs
168
+ )
169
+ self.dt_proj_b = nn.Linear(self.dt_rank, self.d_inner, bias=True, **factory_kwargs)
170
+
171
+ self.D_b = nn.Parameter(torch.ones(self.d_inner, device=device)) # Keep in fp32
172
+ self.D_b._no_weight_decay = True
173
+
174
+ self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)
175
+
176
+ def forward(self, hidden_states, inference_params=None):
177
+ """
178
+ hidden_states: (B, L, D)
179
+ Returns: same shape as hidden_states
180
+ """
181
+ batch, seqlen, dim = hidden_states.shape
182
+ conv_state, ssm_state = None, None
183
+
184
+ if inference_params is not None:
185
+ conv_state, ssm_state = self._get_states_from_cache(inference_params, batch)
186
+ if inference_params.seqlen_offset > 0:
187
+ # The states are updated inplace
188
+ out, _, _ = self.step(hidden_states, conv_state, ssm_state)
189
+ return out
190
+
191
+ # We do matmul and transpose BLH -> HBL at the same time
192
+ xz = rearrange(
193
+ self.in_proj.weight @ rearrange(hidden_states, "b l d -> d (b l)"),
194
+ "d (b l) -> b d l",
195
+ l=seqlen,
196
+ )
197
+ if self.in_proj.bias is not None:
198
+ xz = xz + rearrange(self.in_proj.bias.to(dtype=xz.dtype), "d -> d 1")
199
+
200
+ A = -torch.exp(self.A_log.float()) # (d_inner, d_state)
201
+ # In the backward pass we write dx and dz next to each other to avoid torch.cat
202
+ if self.use_fast_path and inference_params is None: # Doesn't support outputting the states
203
+ if self.bimamba_type == "v1":
204
+ A_b = -torch.exp(self.A_b_log.float())
205
+ out = bimamba_inner_fn(
206
+ xz,
207
+ self.conv1d.weight,
208
+ self.conv1d.bias,
209
+ self.x_proj.weight,
210
+ self.dt_proj.weight,
211
+ self.out_proj.weight,
212
+ self.out_proj.bias,
213
+ A,
214
+ A_b,
215
+ None, # input-dependent B
216
+ None, # input-dependent C
217
+ self.D.float(),
218
+ delta_bias=self.dt_proj.bias.float(),
219
+ delta_softplus=True,
220
+ )
221
+ elif self.bimamba_type == "v2":
222
+ A_b = -torch.exp(self.A_b_log.float())
223
+ out = mamba_inner_fn_no_out_proj(
224
+ xz,
225
+ self.conv1d.weight,
226
+ self.conv1d.bias,
227
+ self.x_proj.weight,
228
+ self.dt_proj.weight,
229
+ A,
230
+ None, # input-dependent B
231
+ None, # input-dependent C
232
+ self.D.float(),
233
+ delta_bias=self.dt_proj.bias.float(),
234
+ delta_softplus=True,
235
+ )
236
+ out_b = mamba_inner_fn_no_out_proj(
237
+ xz.flip([-1]),
238
+ self.conv1d_b.weight,
239
+ self.conv1d_b.bias,
240
+ self.x_proj_b.weight,
241
+ self.dt_proj_b.weight,
242
+ A_b,
243
+ None,
244
+ None,
245
+ self.D_b.float(),
246
+ delta_bias=self.dt_proj_b.bias.float(),
247
+ delta_softplus=True,
248
+ )
249
+
250
+ if not self.if_devide_out:
251
+ out = F.linear(rearrange(out + out_b.flip([-1]), "b d l -> b l d"), self.out_proj.weight, self.out_proj.bias)
252
+ else:
253
+ out = F.linear(rearrange(0.5*out + 0.5*out_b.flip([-1]), "b d l -> b l d"), self.out_proj.weight, self.out_proj.bias)
254
+
255
+ else:
256
+ out = mamba_inner_fn(
257
+ xz,
258
+ self.conv1d.weight,
259
+ self.conv1d.bias,
260
+ self.x_proj.weight,
261
+ self.dt_proj.weight,
262
+ self.out_proj.weight,
263
+ self.out_proj.bias,
264
+ A,
265
+ None, # input-dependent B
266
+ None, # input-dependent C
267
+ self.D.float(),
268
+ delta_bias=self.dt_proj.bias.float(),
269
+ delta_softplus=True,
270
+ )
271
+ else:
272
+ x, z = xz.chunk(2, dim=1)
273
+ # Compute short convolution
274
+ if conv_state is not None:
275
+ # If we just take x[:, :, -self.d_conv :], it will error if seqlen < self.d_conv
276
+ # Instead F.pad will pad with zeros if seqlen < self.d_conv, and truncate otherwise.
277
+ conv_state.copy_(F.pad(x, (self.d_conv - x.shape[-1], 0))) # Update state (B D W)
278
+ if causal_conv1d_fn is None:
279
+ x = self.act(self.conv1d(x)[..., :seqlen])
280
+ else:
281
+ assert self.activation in ["silu", "swish"]
282
+ x = causal_conv1d_fn(
283
+ x=x,
284
+ weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
285
+ bias=self.conv1d.bias,
286
+ activation=self.activation,
287
+ )
288
+
289
+ # We're careful here about the layout, to avoid extra transposes.
290
+ # We want dt to have d as the slowest moving dimension
291
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
292
+ x_dbl = self.x_proj(rearrange(x, "b d l -> (b l) d")) # (bl d)
293
+ dt, B, C = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=-1)
294
+ dt = self.dt_proj.weight @ dt.t()
295
+ dt = rearrange(dt, "d (b l) -> b d l", l=seqlen)
296
+ B = rearrange(B, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
297
+ C = rearrange(C, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
298
+ assert self.activation in ["silu", "swish"]
299
+ y = selective_scan_fn(
300
+ x,
301
+ dt,
302
+ A,
303
+ B,
304
+ C,
305
+ self.D.float(),
306
+ z=z,
307
+ delta_bias=self.dt_proj.bias.float(),
308
+ delta_softplus=True,
309
+ return_last_state=ssm_state is not None,
310
+ )
311
+ if ssm_state is not None:
312
+ y, last_state = y
313
+ ssm_state.copy_(last_state)
314
+ y = rearrange(y, "b d l -> b l d")
315
+ out = self.out_proj(y)
316
+ if self.init_layer_scale is not None:
317
+ out = out * self.gamma
318
+ return out
319
+
320
+ def step(self, hidden_states, conv_state, ssm_state):
321
+ dtype = hidden_states.dtype
322
+ assert hidden_states.shape[1] == 1, "Only support decoding with 1 token at a time for now"
323
+ xz = self.in_proj(hidden_states.squeeze(1)) # (B 2D)
324
+ x, z = xz.chunk(2, dim=-1) # (B D)
325
+
326
+ # Conv step
327
+ if causal_conv1d_update is None:
328
+ conv_state.copy_(torch.roll(conv_state, shifts=-1, dims=-1)) # Update state (B D W)
329
+ conv_state[:, :, -1] = x
330
+ x = torch.sum(conv_state * rearrange(self.conv1d.weight, "d 1 w -> d w"), dim=-1) # (B D)
331
+ if self.conv1d.bias is not None:
332
+ x = x + self.conv1d.bias
333
+ x = self.act(x).to(dtype=dtype)
334
+ else:
335
+ x = causal_conv1d_update(
336
+ x,
337
+ conv_state,
338
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
339
+ self.conv1d.bias,
340
+ self.activation,
341
+ )
342
+
343
+ x_db = self.x_proj(x) # (B dt_rank+2*d_state)
344
+ dt, B, C = torch.split(x_db, [self.dt_rank, self.d_state, self.d_state], dim=-1)
345
+ # Don't add dt_bias here
346
+ dt = F.linear(dt, self.dt_proj.weight) # (B d_inner)
347
+ A = -torch.exp(self.A_log.float()) # (d_inner, d_state)
348
+
349
+ # SSM step
350
+ if selective_state_update is None:
351
+ # Discretize A and B
352
+ dt = F.softplus(dt + self.dt_proj.bias.to(dtype=dt.dtype))
353
+ dA = torch.exp(torch.einsum("bd,dn->bdn", dt, A))
354
+ dB = torch.einsum("bd,bn->bdn", dt, B)
355
+ ssm_state.copy_(ssm_state * dA + rearrange(x, "b d -> b d 1") * dB)
356
+ y = torch.einsum("bdn,bn->bd", ssm_state.to(dtype), C)
357
+ y = y + self.D.to(dtype) * x
358
+ y = y * self.act(z) # (B D)
359
+ else:
360
+ y = selective_state_update(
361
+ ssm_state, x, dt, A, B, C, self.D, z=z, dt_bias=self.dt_proj.bias, dt_softplus=True
362
+ )
363
+
364
+ out = self.out_proj(y)
365
+ return out.unsqueeze(1), conv_state, ssm_state
366
+
367
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
368
+ device = self.out_proj.weight.device
369
+ conv_dtype = self.conv1d.weight.dtype if dtype is None else dtype
370
+ conv_state = torch.zeros(
371
+ batch_size, self.d_model * self.expand, self.d_conv, device=device, dtype=conv_dtype
372
+ )
373
+ ssm_dtype = self.dt_proj.weight.dtype if dtype is None else dtype
374
+ # ssm_dtype = torch.float32
375
+ ssm_state = torch.zeros(
376
+ batch_size, self.d_model * self.expand, self.d_state, device=device, dtype=ssm_dtype
377
+ )
378
+ return conv_state, ssm_state
379
+
380
+ def _get_states_from_cache(self, inference_params, batch_size, initialize_states=False):
381
+ assert self.layer_idx is not None
382
+ if self.layer_idx not in inference_params.key_value_memory_dict:
383
+ batch_shape = (batch_size,)
384
+ conv_state = torch.zeros(
385
+ batch_size,
386
+ self.d_model * self.expand,
387
+ self.d_conv,
388
+ device=self.conv1d.weight.device,
389
+ dtype=self.conv1d.weight.dtype,
390
+ )
391
+ ssm_state = torch.zeros(
392
+ batch_size,
393
+ self.d_model * self.expand,
394
+ self.d_state,
395
+ device=self.dt_proj.weight.device,
396
+ dtype=self.dt_proj.weight.dtype,
397
+ # dtype=torch.float32,
398
+ )
399
+ inference_params.key_value_memory_dict[self.layer_idx] = (conv_state, ssm_state)
400
+ else:
401
+ conv_state, ssm_state = inference_params.key_value_memory_dict[self.layer_idx]
402
+ # TODO: What if batch size changes between generation, and we reuse the same states?
403
+ if initialize_states:
404
+ conv_state.zero_()
405
+ ssm_state.zero_()
406
+ return conv_state, ssm_state
407
+
408
+
409
+ class Block(nn.Module):
410
+ def __init__(
411
+ self, dim, mixer_cls, norm_cls=nn.LayerNorm, fused_add_norm=False, residual_in_fp32=False
412
+ ):
413
+ """
414
+ Simple block wrapping a mixer class with LayerNorm/RMSNorm and residual connection"
415
+
416
+ This Block has a slightly different structure compared to a regular
417
+ prenorm Transformer block.
418
+ The standard block is: LN -> MHA/MLP -> Add.
419
+ [Ref: https://arxiv.org/abs/2002.04745]
420
+ Here we have: Add -> LN -> Mixer, returning both
421
+ the hidden_states (output of the mixer) and the residual.
422
+ This is purely for performance reasons, as we can fuse add and LayerNorm.
423
+ The residual needs to be provided (except for the very first block).
424
+ """
425
+ super().__init__()
426
+ self.residual_in_fp32 = residual_in_fp32
427
+ self.fused_add_norm = fused_add_norm
428
+ self.mixer = mixer_cls(dim)
429
+ self.norm = norm_cls(dim)
430
+ if self.fused_add_norm:
431
+ assert RMSNorm is not None, "RMSNorm import fails"
432
+ assert isinstance(
433
+ self.norm, (nn.LayerNorm, RMSNorm)
434
+ ), "Only LayerNorm and RMSNorm are supported for fused_add_norm"
435
+
436
+ def forward(
437
+ self, hidden_states: Tensor, residual: Optional[Tensor] = None, inference_params=None
438
+ ):
439
+ r"""Pass the input through the encoder layer.
440
+
441
+ Args:
442
+ hidden_states: the sequence to the encoder layer (required).
443
+ residual: hidden_states = Mixer(LN(residual))
444
+ """
445
+ if not self.fused_add_norm:
446
+ residual = (hidden_states + residual) if residual is not None else hidden_states
447
+ hidden_states = self.norm(residual.to(dtype=self.norm.weight.dtype))
448
+ if self.residual_in_fp32:
449
+ residual = residual.to(torch.float32)
450
+ else:
451
+ fused_add_norm_fn = rms_norm_fn if isinstance(self.norm, RMSNorm) else layer_norm_fn
452
+ hidden_states, residual = fused_add_norm_fn(
453
+ hidden_states,
454
+ self.norm.weight,
455
+ self.norm.bias,
456
+ residual=residual,
457
+ prenorm=True,
458
+ residual_in_fp32=self.residual_in_fp32,
459
+ eps=self.norm.eps,
460
+ )
461
+ hidden_states = self.mixer(hidden_states, inference_params=inference_params)
462
+ return hidden_states, residual
463
+
464
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
465
+ return self.mixer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
modules/mamba/mamba_blocks.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Copied and modified from
3
+ https://github.com/state-spaces/mamba/blob/main/mamba_ssm/models/mixer_seq_simple.py
4
+ '''
5
+
6
+ import math
7
+ import torch
8
+ import torch.nn as nn
9
+
10
+ from functools import partial
11
+
12
+ from mamba_ssm import Mamba
13
+ from modules.mamba.bimamba import Mamba as BiMamba
14
+ from modules.mamba.bimamba import Block as PreNormBlock
15
+
16
+ try:
17
+ from mamba_ssm.ops.triton.layernorm import RMSNorm, layer_norm_fn, rms_norm_fn
18
+ except ImportError:
19
+ RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
20
+
21
+
22
+ def create_block(
23
+ d_model,
24
+ ssm_cls=None,
25
+ ssm_cfg=None,
26
+ norm_epsilon=1e-5,
27
+ rms_norm=False,
28
+ residual_in_fp32=False,
29
+ fused_add_norm=True,
30
+ layer_idx=None,
31
+ device=None,
32
+ dtype=None,
33
+ ):
34
+ if ssm_cfg is None:
35
+ ssm_cfg = {}
36
+ factory_kwargs = {"device": device, "dtype": dtype}
37
+ mixer_cls = partial(ssm_cls, layer_idx=layer_idx, **ssm_cfg, **factory_kwargs)
38
+ norm_cls = partial(
39
+ nn.LayerNorm if not rms_norm else RMSNorm, eps=norm_epsilon, **factory_kwargs
40
+ )
41
+ block = PreNormBlock(
42
+ d_model,
43
+ mixer_cls,
44
+ norm_cls=norm_cls,
45
+ fused_add_norm=fused_add_norm,
46
+ residual_in_fp32=residual_in_fp32,
47
+ )
48
+ block.layer_idx = layer_idx
49
+ return block
50
+
51
+
52
+ # https://github.com/huggingface/transformers/blob/c28d04e9e252a1a099944e325685f14d242ecdcd/src/transformers/models/gpt2/modeling_gpt2.py#L454
53
+ def _init_weights(
54
+ module,
55
+ n_layer,
56
+ initializer_range=0.02, # Now only used for embedding layer.
57
+ rescale_prenorm_residual=True,
58
+ n_residuals_per_layer=1, # Change to 2 if we have MLP
59
+ ):
60
+ if isinstance(module, nn.Linear):
61
+ if module.bias is not None:
62
+ if not getattr(module.bias, "_no_reinit", False):
63
+ nn.init.zeros_(module.bias)
64
+ elif isinstance(module, nn.Embedding):
65
+ nn.init.normal_(module.weight, std=initializer_range)
66
+
67
+ if rescale_prenorm_residual:
68
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
69
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
70
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
71
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
72
+ #
73
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
74
+ for name, p in module.named_parameters():
75
+ if name in ["out_proj.weight", "fc2.weight"]:
76
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
77
+ # Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
78
+ # We need to reinit p since this code could be called multiple times
79
+ # Having just p *= scale would repeatedly scale it down
80
+ nn.init.kaiming_uniform_(p, a=math.sqrt(5))
81
+ with torch.no_grad():
82
+ p /= math.sqrt(n_residuals_per_layer * n_layer)
83
+
84
+
85
+ class LnMambaAdd(nn.Module):
86
+
87
+ def __init__(self,
88
+ d_model,
89
+ ssm_cls,
90
+ ssm_cfg,
91
+ rms_norm=False,
92
+ layer_idx=None
93
+ ):
94
+ super().__init__()
95
+ if rms_norm:
96
+ self.norm = RMSNorm(d_model)
97
+ else:
98
+ self.norm = nn.LayerNorm(d_model)
99
+ self.mamba = ssm_cls(d_model=d_model, **ssm_cfg)
100
+
101
+ print(type(self.mamba))
102
+
103
+ print('Created LnMambaAdd.')
104
+
105
+ def forward(self, x, residual=None, inference_params=None):
106
+ if residual != None:
107
+ x = x + residual
108
+ return self.mamba(self.norm(x)), x
109
+
110
+
111
+ class MambaBlocksSequential(nn.Module):
112
+ """
113
+ A wrapper for the Mamba block to replicate it
114
+
115
+ Arguments
116
+ ---------
117
+ n_mamba : int
118
+ Number of Mamba blocks
119
+ d_model : int
120
+ Input dimension to Mamba (bottleneck dimension).
121
+ d_state : int
122
+ Mamba state dimension
123
+ expand: int
124
+ First linear projection d_model -> d_model * expand
125
+ d_conv: int
126
+ Kernel size of Mamba conv
127
+ norm type : str
128
+ The type of normalization, in ['gLN', 'cLN'].
129
+ ---------
130
+ """
131
+
132
+ def __init__(self,
133
+ n_mamba: int,
134
+ bidirectional: bool,
135
+ d_model: int, # bottleneck dimension (B)
136
+ d_state: int = 16,
137
+ expand: int = 2,
138
+ d_conv: int = 4, # kernel_size of 'Conv' in Mamba
139
+ dt_rank: str="auto",
140
+ conv_bias: bool = True,
141
+ bias: bool = False,
142
+ fused_add_norm: bool = True,
143
+ rms_norm: bool = False,
144
+ norm_epsilon: float = 1e-5,
145
+ initializer_cfg=None,
146
+ residual_in_fp32=False,
147
+ use_simple_block=False
148
+ ):
149
+ super().__init__()
150
+ self.residual_in_fp32 = residual_in_fp32
151
+ self.bidirectional = bidirectional
152
+
153
+ # We change the order of residual and layer norm:
154
+ # Instead of LN -> Attn / MLP -> Add, we do:
155
+ # Add -> LN -> Attn / MLP / Mixer, returning both the residual branch (output of Add) and
156
+ # the main branch (output of MLP / Mixer). The model definition is unchanged.
157
+ # This is for performance reason: we can fuse add + layer_norm.
158
+ self.fused_add_norm = fused_add_norm
159
+ if self.fused_add_norm:
160
+ if layer_norm_fn is None or rms_norm_fn is None:
161
+ raise ImportError("Failed to import Triton LayerNorm / RMSNorm kernels")
162
+
163
+ self.use_simple_block = use_simple_block
164
+
165
+ ssm_cfg = {
166
+ "d_state": d_state,
167
+ "expand": expand,
168
+ "d_conv": d_conv,
169
+ "dt_rank": dt_rank,
170
+ "conv_bias": conv_bias,
171
+ "bias": bias
172
+ }
173
+ if bidirectional:
174
+ ssm_cfg["bimamba_type"] = "v2"
175
+
176
+ if use_simple_block:
177
+ self.layers = nn.Sequential(
178
+ *[
179
+ LnMambaAdd(
180
+ d_model=d_model,
181
+ ssm_cls=BiMamba if bidirectional else Mamba,
182
+ ssm_cfg=ssm_cfg,
183
+ rms_norm=rms_norm,
184
+ layer_idx=i
185
+ )
186
+ for i in range(n_mamba)
187
+ ]
188
+ )
189
+ else:
190
+ self.layers = nn.Sequential(
191
+ *[
192
+ create_block(
193
+ d_model=d_model,
194
+ ssm_cls=BiMamba if bidirectional else Mamba,
195
+ ssm_cfg=ssm_cfg,
196
+ norm_epsilon=norm_epsilon,
197
+ rms_norm=rms_norm,
198
+ residual_in_fp32=residual_in_fp32,
199
+ fused_add_norm=fused_add_norm,
200
+ layer_idx=i,
201
+ )
202
+ for i in range(n_mamba)
203
+ ]
204
+ )
205
+
206
+ self.norm_f = (nn.LayerNorm if not rms_norm else RMSNorm)(
207
+ d_model, eps=norm_epsilon
208
+ )
209
+
210
+ self.apply(
211
+ partial(
212
+ _init_weights,
213
+ n_layer=n_mamba,
214
+ **(initializer_cfg if initializer_cfg is not None else {}),
215
+ )
216
+ )
217
+
218
+
219
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
220
+ return {
221
+ i: block.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
222
+ for i, layer in enumerate(self.layers)
223
+ }
224
+
225
+ def forward(self, x, inference_params=None):
226
+
227
+ hidden_states = x
228
+ residual = None
229
+ for i, layer in enumerate(self.layers):
230
+ hidden_states, residual = layer(
231
+ hidden_states, residual, inference_params=inference_params
232
+ )
233
+
234
+ if not self.fused_add_norm:
235
+ residual = (hidden_states + residual) if residual is not None else hidden_states
236
+ hidden_states = self.norm_f(residual.to(dtype=self.norm_f.weight.dtype))
237
+ else:
238
+ # Set prenorm=False here since we don't need the residual
239
+ fused_add_norm_fn = rms_norm_fn if isinstance(self.norm_f, RMSNorm) else layer_norm_fn
240
+
241
+ hidden_states = fused_add_norm_fn(
242
+ hidden_states,
243
+ self.norm_f.weight,
244
+ self.norm_f.bias,
245
+ eps=self.norm_f.eps,
246
+ residual=residual,
247
+ prenorm=False,
248
+ residual_in_fp32=self.residual_in_fp32,
249
+ )
250
+
251
+ return hidden_states
252
+
modules/mamba/selective_scan_interface.py ADDED
@@ -0,0 +1,714 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Copied from
3
+ https://github.com/hustvl/Vim/blob/main/mamba-1p1p1/mamba_ssm/ops/selective_scan_interface.py
4
+ '''
5
+
6
+ # Copyright (c) 2023, Tri Dao, Albert Gu.
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from torch.cuda.amp import custom_bwd, custom_fwd
11
+
12
+ from einops import rearrange, repeat
13
+
14
+ from causal_conv1d import causal_conv1d_fn
15
+ import causal_conv1d_cuda
16
+ import selective_scan_cuda
17
+
18
+
19
+ class SelectiveScanFn(torch.autograd.Function):
20
+
21
+ @staticmethod
22
+ def forward(ctx, u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
23
+ return_last_state=False):
24
+ if u.stride(-1) != 1:
25
+ u = u.contiguous()
26
+ if delta.stride(-1) != 1:
27
+ delta = delta.contiguous()
28
+ if D is not None:
29
+ D = D.contiguous()
30
+ if B.stride(-1) != 1:
31
+ B = B.contiguous()
32
+ if C.stride(-1) != 1:
33
+ C = C.contiguous()
34
+ if z is not None and z.stride(-1) != 1:
35
+ z = z.contiguous()
36
+ if B.dim() == 3:
37
+ B = rearrange(B, "b dstate l -> b 1 dstate l")
38
+ ctx.squeeze_B = True
39
+ if C.dim() == 3:
40
+ C = rearrange(C, "b dstate l -> b 1 dstate l")
41
+ ctx.squeeze_C = True
42
+ out, x, *rest = selective_scan_cuda.fwd(u, delta, A, B, C, D, z, delta_bias, delta_softplus)
43
+ ctx.delta_softplus = delta_softplus
44
+ ctx.has_z = z is not None
45
+ last_state = x[:, :, -1, 1::2] # (batch, dim, dstate)
46
+ if not ctx.has_z:
47
+ ctx.save_for_backward(u, delta, A, B, C, D, delta_bias, x)
48
+ return out if not return_last_state else (out, last_state)
49
+ else:
50
+ ctx.save_for_backward(u, delta, A, B, C, D, z, delta_bias, x, out)
51
+ out_z = rest[0]
52
+ return out_z if not return_last_state else (out_z, last_state)
53
+
54
+ @staticmethod
55
+ def backward(ctx, dout, *args):
56
+ if not ctx.has_z:
57
+ u, delta, A, B, C, D, delta_bias, x = ctx.saved_tensors
58
+ z = None
59
+ out = None
60
+ else:
61
+ u, delta, A, B, C, D, z, delta_bias, x, out = ctx.saved_tensors
62
+ if dout.stride(-1) != 1:
63
+ dout = dout.contiguous()
64
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
65
+ # backward of selective_scan_cuda with the backward of chunk).
66
+ # Here we just pass in None and dz will be allocated in the C++ code.
67
+ du, ddelta, dA, dB, dC, dD, ddelta_bias, *rest = selective_scan_cuda.bwd(
68
+ u, delta, A, B, C, D, z, delta_bias, dout, x, out, None, ctx.delta_softplus,
69
+ False # option to recompute out_z, not used here
70
+ )
71
+ dz = rest[0] if ctx.has_z else None
72
+ dB = dB.squeeze(1) if getattr(ctx, "squeeze_B", False) else dB
73
+ dC = dC.squeeze(1) if getattr(ctx, "squeeze_C", False) else dC
74
+ return (du, ddelta, dA, dB, dC,
75
+ dD if D is not None else None,
76
+ dz,
77
+ ddelta_bias if delta_bias is not None else None,
78
+ None,
79
+ None)
80
+
81
+
82
+ def selective_scan_fn(u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
83
+ return_last_state=False):
84
+ """if return_last_state is True, returns (out, last_state)
85
+ last_state has shape (batch, dim, dstate). Note that the gradient of the last state is
86
+ not considered in the backward pass.
87
+ """
88
+ return SelectiveScanFn.apply(u, delta, A, B, C, D, z, delta_bias, delta_softplus, return_last_state)
89
+
90
+
91
+ def selective_scan_ref(u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
92
+ return_last_state=False):
93
+ """
94
+ u: r(B D L)
95
+ delta: r(B D L)
96
+ A: c(D N) or r(D N)
97
+ B: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L)
98
+ C: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L)
99
+ D: r(D)
100
+ z: r(B D L)
101
+ delta_bias: r(D), fp32
102
+
103
+ out: r(B D L)
104
+ last_state (optional): r(B D dstate) or c(B D dstate)
105
+ """
106
+ dtype_in = u.dtype
107
+ u = u.float()
108
+ delta = delta.float()
109
+ if delta_bias is not None:
110
+ delta = delta + delta_bias[..., None].float()
111
+ if delta_softplus:
112
+ delta = F.softplus(delta)
113
+ batch, dim, dstate = u.shape[0], A.shape[0], A.shape[1]
114
+ is_variable_B = B.dim() >= 3
115
+ is_variable_C = C.dim() >= 3
116
+ if A.is_complex():
117
+ if is_variable_B:
118
+ B = torch.view_as_complex(rearrange(B.float(), "... (L two) -> ... L two", two=2))
119
+ if is_variable_C:
120
+ C = torch.view_as_complex(rearrange(C.float(), "... (L two) -> ... L two", two=2))
121
+ else:
122
+ B = B.float()
123
+ C = C.float()
124
+ x = A.new_zeros((batch, dim, dstate))
125
+ ys = []
126
+ deltaA = torch.exp(torch.einsum('bdl,dn->bdln', delta, A))
127
+ if not is_variable_B:
128
+ deltaB_u = torch.einsum('bdl,dn,bdl->bdln', delta, B, u)
129
+ else:
130
+ if B.dim() == 3:
131
+ deltaB_u = torch.einsum('bdl,bnl,bdl->bdln', delta, B, u)
132
+ else:
133
+ B = repeat(B, "B G N L -> B (G H) N L", H=dim // B.shape[1])
134
+ deltaB_u = torch.einsum('bdl,bdnl,bdl->bdln', delta, B, u)
135
+ if is_variable_C and C.dim() == 4:
136
+ C = repeat(C, "B G N L -> B (G H) N L", H=dim // C.shape[1])
137
+ last_state = None
138
+ for i in range(u.shape[2]):
139
+ x = deltaA[:, :, i] * x + deltaB_u[:, :, i]
140
+ if not is_variable_C:
141
+ y = torch.einsum('bdn,dn->bd', x, C)
142
+ else:
143
+ if C.dim() == 3:
144
+ y = torch.einsum('bdn,bn->bd', x, C[:, :, i])
145
+ else:
146
+ y = torch.einsum('bdn,bdn->bd', x, C[:, :, :, i])
147
+ if i == u.shape[2] - 1:
148
+ last_state = x
149
+ if y.is_complex():
150
+ y = y.real * 2
151
+ ys.append(y)
152
+ y = torch.stack(ys, dim=2) # (batch dim L)
153
+ out = y if D is None else y + u * rearrange(D, "d -> d 1")
154
+ if z is not None:
155
+ out = out * F.silu(z)
156
+ out = out.to(dtype=dtype_in)
157
+ return out if not return_last_state else (out, last_state)
158
+
159
+
160
+ class MambaInnerFnNoOutProj(torch.autograd.Function):
161
+
162
+ @staticmethod
163
+ @custom_fwd
164
+ def forward(ctx, xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
165
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
166
+ C_proj_bias=None, delta_softplus=True, checkpoint_lvl=1):
167
+ """
168
+ xz: (batch, dim, seqlen)
169
+ """
170
+ assert checkpoint_lvl in [0, 1]
171
+ L = xz.shape[-1]
172
+ delta_rank = delta_proj_weight.shape[1]
173
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
174
+ if torch.is_autocast_enabled():
175
+ x_proj_weight = x_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
176
+ delta_proj_weight = delta_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
177
+ if xz.stride(-1) != 1:
178
+ xz = xz.contiguous()
179
+ conv1d_weight = rearrange(conv1d_weight, "d 1 w -> d w")
180
+ x, z = xz.chunk(2, dim=1)
181
+ conv1d_bias = conv1d_bias.contiguous() if conv1d_bias is not None else None
182
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias,None, True)
183
+ # We're being very careful here about the layout, to avoid extra transposes.
184
+ # We want delta to have d as the slowest moving dimension
185
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
186
+ x_dbl = F.linear(rearrange(conv1d_out, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
187
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l = L)
188
+ ctx.is_variable_B = B is None
189
+ ctx.is_variable_C = C is None
190
+ ctx.B_proj_bias_is_None = B_proj_bias is None
191
+ ctx.C_proj_bias_is_None = C_proj_bias is None
192
+ if B is None: # variable B
193
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl dstate)
194
+ if B_proj_bias is not None:
195
+ B = B + B_proj_bias.to(dtype=B.dtype)
196
+ if not A.is_complex():
197
+ # B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
198
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
199
+ else:
200
+ B = rearrange(B, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
201
+ else:
202
+ if B.stride(-1) != 1:
203
+ B = B.contiguous()
204
+ if C is None: # variable C
205
+ C = x_dbl[:, -d_state:] # (bl dstate)
206
+ if C_proj_bias is not None:
207
+ C = C + C_proj_bias.to(dtype=C.dtype)
208
+ if not A.is_complex():
209
+ # C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
210
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
211
+ else:
212
+ C = rearrange(C, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
213
+ else:
214
+ if C.stride(-1) != 1:
215
+ C = C.contiguous()
216
+ if D is not None:
217
+ D = D.contiguous()
218
+ out, scan_intermediates, out_z = selective_scan_cuda.fwd(
219
+ conv1d_out, delta, A, B, C, D, z, delta_bias, delta_softplus
220
+ )
221
+ ctx.delta_softplus = delta_softplus
222
+ ctx.checkpoint_lvl = checkpoint_lvl
223
+ if checkpoint_lvl >= 1: # Will recompute conv1d_out and delta in the backward pass
224
+ conv1d_out, delta = None, None
225
+ ctx.save_for_backward(xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight,
226
+ delta_proj_weight, conv1d_out, delta,
227
+ A, B, C, D, delta_bias, scan_intermediates, out)
228
+ # return rearrange(out_z, "b d l -> b l d")
229
+ return out_z
230
+
231
+ @staticmethod
232
+ @custom_bwd
233
+ def backward(ctx, dout):
234
+ # dout: (batch, seqlen, dim)
235
+ (xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight, delta_proj_weight,
236
+ conv1d_out, delta, A, B, C, D, delta_bias, scan_intermediates, out) = ctx.saved_tensors
237
+ L = xz.shape[-1]
238
+ delta_rank = delta_proj_weight.shape[1]
239
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
240
+ x, z = xz.chunk(2, dim=1)
241
+ if dout.stride(-1) != 1:
242
+ dout = dout.contiguous()
243
+ if ctx.checkpoint_lvl == 1:
244
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias,None, True)
245
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(),
246
+ "d (b l) -> b d l", l = L)
247
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
248
+ # backward of selective_scan_cuda with the backward of chunk).
249
+ dxz = torch.empty_like(xz) # (batch, dim, seqlen)
250
+ dx, dz = dxz.chunk(2, dim=1)
251
+ # dout_y = rearrange(dout, "b l d -> b d l") # because no arrange at end of forward, so dout shape is b d l
252
+ dconv1d_out, ddelta, dA, dB, dC, dD, ddelta_bias, dz, out_z = selective_scan_cuda.bwd(
253
+ conv1d_out, delta, A, B, C, D, z, delta_bias, dout, scan_intermediates, out, dz,
254
+ ctx.delta_softplus,
255
+ True # option to recompute out_z
256
+ )
257
+ dD = dD if D is not None else None
258
+ dx_dbl = torch.empty_like(x_dbl)
259
+ dB_proj_bias = None
260
+ if ctx.is_variable_B:
261
+ if not A.is_complex():
262
+ dB = rearrange(dB, "b 1 dstate l -> (b l) dstate").contiguous()
263
+ else:
264
+ dB = rearrange(dB, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
265
+ dB_proj_bias = dB.sum(0) if not ctx.B_proj_bias_is_None else None
266
+ dx_dbl[:, delta_rank:delta_rank + d_state] = dB # (bl d)
267
+ dB = None
268
+ dC_proj_bias = None
269
+ if ctx.is_variable_C:
270
+ if not A.is_complex():
271
+ dC = rearrange(dC, "b 1 dstate l -> (b l) dstate").contiguous()
272
+ else:
273
+ dC = rearrange(dC, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
274
+ dC_proj_bias = dC.sum(0) if not ctx.C_proj_bias_is_None else None
275
+ dx_dbl[:, -d_state:] = dC # (bl d)
276
+ dC = None
277
+ ddelta = rearrange(ddelta, "b d l -> d (b l)")
278
+ ddelta_proj_weight = torch.einsum("dB,Br->dr", ddelta, x_dbl[:, :delta_rank])
279
+ dx_dbl[:, :delta_rank] = torch.einsum("dB,dr->Br", ddelta, delta_proj_weight)
280
+ dconv1d_out = rearrange(dconv1d_out, "b d l -> d (b l)")
281
+ dx_proj_weight = torch.einsum("Br,Bd->rd", dx_dbl, rearrange(conv1d_out, "b d l -> (b l) d"))
282
+ dconv1d_out = torch.addmm(dconv1d_out, x_proj_weight.t(), dx_dbl.t(), out=dconv1d_out)
283
+ dconv1d_out = rearrange(dconv1d_out, "d (b l) -> b d l", b=x.shape[0], l=x.shape[-1])
284
+ # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
285
+ # backward of conv1d with the backward of chunk).
286
+ dx, dconv1d_weight, dconv1d_bias = causal_conv1d_cuda.causal_conv1d_bwd(
287
+ x, conv1d_weight, conv1d_bias, dconv1d_out, None, dx, True
288
+ )
289
+ dconv1d_bias = dconv1d_bias if conv1d_bias is not None else None
290
+ dconv1d_weight = rearrange(dconv1d_weight, "d w -> d 1 w")
291
+ return (dxz, dconv1d_weight, dconv1d_bias, dx_proj_weight, ddelta_proj_weight,
292
+ dA, dB, dC, dD,
293
+ ddelta_bias if delta_bias is not None else None,
294
+ dB_proj_bias, dC_proj_bias, None)
295
+
296
+
297
+ class MambaInnerFn(torch.autograd.Function):
298
+
299
+ @staticmethod
300
+ @custom_fwd
301
+ def forward(ctx, xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
302
+ out_proj_weight, out_proj_bias,
303
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
304
+ C_proj_bias=None, delta_softplus=True, checkpoint_lvl=1):
305
+ """
306
+ xz: (batch, dim, seqlen)
307
+ """
308
+ assert checkpoint_lvl in [0, 1]
309
+ L = xz.shape[-1]
310
+ delta_rank = delta_proj_weight.shape[1]
311
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
312
+ if torch.is_autocast_enabled():
313
+ x_proj_weight = x_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
314
+ delta_proj_weight = delta_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
315
+ out_proj_weight = out_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
316
+ out_proj_bias = (out_proj_bias.to(dtype=torch.get_autocast_gpu_dtype())
317
+ if out_proj_bias is not None else None)
318
+ if xz.stride(-1) != 1:
319
+ xz = xz.contiguous()
320
+ conv1d_weight = rearrange(conv1d_weight, "d 1 w -> d w")
321
+ x, z = xz.chunk(2, dim=1)
322
+ conv1d_bias = conv1d_bias.contiguous() if conv1d_bias is not None else None
323
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias, None, True)
324
+ # We're being very careful here about the layout, to avoid extra transposes.
325
+ # We want delta to have d as the slowest moving dimension
326
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
327
+ x_dbl = F.linear(rearrange(conv1d_out, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
328
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l = L)
329
+ ctx.is_variable_B = B is None
330
+ ctx.is_variable_C = C is None
331
+ ctx.B_proj_bias_is_None = B_proj_bias is None
332
+ ctx.C_proj_bias_is_None = C_proj_bias is None
333
+ if B is None: # variable B
334
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl dstate)
335
+ if B_proj_bias is not None:
336
+ B = B + B_proj_bias.to(dtype=B.dtype)
337
+ if not A.is_complex():
338
+ # B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
339
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
340
+ else:
341
+ B = rearrange(B, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
342
+ else:
343
+ if B.stride(-1) != 1:
344
+ B = B.contiguous()
345
+ if C is None: # variable C
346
+ C = x_dbl[:, -d_state:] # (bl dstate)
347
+ if C_proj_bias is not None:
348
+ C = C + C_proj_bias.to(dtype=C.dtype)
349
+ if not A.is_complex():
350
+ # C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
351
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
352
+ else:
353
+ C = rearrange(C, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
354
+ else:
355
+ if C.stride(-1) != 1:
356
+ C = C.contiguous()
357
+ if D is not None:
358
+ D = D.contiguous()
359
+ out, scan_intermediates, out_z = selective_scan_cuda.fwd(
360
+ conv1d_out, delta, A, B, C, D, z, delta_bias, delta_softplus
361
+ )
362
+ ctx.delta_softplus = delta_softplus
363
+ ctx.out_proj_bias_is_None = out_proj_bias is None
364
+ ctx.checkpoint_lvl = checkpoint_lvl
365
+ if checkpoint_lvl >= 1: # Will recompute conv1d_out and delta in the backward pass
366
+ conv1d_out, delta = None, None
367
+ ctx.save_for_backward(xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight,
368
+ delta_proj_weight, out_proj_weight, conv1d_out, delta,
369
+ A, B, C, D, delta_bias, scan_intermediates, out)
370
+ return F.linear(rearrange(out_z, "b d l -> b l d"), out_proj_weight, out_proj_bias)
371
+
372
+ @staticmethod
373
+ @custom_bwd
374
+ def backward(ctx, dout):
375
+ # dout: (batch, seqlen, dim)
376
+ (xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight, delta_proj_weight, out_proj_weight,
377
+ conv1d_out, delta, A, B, C, D, delta_bias, scan_intermediates, out) = ctx.saved_tensors
378
+ L = xz.shape[-1]
379
+ delta_rank = delta_proj_weight.shape[1]
380
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
381
+ x, z = xz.chunk(2, dim=1)
382
+ if dout.stride(-1) != 1:
383
+ dout = dout.contiguous()
384
+ if ctx.checkpoint_lvl == 1:
385
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias, None, True)
386
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(),
387
+ "d (b l) -> b d l", l = L)
388
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
389
+ # backward of selective_scan_cuda with the backward of chunk).
390
+ dxz = torch.empty_like(xz) # (batch, dim, seqlen)
391
+ dx, dz = dxz.chunk(2, dim=1)
392
+ dout = rearrange(dout, "b l e -> e (b l)")
393
+ dout_y = rearrange(out_proj_weight.t() @ dout, "d (b l) -> b d l", l=L)
394
+ dconv1d_out, ddelta, dA, dB, dC, dD, ddelta_bias, dz, out_z = selective_scan_cuda.bwd(
395
+ conv1d_out, delta, A, B, C, D, z, delta_bias, dout_y, scan_intermediates, out, dz,
396
+ ctx.delta_softplus,
397
+ True # option to recompute out_z
398
+ )
399
+ dout_proj_weight = torch.einsum("eB,dB->ed", dout, rearrange(out_z, "b d l -> d (b l)"))
400
+ dout_proj_bias = dout.sum(dim=(0, 1)) if not ctx.out_proj_bias_is_None else None
401
+ dD = dD if D is not None else None
402
+ dx_dbl = torch.empty_like(x_dbl)
403
+ dB_proj_bias = None
404
+ if ctx.is_variable_B:
405
+ if not A.is_complex():
406
+ dB = rearrange(dB, "b 1 dstate l -> (b l) dstate").contiguous()
407
+ else:
408
+ dB = rearrange(dB, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
409
+ dB_proj_bias = dB.sum(0) if not ctx.B_proj_bias_is_None else None
410
+ dx_dbl[:, delta_rank:delta_rank + d_state] = dB # (bl d)
411
+ dB = None
412
+ dC_proj_bias = None
413
+ if ctx.is_variable_C:
414
+ if not A.is_complex():
415
+ dC = rearrange(dC, "b 1 dstate l -> (b l) dstate").contiguous()
416
+ else:
417
+ dC = rearrange(dC, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
418
+ dC_proj_bias = dC.sum(0) if not ctx.C_proj_bias_is_None else None
419
+ dx_dbl[:, -d_state:] = dC # (bl d)
420
+ dC = None
421
+ ddelta = rearrange(ddelta, "b d l -> d (b l)")
422
+ ddelta_proj_weight = torch.einsum("dB,Br->dr", ddelta, x_dbl[:, :delta_rank])
423
+ dx_dbl[:, :delta_rank] = torch.einsum("dB,dr->Br", ddelta, delta_proj_weight)
424
+ dconv1d_out = rearrange(dconv1d_out, "b d l -> d (b l)")
425
+ dx_proj_weight = torch.einsum("Br,Bd->rd", dx_dbl, rearrange(conv1d_out, "b d l -> (b l) d"))
426
+ dconv1d_out = torch.addmm(dconv1d_out, x_proj_weight.t(), dx_dbl.t(), out=dconv1d_out)
427
+ dconv1d_out = rearrange(dconv1d_out, "d (b l) -> b d l", b=x.shape[0], l=x.shape[-1])
428
+ # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
429
+ # backward of conv1d with the backward of chunk).
430
+ dx, dconv1d_weight, dconv1d_bias = causal_conv1d_cuda.causal_conv1d_bwd(
431
+ x, conv1d_weight, conv1d_bias, dconv1d_out, None, dx, True
432
+ )
433
+ dconv1d_bias = dconv1d_bias if conv1d_bias is not None else None
434
+ dconv1d_weight = rearrange(dconv1d_weight, "d w -> d 1 w")
435
+ return (dxz, dconv1d_weight, dconv1d_bias, dx_proj_weight, ddelta_proj_weight,
436
+ dout_proj_weight, dout_proj_bias,
437
+ dA, dB, dC, dD,
438
+ ddelta_bias if delta_bias is not None else None,
439
+ dB_proj_bias, dC_proj_bias, None)
440
+
441
+
442
+ class BiMambaInnerFn(torch.autograd.Function):
443
+
444
+ @staticmethod
445
+ @custom_fwd
446
+ def forward(ctx, xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
447
+ out_proj_weight, out_proj_bias,
448
+ A, A_b, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
449
+ C_proj_bias=None, delta_softplus=True, checkpoint_lvl=1):
450
+ """
451
+ xz: (batch, dim, seqlen)
452
+ """
453
+ assert checkpoint_lvl in [0, 1]
454
+ L = xz.shape[-1]
455
+ delta_rank = delta_proj_weight.shape[1]
456
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
457
+ if torch.is_autocast_enabled():
458
+ x_proj_weight = x_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
459
+ delta_proj_weight = delta_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
460
+ out_proj_weight = out_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
461
+ out_proj_bias = (out_proj_bias.to(dtype=torch.get_autocast_gpu_dtype())
462
+ if out_proj_bias is not None else None)
463
+ if xz.stride(-1) != 1:
464
+ xz = xz.contiguous()
465
+ conv1d_weight = rearrange(conv1d_weight, "d 1 w -> d w")
466
+ x, z = xz.chunk(2, dim=1)
467
+ conv1d_bias = conv1d_bias.contiguous() if conv1d_bias is not None else None
468
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias,None, True)
469
+ # We're being very careful here about the layout, to avoid extra transposes.
470
+ # We want delta to have d as the slowest moving dimension
471
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
472
+ x_dbl = F.linear(rearrange(conv1d_out, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
473
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l = L)
474
+ ctx.is_variable_B = B is None
475
+ ctx.is_variable_C = C is None
476
+ ctx.B_proj_bias_is_None = B_proj_bias is None
477
+ ctx.C_proj_bias_is_None = C_proj_bias is None
478
+ if B is None: # variable B
479
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl dstate)
480
+ if B_proj_bias is not None:
481
+ B = B + B_proj_bias.to(dtype=B.dtype)
482
+ if not A.is_complex():
483
+ # B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
484
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
485
+ else:
486
+ B = rearrange(B, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
487
+ else:
488
+ if B.stride(-1) != 1:
489
+ B = B.contiguous()
490
+ if C is None: # variable C
491
+ C = x_dbl[:, -d_state:] # (bl dstate)
492
+ if C_proj_bias is not None:
493
+ C = C + C_proj_bias.to(dtype=C.dtype)
494
+ if not A.is_complex():
495
+ # C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
496
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
497
+ else:
498
+ C = rearrange(C, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
499
+ else:
500
+ if C.stride(-1) != 1:
501
+ C = C.contiguous()
502
+ if D is not None:
503
+ D = D.contiguous()
504
+ out_f, scan_intermediates_f, out_z_f = selective_scan_cuda.fwd(
505
+ conv1d_out, delta, A, B, C, D, z, delta_bias, delta_softplus
506
+ )
507
+ assert not A_b.is_complex(), "A should not be complex!!"
508
+ out_b, scan_intermediates_b, out_z_b = selective_scan_cuda.fwd(
509
+ conv1d_out.flip([-1]), delta.flip([-1]), A_b, B.flip([-1]), C.flip([-1]), D, z.flip([-1]), delta_bias, delta_softplus,
510
+ )
511
+
512
+ out_z = out_z_f + out_z_b.flip([-1])
513
+
514
+ ctx.delta_softplus = delta_softplus
515
+ ctx.out_proj_bias_is_None = out_proj_bias is None
516
+ ctx.checkpoint_lvl = checkpoint_lvl
517
+ if checkpoint_lvl >= 1: # Will recompute conv1d_out and delta in the backward pass
518
+ conv1d_out, delta = None, None
519
+ ctx.save_for_backward(xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight,
520
+ delta_proj_weight, out_proj_weight, conv1d_out, delta,
521
+ A, A_b, B, C, D, delta_bias, scan_intermediates_f, scan_intermediates_b, out_f, out_b)
522
+ return F.linear(rearrange(out_z, "b d l -> b l d"), out_proj_weight, out_proj_bias)
523
+
524
+ @staticmethod
525
+ @custom_bwd
526
+ def backward(ctx, dout):
527
+ # dout: (batch, seqlen, dim)
528
+ (xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight, delta_proj_weight, out_proj_weight,
529
+ conv1d_out, delta, A, A_b, B, C, D, delta_bias, scan_intermediates_f, scan_intermediates_b, out_f, out_b) = ctx.saved_tensors
530
+ L = xz.shape[-1]
531
+ delta_rank = delta_proj_weight.shape[1]
532
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
533
+ x, z = xz.chunk(2, dim=1)
534
+ if dout.stride(-1) != 1:
535
+ dout = dout.contiguous()
536
+ if ctx.checkpoint_lvl == 1:
537
+ conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias, None, True)
538
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(),
539
+ "d (b l) -> b d l", l = L)
540
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
541
+ # backward of selective_scan_cuda with the backward of chunk).
542
+ dxz = torch.empty_like(xz) # (batch, dim, seqlen)
543
+ dx, dz = dxz.chunk(2, dim=1)
544
+ dout = rearrange(dout, "b l e -> e (b l)")
545
+ dout_y = rearrange(out_proj_weight.t() @ dout, "d (b l) -> b d l", l=L)
546
+ dconv1d_out, ddelta, dA, dB, dC, dD, ddelta_bias, dz, out_z_f = selective_scan_cuda.bwd(
547
+ conv1d_out, delta, A, B, C, D, z, delta_bias, dout_y, scan_intermediates_f, out_f, dz,
548
+ ctx.delta_softplus,
549
+ True # option to recompute out_z
550
+ )
551
+ # flip one
552
+ dz_b = torch.empty_like(dz)
553
+ dconv1d_out_f_b, ddelta_f_b, dA_b, dB_f_b, dC_f_b, dD_b, ddelta_bias_b, dz_b, out_z_b = selective_scan_cuda.bwd(
554
+ conv1d_out.flip([-1]), delta.flip([-1]), A_b, B.flip([-1]), C.flip([-1]), D, z.flip([-1]), delta_bias, dout_y.flip([-1]), scan_intermediates_b, out_b, dz_b,
555
+ ctx.delta_softplus,
556
+ True # option to recompute out_z
557
+ )
558
+
559
+ dconv1d_out = dconv1d_out + dconv1d_out_f_b.flip([-1])
560
+ ddelta = ddelta + ddelta_f_b.flip([-1])
561
+ dB = dB + dB_f_b.flip([-1])
562
+ dC = dC + dC_f_b.flip([-1])
563
+ dD = dD + dD_b
564
+ ddelta_bias = ddelta_bias + ddelta_bias_b
565
+ dz = dz + dz_b.flip([-1])
566
+ out_z = out_z_f + out_z_b.flip([-1])
567
+
568
+ dout_proj_weight = torch.einsum("eB,dB->ed", dout, rearrange(out_z, "b d l -> d (b l)"))
569
+ dout_proj_bias = dout.sum(dim=(0, 1)) if not ctx.out_proj_bias_is_None else None
570
+ dD = dD if D is not None else None
571
+ dx_dbl = torch.empty_like(x_dbl)
572
+ dB_proj_bias = None
573
+ if ctx.is_variable_B:
574
+ if not A.is_complex():
575
+ dB = rearrange(dB, "b 1 dstate l -> (b l) dstate").contiguous()
576
+ else:
577
+ dB = rearrange(dB, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
578
+ dB_proj_bias = dB.sum(0) if not ctx.B_proj_bias_is_None else None
579
+ dx_dbl[:, delta_rank:delta_rank + d_state] = dB # (bl d)
580
+ dB = None
581
+ dC_proj_bias = None
582
+ if ctx.is_variable_C:
583
+ if not A.is_complex():
584
+ dC = rearrange(dC, "b 1 dstate l -> (b l) dstate").contiguous()
585
+ else:
586
+ dC = rearrange(dC, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
587
+ dC_proj_bias = dC.sum(0) if not ctx.C_proj_bias_is_None else None
588
+ dx_dbl[:, -d_state:] = dC # (bl d)
589
+ dC = None
590
+ ddelta = rearrange(ddelta, "b d l -> d (b l)")
591
+ ddelta_proj_weight = torch.einsum("dB,Br->dr", ddelta, x_dbl[:, :delta_rank])
592
+ dx_dbl[:, :delta_rank] = torch.einsum("dB,dr->Br", ddelta, delta_proj_weight)
593
+ dconv1d_out = rearrange(dconv1d_out, "b d l -> d (b l)")
594
+ dx_proj_weight = torch.einsum("Br,Bd->rd", dx_dbl, rearrange(conv1d_out, "b d l -> (b l) d"))
595
+ dconv1d_out = torch.addmm(dconv1d_out, x_proj_weight.t(), dx_dbl.t(), out=dconv1d_out)
596
+ dconv1d_out = rearrange(dconv1d_out, "d (b l) -> b d l", b=x.shape[0], l=x.shape[-1])
597
+ # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
598
+ # backward of conv1d with the backward of chunk).
599
+ dx, dconv1d_weight, dconv1d_bias = causal_conv1d_cuda.causal_conv1d_bwd(
600
+ x, conv1d_weight, conv1d_bias, dconv1d_out, None, dx, True
601
+ )
602
+ dconv1d_bias = dconv1d_bias if conv1d_bias is not None else None
603
+ dconv1d_weight = rearrange(dconv1d_weight, "d w -> d 1 w")
604
+ return (dxz, dconv1d_weight, dconv1d_bias, dx_proj_weight, ddelta_proj_weight,
605
+ dout_proj_weight, dout_proj_bias,
606
+ dA, dA_b, dB, dC, dD,
607
+ ddelta_bias if delta_bias is not None else None,
608
+ dB_proj_bias, dC_proj_bias, None)
609
+
610
+
611
+ def mamba_inner_fn(
612
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
613
+ out_proj_weight, out_proj_bias,
614
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
615
+ C_proj_bias=None, delta_softplus=True
616
+ ):
617
+ return MambaInnerFn.apply(xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
618
+ out_proj_weight, out_proj_bias,
619
+ A, B, C, D, delta_bias, B_proj_bias, C_proj_bias, delta_softplus)
620
+
621
+ def bimamba_inner_fn(
622
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
623
+ out_proj_weight, out_proj_bias,
624
+ A, A_b, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
625
+ C_proj_bias=None, delta_softplus=True
626
+ ):
627
+ return BiMambaInnerFn.apply(xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
628
+ out_proj_weight, out_proj_bias,
629
+ A, A_b, B, C, D, delta_bias, B_proj_bias, C_proj_bias, delta_softplus)
630
+
631
+
632
+ def mamba_inner_fn_no_out_proj(
633
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
634
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
635
+ C_proj_bias=None, delta_softplus=True
636
+ ):
637
+ return MambaInnerFnNoOutProj.apply(xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
638
+ A, B, C, D, delta_bias, B_proj_bias, C_proj_bias, delta_softplus)
639
+
640
+
641
+ def mamba_inner_ref(
642
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
643
+ out_proj_weight, out_proj_bias,
644
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
645
+ C_proj_bias=None, delta_softplus=True
646
+ ):
647
+ L = xz.shape[-1]
648
+ delta_rank = delta_proj_weight.shape[1]
649
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
650
+ x, z = xz.chunk(2, dim=1)
651
+ x = causal_conv1d_fn(x, rearrange(conv1d_weight, "d 1 w -> d w"), conv1d_bias, "silu")
652
+ # We're being very careful here about the layout, to avoid extra transposes.
653
+ # We want delta to have d as the slowest moving dimension
654
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
655
+ x_dbl = F.linear(rearrange(x, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
656
+ delta = delta_proj_weight @ x_dbl[:, :delta_rank].t()
657
+ delta = rearrange(delta, "d (b l) -> b d l", l=L)
658
+ if B is None: # variable B
659
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl d)
660
+ if B_proj_bias is not None:
661
+ B = B + B_proj_bias.to(dtype=B.dtype)
662
+ if not A.is_complex():
663
+ B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
664
+ else:
665
+ B = rearrange(B, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
666
+ if C is None: # variable B
667
+ C = x_dbl[:, -d_state:] # (bl d)
668
+ if C_proj_bias is not None:
669
+ C = C + C_proj_bias.to(dtype=C.dtype)
670
+ if not A.is_complex():
671
+ C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
672
+ else:
673
+ C = rearrange(C, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
674
+ y = selective_scan_fn(x, delta, A, B, C, D, z=z, delta_bias=delta_bias, delta_softplus=True)
675
+ return F.linear(rearrange(y, "b d l -> b l d"), out_proj_weight, out_proj_bias)
676
+
677
+
678
+ def bimamba_inner_ref(
679
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
680
+ out_proj_weight, out_proj_bias,
681
+ A, A_b, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
682
+ C_proj_bias=None, delta_softplus=True
683
+ ):
684
+ L = xz.shape[-1]
685
+ delta_rank = delta_proj_weight.shape[1]
686
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
687
+ x, z = xz.chunk(2, dim=1)
688
+ x = causal_conv1d_fn(x, rearrange(conv1d_weight, "d 1 w -> d w"), conv1d_bias, "silu")
689
+ # We're being very careful here about the layout, to avoid extra transposes.
690
+ # We want delta to have d as the slowest moving dimension
691
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
692
+ x_dbl = F.linear(rearrange(x, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
693
+ delta = delta_proj_weight @ x_dbl[:, :delta_rank].t()
694
+ delta = rearrange(delta, "d (b l) -> b d l", l=L)
695
+ if B is None: # variable B
696
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl d)
697
+ if B_proj_bias is not None:
698
+ B = B + B_proj_bias.to(dtype=B.dtype)
699
+ if not A.is_complex():
700
+ B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
701
+ else:
702
+ B = rearrange(B, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
703
+ if C is None: # variable B
704
+ C = x_dbl[:, -d_state:] # (bl d)
705
+ if C_proj_bias is not None:
706
+ C = C + C_proj_bias.to(dtype=C.dtype)
707
+ if not A.is_complex():
708
+ C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
709
+ else:
710
+ C = rearrange(C, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
711
+ y = selective_scan_fn(x, delta, A, B, C, D, z=z, delta_bias=delta_bias, delta_softplus=True)
712
+ y_b = selective_scan_fn(x.flip([-1]), delta.flip([-1]), A_b, B.flip([-1]), C.flip([-1]), D, z.flip([-1]), delta_bias, delta_softplus=True)
713
+ y = y + y_b.flip([-1])
714
+ return F.linear(rearrange(y, "b d l -> b l d"), out_proj_weight, out_proj_bias)
requirements.txt ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Tested with Python 3.10 and CUDA 11.8.
2
+ # The CUDA extensions at the end of this file must be built after PyTorch and
3
+ # the build tools are installed; the model card documents the install order.
4
+
5
+ --extra-index-url https://download.pytorch.org/whl/cu118
6
+
7
+ # PyTorch CUDA runtime
8
+ torch==2.0.1+cu118
9
+ torchaudio==2.0.2+cu118
10
+ triton==2.0.0
11
+
12
+ # Extension build requirements
13
+ setuptools==68.2.2
14
+ wheel==0.41.3
15
+ Cython==0.29.37
16
+ ninja==1.11.1.1
17
+ packaging==24.0
18
+ buildtools==1.0.6
19
+
20
+ # ConMamba and SpeechBrain runtime
21
+ numpy==1.26.4
22
+ scipy==1.15.3
23
+ soundfile==0.14.0
24
+ einops==0.8.1
25
+ sentencepiece==0.2.0
26
+ speechbrain==1.0.0
27
+ HyperPyYAML==1.2.2
28
+ ruamel.yaml==0.19.1
29
+ huggingface-hub==0.20.3
30
+ joblib==1.5.3
31
+ tqdm==4.70.0
32
+ transformers==4.35.2
33
+ tokenizers==0.15.2
34
+ safetensors==0.8.0
35
+
36
+ # CUDA extensions used by the bundled Mamba implementation
37
+ causal-conv1d @ git+https://github.com/Dao-AILab/causal-conv1d.git@89e4aa413ea99a079a39364b0a58f54239b4306c
38
+ mamba-ssm @ git+https://github.com/state-spaces/mamba.git@009bec5ee37f586844a3fc89c040a9c1a9d8badf
run_inference.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Transcribe an audio file with the local ConMamba-small-ca checkpoint."""
3
+
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import torch
9
+ import torchaudio
10
+ from hyperpyyaml import load_hyperpyyaml
11
+
12
+
13
+ MODEL_ROOT = Path(__file__).resolve().parent
14
+ CONFIG_PATH = MODEL_ROOT / "inference.yaml"
15
+
16
+
17
+ class ConMambaASR:
18
+ """Standalone inference wrapper for ConMamba-small-ca."""
19
+
20
+ def __init__(self, device: str = "cuda"):
21
+ self.device = torch.device(device)
22
+ if self.device.type == "cuda" and not torch.cuda.is_available():
23
+ raise RuntimeError(
24
+ "ConMamba-small-ca requires a CUDA-capable GPU for the "
25
+ "default device, but CUDA is not available."
26
+ )
27
+
28
+ self._validate_repository()
29
+ self._make_local_modules_importable()
30
+ hparams = self._load_hparams()
31
+
32
+ self.model = hparams["model"]
33
+ self.compute_features = hparams["compute_features"]
34
+ self.normalize = hparams["modules"]["normalize"]
35
+ self.cnn = hparams["modules"]["CNN"]
36
+ self.transformer = hparams["modules"]["Transformer"]
37
+ self.search = hparams["beam_search"]
38
+ self.tokenizer = hparams["tokenizer"]
39
+ self.bos_index = int(hparams["bos_index"])
40
+ self.pad_index = int(hparams["pad_index"])
41
+ self.sample_rate = int(hparams["sample_rate"])
42
+
43
+ self._load_artifacts(hparams)
44
+
45
+ @staticmethod
46
+ def _make_local_modules_importable() -> None:
47
+ model_root = str(MODEL_ROOT)
48
+ if model_root not in sys.path:
49
+ sys.path.insert(0, model_root)
50
+
51
+ @staticmethod
52
+ def _validate_repository() -> None:
53
+ required_paths = [
54
+ CONFIG_PATH,
55
+ MODEL_ROOT / "model.ckpt",
56
+ MODEL_ROOT / "tokenizer.ckpt",
57
+ MODEL_ROOT / "normalizer.ckpt",
58
+ MODEL_ROOT / "modules" / "TransformerASR.py",
59
+ ]
60
+ missing = [str(path) for path in required_paths if not path.is_file()]
61
+ if missing:
62
+ raise FileNotFoundError(
63
+ "The ConMamba-small-ca repository is incomplete; missing: "
64
+ + ", ".join(missing)
65
+ )
66
+
67
+ @staticmethod
68
+ def _load_hparams():
69
+ # HyperPyYAML 1.2.2 predates ruamel.yaml 0.19, which expects custom
70
+ # loaders to expose this optional recursion-limit attribute.
71
+ import ruamel.yaml
72
+
73
+ if not hasattr(ruamel.yaml.Loader, "max_depth"):
74
+ ruamel.yaml.Loader.max_depth = 0
75
+
76
+ overrides = {"model_dir": str(MODEL_ROOT)}
77
+ with CONFIG_PATH.open("r", encoding="utf-8") as config_file:
78
+ return load_hyperpyyaml(config_file, overrides)
79
+
80
+ def _load_artifacts(self, hparams) -> None:
81
+ model_path = Path(hparams["model_checkpoint"])
82
+ normalizer_path = Path(hparams["normalizer_checkpoint"])
83
+ tokenizer_path = Path(hparams["tokenizer_checkpoint"])
84
+
85
+ model_state = torch.load(model_path, map_location="cpu")
86
+ self.model.load_state_dict(model_state, strict=True)
87
+ self.normalize._load(normalizer_path)
88
+
89
+ if not self.tokenizer.load(str(tokenizer_path)):
90
+ raise RuntimeError(f"Failed to load tokenizer: {tokenizer_path}")
91
+
92
+ self.model.to(self.device).eval()
93
+ self.normalize.eval()
94
+ if hasattr(self.compute_features, "to"):
95
+ self.compute_features.to(self.device)
96
+ if hasattr(self.compute_features, "eval"):
97
+ self.compute_features.eval()
98
+
99
+ def _load_audio(self, audio_path: Path) -> torch.Tensor:
100
+ if not audio_path.is_file():
101
+ raise FileNotFoundError(f"Audio file not found: {audio_path}")
102
+
103
+ waveform, sample_rate = torchaudio.load(str(audio_path))
104
+ if waveform.ndim != 2:
105
+ raise ValueError(f"Unsupported waveform shape: {tuple(waveform.shape)}")
106
+
107
+ waveform = waveform.mean(dim=0)
108
+ if sample_rate != self.sample_rate:
109
+ waveform = torchaudio.functional.resample(
110
+ waveform,
111
+ sample_rate,
112
+ self.sample_rate,
113
+ )
114
+ return waveform.unsqueeze(0).to(self.device)
115
+
116
+ @torch.inference_mode()
117
+ def transcribe(self, audio_path: Path) -> str:
118
+ wavs = self._load_audio(audio_path)
119
+ wav_lens = torch.ones(1, dtype=torch.float32, device=self.device)
120
+
121
+ features = self.compute_features(wavs)
122
+ features = self.normalize(features, wav_lens, epoch=0)
123
+ source = self.cnn(features)
124
+
125
+ dummy_bos = torch.full(
126
+ (wavs.shape[0], 1),
127
+ self.bos_index,
128
+ dtype=torch.long,
129
+ device=self.device,
130
+ )
131
+ encoder_output, _ = self.transformer(
132
+ source,
133
+ dummy_bos,
134
+ wav_lens,
135
+ pad_idx=self.pad_index,
136
+ )
137
+
138
+ search_output = self.search(encoder_output, wav_lens)
139
+ hypotheses = (
140
+ search_output[0]
141
+ if isinstance(search_output, (tuple, list))
142
+ else search_output
143
+ )
144
+ token_ids = hypotheses[0]
145
+ if isinstance(token_ids, torch.Tensor):
146
+ token_ids = token_ids.tolist()
147
+
148
+ return self.tokenizer.decode_ids(
149
+ [int(token) for token in token_ids]
150
+ ).strip()
151
+
152
+
153
+ def parse_args() -> argparse.Namespace:
154
+ parser = argparse.ArgumentParser(
155
+ description="Transcribe one audio file with ConMamba-small-ca."
156
+ )
157
+ parser.add_argument("audio", type=Path, help="Path to the input audio file")
158
+ parser.add_argument(
159
+ "--device",
160
+ default="cuda",
161
+ help="PyTorch device used for inference (default: cuda)",
162
+ )
163
+ return parser.parse_args()
164
+
165
+
166
+ def main() -> None:
167
+ args = parse_args()
168
+ recognizer = ConMambaASR(device=args.device)
169
+ print(recognizer.transcribe(args.audio.expanduser().resolve()))
170
+
171
+
172
+ if __name__ == "__main__":
173
+ main()
tokenizer.ckpt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:69808ba4897ce1a6ce492f0759a8326715106b60ca148842c362b9dc09463942
3
+ size 253025