mrfakename commited on
Commit
c14c4fe
·
verified ·
1 Parent(s): 167f5d0

Mirror wlc952/FunASR-bmodel from ModelScope (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. funasr/models/paraformer_streaming/export_meta.py +136 -0
  3. funasr/models/paraformer_streaming/model.py +702 -0
  4. funasr/models/paraformer_streaming/template.yaml +143 -0
  5. funasr/models/qwen_audio/__init__.py +0 -0
  6. funasr/models/qwen_audio/audio.py +442 -0
  7. funasr/models/qwen_audio/model.py +151 -0
  8. funasr/models/qwen_audio/template.yaml +46 -0
  9. funasr/models/rwkv_bat/__init__.py +0 -0
  10. funasr/models/rwkv_bat/cuda_decoder/wkv_cuda.cu +135 -0
  11. funasr/models/rwkv_bat/cuda_decoder/wkv_op.cpp +37 -0
  12. funasr/models/rwkv_bat/cuda_encoder/wkv_cuda.cu +135 -0
  13. funasr/models/rwkv_bat/cuda_encoder/wkv_op.cpp +37 -0
  14. funasr/models/rwkv_bat/rwkv.py +145 -0
  15. funasr/models/rwkv_bat/rwkv_attention.py +608 -0
  16. funasr/models/rwkv_bat/rwkv_encoder.py +158 -0
  17. funasr/models/rwkv_bat/rwkv_feed_forward.py +89 -0
  18. funasr/models/rwkv_bat/rwkv_subsampling.py +250 -0
  19. funasr/models/rwkv_bat/template.yaml +64 -0
  20. funasr/models/sa_asr/__init__.py +0 -0
  21. funasr/models/sa_asr/attention.py +50 -0
  22. funasr/models/sa_asr/beam_search_sa_asr.py +540 -0
  23. funasr/models/sa_asr/e2e_sa_asr.py +508 -0
  24. funasr/models/sa_asr/transformer_decoder.py +731 -0
  25. funasr/models/sanm/__init__.py +0 -0
  26. funasr/models/sanm/attention.py +903 -0
  27. funasr/models/sanm/decoder.py +498 -0
  28. funasr/models/sanm/encoder.py +612 -0
  29. funasr/models/sanm/model.py +28 -0
  30. funasr/models/sanm/multihead_att.py +241 -0
  31. funasr/models/sanm/positionwise_feed_forward.py +33 -0
  32. funasr/models/sanm/template.yaml +121 -0
  33. funasr/models/sanm_kws/__init__.py +0 -0
  34. funasr/models/sanm_kws/export_meta.py +98 -0
  35. funasr/models/sanm_kws/model.py +266 -0
  36. funasr/models/sanm_kws_streaming/__init__.py +0 -0
  37. funasr/models/sanm_kws_streaming/export_meta.py +98 -0
  38. funasr/models/sanm_kws_streaming/model.py +442 -0
  39. funasr/models/scama/__init__.py +0 -0
  40. funasr/models/scama/beam_search.py +978 -0
  41. funasr/models/scama/chunk_utilis.py +493 -0
  42. funasr/models/scama/decoder.py +498 -0
  43. funasr/models/scama/encoder.py +499 -0
  44. funasr/models/scama/model.py +739 -0
  45. funasr/models/scama/template.yaml +127 -0
  46. funasr/models/scama/utils.py +89 -0
  47. funasr/models/seaco_paraformer/__init__.py +0 -0
  48. funasr/models/seaco_paraformer/export_meta.py +202 -0
  49. funasr/models/seaco_paraformer/model.py +661 -0
  50. funasr/models/seaco_paraformer/template.yaml +156 -0
.gitattributes CHANGED
@@ -46,3 +46,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
46
  *.zst filter=lfs diff=lfs merge=lfs -text
47
  *tfevents* filter=lfs diff=lfs merge=lfs -text
48
  *.bmodel filter=lfs diff=lfs merge=lfs -textbmodel/tpu_perf-1.2.35-py3-none-manylinux2014_aarch64.whl filter=lfs diff=lfs merge=lfs -text
 
 
46
  *.zst filter=lfs diff=lfs merge=lfs -text
47
  *tfevents* filter=lfs diff=lfs merge=lfs -text
48
  *.bmodel filter=lfs diff=lfs merge=lfs -textbmodel/tpu_perf-1.2.35-py3-none-manylinux2014_aarch64.whl filter=lfs diff=lfs merge=lfs -text
49
+ system_ref.wav filter=lfs diff=lfs merge=lfs -text
funasr/models/paraformer_streaming/export_meta.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import types
7
+ import torch
8
+ from funasr.register import tables
9
+
10
+
11
+ def export_rebuild_model(model, **kwargs):
12
+ # self.device = kwargs.get("device")
13
+ is_onnx = kwargs.get("type", "onnx") == "onnx"
14
+ encoder_class = tables.encoder_classes.get(kwargs["encoder"] + "Export")
15
+ model.encoder = encoder_class(model.encoder, onnx=is_onnx)
16
+
17
+ predictor_class = tables.predictor_classes.get(kwargs["predictor"] + "Export")
18
+ model.predictor = predictor_class(model.predictor, onnx=is_onnx)
19
+
20
+ if kwargs["decoder"] == "ParaformerSANMDecoder":
21
+ kwargs["decoder"] = "ParaformerSANMDecoderOnline"
22
+ decoder_class = tables.decoder_classes.get(kwargs["decoder"] + "Export")
23
+ model.decoder = decoder_class(model.decoder, onnx=is_onnx)
24
+
25
+ from funasr.utils.torch_function import sequence_mask
26
+
27
+ model.make_pad_mask = sequence_mask(max_seq_len=None, flip=False)
28
+
29
+ import copy
30
+ import types
31
+
32
+ encoder_model = copy.copy(model)
33
+ decoder_model = copy.copy(model)
34
+
35
+ # encoder
36
+ encoder_model.forward = types.MethodType(export_encoder_forward, encoder_model)
37
+ encoder_model.export_dummy_inputs = types.MethodType(export_encoder_dummy_inputs, encoder_model)
38
+ encoder_model.export_input_names = types.MethodType(export_encoder_input_names, encoder_model)
39
+ encoder_model.export_output_names = types.MethodType(export_encoder_output_names, encoder_model)
40
+ encoder_model.export_dynamic_axes = types.MethodType(export_encoder_dynamic_axes, encoder_model)
41
+ encoder_model.export_name = "model" # types.MethodType(export_encoder_name, encoder_model)
42
+
43
+ # decoder
44
+ decoder_model.forward = types.MethodType(export_decoder_forward, decoder_model)
45
+ decoder_model.export_dummy_inputs = types.MethodType(export_decoder_dummy_inputs, decoder_model)
46
+ decoder_model.export_input_names = types.MethodType(export_decoder_input_names, decoder_model)
47
+ decoder_model.export_output_names = types.MethodType(export_decoder_output_names, decoder_model)
48
+ decoder_model.export_dynamic_axes = types.MethodType(export_decoder_dynamic_axes, decoder_model)
49
+ decoder_model.export_name = "decoder" # types.MethodType(export_decoder_name, decoder_model)
50
+
51
+ return encoder_model, decoder_model
52
+
53
+
54
+ def export_encoder_forward(
55
+ self,
56
+ speech: torch.Tensor,
57
+ speech_lengths: torch.Tensor,
58
+ ):
59
+ # a. To device
60
+ batch = {"speech": speech, "speech_lengths": speech_lengths, "online": True}
61
+ # batch = to_device(batch, device=self.device)
62
+
63
+ enc, enc_len = self.encoder(**batch)
64
+ mask = self.make_pad_mask(enc_len)[:, None, :]
65
+ alphas, _ = self.predictor.forward_cnn(enc, mask)
66
+
67
+ return enc, enc_len, alphas
68
+
69
+
70
+ def export_encoder_dummy_inputs(self):
71
+ speech = torch.randn(2, 30, 560)
72
+ speech_lengths = torch.tensor([6, 30], dtype=torch.int32)
73
+ return (speech, speech_lengths)
74
+
75
+
76
+ def export_encoder_input_names(self):
77
+ return ["speech", "speech_lengths"]
78
+
79
+
80
+ def export_encoder_output_names(self):
81
+ return ["enc", "enc_len", "alphas"]
82
+
83
+
84
+ def export_encoder_dynamic_axes(self):
85
+ return {
86
+ "speech": {0: "batch_size", 1: "feats_length"},
87
+ "speech_lengths": {
88
+ 0: "batch_size",
89
+ },
90
+ "enc": {0: "batch_size", 1: "feats_length"},
91
+ "enc_len": {
92
+ 0: "batch_size",
93
+ },
94
+ "alphas": {0: "batch_size", 1: "feats_length"},
95
+ }
96
+
97
+
98
+ def export_encoder_name(self):
99
+ return "model.onnx"
100
+
101
+
102
+ def export_decoder_forward(
103
+ self,
104
+ enc: torch.Tensor,
105
+ enc_len: torch.Tensor,
106
+ acoustic_embeds: torch.Tensor,
107
+ acoustic_embeds_len: torch.Tensor,
108
+ *args,
109
+ ):
110
+ decoder_out, out_caches = self.decoder(
111
+ enc, enc_len, acoustic_embeds, acoustic_embeds_len, *args
112
+ )
113
+ sample_ids = decoder_out.argmax(dim=-1)
114
+
115
+ return decoder_out, sample_ids, out_caches
116
+
117
+
118
+ def export_decoder_dummy_inputs(self):
119
+ dummy_inputs = self.decoder.get_dummy_inputs(enc_size=self.encoder._output_size)
120
+ return dummy_inputs
121
+
122
+
123
+ def export_decoder_input_names(self):
124
+ return self.decoder.get_input_names()
125
+
126
+
127
+ def export_decoder_output_names(self):
128
+ return self.decoder.get_output_names()
129
+
130
+
131
+ def export_decoder_dynamic_axes(self):
132
+ return self.decoder.get_dynamic_axes()
133
+
134
+
135
+ def export_decoder_name(self):
136
+ return "decoder.onnx"
funasr/models/paraformer_streaming/model.py ADDED
@@ -0,0 +1,702 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import time
7
+ import torch
8
+ import logging
9
+ from typing import Dict, Tuple
10
+ from contextlib import contextmanager
11
+ from distutils.version import LooseVersion
12
+
13
+ from funasr.register import tables
14
+ from funasr.models.ctc.ctc import CTC
15
+ from funasr.utils import postprocess_utils
16
+ from funasr.metrics.compute_acc import th_accuracy
17
+ from funasr.utils.datadir_writer import DatadirWriter
18
+ from funasr.models.paraformer.model import Paraformer
19
+ from funasr.models.paraformer.search import Hypothesis
20
+ from funasr.models.paraformer.cif_predictor import mae_loss
21
+ from funasr.train_utils.device_funcs import force_gatherable
22
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss
23
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
24
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask, pad_list
25
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
26
+
27
+ from funasr.utils.run_bmodel import EngineOV
28
+ import numpy as np
29
+
30
+ if LooseVersion(torch.__version__) >= LooseVersion("1.6.0"):
31
+ from torch.amp import autocast
32
+ else:
33
+ # Nothing to do if torch<1.6.0
34
+ @contextmanager
35
+ def autocast(enabled=True):
36
+ yield
37
+
38
+
39
+ @tables.register("model_classes", "ParaformerStreaming")
40
+ class ParaformerStreaming(Paraformer):
41
+ """
42
+ Author: Speech Lab of DAMO Academy, Alibaba Group
43
+ Paraformer: Fast and Accurate Parallel Transformer for Non-autoregressive End-to-End Speech Recognition
44
+ https://arxiv.org/abs/2206.08317
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ *args,
50
+ **kwargs,
51
+ ):
52
+
53
+ super().__init__(*args, **kwargs)
54
+
55
+ self.sampling_ratio = kwargs.get("sampling_ratio", 0.2)
56
+
57
+ self.scama_mask = None
58
+ if (
59
+ hasattr(self.encoder, "overlap_chunk_cls")
60
+ and self.encoder.overlap_chunk_cls is not None
61
+ ):
62
+ from funasr.models.scama.chunk_utilis import (
63
+ build_scama_mask_for_cross_attention_decoder,
64
+ )
65
+
66
+ self.build_scama_mask_for_cross_attention_decoder_fn = (
67
+ build_scama_mask_for_cross_attention_decoder
68
+ )
69
+ self.decoder_attention_chunk_type = kwargs.get("decoder_attention_chunk_type", "chunk")
70
+
71
+ self.encoder_bmodel = EngineOV(kwargs["model_path"]+"/encoder_fp32.bmodel", device_id=kwargs['dev_id'])
72
+ self.decoder0_bmodel = EngineOV(kwargs["model_path"]+"/decoder0_fp32.bmodel", device_id=kwargs['dev_id'])
73
+ self.decoder1_bmodel = EngineOV(kwargs["model_path"]+"/decoder1_fp32.bmodel", device_id=kwargs['dev_id'])
74
+
75
+ self.predictor.cif_conv1d.weight = torch.load(kwargs["model_path"]+"/cif_conv1d_weight.pt",weights_only=True)
76
+ self.predictor.cif_conv1d.bias = torch.load(kwargs["model_path"]+"/cif_conv1d_bias.pt",weights_only=True)
77
+ self.predictor.cif_output.weight = torch.load(kwargs["model_path"]+"/cif_output_weight.pt",weights_only=True)
78
+ self.predictor.cif_output.bias = torch.load(kwargs["model_path"]+"/cif_output_bias.pt",weights_only=True)
79
+
80
+ def forward(
81
+ self,
82
+ speech: torch.Tensor,
83
+ speech_lengths: torch.Tensor,
84
+ text: torch.Tensor,
85
+ text_lengths: torch.Tensor,
86
+ **kwargs,
87
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
88
+ """Encoder + Decoder + Calc loss
89
+ Args:
90
+ speech: (Batch, Length, ...)
91
+ speech_lengths: (Batch, )
92
+ text: (Batch, Length)
93
+ text_lengths: (Batch,)
94
+ """
95
+ decoding_ind = kwargs.get("decoding_ind")
96
+ if len(text_lengths.size()) > 1:
97
+ text_lengths = text_lengths[:, 0]
98
+ if len(speech_lengths.size()) > 1:
99
+ speech_lengths = speech_lengths[:, 0]
100
+
101
+ batch_size = speech.shape[0]
102
+
103
+ # Encoder
104
+ if hasattr(self.encoder, "overlap_chunk_cls"):
105
+ ind = self.encoder.overlap_chunk_cls.random_choice(self.training, decoding_ind)
106
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths, ind=ind)
107
+ else:
108
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
109
+
110
+ loss_ctc, cer_ctc = None, None
111
+ loss_pre = None
112
+ stats = dict()
113
+
114
+ # decoder: CTC branch
115
+
116
+ if self.ctc_weight > 0.0:
117
+ if hasattr(self.encoder, "overlap_chunk_cls"):
118
+ encoder_out_ctc, encoder_out_lens_ctc = self.encoder.overlap_chunk_cls.remove_chunk(
119
+ encoder_out, encoder_out_lens, chunk_outs=None
120
+ )
121
+ else:
122
+ encoder_out_ctc, encoder_out_lens_ctc = encoder_out, encoder_out_lens
123
+
124
+ loss_ctc, cer_ctc = self._calc_ctc_loss(
125
+ encoder_out_ctc, encoder_out_lens_ctc, text, text_lengths
126
+ )
127
+ # Collect CTC branch stats
128
+ stats["loss_ctc"] = loss_ctc.detach() if loss_ctc is not None else None
129
+ stats["cer_ctc"] = cer_ctc
130
+
131
+ # decoder: Attention decoder branch
132
+ loss_att, acc_att, cer_att, wer_att, loss_pre, pre_loss_att = self._calc_att_predictor_loss(
133
+ encoder_out, encoder_out_lens, text, text_lengths
134
+ )
135
+
136
+ # 3. CTC-Att loss definition
137
+ if self.ctc_weight == 0.0:
138
+ loss = loss_att + loss_pre * self.predictor_weight
139
+ else:
140
+ loss = (
141
+ self.ctc_weight * loss_ctc
142
+ + (1 - self.ctc_weight) * loss_att
143
+ + loss_pre * self.predictor_weight
144
+ )
145
+
146
+ # Collect Attn branch stats
147
+ stats["loss_att"] = loss_att.detach() if loss_att is not None else None
148
+ stats["pre_loss_att"] = pre_loss_att.detach() if pre_loss_att is not None else None
149
+ stats["acc"] = acc_att
150
+ stats["cer"] = cer_att
151
+ stats["wer"] = wer_att
152
+ stats["loss_pre"] = loss_pre.detach().cpu() if loss_pre is not None else None
153
+
154
+ stats["loss"] = torch.clone(loss.detach())
155
+
156
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
157
+ if self.length_normalized_loss:
158
+ batch_size = (text_lengths + self.predictor_bias).sum()
159
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
160
+ return loss, stats, weight
161
+
162
+ def encode_chunk(
163
+ self,
164
+ speech: torch.Tensor,
165
+ speech_lengths: torch.Tensor,
166
+ cache: dict = None,
167
+ **kwargs,
168
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
169
+ """Frontend + Encoder. Note that this method is used by asr_inference.py
170
+ Args:
171
+ speech: (Batch, Length, ...)
172
+ speech_lengths: (Batch, )
173
+ ind: int
174
+ """
175
+ with autocast("cuda",enabled=False):
176
+
177
+ # Data augmentation
178
+ if self.specaug is not None and self.training:
179
+ speech, speech_lengths = self.specaug(speech, speech_lengths)
180
+
181
+ # Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
182
+ if self.normalize is not None:
183
+ speech, speech_lengths = self.normalize(speech, speech_lengths)
184
+
185
+ # Forward encoder
186
+ encoder_out, encoder_out_lens, _ = self.encoder.forward_chunk(
187
+ speech, speech_lengths, cache=cache["encoder"]
188
+ )
189
+ if isinstance(encoder_out, tuple):
190
+ encoder_out = encoder_out[0]
191
+
192
+ return encoder_out, torch.tensor([encoder_out.size(1)])
193
+
194
+ def _calc_att_predictor_loss(
195
+ self,
196
+ encoder_out: torch.Tensor,
197
+ encoder_out_lens: torch.Tensor,
198
+ ys_pad: torch.Tensor,
199
+ ys_pad_lens: torch.Tensor,
200
+ ):
201
+ encoder_out_mask = (
202
+ ~make_pad_mask(encoder_out_lens, maxlen=encoder_out.size(1))[:, None, :]
203
+ ).to(encoder_out.device)
204
+ if self.predictor_bias == 1:
205
+ _, ys_pad = add_sos_eos(ys_pad, self.sos, self.eos, self.ignore_id)
206
+ ys_pad_lens = ys_pad_lens + self.predictor_bias
207
+ mask_chunk_predictor = None
208
+ if self.encoder.overlap_chunk_cls is not None:
209
+ mask_chunk_predictor = self.encoder.overlap_chunk_cls.get_mask_chunk_predictor(
210
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
211
+ )
212
+ mask_shfit_chunk = self.encoder.overlap_chunk_cls.get_mask_shfit_chunk(
213
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
214
+ )
215
+ encoder_out = encoder_out * mask_shfit_chunk
216
+ pre_acoustic_embeds, pre_token_length, pre_alphas, _ = self.predictor(
217
+ encoder_out,
218
+ ys_pad,
219
+ encoder_out_mask,
220
+ ignore_id=self.ignore_id,
221
+ mask_chunk_predictor=mask_chunk_predictor,
222
+ target_label_length=ys_pad_lens,
223
+ )
224
+ predictor_alignments, predictor_alignments_len = self.predictor.gen_frame_alignments(
225
+ pre_alphas, encoder_out_lens
226
+ )
227
+
228
+ scama_mask = None
229
+ if (
230
+ self.encoder.overlap_chunk_cls is not None
231
+ and self.decoder_attention_chunk_type == "chunk"
232
+ ):
233
+ encoder_chunk_size = self.encoder.overlap_chunk_cls.chunk_size_pad_shift_cur
234
+ attention_chunk_center_bias = 0
235
+ attention_chunk_size = encoder_chunk_size
236
+ decoder_att_look_back_factor = (
237
+ self.encoder.overlap_chunk_cls.decoder_att_look_back_factor_cur
238
+ )
239
+ mask_shift_att_chunk_decoder = (
240
+ self.encoder.overlap_chunk_cls.get_mask_shift_att_chunk_decoder(
241
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
242
+ )
243
+ )
244
+ scama_mask = self.build_scama_mask_for_cross_attention_decoder_fn(
245
+ predictor_alignments=predictor_alignments,
246
+ encoder_sequence_length=encoder_out_lens,
247
+ chunk_size=1,
248
+ encoder_chunk_size=encoder_chunk_size,
249
+ attention_chunk_center_bias=attention_chunk_center_bias,
250
+ attention_chunk_size=attention_chunk_size,
251
+ attention_chunk_type=self.decoder_attention_chunk_type,
252
+ step=None,
253
+ predictor_mask_chunk_hopping=mask_chunk_predictor,
254
+ decoder_att_look_back_factor=decoder_att_look_back_factor,
255
+ mask_shift_att_chunk_decoder=mask_shift_att_chunk_decoder,
256
+ target_length=ys_pad_lens,
257
+ is_training=self.training,
258
+ )
259
+ elif self.encoder.overlap_chunk_cls is not None:
260
+ encoder_out, encoder_out_lens = self.encoder.overlap_chunk_cls.remove_chunk(
261
+ encoder_out, encoder_out_lens, chunk_outs=None
262
+ )
263
+ # 0. sampler
264
+ decoder_out_1st = None
265
+ pre_loss_att = None
266
+ if self.sampling_ratio > 0.0:
267
+
268
+ if self.use_1st_decoder_loss:
269
+ sematic_embeds, decoder_out_1st, pre_loss_att = self.sampler_with_grad(
270
+ encoder_out,
271
+ encoder_out_lens,
272
+ ys_pad,
273
+ ys_pad_lens,
274
+ pre_acoustic_embeds,
275
+ scama_mask,
276
+ )
277
+ else:
278
+ sematic_embeds, decoder_out_1st = self.sampler(
279
+ encoder_out,
280
+ encoder_out_lens,
281
+ ys_pad,
282
+ ys_pad_lens,
283
+ pre_acoustic_embeds,
284
+ scama_mask,
285
+ )
286
+ else:
287
+ sematic_embeds = pre_acoustic_embeds
288
+
289
+ # 1. Forward decoder
290
+ decoder_outs = self.decoder(
291
+ encoder_out, encoder_out_lens, sematic_embeds, ys_pad_lens, scama_mask
292
+ )
293
+ decoder_out, _ = decoder_outs[0], decoder_outs[1]
294
+
295
+ if decoder_out_1st is None:
296
+ decoder_out_1st = decoder_out
297
+ # 2. Compute attention loss
298
+ loss_att = self.criterion_att(decoder_out, ys_pad)
299
+ acc_att = th_accuracy(
300
+ decoder_out_1st.view(-1, self.vocab_size),
301
+ ys_pad,
302
+ ignore_label=self.ignore_id,
303
+ )
304
+ loss_pre = self.criterion_pre(ys_pad_lens.type_as(pre_token_length), pre_token_length)
305
+
306
+ # Compute cer/wer using attention-decoder
307
+ if self.training or self.error_calculator is None:
308
+ cer_att, wer_att = None, None
309
+ else:
310
+ ys_hat = decoder_out_1st.argmax(dim=-1)
311
+ cer_att, wer_att = self.error_calculator(ys_hat.cpu(), ys_pad.cpu())
312
+
313
+ return loss_att, acc_att, cer_att, wer_att, loss_pre, pre_loss_att
314
+
315
+ def sampler(
316
+ self,
317
+ encoder_out,
318
+ encoder_out_lens,
319
+ ys_pad,
320
+ ys_pad_lens,
321
+ pre_acoustic_embeds,
322
+ chunk_mask=None,
323
+ ):
324
+
325
+ tgt_mask = (~make_pad_mask(ys_pad_lens, maxlen=ys_pad_lens.max())[:, :, None]).to(
326
+ ys_pad.device
327
+ )
328
+ ys_pad_masked = ys_pad * tgt_mask[:, :, 0]
329
+ if self.share_embedding:
330
+ ys_pad_embed = self.decoder.output_layer.weight[ys_pad_masked]
331
+ else:
332
+ ys_pad_embed = self.decoder.embed(ys_pad_masked)
333
+ with torch.no_grad():
334
+ decoder_outs = self.decoder(
335
+ encoder_out, encoder_out_lens, pre_acoustic_embeds, ys_pad_lens, chunk_mask
336
+ )
337
+ decoder_out, _ = decoder_outs[0], decoder_outs[1]
338
+ pred_tokens = decoder_out.argmax(-1)
339
+ nonpad_positions = ys_pad.ne(self.ignore_id)
340
+ seq_lens = (nonpad_positions).sum(1)
341
+ same_num = ((pred_tokens == ys_pad) & nonpad_positions).sum(1)
342
+ input_mask = torch.ones_like(nonpad_positions)
343
+ bsz, seq_len = ys_pad.size()
344
+ for li in range(bsz):
345
+ target_num = (
346
+ ((seq_lens[li] - same_num[li].sum()).float()) * self.sampling_ratio
347
+ ).long()
348
+ if target_num > 0:
349
+ input_mask[li].scatter_(
350
+ dim=0, index=torch.randperm(seq_lens[li])[:target_num].cuda(), value=0
351
+ )
352
+ input_mask = input_mask.eq(1)
353
+ input_mask = input_mask.masked_fill(~nonpad_positions, False)
354
+ input_mask_expand_dim = input_mask.unsqueeze(2).to(pre_acoustic_embeds.device)
355
+
356
+ sematic_embeds = pre_acoustic_embeds.masked_fill(
357
+ ~input_mask_expand_dim, 0
358
+ ) + ys_pad_embed.masked_fill(input_mask_expand_dim, 0)
359
+ return sematic_embeds * tgt_mask, decoder_out * tgt_mask
360
+
361
+ def calc_predictor(self, encoder_out, encoder_out_lens):
362
+
363
+ encoder_out_mask = (
364
+ ~make_pad_mask(encoder_out_lens, maxlen=encoder_out.size(1))[:, None, :]
365
+ ).to(encoder_out.device)
366
+ mask_chunk_predictor = None
367
+ if self.encoder.overlap_chunk_cls is not None:
368
+ mask_chunk_predictor = self.encoder.overlap_chunk_cls.get_mask_chunk_predictor(
369
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
370
+ )
371
+ mask_shfit_chunk = self.encoder.overlap_chunk_cls.get_mask_shfit_chunk(
372
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
373
+ )
374
+ encoder_out = encoder_out * mask_shfit_chunk
375
+ pre_acoustic_embeds, pre_token_length, pre_alphas, pre_peak_index = self.predictor(
376
+ encoder_out,
377
+ None,
378
+ encoder_out_mask,
379
+ ignore_id=self.ignore_id,
380
+ mask_chunk_predictor=mask_chunk_predictor,
381
+ target_label_length=None,
382
+ )
383
+ predictor_alignments, predictor_alignments_len = self.predictor.gen_frame_alignments(
384
+ pre_alphas,
385
+ encoder_out_lens + 1 if self.predictor.tail_threshold > 0.0 else encoder_out_lens,
386
+ )
387
+
388
+ scama_mask = None
389
+ if (
390
+ self.encoder.overlap_chunk_cls is not None
391
+ and self.decoder_attention_chunk_type == "chunk"
392
+ ):
393
+ encoder_chunk_size = self.encoder.overlap_chunk_cls.chunk_size_pad_shift_cur
394
+ attention_chunk_center_bias = 0
395
+ attention_chunk_size = encoder_chunk_size
396
+ decoder_att_look_back_factor = (
397
+ self.encoder.overlap_chunk_cls.decoder_att_look_back_factor_cur
398
+ )
399
+ mask_shift_att_chunk_decoder = (
400
+ self.encoder.overlap_chunk_cls.get_mask_shift_att_chunk_decoder(
401
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
402
+ )
403
+ )
404
+ scama_mask = self.build_scama_mask_for_cross_attention_decoder_fn(
405
+ predictor_alignments=predictor_alignments,
406
+ encoder_sequence_length=encoder_out_lens,
407
+ chunk_size=1,
408
+ encoder_chunk_size=encoder_chunk_size,
409
+ attention_chunk_center_bias=attention_chunk_center_bias,
410
+ attention_chunk_size=attention_chunk_size,
411
+ attention_chunk_type=self.decoder_attention_chunk_type,
412
+ step=None,
413
+ predictor_mask_chunk_hopping=mask_chunk_predictor,
414
+ decoder_att_look_back_factor=decoder_att_look_back_factor,
415
+ mask_shift_att_chunk_decoder=mask_shift_att_chunk_decoder,
416
+ target_length=None,
417
+ is_training=self.training,
418
+ )
419
+ self.scama_mask = scama_mask
420
+
421
+ return pre_acoustic_embeds, pre_token_length, pre_alphas, pre_peak_index
422
+
423
+ def calc_predictor_chunk(self, encoder_out, encoder_out_lens, cache=None, **kwargs):
424
+ is_final = kwargs.get("is_final", False)
425
+
426
+ return self.predictor.forward_chunk(encoder_out, cache["encoder"], is_final=is_final)
427
+
428
+ def cal_decoder_with_predictor(
429
+ self, encoder_out, encoder_out_lens, sematic_embeds, ys_pad_lens
430
+ ):
431
+ decoder_outs = self.decoder(
432
+ encoder_out, encoder_out_lens, sematic_embeds, ys_pad_lens, self.scama_mask
433
+ )
434
+ decoder_out = decoder_outs[0]
435
+ decoder_out = torch.log_softmax(decoder_out, dim=-1)
436
+ return decoder_out, ys_pad_lens
437
+
438
+ def cal_decoder_with_predictor_chunk(
439
+ self, encoder_out, encoder_out_lens, sematic_embeds, ys_pad_lens, cache=None
440
+ ):
441
+ decoder_outs = self.decoder.forward_chunk(encoder_out, sematic_embeds, cache["decoder"])
442
+ decoder_out = decoder_outs
443
+ decoder_out = torch.log_softmax(decoder_out, dim=-1)
444
+ return decoder_out, ys_pad_lens
445
+
446
+ def init_cache(self, cache: dict = {}, **kwargs):
447
+ chunk_size = kwargs.get("chunk_size", [0, 10, 5])
448
+ encoder_chunk_look_back = kwargs.get("encoder_chunk_look_back", 0)
449
+ decoder_chunk_look_back = kwargs.get("decoder_chunk_look_back", 0)
450
+ batch_size = 1
451
+
452
+ enc_output_size = kwargs["encoder_conf"]["output_size"]
453
+ feats_dims = kwargs["frontend_conf"]["n_mels"] * kwargs["frontend_conf"]["lfr_m"]
454
+ cache_encoder = {
455
+ "start_idx": 0,
456
+ "cif_hidden": torch.zeros((batch_size, 1, enc_output_size)),
457
+ "cif_alphas": torch.zeros((batch_size, 1)),
458
+ "chunk_size": chunk_size,
459
+ "encoder_chunk_look_back": encoder_chunk_look_back,
460
+ "last_chunk": False,
461
+ "opt": None,
462
+ "feats": torch.zeros((batch_size, chunk_size[0] + chunk_size[2], feats_dims)),
463
+ "tail_chunk": False,
464
+ }
465
+ cache["encoder"] = cache_encoder
466
+
467
+ cache_decoder = {
468
+ "decode_fsmn": None,
469
+ "decoder_chunk_look_back": decoder_chunk_look_back,
470
+ "opt": None,
471
+ "chunk_size": chunk_size,
472
+ }
473
+ cache["decoder"] = cache_decoder
474
+ cache["frontend"] = {}
475
+ cache["prev_samples"] = torch.empty(0)
476
+
477
+ return cache
478
+
479
+ def generate_chunk(
480
+ self,
481
+ speech,
482
+ speech_lengths=None,
483
+ key: list = None,
484
+ tokenizer=None,
485
+ frontend=None,
486
+ **kwargs,
487
+ ):
488
+ cache = kwargs.get("cache", {})
489
+ speech = speech.to(device=kwargs["device"])
490
+ speech_lengths = speech_lengths.to(device=kwargs["device"])
491
+
492
+ # Encoder
493
+ """
494
+ encoder_out, encoder_out_lens = self.encode_chunk(
495
+ speech, speech_lengths, cache=cache, is_final=kwargs.get("is_final", False)
496
+ )
497
+ if isinstance(encoder_out, tuple):
498
+ encoder_out = encoder_out[0]
499
+ """
500
+ speech = speech.detach().numpy()
501
+ start_idx = (torch.tensor([0], dtype=torch.int64).detach().numpy()).astype(np.int32)
502
+ chunk_size = (torch.tensor(cache['encoder']['chunk_size'], dtype=torch.int64).detach().numpy()).astype(np.int32)
503
+ feats = cache['encoder']['feats'].detach().numpy()
504
+ outputs = self.encoder_bmodel([speech, start_idx, chunk_size, feats])
505
+ cache['encoder']['start_idx'] = torch.from_numpy(outputs[1])
506
+ cache['encoder']['feats'] = torch.from_numpy(outputs[2])
507
+ encoder_out = torch.from_numpy(outputs[0])
508
+ encoder_out_lens = torch.tensor([encoder_out.size(1)])
509
+
510
+ # predictor
511
+ predictor_outs = self.calc_predictor_chunk(
512
+ encoder_out, encoder_out_lens, cache=cache, is_final=kwargs.get("is_final", False)
513
+ )
514
+ pre_acoustic_embeds, pre_token_length, alphas, pre_peak_index = (
515
+ predictor_outs[0],
516
+ predictor_outs[1],
517
+ predictor_outs[2],
518
+ predictor_outs[3],
519
+ )
520
+ pre_token_length = pre_token_length.round().long()
521
+ if torch.max(pre_token_length) < 1:
522
+ return []
523
+ """
524
+ decoder_outs = self.cal_decoder_with_predictor_chunk(
525
+ encoder_out, encoder_out_lens, pre_acoustic_embeds, pre_token_length, cache=cache
526
+ )
527
+ decoder_out, ys_pad_lens = decoder_outs[0], decoder_outs[1]
528
+ """
529
+ encoder_out = encoder_out.detach().numpy()
530
+ pre_acoustic_embeds = pre_acoustic_embeds.detach().numpy()
531
+ lens = (pre_token_length.detach().numpy()).astype(np.int32)
532
+ input = [encoder_out, pre_acoustic_embeds]
533
+ if cache['decoder']['decode_fsmn'] is None:
534
+ outputs = self.decoder0_bmodel(input)
535
+ else:
536
+ input.append(lens)
537
+ for i in range(16):
538
+ input.append(cache['decoder']['decode_fsmn'][i].detach().numpy())
539
+ outputs = self.decoder1_bmodel(input)
540
+ decode_fsmn = []
541
+ for i in range(16):
542
+ decode_fsmn.append(torch.from_numpy(outputs[i+1]))
543
+ cache['decoder']['decode_fsmn'] = decode_fsmn
544
+ decoder_out = torch.from_numpy(outputs[0])
545
+
546
+ results = []
547
+ b, n, d = decoder_out.size()
548
+ if isinstance(key[0], (list, tuple)):
549
+ key = key[0]
550
+ for i in range(b):
551
+ x = encoder_out[i, : encoder_out_lens[i], :]
552
+ am_scores = decoder_out[i, : pre_token_length[i], :]
553
+ if self.beam_search is not None:
554
+ nbest_hyps = self.beam_search(
555
+ x=x,
556
+ am_scores=am_scores,
557
+ maxlenratio=kwargs.get("maxlenratio", 0.0),
558
+ minlenratio=kwargs.get("minlenratio", 0.0),
559
+ )
560
+
561
+ nbest_hyps = nbest_hyps[: self.nbest]
562
+ else:
563
+
564
+ yseq = am_scores.argmax(dim=-1)
565
+ score = am_scores.max(dim=-1)[0]
566
+ score = torch.sum(score, dim=-1)
567
+ # pad with mask tokens to ensure compatibility with sos/eos tokens
568
+ yseq = torch.tensor([self.sos] + yseq.tolist() + [self.eos], device=yseq.device)
569
+ nbest_hyps = [Hypothesis(yseq=yseq, score=score)]
570
+ for nbest_idx, hyp in enumerate(nbest_hyps):
571
+
572
+ # remove sos/eos and get results
573
+ last_pos = -1
574
+ if isinstance(hyp.yseq, list):
575
+ token_int = hyp.yseq[1:last_pos]
576
+ else:
577
+ token_int = hyp.yseq[1:last_pos].tolist()
578
+
579
+ # remove blank symbol id, which is assumed to be 0
580
+ token_int = list(
581
+ filter(
582
+ lambda x: x != self.eos and x != self.sos and x != self.blank_id, token_int
583
+ )
584
+ )
585
+
586
+ # Change integer-ids to tokens
587
+ token = tokenizer.ids2tokens(token_int)
588
+ # text = tokenizer.tokens2text(token)
589
+
590
+ result_i = token
591
+
592
+ results.extend(result_i)
593
+
594
+ return results
595
+
596
+ def inference(
597
+ self,
598
+ data_in,
599
+ data_lengths=None,
600
+ key: list = None,
601
+ tokenizer=None,
602
+ frontend=None,
603
+ cache: dict = {},
604
+ **kwargs,
605
+ ):
606
+
607
+ # init beamsearch
608
+ is_use_ctc = kwargs.get("decoding_ctc_weight", 0.0) > 0.00001 and self.ctc != None
609
+ is_use_lm = (
610
+ kwargs.get("lm_weight", 0.0) > 0.00001 and kwargs.get("lm_file", None) is not None
611
+ )
612
+ if self.beam_search is None and (is_use_lm or is_use_ctc):
613
+ logging.info("enable beam_search")
614
+ self.init_beam_search(**kwargs)
615
+ self.nbest = kwargs.get("nbest", 1)
616
+
617
+ if len(cache) == 0:
618
+ self.init_cache(cache, **kwargs)
619
+
620
+ meta_data = {}
621
+ chunk_size = kwargs.get("chunk_size", [0, 10, 5])
622
+ chunk_stride_samples = int(chunk_size[1] * 960) # 600ms
623
+
624
+ time1 = time.perf_counter()
625
+ cfg = {"is_final": kwargs.get("is_final", False)}
626
+ audio_sample_list = load_audio_text_image_video(
627
+ data_in,
628
+ fs=frontend.fs,
629
+ audio_fs=kwargs.get("fs", 16000),
630
+ data_type=kwargs.get("data_type", "sound"),
631
+ tokenizer=tokenizer,
632
+ cache=cfg,
633
+ )
634
+ _is_final = cfg["is_final"] # if data_in is a file or url, set is_final=True
635
+
636
+ time2 = time.perf_counter()
637
+ meta_data["load_data"] = f"{time2 - time1:0.3f}"
638
+ assert len(audio_sample_list) == 1, "batch_size must be set 1"
639
+
640
+ audio_sample = torch.cat((cache["prev_samples"], audio_sample_list[0]))
641
+
642
+ n = int(len(audio_sample) // chunk_stride_samples + int(_is_final))
643
+ m = int(len(audio_sample) % chunk_stride_samples * (1 - int(_is_final)))
644
+ tokens = []
645
+ for i in range(n):
646
+ kwargs["is_final"] = _is_final and i == n - 1
647
+ audio_sample_i = audio_sample[i * chunk_stride_samples : (i + 1) * chunk_stride_samples]
648
+ if kwargs["is_final"] and len(audio_sample_i) < 960:
649
+ cache["encoder"]["tail_chunk"] = True
650
+ speech = cache["encoder"]["feats"]
651
+ speech_lengths = torch.tensor([speech.shape[1]], dtype=torch.int64).to(
652
+ speech.device
653
+ )
654
+ else:
655
+ # extract fbank feats
656
+ speech, speech_lengths = extract_fbank(
657
+ [audio_sample_i],
658
+ data_type=kwargs.get("data_type", "sound"),
659
+ frontend=frontend,
660
+ cache=cache["frontend"],
661
+ is_final=kwargs["is_final"],
662
+ )
663
+ time3 = time.perf_counter()
664
+ meta_data["extract_feat"] = f"{time3 - time2:0.3f}"
665
+ meta_data["batch_data_time"] = (
666
+ speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000
667
+ )
668
+
669
+ tokens_i = self.generate_chunk(
670
+ speech,
671
+ speech_lengths,
672
+ key=key,
673
+ tokenizer=tokenizer,
674
+ cache=cache,
675
+ frontend=frontend,
676
+ **kwargs,
677
+ )
678
+ tokens.extend(tokens_i)
679
+
680
+ text_postprocessed, _ = postprocess_utils.sentence_postprocess(tokens)
681
+
682
+ result_i = {"key": key[0], "text": text_postprocessed}
683
+ result = [result_i]
684
+
685
+ cache["prev_samples"] = audio_sample[:-m]
686
+ if _is_final:
687
+ self.init_cache(cache, **kwargs)
688
+
689
+ if kwargs.get("output_dir"):
690
+ if not hasattr(self, "writer"):
691
+ self.writer = DatadirWriter(kwargs.get("output_dir"))
692
+ ibest_writer = self.writer[f"{1}best_recog"]
693
+ ibest_writer["token"][key[0]] = " ".join(tokens)
694
+ ibest_writer["text"][key[0]] = text_postprocessed
695
+
696
+ return result, meta_data
697
+
698
+ def export(self, **kwargs):
699
+ from .export_meta import export_rebuild_model
700
+
701
+ models = export_rebuild_model(model=self, **kwargs)
702
+ return models
funasr/models/paraformer_streaming/template.yaml ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is an example that demonstrates how to configure a model file.
2
+ # You can modify the configuration according to your own requirements.
3
+
4
+ # to print the register_table:
5
+ # from funasr.register import tables
6
+ # tables.print()
7
+
8
+ # network architecture
9
+ model: ParaformerStreaming
10
+ model_conf:
11
+ ctc_weight: 0.0
12
+ lsm_weight: 0.1
13
+ length_normalized_loss: true
14
+ predictor_weight: 1.0
15
+ predictor_bias: 1
16
+ sampling_ratio: 0.75
17
+
18
+ # encoder
19
+ encoder: SANMEncoderChunkOpt
20
+ encoder_conf:
21
+ output_size: 512
22
+ attention_heads: 4
23
+ linear_units: 2048
24
+ num_blocks: 50
25
+ dropout_rate: 0.1
26
+ positional_dropout_rate: 0.1
27
+ attention_dropout_rate: 0.1
28
+ input_layer: pe_online
29
+ pos_enc_class: SinusoidalPositionEncoder
30
+ normalize_before: true
31
+ kernel_size: 11
32
+ sanm_shfit: 0
33
+ selfattention_layer_type: sanm
34
+ chunk_size:
35
+ - 12
36
+ - 15
37
+ stride:
38
+ - 8
39
+ - 10
40
+ pad_left:
41
+ - 0
42
+ - 0
43
+ encoder_att_look_back_factor:
44
+ - 4
45
+ - 4
46
+ decoder_att_look_back_factor:
47
+ - 1
48
+ - 1
49
+
50
+ # decoder
51
+ decoder: ParaformerSANMDecoder
52
+ decoder_conf:
53
+ attention_heads: 4
54
+ linear_units: 2048
55
+ num_blocks: 16
56
+ dropout_rate: 0.1
57
+ positional_dropout_rate: 0.1
58
+ self_attention_dropout_rate: 0.1
59
+ src_attention_dropout_rate: 0.1
60
+ att_layer_num: 16
61
+ kernel_size: 11
62
+ sanm_shfit: 5
63
+
64
+ predictor: CifPredictorV2
65
+ predictor_conf:
66
+ idim: 512
67
+ threshold: 1.0
68
+ l_order: 1
69
+ r_order: 1
70
+ tail_threshold: 0.45
71
+
72
+ # frontend related
73
+ frontend: WavFrontendOnline
74
+ frontend_conf:
75
+ fs: 16000
76
+ window: hamming
77
+ n_mels: 80
78
+ frame_length: 25
79
+ frame_shift: 10
80
+ lfr_m: 7
81
+ lfr_n: 6
82
+
83
+ specaug: SpecAugLFR
84
+ specaug_conf:
85
+ apply_time_warp: false
86
+ time_warp_window: 5
87
+ time_warp_mode: bicubic
88
+ apply_freq_mask: true
89
+ freq_mask_width_range:
90
+ - 0
91
+ - 30
92
+ lfr_rate: 6
93
+ num_freq_mask: 1
94
+ apply_time_mask: true
95
+ time_mask_width_range:
96
+ - 0
97
+ - 12
98
+ num_time_mask: 1
99
+
100
+ train_conf:
101
+ accum_grad: 1
102
+ grad_clip: 5
103
+ max_epoch: 150
104
+ val_scheduler_criterion:
105
+ - valid
106
+ - acc
107
+ best_model_criterion:
108
+ - - valid
109
+ - acc
110
+ - max
111
+ keep_nbest_models: 10
112
+ log_interval: 50
113
+
114
+ optim: adam
115
+ optim_conf:
116
+ lr: 0.0005
117
+ scheduler: warmuplr
118
+ scheduler_conf:
119
+ warmup_steps: 30000
120
+
121
+ dataset: AudioDataset
122
+ dataset_conf:
123
+ index_ds: IndexDSJsonl
124
+ batch_sampler: BatchSampler
125
+ batch_type: example # example or length
126
+ batch_size: 1 # if batch_type is example, batch_size is the numbers of samples; if length, batch_size is source_token_len+target_token_len;
127
+ max_token_length: 2048 # filter samples if source_token_len+target_token_len > max_token_length,
128
+ buffer_size: 500
129
+ shuffle: True
130
+ num_workers: 0
131
+
132
+ tokenizer: CharTokenizer
133
+ tokenizer_conf:
134
+ unk_symbol: <unk>
135
+ split_with_space: true
136
+
137
+
138
+ ctc_conf:
139
+ dropout_rate: 0.0
140
+ ctc_type: builtin
141
+ reduce: true
142
+ ignore_nan_grad: true
143
+ normalize: null
funasr/models/qwen_audio/__init__.py ADDED
File without changes
funasr/models/qwen_audio/audio.py ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import gzip
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Iterable, Optional, List
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from torch import Tensor, nn
10
+ from subprocess import CalledProcessError, run, Popen, PIPE
11
+
12
+ import os
13
+ from functools import lru_cache
14
+ from typing import Optional, Union
15
+ from funasr.register import tables
16
+
17
+
18
+ def exact_div(x, y):
19
+ assert x % y == 0
20
+ return x // y
21
+
22
+
23
+ # hard-coded audio hyperparameters
24
+ SAMPLE_RATE = 16000
25
+ N_FFT = 400
26
+ N_MELS = 80
27
+ HOP_LENGTH = 160
28
+ CHUNK_LENGTH = 30
29
+ N_SAMPLES = CHUNK_LENGTH * SAMPLE_RATE # 480000 samples in a 30-second chunk
30
+ N_FRAMES = exact_div(N_SAMPLES, HOP_LENGTH) # 3000 frames in a mel spectrogram input
31
+
32
+ N_SAMPLES_PER_TOKEN = HOP_LENGTH * 2 # the initial convolutions has stride 2
33
+ FRAMES_PER_SECOND = exact_div(SAMPLE_RATE, HOP_LENGTH) # 10ms per audio frame
34
+ TOKENS_PER_SECOND = exact_div(SAMPLE_RATE, N_SAMPLES_PER_TOKEN) # 20ms per audio token
35
+
36
+
37
+ def get_T_after_cnn(L_in, dilation=1):
38
+ for padding, kernel_size, stride in eval("[(1,3,1)] + [(1,3,2)] "):
39
+ L_out = L_in + 2 * padding - dilation * (kernel_size - 1) - 1
40
+ L_out = 1 + L_out // stride
41
+ L_in = L_out
42
+ return L_out
43
+
44
+
45
+ def load_bytesio_audio(content, sr: int = SAMPLE_RATE):
46
+ cmd = [
47
+ "ffmpeg",
48
+ "-nostdin",
49
+ "-threads",
50
+ "0",
51
+ "-i",
52
+ "pipe:",
53
+ "-f",
54
+ "s16le",
55
+ "-ac",
56
+ "1",
57
+ "-acodec",
58
+ "pcm_s16le",
59
+ "-ar",
60
+ str(sr),
61
+ "pipe:",
62
+ ]
63
+ p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
64
+ out, _ = p.communicate(input=content)
65
+ return np.frombuffer(out, np.int16).flatten().astype(np.float32) / 32768.0
66
+
67
+
68
+ def load_audio(file: str, sr: int = SAMPLE_RATE):
69
+ """
70
+ Open an audio file and read as mono waveform, resampling as necessary
71
+
72
+ Parameters
73
+ ----------
74
+ file: str
75
+ The audio file to open
76
+
77
+ sr: int
78
+ The sample rate to resample the audio if necessary
79
+
80
+ Returns
81
+ -------
82
+ A NumPy array containing the audio waveform, in float32 dtype.
83
+ """
84
+
85
+ # This launches a subprocess to decode audio while down-mixing
86
+ # and resampling as necessary. Requires the ffmpeg CLI in PATH.
87
+ # fmt: off
88
+ cmd = [
89
+ "ffmpeg",
90
+ "-nostdin",
91
+ "-threads", "0",
92
+ "-i", file,
93
+ "-f", "s16le",
94
+ "-ac", "1",
95
+ "-acodec", "pcm_s16le",
96
+ "-ar", str(sr),
97
+ "-"
98
+ ]
99
+ # fmt: on
100
+ try:
101
+ out = run(cmd, capture_output=True, check=True).stdout
102
+ except CalledProcessError as e:
103
+ raise RuntimeError(f"Failed to load audio: {e.stderr.decode()}") from e
104
+
105
+ return np.frombuffer(out, np.int16).flatten().astype(np.float32) / 32768.0
106
+
107
+
108
+ def pad_or_trim(array, length: int = N_SAMPLES, *, axis: int = -1):
109
+ """
110
+ Pad or trim the audio array to N_SAMPLES, as expected by the encoder.
111
+ """
112
+ if torch.is_tensor(array):
113
+ if array.shape[axis] > length:
114
+ array = array.index_select(dim=axis, index=torch.arange(length, device=array.device))
115
+
116
+ if array.shape[axis] < length:
117
+ pad_widths = [(0, 0)] * array.ndim
118
+ pad_widths[axis] = (0, length - array.shape[axis])
119
+ array = F.pad(array, [pad for sizes in pad_widths[::-1] for pad in sizes])
120
+ else:
121
+ if array.shape[axis] > length:
122
+ array = array.take(indices=range(length), axis=axis)
123
+
124
+ if array.shape[axis] < length:
125
+ pad_widths = [(0, 0)] * array.ndim
126
+ pad_widths[axis] = (0, length - array.shape[axis])
127
+ array = np.pad(array, pad_widths)
128
+
129
+ return array
130
+
131
+
132
+ def trim(array, length: int = N_SAMPLES, *, axis: int = -1):
133
+ """
134
+ Pad or trim the audio array to N_SAMPLES, as expected by the encoder.
135
+ """
136
+ if torch.is_tensor(array):
137
+ if array.shape[axis] > length:
138
+ array = array.index_select(dim=axis, index=torch.arange(length, device=array.device))
139
+ else:
140
+ if array.shape[axis] > length:
141
+ array = array.take(indices=range(length), axis=axis)
142
+ return array
143
+
144
+
145
+ @lru_cache(maxsize=None)
146
+ def mel_filters(device, n_mels: int = N_MELS) -> torch.Tensor:
147
+ """
148
+ load the mel filterbank matrix for projecting STFT into a Mel spectrogram.
149
+ Allows decoupling librosa dependency; saved using:
150
+
151
+ np.savez_compressed(
152
+ "mel_filters.npz",
153
+ mel_80=librosa.filters.mel(sr=16000, n_fft=400, n_mels=80),
154
+ )
155
+ """
156
+ assert n_mels == 80, f"Unsupported n_mels: {n_mels}"
157
+ with np.load(
158
+ os.path.join(os.path.dirname(__file__), "mel_filters.npz") # todo
159
+ # os.path.join("assets", "mel_filters.npz")
160
+ ) as f:
161
+ return torch.from_numpy(f[f"mel_{n_mels}"]).to(device)
162
+
163
+
164
+ def log_mel_spectrogram(
165
+ audio: Union[str, np.ndarray, torch.Tensor],
166
+ n_mels: int = N_MELS,
167
+ padding: int = 0,
168
+ device: Optional[Union[str, torch.device]] = None,
169
+ ):
170
+ """
171
+ Compute the log-Mel spectrogram of
172
+
173
+ Parameters
174
+ ----------
175
+ audio: Union[str, np.ndarray, torch.Tensor], shape = (*)
176
+ The path to audio or either a NumPy array or Tensor containing the audio waveform in 16 kHz
177
+
178
+ n_mels: int
179
+ The number of Mel-frequency filters, only 80 is supported
180
+
181
+ padding: int
182
+ Number of zero samples to pad to the right
183
+
184
+ device: Optional[Union[str, torch.device]]
185
+ If given, the audio tensor is moved to this device before STFT
186
+
187
+ Returns
188
+ -------
189
+ torch.Tensor, shape = (80, n_frames)
190
+ A Tensor that contains the Mel spectrogram
191
+ """
192
+ if not torch.is_tensor(audio):
193
+ if isinstance(audio, str):
194
+ audio = load_audio(audio)
195
+ audio = torch.from_numpy(audio)
196
+
197
+ if device is not None:
198
+ audio = audio.to(device)
199
+ if padding > 0:
200
+ audio = F.pad(audio, (0, padding))
201
+ window = torch.hann_window(N_FFT).to(audio.device)
202
+ stft = torch.stft(audio, N_FFT, HOP_LENGTH, window=window, return_complex=True)
203
+ magnitudes = stft[..., :-1].abs() ** 2
204
+
205
+ filters = mel_filters(audio.device, n_mels)
206
+ mel_spec = filters @ magnitudes
207
+
208
+ log_spec = torch.clamp(mel_spec, min=1e-10).log10()
209
+ log_spec = torch.maximum(log_spec, log_spec.max() - 8.0)
210
+ log_spec = (log_spec + 4.0) / 4.0
211
+ return log_spec
212
+
213
+
214
+ @dataclass
215
+ class ModelDimensions:
216
+ n_mels: int
217
+ n_audio_ctx: int
218
+ n_audio_state: int
219
+ n_audio_head: int
220
+ n_audio_layer: int
221
+ n_vocab: int
222
+ n_text_ctx: int
223
+ n_text_state: int
224
+ n_text_head: int
225
+ n_text_layer: int
226
+
227
+
228
+ class LayerNorm(nn.LayerNorm):
229
+ def forward(self, x: Tensor) -> Tensor:
230
+ # return super().forward(x.float()).type(x.dtype)
231
+ return super().forward(x).type(x.dtype)
232
+
233
+
234
+ class Linear(nn.Linear):
235
+ def forward(self, x: Tensor) -> Tensor:
236
+ return F.linear(
237
+ x,
238
+ self.weight.to(x.dtype),
239
+ None if self.bias is None else self.bias.to(x.dtype),
240
+ )
241
+
242
+
243
+ class Conv1d(nn.Conv1d):
244
+ def _conv_forward(self, x: Tensor, weight: Tensor, bias: Optional[Tensor]) -> Tensor:
245
+ return super()._conv_forward(
246
+ x, weight.to(x.dtype), None if bias is None else bias.to(x.dtype)
247
+ )
248
+
249
+
250
+ def sinusoids(length, channels, max_timescale=10000):
251
+ """Returns sinusoids for positional embedding"""
252
+ assert channels % 2 == 0
253
+ log_timescale_increment = np.log(max_timescale) / (channels // 2 - 1)
254
+ inv_timescales = torch.exp(-log_timescale_increment * torch.arange(channels // 2))
255
+ scaled_time = torch.arange(length)[:, np.newaxis] * inv_timescales[np.newaxis, :]
256
+ return torch.cat([torch.sin(scaled_time), torch.cos(scaled_time)], dim=1)
257
+
258
+
259
+ class MultiHeadAttention(nn.Module):
260
+ def __init__(self, n_state: int, n_head: int):
261
+ super().__init__()
262
+ self.n_head = n_head
263
+ self.query = Linear(n_state, n_state)
264
+ self.key = Linear(n_state, n_state, bias=False)
265
+ self.value = Linear(n_state, n_state)
266
+ self.out = Linear(n_state, n_state)
267
+
268
+ def forward(
269
+ self,
270
+ x: Tensor,
271
+ xa: Optional[Tensor] = None,
272
+ mask: Optional[Tensor] = None,
273
+ kv_cache: Optional[dict] = None,
274
+ ):
275
+ q = self.query(x)
276
+
277
+ if kv_cache is None or xa is None or self.key not in kv_cache:
278
+ # hooks, if installed (i.e. kv_cache is not None), will prepend the cached kv tensors;
279
+ # otherwise, perform key/value projections for self- or cross-attention as usual.
280
+ k = self.key(x if xa is None else xa)
281
+ v = self.value(x if xa is None else xa)
282
+ else:
283
+ # for cross-attention, calculate keys and values once and reuse in subsequent calls.
284
+ k = kv_cache[self.key]
285
+ v = kv_cache[self.value]
286
+
287
+ wv, qk = self.qkv_attention(q, k, v, mask)
288
+ return self.out(wv), qk
289
+
290
+ def qkv_attention(self, q: Tensor, k: Tensor, v: Tensor, mask: Optional[Tensor] = None):
291
+ n_batch, n_ctx, n_state = q.shape
292
+ scale = (n_state // self.n_head) ** -0.25
293
+ q = q.view(*q.shape[:2], self.n_head, -1).permute(0, 2, 1, 3) * scale
294
+ k = k.view(*k.shape[:2], self.n_head, -1).permute(0, 2, 3, 1) * scale
295
+ v = v.view(*v.shape[:2], self.n_head, -1).permute(0, 2, 1, 3)
296
+
297
+ qk = q @ k
298
+ if mask is not None:
299
+ qk += mask
300
+
301
+ w = F.softmax(qk, dim=-1).to(q.dtype)
302
+ return (w @ v).permute(0, 2, 1, 3).flatten(start_dim=2), qk.detach()
303
+
304
+
305
+ class ResidualAttentionBlock(nn.Module):
306
+ def __init__(self, n_state: int, n_head: int, cross_attention: bool = False):
307
+ super().__init__()
308
+
309
+ self.attn = MultiHeadAttention(n_state, n_head)
310
+ self.attn_ln = LayerNorm(n_state)
311
+
312
+ self.cross_attn = MultiHeadAttention(n_state, n_head) if cross_attention else None
313
+ self.cross_attn_ln = LayerNorm(n_state) if cross_attention else None
314
+
315
+ n_mlp = n_state * 4
316
+ self.mlp = nn.Sequential(Linear(n_state, n_mlp), nn.GELU(), Linear(n_mlp, n_state))
317
+ self.mlp_ln = LayerNorm(n_state)
318
+
319
+ def forward(
320
+ self,
321
+ x: Tensor,
322
+ xa: Optional[Tensor] = None,
323
+ mask: Optional[Tensor] = None,
324
+ kv_cache: Optional[dict] = None,
325
+ ):
326
+ x = x + self.attn(self.attn_ln(x), mask=mask, kv_cache=kv_cache)[0]
327
+ if self.cross_attn:
328
+ x = x + self.cross_attn(self.cross_attn_ln(x), xa, kv_cache=kv_cache)[0]
329
+ x = x + self.mlp(self.mlp_ln(x))
330
+ return x
331
+
332
+
333
+ @tables.register("encoder_classes", "QwenAudioEncoder")
334
+ class QwenAudioEncoder(nn.Module):
335
+ def __init__(
336
+ self,
337
+ n_mels: int,
338
+ n_ctx: int,
339
+ n_state: int,
340
+ n_head: int,
341
+ n_layer: int,
342
+ output_dim: int = 512,
343
+ avg_pool: bool = True,
344
+ add_audio_bos_eos_token: bool = True,
345
+ **kwargs,
346
+ ):
347
+ super().__init__()
348
+ self.conv1 = Conv1d(n_mels, n_state, kernel_size=3, padding=1)
349
+ self.conv2 = Conv1d(n_state, n_state, kernel_size=3, stride=2, padding=1)
350
+ self.register_buffer("positional_embedding", sinusoids(n_ctx, n_state))
351
+
352
+ self.blocks: Iterable[ResidualAttentionBlock] = nn.ModuleList(
353
+ [ResidualAttentionBlock(n_state, n_head) for _ in range(n_layer)]
354
+ )
355
+ self.ln_post = LayerNorm(n_state)
356
+
357
+ if avg_pool:
358
+ self.avg_pooler = nn.AvgPool1d(2, stride=2)
359
+ else:
360
+ self.avg_pooler = None
361
+ self.proj = nn.Linear(n_state, output_dim)
362
+ if add_audio_bos_eos_token:
363
+ self.audio_bos_eos_token = nn.Embedding(2, output_dim)
364
+ else:
365
+ self.audio_bos_eos_token = None
366
+ self.output_dim = output_dim
367
+ self.n_head = n_head
368
+
369
+ def forward(self, x: Tensor, padding_mask: Tensor = None, audio_lengths: Tensor = None):
370
+ """
371
+ x : torch.Tensor, shape = (batch_size, n_mels, n_ctx)
372
+ the mel spectrogram of the audio
373
+ """
374
+ x = x.to(dtype=self.conv1.weight.dtype, device=self.conv1.weight.device)
375
+ if audio_lengths is not None:
376
+ input_mel_len = audio_lengths[:, 0] * 2
377
+ max_mel_len_in_batch = input_mel_len.max()
378
+ x = x[:, :, :max_mel_len_in_batch]
379
+ x = F.gelu(self.conv1(x))
380
+ x = F.gelu(self.conv2(x))
381
+ x = x.permute(0, 2, 1) # B, L, D
382
+ bsz = x.size(0)
383
+ src_len = x.size(1)
384
+
385
+ self.input_positional_embedding = self.positional_embedding[:src_len]
386
+ assert (
387
+ x.shape[1:] == self.input_positional_embedding.shape
388
+ ), f"incorrect audio shape: {x.shape[1:], self.input_positional_embedding.shape}"
389
+ x = (x + self.input_positional_embedding).to(x.dtype)
390
+ if padding_mask is not None:
391
+ padding_mask = padding_mask.to(
392
+ dtype=self.conv1.weight.dtype, device=self.conv1.weight.device
393
+ )
394
+ batch_src_len = padding_mask.size(1)
395
+ x = x[:, :batch_src_len, :]
396
+ padding_mask = padding_mask.view(bsz, -1, batch_src_len)
397
+ padding_mask_ = padding_mask.all(1)
398
+ x[padding_mask_] = 0
399
+ key_padding_mask = (
400
+ padding_mask_.view(bsz, 1, 1, batch_src_len)
401
+ .expand(-1, self.n_head, -1, -1)
402
+ .reshape(bsz, self.n_head, 1, batch_src_len)
403
+ )
404
+ new_padding_mask = torch.zeros_like(key_padding_mask, dtype=x.dtype)
405
+ padding_mask = new_padding_mask.masked_fill(key_padding_mask, float("-inf"))
406
+
407
+ for block in self.blocks:
408
+ x = block(x, mask=padding_mask)
409
+
410
+ if self.avg_pooler:
411
+ x = x.permute(0, 2, 1)
412
+ x = self.avg_pooler(x)
413
+ x = x.permute(0, 2, 1)
414
+
415
+ x = self.ln_post(x)
416
+ x = self.proj(x)
417
+
418
+ if self.audio_bos_eos_token is not None:
419
+ bos = self.audio_bos_eos_token.weight[0][None, :]
420
+ eos = self.audio_bos_eos_token.weight[1][None, :]
421
+ else:
422
+ bos, eos = None, None
423
+ return x, bos, eos
424
+
425
+ def encode(self, input_audios: Tensor, input_audio_lengths: Tensor, audio_span_tokens: List):
426
+ real_input_audio_lens = input_audio_lengths[:, 0].tolist()
427
+ max_len_in_batch = max(real_input_audio_lens)
428
+ padding_mask = torch.ones([input_audios.size(0), max_len_in_batch]).to(
429
+ dtype=self.conv1.weight.dtype, device=self.conv1.weight.device
430
+ )
431
+ for index in range(len(input_audios)):
432
+ padding_mask[index, : input_audio_lengths[index][0].item()] = 0
433
+ x, bos, eos = self(input_audios, padding_mask, input_audio_lengths)
434
+ output_audios = []
435
+ for i in range(len(audio_span_tokens)):
436
+ audio_span = audio_span_tokens[i]
437
+ audio = x[i][: audio_span - 2]
438
+ if bos is not None:
439
+ audio = torch.concat([bos, audio, eos])
440
+ assert len(audio) == audio_span
441
+ output_audios.append(audio)
442
+ return output_audios
funasr/models/qwen_audio/model.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from typing import Dict
3
+ from typing import Iterable, Optional
4
+ import time
5
+ import numpy as np
6
+ import torch
7
+ import torch.nn.functional as F
8
+ from torch import Tensor
9
+ from torch import nn
10
+ import whisper
11
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
12
+
13
+
14
+ from funasr.register import tables
15
+
16
+
17
+ @tables.register("model_classes", "Qwen/Qwen-Audio")
18
+ @tables.register("model_classes", "Qwen-Audio")
19
+ @tables.register("model_classes", "Qwen/QwenAudio")
20
+ @tables.register("model_classes", "QwenAudio")
21
+ @tables.register("model_classes", "QwenAudioWarp")
22
+ class QwenAudioWarp(nn.Module):
23
+ """
24
+ Qwen-Audio: Advancing Universal Audio Understanding via Unified Large-Scale Audio-Language Models
25
+ https://arxiv.org/abs/2311.07919
26
+ Modified from https://github.com/QwenLM/Qwen-Audio
27
+ """
28
+
29
+ def __init__(self, *args, **kwargs):
30
+ super().__init__()
31
+ from transformers import AutoModelForCausalLM, AutoTokenizer
32
+ from transformers.generation import GenerationConfig
33
+
34
+ model_or_path = kwargs.get("model_path", "QwenAudio")
35
+ model = AutoModelForCausalLM.from_pretrained(
36
+ model_or_path, device_map="cpu", trust_remote_code=True
37
+ )
38
+ tokenizer = AutoTokenizer.from_pretrained(model_or_path, trust_remote_code=True)
39
+
40
+ self.model = model
41
+ self.tokenizer = tokenizer
42
+
43
+ def forward(
44
+ self,
45
+ ):
46
+ pass
47
+
48
+ def inference(
49
+ self,
50
+ data_in,
51
+ data_lengths=None,
52
+ key: list = None,
53
+ tokenizer=None,
54
+ frontend=None,
55
+ **kwargs,
56
+ ):
57
+ if kwargs.get("batch_size", 1) > 1:
58
+ raise NotImplementedError("batch decoding is not implemented")
59
+
60
+ meta_data = {}
61
+ # meta_data["batch_data_time"] = -1
62
+ prompt = kwargs.get(
63
+ "prompt", "<|startoftranscription|><|en|><|transcribe|><|en|><|notimestamps|><|wo_itn|>"
64
+ )
65
+ query = f"<audio>{data_in[0]}</audio>{prompt}"
66
+ audio_info = self.tokenizer.process_audio(query)
67
+ inputs = self.tokenizer(query, return_tensors="pt", audio_info=audio_info)
68
+ inputs = inputs.to(self.model.device)
69
+ pred = self.model.generate(**inputs, audio_info=audio_info)
70
+ response = self.tokenizer.decode(
71
+ pred.cpu()[0], skip_special_tokens=False, audio_info=audio_info
72
+ )
73
+
74
+ results = []
75
+ result_i = {"key": key[0], "text": response}
76
+
77
+ results.append(result_i)
78
+
79
+ return results, meta_data
80
+
81
+
82
+ @tables.register("model_classes", "Qwen/Qwen-Audio-Chat")
83
+ @tables.register("model_classes", "Qwen/QwenAudioChat")
84
+ @tables.register("model_classes", "Qwen-Audio-Chat")
85
+ @tables.register("model_classes", "QwenAudioChat")
86
+ @tables.register("model_classes", "QwenAudioChatWarp")
87
+ class QwenAudioChatWarp(nn.Module):
88
+ def __init__(self, *args, **kwargs):
89
+ """
90
+ Qwen-Audio: Advancing Universal Audio Understanding via Unified Large-Scale Audio-Language Models
91
+ https://arxiv.org/abs/2311.07919
92
+ Modified from https://github.com/QwenLM/Qwen-Audio
93
+ """
94
+ super().__init__()
95
+ from transformers import AutoModelForCausalLM, AutoTokenizer
96
+ from transformers.generation import GenerationConfig
97
+
98
+ model_or_path = kwargs.get("model_path", "QwenAudio")
99
+ bf16 = kwargs.get("bf16", False)
100
+ fp16 = kwargs.get("fp16", False)
101
+ model = AutoModelForCausalLM.from_pretrained(
102
+ model_or_path, device_map="cpu", bf16=bf16, fp16=fp16, trust_remote_code=True
103
+ )
104
+ tokenizer = AutoTokenizer.from_pretrained(model_or_path, trust_remote_code=True)
105
+
106
+ self.model = model
107
+ self.tokenizer = tokenizer
108
+
109
+ def forward(
110
+ self,
111
+ ):
112
+ pass
113
+
114
+ def inference(
115
+ self,
116
+ data_in,
117
+ data_lengths=None,
118
+ key: list = None,
119
+ tokenizer=None,
120
+ frontend=None,
121
+ **kwargs,
122
+ ):
123
+ if kwargs.get("batch_size", 1) > 1:
124
+ raise NotImplementedError("batch decoding is not implemented")
125
+
126
+ meta_data = {}
127
+
128
+ prompt = kwargs.get("prompt", "what does the person say?")
129
+ cache = kwargs.get("cache", {})
130
+ history = cache.get("history", None)
131
+ if data_in[0] is not None:
132
+ # 1st dialogue turn
133
+ query = self.tokenizer.from_list_format(
134
+ [
135
+ {"audio": data_in[0]}, # Either a local path or an url
136
+ {"text": prompt},
137
+ ]
138
+ )
139
+ else:
140
+ query = prompt
141
+ response, history = self.model.chat(self.tokenizer, query=query, history=history)
142
+ cache["history"] = history
143
+ # print(response)
144
+ # The person says: "mister quilter is the apostle of the middle classes and we are glad to welcome his gospel".
145
+
146
+ results = []
147
+ result_i = {"key": key[0], "text": response}
148
+
149
+ results.append(result_i)
150
+
151
+ return results, meta_data
funasr/models/qwen_audio/template.yaml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is an example that demonstrates how to configure a model file.
2
+ # You can modify the configuration according to your own requirements.
3
+
4
+ # to print the register_table:
5
+ # from funasr.register import tables
6
+ # tables.print()
7
+
8
+ # network architecture
9
+ model: WhisperWarp
10
+ model_conf:
11
+ lsm_weight: 0.1
12
+ length_normalized_loss: true
13
+ hub: funasr # openai
14
+ init_param_path: null # large-v2 or large-v3 if hub == "openai"
15
+
16
+
17
+
18
+ # only use for hub == funasr,
19
+ # if hub == openai, whisper_dims is automaticall download
20
+ whisper_dims:
21
+ 'n_mels': 80
22
+ 'n_vocab': 51865
23
+ 'n_audio_ctx': 1500
24
+ 'n_audio_state': 1280
25
+ 'n_audio_head': 20
26
+ 'n_audio_layer': 32
27
+ 'n_text_ctx': 448
28
+ 'n_text_state': 1280
29
+ 'n_text_head': 20
30
+ 'n_text_layer': 32
31
+
32
+ # frontend related
33
+ frontend: WhisperFrontend
34
+ frontend_conf:
35
+ fs: 16000
36
+ n_mels: 80
37
+ do_pad_trim: true
38
+
39
+ tokenizer: WhisperTokenizer
40
+ tokenizer_conf:
41
+ language: null
42
+ task: transcribe
43
+ is_multilingual: true
44
+ num_languages: 99
45
+
46
+ scope_map: ['none', "model."]
funasr/models/rwkv_bat/__init__.py ADDED
File without changes
funasr/models/rwkv_bat/cuda_decoder/wkv_cuda.cu ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copied from https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v4/cuda/wkv_cuda.cu
2
+
3
+ #include <stdio.h>
4
+ #include <assert.h>
5
+
6
+ #define MIN_VALUE (-1e38)
7
+
8
+ template <typename F>
9
+ __global__ void kernel_forward(const int B, const int T, const int C,
10
+ const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v,
11
+ F *__restrict__ const _y) {
12
+ const int idx = blockIdx.x * blockDim.x + threadIdx.x;
13
+ const int _b = idx / C;
14
+ const int _c = idx % C;
15
+ const int _offset = _b * T * C + _c;
16
+
17
+ F u = _u[_c];
18
+ F w = _w[_c];
19
+ const F *__restrict__ const k = _k + _offset;
20
+ const F *__restrict__ const v = _v + _offset;
21
+ F *__restrict__ const y = _y + _offset;
22
+
23
+ // aa and bb are running sums divided by exp(pp) (to avoid overflow)
24
+ F aa = 0, bb = 0, pp = MIN_VALUE;
25
+ for (int i = 0; i < T; i++) {
26
+ const int ii = i * C;
27
+ const F kk = k[ii];
28
+ const F vv = v[ii];
29
+
30
+ F ww = u + kk;
31
+ F p = max(pp, ww);
32
+ F e1 = exp(pp - p);
33
+ F e2 = exp(ww - p);
34
+ y[ii] = (e1 * aa + e2 * vv) / (e1 * bb + e2);
35
+
36
+ ww = w + pp;
37
+ p = max(ww, kk);
38
+ e1 = exp(ww - p);
39
+ e2 = exp(kk - p);
40
+ aa = e1 * aa + e2 * vv;
41
+ bb = e1 * bb + e2;
42
+ pp = p;
43
+ }
44
+ }
45
+
46
+ template <typename F>
47
+ __global__ void kernel_backward(const int B, const int T, const int C,
48
+ const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v,
49
+ const F *__restrict__ const _y, const F *__restrict__ const _gy,
50
+ F *__restrict__ const _gw, F *__restrict__ const _gu, F *__restrict__ const _gk, F *__restrict__ const _gv) {
51
+ const int idx = blockIdx.x * blockDim.x + threadIdx.x;
52
+ const int _b = idx / C;
53
+ const int _c = idx % C;
54
+ const int _offset = _b * T * C + _c;
55
+
56
+ F u = _u[_c];
57
+ F w = _w[_c];
58
+ const F *__restrict__ const k = _k + _offset;
59
+ const F *__restrict__ const v = _v + _offset;
60
+ const F *__restrict__ const y = _y + _offset;
61
+ const F *__restrict__ const gy = _gy + _offset;
62
+ F *__restrict__ const gk = _gk + _offset;
63
+ F *__restrict__ const gv = _gv + _offset;
64
+
65
+ F q[Tmax], r[Tmax];
66
+
67
+ F gw = 0, gu = 0, aa = 0, bb = 0, ga = 0, gb = 0, pp = MIN_VALUE;
68
+ for (int i = 0; i < T; i++) {
69
+ const int ii = i * C;
70
+ const F kk = k[ii];
71
+ const F vv = v[ii];
72
+ const F yy = y[ii];
73
+
74
+ F ww = u + kk;
75
+ F p = max(pp, ww);
76
+ F e1 = exp(pp - p);
77
+ F e2 = exp(ww - p);
78
+ const F qq = gy[ii] / (e1 * bb + e2);
79
+ gw += (ga - gb * yy) * e1 * qq;
80
+ gu += (vv - yy) * e2 * qq;
81
+ q[i] = qq;
82
+ r[i] = ww - p;
83
+
84
+ ww = w + pp;
85
+ p = max(ww, kk);
86
+ e1 = exp(ww - p);
87
+ e2 = exp(kk - p);
88
+ ga = e1 * (aa + ga);
89
+ gb = e1 * (bb + gb);
90
+ aa = e1 * aa + e2 * vv;
91
+ bb = e1 * bb + e2;
92
+ pp = p;
93
+ }
94
+ const int _offsetBC = _b * C + _c;
95
+ _gw[_offsetBC] = gw * _w[_c]; // multiply by w because of w -> -exp(w) in python forward()
96
+ _gu[_offsetBC] = gu;
97
+
98
+ aa = 0, bb = 0, pp = MIN_VALUE;
99
+ for (int i = T - 1; i >= 0; i--) {
100
+ const int ii = i * C;
101
+ const F kk = k[ii];
102
+ const F vv = v[ii];
103
+ const F yy = y[ii];
104
+ const F qq = q[i];
105
+ const F rr = r[i];
106
+
107
+ F e1 = qq * exp(rr);
108
+ F e2 = exp(kk + pp);
109
+ gk[ii] = e1 * (vv - yy) + e2 * (aa * vv + bb);
110
+ gv[ii] = e1 + e2 * aa;
111
+
112
+ const F ww = w + pp;
113
+ const F www = rr - u - kk;
114
+ const F p = max(ww, www);
115
+ e1 = exp(ww - p);
116
+ e2 = qq * exp(www - p);
117
+ aa = e1 * aa + e2;
118
+ bb = e1 * bb - e2 * yy;
119
+ pp = p;
120
+ }
121
+ }
122
+
123
+ void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y) {
124
+ dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance
125
+ assert(B * C % threadsPerBlock.x == 0);
126
+ dim3 numBlocks(B * C / threadsPerBlock.x);
127
+ kernel_forward<<<numBlocks, threadsPerBlock>>>(B, T, C, w, u, k, v, y);
128
+ }
129
+
130
+ void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv) {
131
+ dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance
132
+ assert(B * C % threadsPerBlock.x == 0);
133
+ dim3 numBlocks(B * C / threadsPerBlock.x);
134
+ kernel_backward<<<numBlocks, threadsPerBlock>>>(B, T, C, w, u, k, v, y, gy, gw, gu, gk, gv);
135
+ }
funasr/models/rwkv_bat/cuda_decoder/wkv_op.cpp ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Bsed on https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v4/cuda/wkv_op.cpp
3
+ Function signatures were modified based on https://github.com/huggingface/transformers/blob/main/src/transformers/kernels/rwkv/wkv_op.cpp
4
+
5
+ */
6
+
7
+ #include <torch/extension.h>
8
+
9
+ void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y);
10
+
11
+ void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv);
12
+
13
+ void forward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y) {
14
+ const int B = k.size(0);
15
+ const int T = k.size(1);
16
+ const int C = k.size(2);
17
+
18
+ cuda_forward(B, T, C, w.data_ptr<float>(), u.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), y.data_ptr<float>());
19
+ }
20
+
21
+ void backward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &gy, torch::Tensor &gw, torch::Tensor &gu, torch::Tensor &gk, torch::Tensor &gv) {
22
+ const int B = k.size(0);
23
+ const int T = k.size(1);
24
+ const int C = k.size(2);
25
+
26
+ cuda_backward(B, T, C, w.data_ptr<float>(), u.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), y.data_ptr<float>(), gy.data_ptr<float>(), gw.data_ptr<float>(), gu.data_ptr<float>(), gk.data_ptr<float>(), gv.data_ptr<float>());
27
+ }
28
+
29
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
30
+ m.def("forward", &forward, "wkv forward");
31
+ m.def("backward", &backward, "wkv backward");
32
+ }
33
+
34
+ TORCH_LIBRARY(wkv_decoder, m) {
35
+ m.def("forward", forward);
36
+ m.def("backward", backward);
37
+ }
funasr/models/rwkv_bat/cuda_encoder/wkv_cuda.cu ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copied from https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v4/cuda/wkv_cuda.cu
2
+
3
+ #include <stdio.h>
4
+ #include <assert.h>
5
+
6
+ #define MIN_VALUE (-1e38)
7
+
8
+ template <typename F>
9
+ __global__ void kernel_forward(const int B, const int T, const int C,
10
+ const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v,
11
+ F *__restrict__ const _y) {
12
+ const int idx = blockIdx.x * blockDim.x + threadIdx.x;
13
+ const int _b = idx / C;
14
+ const int _c = idx % C;
15
+ const int _offset = _b * T * C + _c;
16
+
17
+ F u = _u[_c];
18
+ F w = _w[_c];
19
+ const F *__restrict__ const k = _k + _offset;
20
+ const F *__restrict__ const v = _v + _offset;
21
+ F *__restrict__ const y = _y + _offset;
22
+
23
+ // aa and bb are running sums divided by exp(pp) (to avoid overflow)
24
+ F aa = 0, bb = 0, pp = MIN_VALUE;
25
+ for (int i = 0; i < T; i++) {
26
+ const int ii = i * C;
27
+ const F kk = k[ii];
28
+ const F vv = v[ii];
29
+
30
+ F ww = u + kk;
31
+ F p = max(pp, ww);
32
+ F e1 = exp(pp - p);
33
+ F e2 = exp(ww - p);
34
+ y[ii] = (e1 * aa + e2 * vv) / (e1 * bb + e2);
35
+
36
+ ww = w + pp;
37
+ p = max(ww, kk);
38
+ e1 = exp(ww - p);
39
+ e2 = exp(kk - p);
40
+ aa = e1 * aa + e2 * vv;
41
+ bb = e1 * bb + e2;
42
+ pp = p;
43
+ }
44
+ }
45
+
46
+ template <typename F>
47
+ __global__ void kernel_backward(const int B, const int T, const int C,
48
+ const F *__restrict__ const _w, const F *__restrict__ const _u, const F *__restrict__ const _k, const F *__restrict__ const _v,
49
+ const F *__restrict__ const _y, const F *__restrict__ const _gy,
50
+ F *__restrict__ const _gw, F *__restrict__ const _gu, F *__restrict__ const _gk, F *__restrict__ const _gv) {
51
+ const int idx = blockIdx.x * blockDim.x + threadIdx.x;
52
+ const int _b = idx / C;
53
+ const int _c = idx % C;
54
+ const int _offset = _b * T * C + _c;
55
+
56
+ F u = _u[_c];
57
+ F w = _w[_c];
58
+ const F *__restrict__ const k = _k + _offset;
59
+ const F *__restrict__ const v = _v + _offset;
60
+ const F *__restrict__ const y = _y + _offset;
61
+ const F *__restrict__ const gy = _gy + _offset;
62
+ F *__restrict__ const gk = _gk + _offset;
63
+ F *__restrict__ const gv = _gv + _offset;
64
+
65
+ F q[Tmax], r[Tmax];
66
+
67
+ F gw = 0, gu = 0, aa = 0, bb = 0, ga = 0, gb = 0, pp = MIN_VALUE;
68
+ for (int i = 0; i < T; i++) {
69
+ const int ii = i * C;
70
+ const F kk = k[ii];
71
+ const F vv = v[ii];
72
+ const F yy = y[ii];
73
+
74
+ F ww = u + kk;
75
+ F p = max(pp, ww);
76
+ F e1 = exp(pp - p);
77
+ F e2 = exp(ww - p);
78
+ const F qq = gy[ii] / (e1 * bb + e2);
79
+ gw += (ga - gb * yy) * e1 * qq;
80
+ gu += (vv - yy) * e2 * qq;
81
+ q[i] = qq;
82
+ r[i] = ww - p;
83
+
84
+ ww = w + pp;
85
+ p = max(ww, kk);
86
+ e1 = exp(ww - p);
87
+ e2 = exp(kk - p);
88
+ ga = e1 * (aa + ga);
89
+ gb = e1 * (bb + gb);
90
+ aa = e1 * aa + e2 * vv;
91
+ bb = e1 * bb + e2;
92
+ pp = p;
93
+ }
94
+ const int _offsetBC = _b * C + _c;
95
+ _gw[_offsetBC] = gw * _w[_c]; // multiply by w because of w -> -exp(w) in python forward()
96
+ _gu[_offsetBC] = gu;
97
+
98
+ aa = 0, bb = 0, pp = MIN_VALUE;
99
+ for (int i = T - 1; i >= 0; i--) {
100
+ const int ii = i * C;
101
+ const F kk = k[ii];
102
+ const F vv = v[ii];
103
+ const F yy = y[ii];
104
+ const F qq = q[i];
105
+ const F rr = r[i];
106
+
107
+ F e1 = qq * exp(rr);
108
+ F e2 = exp(kk + pp);
109
+ gk[ii] = e1 * (vv - yy) + e2 * (aa * vv + bb);
110
+ gv[ii] = e1 + e2 * aa;
111
+
112
+ const F ww = w + pp;
113
+ const F www = rr - u - kk;
114
+ const F p = max(ww, www);
115
+ e1 = exp(ww - p);
116
+ e2 = qq * exp(www - p);
117
+ aa = e1 * aa + e2;
118
+ bb = e1 * bb - e2 * yy;
119
+ pp = p;
120
+ }
121
+ }
122
+
123
+ void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y) {
124
+ dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance
125
+ assert(B * C % threadsPerBlock.x == 0);
126
+ dim3 numBlocks(B * C / threadsPerBlock.x);
127
+ kernel_forward<<<numBlocks, threadsPerBlock>>>(B, T, C, w, u, k, v, y);
128
+ }
129
+
130
+ void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv) {
131
+ dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance
132
+ assert(B * C % threadsPerBlock.x == 0);
133
+ dim3 numBlocks(B * C / threadsPerBlock.x);
134
+ kernel_backward<<<numBlocks, threadsPerBlock>>>(B, T, C, w, u, k, v, y, gy, gw, gu, gk, gv);
135
+ }
funasr/models/rwkv_bat/cuda_encoder/wkv_op.cpp ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Bsed on https://github.com/BlinkDL/RWKV-LM/blob/main/RWKV-v4/cuda/wkv_op.cpp
3
+ Function signatures were modified based on https://github.com/huggingface/transformers/blob/main/src/transformers/kernels/rwkv/wkv_op.cpp
4
+
5
+ */
6
+
7
+ #include <torch/extension.h>
8
+
9
+ void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y);
10
+
11
+ void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv);
12
+
13
+ void forward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y) {
14
+ const int B = k.size(0);
15
+ const int T = k.size(1);
16
+ const int C = k.size(2);
17
+
18
+ cuda_forward(B, T, C, w.data_ptr<float>(), u.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), y.data_ptr<float>());
19
+ }
20
+
21
+ void backward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &gy, torch::Tensor &gw, torch::Tensor &gu, torch::Tensor &gk, torch::Tensor &gv) {
22
+ const int B = k.size(0);
23
+ const int T = k.size(1);
24
+ const int C = k.size(2);
25
+
26
+ cuda_backward(B, T, C, w.data_ptr<float>(), u.data_ptr<float>(), k.data_ptr<float>(), v.data_ptr<float>(), y.data_ptr<float>(), gy.data_ptr<float>(), gw.data_ptr<float>(), gu.data_ptr<float>(), gk.data_ptr<float>(), gv.data_ptr<float>());
27
+ }
28
+
29
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
30
+ m.def("forward", &forward, "wkv forward");
31
+ m.def("backward", &backward, "wkv backward");
32
+ }
33
+
34
+ TORCH_LIBRARY(wkv_encoder, m) {
35
+ m.def("forward", forward);
36
+ m.def("backward", backward);
37
+ }
funasr/models/rwkv_bat/rwkv.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import torch
7
+ from typing import Dict, Optional, Tuple
8
+
9
+ from funasr.models.transformer.layer_norm import LayerNorm
10
+ from funasr.models.rwkv_bat.rwkv_feed_forward import FeedForward
11
+ from funasr.models.rwkv_bat.rwkv_attention import EncoderSelfAttention, DecoderSelfAttention
12
+
13
+
14
+ class RWKV(torch.nn.Module):
15
+ """RWKV module.
16
+
17
+ Args:
18
+ size: Input/Output size.
19
+ linear_size: Feed-forward hidden size.
20
+ attention_size: SelfAttention hidden size.
21
+ context_size: Context size for WKV computation.
22
+ block_id: Block index.
23
+ num_blocks: Number of blocks in the architecture.
24
+ normalization_class: Normalization layer class.
25
+ normalization_args: Normalization layer arguments.
26
+ att_dropout_rate: Dropout rate for the attention module.
27
+ ffn_dropout_rate: Dropout rate for the feed-forward module.
28
+
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ size: int,
34
+ linear_size: int,
35
+ attention_size: int,
36
+ context_size: int,
37
+ block_id: int,
38
+ num_blocks: int,
39
+ att_dropout_rate: float = 0.0,
40
+ ffn_dropout_rate: float = 0.0,
41
+ dropout_rate: float = 0.0,
42
+ ) -> None:
43
+ """Construct a RWKV object."""
44
+ super().__init__()
45
+
46
+ self.layer_norm_att = LayerNorm(size)
47
+ self.layer_norm_ffn = LayerNorm(size)
48
+
49
+ self.att = EncoderSelfAttention(
50
+ size, attention_size, context_size, block_id, att_dropout_rate, num_blocks
51
+ )
52
+ self.dropout_att = torch.nn.Dropout(p=dropout_rate)
53
+
54
+ self.ffn = FeedForward(size, linear_size, block_id, ffn_dropout_rate, num_blocks)
55
+ self.dropout_ffn = torch.nn.Dropout(p=dropout_rate)
56
+
57
+ def forward(
58
+ self,
59
+ x: torch.Tensor,
60
+ state: Optional[torch.Tensor] = None,
61
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
62
+ """Compute receptance weighted key value.
63
+
64
+ Args:
65
+ x: RWKV input sequences. (B, L, size)
66
+ state: Decoder hidden states. [5 x (B, D_att/size, N)]
67
+
68
+ Returns:
69
+ x: RWKV output sequences. (B, L, size)
70
+ x: Decoder hidden states. [5 x (B, D_att/size, N)]
71
+
72
+ """
73
+ att, state = self.att(self.layer_norm_att(x), state=state)
74
+ x = x + self.dropout_att(att)
75
+ ffn, state = self.ffn(self.layer_norm_ffn(x), state=state)
76
+ x = x + self.dropout_ffn(ffn)
77
+ return x, state
78
+
79
+
80
+ class RWKVDecoderLayer(torch.nn.Module):
81
+ """RWKV module.
82
+
83
+ Args:
84
+ size: Input/Output size.
85
+ linear_size: Feed-forward hidden size.
86
+ attention_size: SelfAttention hidden size.
87
+ context_size: Context size for WKV computation.
88
+ block_id: Block index.
89
+ num_blocks: Number of blocks in the architecture.
90
+ normalization_class: Normalization layer class.
91
+ normalization_args: Normalization layer arguments.
92
+ att_dropout_rate: Dropout rate for the attention module.
93
+ ffn_dropout_rate: Dropout rate for the feed-forward module.
94
+
95
+ """
96
+
97
+ def __init__(
98
+ self,
99
+ size: int,
100
+ linear_size: int,
101
+ attention_size: int,
102
+ context_size: int,
103
+ block_id: int,
104
+ num_blocks: int,
105
+ att_dropout_rate: float = 0.0,
106
+ ffn_dropout_rate: float = 0.0,
107
+ dropout_rate: float = 0.0,
108
+ ) -> None:
109
+ """Construct a RWKV object."""
110
+ super().__init__()
111
+
112
+ self.layer_norm_att = LayerNorm(size)
113
+ self.layer_norm_ffn = LayerNorm(size)
114
+
115
+ self.att = DecoderSelfAttention(
116
+ size, attention_size, context_size, block_id, att_dropout_rate, num_blocks
117
+ )
118
+ self.dropout_att = torch.nn.Dropout(p=dropout_rate)
119
+
120
+ self.ffn = FeedForward(size, linear_size, block_id, ffn_dropout_rate, num_blocks)
121
+ self.dropout_ffn = torch.nn.Dropout(p=dropout_rate)
122
+
123
+ def forward(
124
+ self,
125
+ x: torch.Tensor,
126
+ state: Optional[torch.Tensor] = None,
127
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
128
+ """Compute receptance weighted key value.
129
+
130
+ Args:
131
+ x: RWKV input sequences. (B, L, size)
132
+ state: Decoder hidden states. [5 x (B, D_att/size, N)]
133
+
134
+ Returns:
135
+ x: RWKV output sequences. (B, L, size)
136
+ x: Decoder hidden states. [5 x (B, D_att/size, N)]
137
+
138
+ """
139
+ att, state = self.att(self.layer_norm_att(x), state=state)
140
+ x = x + self.dropout_att(att)
141
+
142
+ ffn, state = self.ffn(self.layer_norm_ffn(x), state=state)
143
+ x = x + self.dropout_ffn(ffn)
144
+
145
+ return x, state
funasr/models/rwkv_bat/rwkv_attention.py ADDED
@@ -0,0 +1,608 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import math
7
+ import torch
8
+ from pathlib import Path
9
+ from importlib.util import find_spec
10
+ from typing import List, Optional, Tuple, Union
11
+
12
+
13
+ wkv_kernel_encoder = None
14
+ wkv_kernel_decoder = None
15
+
16
+
17
+ class WKVLinearAttentionEncoder(torch.autograd.Function):
18
+ """WKVLinearAttention function definition."""
19
+
20
+ @staticmethod
21
+ def forward(
22
+ ctx,
23
+ time_decay: torch.Tensor,
24
+ time_first: torch.Tensor,
25
+ key: torch.Tensor,
26
+ value: torch.tensor,
27
+ ) -> torch.Tensor:
28
+ """WKVLinearAttention function forward pass.
29
+
30
+ Args:
31
+ time_decay: Channel-wise time decay vector. (D_att)
32
+ time_first: Channel-wise time first vector. (D_att)
33
+ key: Key tensor. (B, U, D_att)
34
+ value: Value tensor. (B, U, D_att)
35
+
36
+ Returns:
37
+ out: Weighted Key-Value tensor. (B, U, D_att)
38
+
39
+ """
40
+ batch, length, dim = key.size()
41
+
42
+ assert length <= wkv_kernel_encoder.context_size, (
43
+ f"Cannot process key of length {length} while context_size "
44
+ f"is ({wkv_kernel_encoder.context_size}). Limit should be increased."
45
+ )
46
+
47
+ assert batch * dim % min(dim, 32) == 0, (
48
+ f"batch size ({batch}) by dimension ({dim}) should be a multiple of " f"{min(dim, 32)}"
49
+ )
50
+
51
+ ctx.input_dtype = key.dtype
52
+
53
+ time_decay = -torch.exp(time_decay.float().contiguous())
54
+ time_first = time_first.float().contiguous()
55
+
56
+ key = key.float().contiguous()
57
+ value = value.float().contiguous()
58
+
59
+ out = torch.empty_like(key, memory_format=torch.contiguous_format)
60
+
61
+ wkv_kernel_encoder.forward(time_decay, time_first, key, value, out)
62
+ ctx.save_for_backward(time_decay, time_first, key, value, out)
63
+
64
+ return out
65
+
66
+ @staticmethod
67
+ def backward(
68
+ ctx, grad_output: torch.Tensor
69
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
70
+ """WKVLinearAttention function backward pass.
71
+
72
+ Args:
73
+ grad_output: Output gradient. (B, U, D_att)
74
+
75
+ Returns:
76
+ grad_time_decay: Gradient for channel-wise time decay vector. (D_att)
77
+ grad_time_first: Gradient for channel-wise time first vector. (D_att)
78
+ grad_key: Gradient for key tensor. (B, U, D_att)
79
+ grad_value: Gradient for value tensor. (B, U, D_att)
80
+
81
+ """
82
+ time_decay, time_first, key, value, output = ctx.saved_tensors
83
+ grad_dtype = ctx.input_dtype
84
+
85
+ batch, _, dim = key.size()
86
+
87
+ grad_time_decay = torch.empty(
88
+ (batch, dim),
89
+ memory_format=torch.contiguous_format,
90
+ dtype=time_decay.dtype,
91
+ device=time_decay.device,
92
+ )
93
+
94
+ grad_time_first = torch.empty(
95
+ (batch, dim),
96
+ memory_format=torch.contiguous_format,
97
+ dtype=time_decay.dtype,
98
+ device=time_decay.device,
99
+ )
100
+
101
+ grad_key = torch.empty_like(key, memory_format=torch.contiguous_format)
102
+ grad_value = torch.empty_like(value, memory_format=torch.contiguous_format)
103
+
104
+ wkv_kernel_encoder.backward(
105
+ time_decay,
106
+ time_first,
107
+ key,
108
+ value,
109
+ output,
110
+ grad_output.contiguous(),
111
+ grad_time_decay,
112
+ grad_time_first,
113
+ grad_key,
114
+ grad_value,
115
+ )
116
+
117
+ grad_time_decay = torch.sum(grad_time_decay, dim=0)
118
+ grad_time_first = torch.sum(grad_time_first, dim=0)
119
+
120
+ return (
121
+ grad_time_decay,
122
+ grad_time_first,
123
+ grad_key,
124
+ grad_value,
125
+ )
126
+
127
+
128
+ class WKVLinearAttentionDecoder(torch.autograd.Function):
129
+ """WKVLinearAttention function definition."""
130
+
131
+ @staticmethod
132
+ def forward(
133
+ ctx,
134
+ time_decay: torch.Tensor,
135
+ time_first: torch.Tensor,
136
+ key: torch.Tensor,
137
+ value: torch.tensor,
138
+ ) -> torch.Tensor:
139
+ """WKVLinearAttention function forward pass.
140
+
141
+ Args:
142
+ time_decay: Channel-wise time decay vector. (D_att)
143
+ time_first: Channel-wise time first vector. (D_att)
144
+ key: Key tensor. (B, U, D_att)
145
+ value: Value tensor. (B, U, D_att)
146
+
147
+ Returns:
148
+ out: Weighted Key-Value tensor. (B, U, D_att)
149
+
150
+ """
151
+ batch, length, dim = key.size()
152
+
153
+ assert length <= wkv_kernel_decoder.context_size, (
154
+ f"Cannot process key of length {length} while context_size "
155
+ f"is ({wkv_kernel.context_size}). Limit should be increased."
156
+ )
157
+
158
+ assert batch * dim % min(dim, 32) == 0, (
159
+ f"batch size ({batch}) by dimension ({dim}) should be a multiple of " f"{min(dim, 32)}"
160
+ )
161
+
162
+ ctx.input_dtype = key.dtype
163
+
164
+ time_decay = -torch.exp(time_decay.float().contiguous())
165
+ time_first = time_first.float().contiguous()
166
+
167
+ key = key.float().contiguous()
168
+ value = value.float().contiguous()
169
+
170
+ out = torch.empty_like(key, memory_format=torch.contiguous_format)
171
+
172
+ wkv_kernel_decoder.forward(time_decay, time_first, key, value, out)
173
+ ctx.save_for_backward(time_decay, time_first, key, value, out)
174
+
175
+ return out
176
+
177
+ @staticmethod
178
+ def backward(
179
+ ctx, grad_output: torch.Tensor
180
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
181
+ """WKVLinearAttention function backward pass.
182
+
183
+ Args:
184
+ grad_output: Output gradient. (B, U, D_att)
185
+
186
+ Returns:
187
+ grad_time_decay: Gradient for channel-wise time decay vector. (D_att)
188
+ grad_time_first: Gradient for channel-wise time first vector. (D_att)
189
+ grad_key: Gradient for key tensor. (B, U, D_att)
190
+ grad_value: Gradient for value tensor. (B, U, D_att)
191
+
192
+ """
193
+ time_decay, time_first, key, value, output = ctx.saved_tensors
194
+ grad_dtype = ctx.input_dtype
195
+
196
+ batch, _, dim = key.size()
197
+
198
+ grad_time_decay = torch.empty(
199
+ (batch, dim),
200
+ memory_format=torch.contiguous_format,
201
+ dtype=time_decay.dtype,
202
+ device=time_decay.device,
203
+ )
204
+
205
+ grad_time_first = torch.empty(
206
+ (batch, dim),
207
+ memory_format=torch.contiguous_format,
208
+ dtype=time_decay.dtype,
209
+ device=time_decay.device,
210
+ )
211
+
212
+ grad_key = torch.empty_like(key, memory_format=torch.contiguous_format)
213
+ grad_value = torch.empty_like(value, memory_format=torch.contiguous_format)
214
+
215
+ wkv_kernel_decoder.backward(
216
+ time_decay,
217
+ time_first,
218
+ key,
219
+ value,
220
+ output,
221
+ grad_output.contiguous(),
222
+ grad_time_decay,
223
+ grad_time_first,
224
+ grad_key,
225
+ grad_value,
226
+ )
227
+
228
+ grad_time_decay = torch.sum(grad_time_decay, dim=0)
229
+ grad_time_first = torch.sum(grad_time_first, dim=0)
230
+
231
+ return (
232
+ grad_time_decay,
233
+ grad_time_first,
234
+ grad_key,
235
+ grad_value,
236
+ )
237
+
238
+
239
+ def load_encoder_wkv_kernel(context_size: int) -> None:
240
+ """Load WKV CUDA kernel.
241
+
242
+ Args:
243
+ context_size: Context size.
244
+
245
+ """
246
+ from torch.utils.cpp_extension import load
247
+
248
+ global wkv_kernel_encoder
249
+
250
+ if wkv_kernel_encoder is not None and wkv_kernel_encoder.context_size == context_size:
251
+ return
252
+
253
+ if find_spec("ninja") is None:
254
+ raise ImportError(
255
+ "Ninja package was not found. WKV kernel module can't be loaded "
256
+ "for training. Please, 'pip install ninja' in your environment."
257
+ )
258
+
259
+ if not torch.cuda.is_available():
260
+ raise ImportError(
261
+ "CUDA is currently a requirement for WKV kernel loading. "
262
+ "Please set your devices properly and launch again."
263
+ )
264
+
265
+ kernel_folder = Path(__file__).resolve().parent / "cuda_encoder"
266
+ kernel_files = [kernel_folder / f for f in ["wkv_op.cpp", "wkv_cuda.cu"]]
267
+
268
+ kernel_cflags = [
269
+ "-res-usage",
270
+ "--maxrregcount 60",
271
+ "--use_fast_math",
272
+ "-O3",
273
+ "-Xptxas -O3",
274
+ f"-DTmax={context_size}",
275
+ ]
276
+ wkv_kernel_encoder = load(
277
+ name=f"encoder_wkv_{context_size}",
278
+ sources=kernel_files,
279
+ verbose=True,
280
+ extra_cuda_cflags=kernel_cflags,
281
+ )
282
+ wkv_kernel_encoder.context_size = context_size
283
+
284
+
285
+ def load_decoder_wkv_kernel(context_size: int) -> None:
286
+ """Load WKV CUDA kernel.
287
+
288
+ Args:
289
+ context_size: Context size.
290
+
291
+ """
292
+ from torch.utils.cpp_extension import load
293
+
294
+ global wkv_kernel_decoder
295
+
296
+ if wkv_kernel_decoder is not None and wkv_kernel_decoder.context_size == context_size:
297
+ return
298
+
299
+ if find_spec("ninja") is None:
300
+ raise ImportError(
301
+ "Ninja package was not found. WKV kernel module can't be loaded "
302
+ "for training. Please, 'pip install ninja' in your environment."
303
+ )
304
+
305
+ if not torch.cuda.is_available():
306
+ raise ImportError(
307
+ "CUDA is currently a requirement for WKV kernel loading. "
308
+ "Please set your devices properly and launch again."
309
+ )
310
+
311
+ kernel_folder = Path(__file__).resolve().parent / "cuda_decoder"
312
+ kernel_files = [kernel_folder / f for f in ["wkv_op.cpp", "wkv_cuda.cu"]]
313
+
314
+ kernel_cflags = [
315
+ "-res-usage",
316
+ "--maxrregcount 60",
317
+ "--use_fast_math",
318
+ "-O3",
319
+ "-Xptxas -O3",
320
+ f"-DTmax={context_size}",
321
+ ]
322
+ wkv_kernel_decoder = load(
323
+ name=f"decoder_wkv_{context_size}",
324
+ sources=kernel_files,
325
+ verbose=True,
326
+ extra_cuda_cflags=kernel_cflags,
327
+ )
328
+ wkv_kernel_decoder.context_size = context_size
329
+
330
+
331
+ class SelfAttention(torch.nn.Module):
332
+ """SelfAttention module definition.
333
+
334
+ Args:
335
+ size: Input/Output size.
336
+ attention_size: Attention hidden size.
337
+ context_size: Context size for WKV kernel.
338
+ block_id: Block index.
339
+ num_blocks: Number of blocks in the architecture.
340
+
341
+ """
342
+
343
+ def __init__(
344
+ self,
345
+ size: int,
346
+ attention_size: int,
347
+ block_id: int,
348
+ dropout_rate: float,
349
+ num_blocks: int,
350
+ ) -> None:
351
+ """Construct a SelfAttention object."""
352
+ super().__init__()
353
+ self.time_shift = torch.nn.ZeroPad2d((0, 0, 1, -1))
354
+
355
+ self.time_decay = torch.nn.Parameter(torch.empty(attention_size))
356
+ self.time_first = torch.nn.Parameter(torch.empty(attention_size))
357
+
358
+ self.time_mix_key = torch.nn.Parameter(torch.empty(1, 1, size))
359
+ self.time_mix_value = torch.nn.Parameter(torch.empty(1, 1, size))
360
+ self.time_mix_receptance = torch.nn.Parameter(torch.empty(1, 1, size))
361
+
362
+ self.proj_key = torch.nn.Linear(size, attention_size, bias=True)
363
+ self.proj_value = torch.nn.Linear(size, attention_size, bias=True)
364
+ self.proj_receptance = torch.nn.Linear(size, attention_size, bias=True)
365
+
366
+ self.proj_output = torch.nn.Linear(attention_size, size, bias=True)
367
+
368
+ self.block_id = block_id
369
+
370
+ self.reset_parameters(size, attention_size, block_id, num_blocks)
371
+ self.dropout = torch.nn.Dropout(p=dropout_rate)
372
+
373
+ def reset_parameters(
374
+ self, size: int, attention_size: int, block_id: int, num_blocks: int
375
+ ) -> None:
376
+ """Reset module parameters.
377
+
378
+ Args:
379
+ size: Block size.
380
+ attention_size: Attention hidden size.
381
+ block_id: Block index.
382
+ num_blocks: Number of blocks in the architecture.
383
+
384
+ """
385
+ ratio_0_to_1 = block_id / (num_blocks - 1)
386
+ ratio_1_to_almost0 = 1.0 - (block_id / num_blocks)
387
+
388
+ time_weight = torch.ones(1, 1, size)
389
+
390
+ for i in range(size):
391
+ time_weight[0, 0, i] = i / size
392
+
393
+ decay_speed = [
394
+ -5 + 8 * (h / (attention_size - 1)) ** (0.7 + 1.3 * ratio_0_to_1)
395
+ for h in range(attention_size)
396
+ ]
397
+ decay_speed = torch.tensor(
398
+ decay_speed, dtype=self.time_decay.dtype, device=self.time_decay.device
399
+ )
400
+
401
+ zigzag = (
402
+ torch.tensor(
403
+ [(i + 1) % 3 - 1 for i in range(attention_size)],
404
+ dtype=self.time_first.dtype,
405
+ device=self.time_first.device,
406
+ )
407
+ * 0.5
408
+ )
409
+
410
+ with torch.no_grad():
411
+ self.time_decay.data = decay_speed
412
+ self.time_first.data = torch.ones_like(self.time_first * math.log(0.3) + zigzag)
413
+
414
+ self.time_mix_key.data = torch.pow(time_weight, ratio_1_to_almost0)
415
+ self.time_mix_value.data = (
416
+ torch.pow(time_weight, ratio_1_to_almost0) + 0.3 * ratio_0_to_1
417
+ )
418
+ self.time_mix_receptance.data = torch.pow(time_weight, 0.5 * ratio_1_to_almost0)
419
+
420
+ @torch.no_grad()
421
+ def wkv_linear_attention(
422
+ self,
423
+ time_decay: torch.Tensor,
424
+ time_first: torch.Tensor,
425
+ key: torch.Tensor,
426
+ value: torch.Tensor,
427
+ state: Tuple[torch.Tensor, torch.Tensor, torch.Tensor],
428
+ ) -> Tuple[torch.Tensor, Tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
429
+ """Compute WKV with state (i.e.: for inference).
430
+
431
+ Args:
432
+ time_decay: Channel-wise time decay vector. (D_att)
433
+ time_first: Channel-wise time first vector. (D_att)
434
+ key: Key tensor. (B, 1, D_att)
435
+ value: Value tensor. (B, 1, D_att)
436
+ state: Decoder hidden states. [3 x (B, D_att)]
437
+
438
+ Returns:
439
+ output: Weighted Key-Value. (B, 1, D_att)
440
+ state: Decoder hidden states. [3 x (B, 1, D_att)]
441
+
442
+ """
443
+ num_state, den_state, max_state = state
444
+ time_decay = -torch.exp(time_decay)
445
+ max_for_output = torch.maximum(max_state, (time_first + key))
446
+
447
+ e1 = torch.exp(max_state - max_for_output)
448
+ e2 = torch.exp((time_first + key) - max_for_output)
449
+
450
+ numerator = e1 * num_state + e2 * value
451
+ denominator = e1 * den_state + e2
452
+
453
+ max_for_state = torch.maximum(key, (max_state + time_decay))
454
+
455
+ e1 = torch.exp((max_state + time_decay) - max_for_state)
456
+ e2 = torch.exp(key - max_for_state)
457
+
458
+ wkv = numerator / denominator
459
+
460
+ state = [e1 * num_state + e2 * value, e1 * den_state + e2, max_for_state]
461
+
462
+ return wkv, state
463
+
464
+
465
+ class DecoderSelfAttention(SelfAttention):
466
+ """SelfAttention module definition.
467
+
468
+ Args:
469
+ size: Input/Output size.
470
+ attention_size: Attention hidden size.
471
+ context_size: Context size for WKV kernel.
472
+ block_id: Block index.
473
+ num_blocks: Number of blocks in the architecture.
474
+
475
+ """
476
+
477
+ def __init__(
478
+ self,
479
+ size: int,
480
+ attention_size: int,
481
+ context_size: int,
482
+ block_id: int,
483
+ dropout_rate: float,
484
+ num_blocks: int,
485
+ ) -> None:
486
+ """Construct a SelfAttention object."""
487
+ super().__init__(size, attention_size, block_id, dropout_rate, num_blocks)
488
+ # load_decoder_wkv_kernel(context_size)
489
+
490
+ def forward(
491
+ self,
492
+ x: torch.Tensor,
493
+ state: Optional[List[torch.Tensor]] = None,
494
+ ) -> Tuple[torch.Tensor, Optional[List[torch.Tensor]]]:
495
+ """Compute time mixing.
496
+
497
+ Args:
498
+ x: SelfAttention input sequences. (B, U, size)
499
+ state: Decoder hidden states. [5 x (B, 1, D_att, N)]
500
+
501
+ Returns:
502
+ x: SelfAttention output sequences. (B, U, size)
503
+
504
+ """
505
+ shifted_x = self.time_shift(x) if state is None else state[1][..., self.block_id]
506
+
507
+ key = x * self.time_mix_key + shifted_x * (1 - self.time_mix_key)
508
+ value = x * self.time_mix_value + shifted_x * (1 - self.time_mix_value)
509
+ receptance = x * self.time_mix_receptance + shifted_x * (1 - self.time_mix_receptance)
510
+
511
+ key = self.proj_key(key)
512
+ value = self.proj_value(value)
513
+ receptance = torch.sigmoid(self.proj_receptance(receptance))
514
+
515
+ if state is not None:
516
+ state[1][..., self.block_id] = x
517
+
518
+ wkv, att_state = self.wkv_linear_attention(
519
+ self.time_decay,
520
+ self.time_first,
521
+ key,
522
+ value,
523
+ tuple(s[..., self.block_id] for s in state[2:]),
524
+ )
525
+
526
+ state[2][..., self.block_id] = att_state[0]
527
+ state[3][..., self.block_id] = att_state[1]
528
+ state[4][..., self.block_id] = att_state[2]
529
+ else:
530
+ wkv = WKVLinearAttentionDecoder.apply(self.time_decay, self.time_first, key, value)
531
+
532
+ wkv = self.dropout(wkv)
533
+ x = self.proj_output(receptance * wkv)
534
+
535
+ return x, state
536
+
537
+
538
+ class EncoderSelfAttention(SelfAttention):
539
+ """SelfAttention module definition.
540
+
541
+ Args:
542
+ size: Input/Output size.
543
+ attention_size: Attention hidden size.
544
+ context_size: Context size for WKV kernel.
545
+ block_id: Block index.
546
+ num_blocks: Number of blocks in the architecture.
547
+
548
+ """
549
+
550
+ def __init__(
551
+ self,
552
+ size: int,
553
+ attention_size: int,
554
+ context_size: int,
555
+ block_id: int,
556
+ dropout_rate: float,
557
+ num_blocks: int,
558
+ ) -> None:
559
+ """Construct a SelfAttention object."""
560
+ super().__init__(size, attention_size, block_id, dropout_rate, num_blocks)
561
+ # load_encoder_wkv_kernel(context_size)
562
+
563
+ def forward(
564
+ self,
565
+ x: torch.Tensor,
566
+ state: Optional[List[torch.Tensor]] = None,
567
+ ) -> Tuple[torch.Tensor, Optional[List[torch.Tensor]]]:
568
+ """Compute time mixing.
569
+
570
+ Args:
571
+ x: SelfAttention input sequences. (B, U, size)
572
+ state: Decoder hidden states. [5 x (B, 1, D_att, N)]
573
+
574
+ Returns:
575
+ x: SelfAttention output sequences. (B, U, size)
576
+
577
+ """
578
+ shifted_x = self.time_shift(x) if state is None else state[1][..., self.block_id]
579
+
580
+ key = x * self.time_mix_key + shifted_x * (1 - self.time_mix_key)
581
+ value = x * self.time_mix_value + shifted_x * (1 - self.time_mix_value)
582
+ receptance = x * self.time_mix_receptance + shifted_x * (1 - self.time_mix_receptance)
583
+
584
+ key = self.proj_key(key)
585
+ value = self.proj_value(value)
586
+ receptance = torch.sigmoid(self.proj_receptance(receptance))
587
+
588
+ if state is not None:
589
+ state[1][..., self.block_id] = x
590
+
591
+ wkv, att_state = self.wkv_linear_attention(
592
+ self.time_decay,
593
+ self.time_first,
594
+ key,
595
+ value,
596
+ tuple(s[..., self.block_id] for s in state[2:]),
597
+ )
598
+
599
+ state[2][..., self.block_id] = att_state[0]
600
+ state[3][..., self.block_id] = att_state[1]
601
+ state[4][..., self.block_id] = att_state[2]
602
+ else:
603
+ wkv = WKVLinearAttentionEncoder.apply(self.time_decay, self.time_first, key, value)
604
+
605
+ wkv = self.dropout(wkv)
606
+ x = self.proj_output(receptance * wkv)
607
+
608
+ return x, state
funasr/models/rwkv_bat/rwkv_encoder.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import torch
7
+ from typing import Dict, List, Optional, Tuple
8
+
9
+ from funasr.register import tables
10
+ from funasr.models.rwkv_bat.rwkv import RWKV
11
+ from funasr.models.transformer.layer_norm import LayerNorm
12
+ from funasr.models.transformer.utils.nets_utils import make_source_mask
13
+ from funasr.models.rwkv_bat.rwkv_subsampling import RWKVConvInput
14
+
15
+
16
+ @tables.register("encoder_classes", "RWKVEncoder")
17
+ class RWKVEncoder(torch.nn.Module):
18
+ """RWKV encoder module.
19
+
20
+ Based on https://arxiv.org/pdf/2305.13048.pdf.
21
+
22
+ Args:
23
+ vocab_size: Vocabulary size.
24
+ output_size: Input/Output size.
25
+ context_size: Context size for WKV computation.
26
+ linear_size: FeedForward hidden size.
27
+ attention_size: SelfAttention hidden size.
28
+ normalization_type: Normalization layer type.
29
+ normalization_args: Normalization layer arguments.
30
+ num_blocks: Number of RWKV blocks.
31
+ embed_dropout_rate: Dropout rate for embedding layer.
32
+ att_dropout_rate: Dropout rate for the attention module.
33
+ ffn_dropout_rate: Dropout rate for the feed-forward module.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ input_size: int,
39
+ output_size: int = 512,
40
+ context_size: int = 1024,
41
+ linear_size: Optional[int] = None,
42
+ attention_size: Optional[int] = None,
43
+ num_blocks: int = 4,
44
+ att_dropout_rate: float = 0.0,
45
+ ffn_dropout_rate: float = 0.0,
46
+ dropout_rate: float = 0.0,
47
+ subsampling_factor: int = 4,
48
+ time_reduction_factor: int = 1,
49
+ kernel: int = 3,
50
+ **kwargs,
51
+ ) -> None:
52
+ """Construct a RWKVEncoder object."""
53
+ super().__init__()
54
+
55
+ self.embed = RWKVConvInput(
56
+ input_size,
57
+ [output_size // 4, output_size // 2, output_size],
58
+ subsampling_factor,
59
+ conv_kernel_size=kernel,
60
+ output_size=output_size,
61
+ )
62
+
63
+ self.subsampling_factor = subsampling_factor
64
+
65
+ linear_size = output_size * 4 if linear_size is None else linear_size
66
+ attention_size = output_size if attention_size is None else attention_size
67
+
68
+ self.rwkv_blocks = torch.nn.ModuleList(
69
+ [
70
+ RWKV(
71
+ output_size,
72
+ linear_size,
73
+ attention_size,
74
+ context_size,
75
+ block_id,
76
+ num_blocks,
77
+ att_dropout_rate=att_dropout_rate,
78
+ ffn_dropout_rate=ffn_dropout_rate,
79
+ dropout_rate=dropout_rate,
80
+ )
81
+ for block_id in range(num_blocks)
82
+ ]
83
+ )
84
+
85
+ self.embed_norm = LayerNorm(output_size)
86
+ self.final_norm = LayerNorm(output_size)
87
+
88
+ self._output_size = output_size
89
+ self.context_size = context_size
90
+
91
+ self.num_blocks = num_blocks
92
+ self.time_reduction_factor = time_reduction_factor
93
+
94
+ def output_size(self) -> int:
95
+ return self._output_size
96
+
97
+ def forward(self, x: torch.Tensor, x_len) -> torch.Tensor:
98
+ """Encode source label sequences.
99
+
100
+ Args:
101
+ x: Encoder input sequences. (B, L)
102
+
103
+ Returns:
104
+ out: Encoder output sequences. (B, U, D)
105
+
106
+ """
107
+ _, length, _ = x.size()
108
+
109
+ assert (
110
+ length <= self.context_size * self.subsampling_factor
111
+ ), "Context size is too short for current length: %d versus %d" % (
112
+ length,
113
+ self.context_size * self.subsampling_factor,
114
+ )
115
+ mask = make_source_mask(x_len).to(x.device)
116
+ x, mask = self.embed(x, mask, None)
117
+ x = self.embed_norm(x)
118
+ olens = mask.eq(0).sum(1)
119
+
120
+ if self.training:
121
+ for block in self.rwkv_blocks:
122
+ x, _ = block(x)
123
+ else:
124
+ x = self.rwkv_infer(x)
125
+
126
+ x = self.final_norm(x)
127
+
128
+ if self.time_reduction_factor > 1:
129
+ x = x[:, :: self.time_reduction_factor, :]
130
+ olens = torch.floor_divide(olens - 1, self.time_reduction_factor) + 1
131
+
132
+ return x, olens, None
133
+
134
+ def rwkv_infer(self, xs_pad):
135
+
136
+ batch_size = xs_pad.shape[0]
137
+
138
+ hidden_sizes = [self._output_size for i in range(5)]
139
+
140
+ state = [
141
+ torch.zeros(
142
+ (batch_size, 1, hidden_sizes[i], self.num_blocks),
143
+ dtype=torch.float32,
144
+ device=xs_pad.device,
145
+ )
146
+ for i in range(5)
147
+ ]
148
+
149
+ state[4] -= 1e-30
150
+
151
+ xs_out = []
152
+ for t in range(xs_pad.shape[1]):
153
+ x_t = xs_pad[:, t, :]
154
+ for idx, block in enumerate(self.rwkv_blocks):
155
+ x_t, state = block(x_t, state=state)
156
+ xs_out.append(x_t)
157
+ xs_out = torch.cat(xs_out, dim=1)
158
+ return xs_out
funasr/models/rwkv_bat/rwkv_feed_forward.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import torch
7
+ from typing import List, Optional, Tuple
8
+
9
+
10
+ class FeedForward(torch.nn.Module):
11
+ """FeedForward module definition.
12
+
13
+ Args:
14
+ size: Input/Output size.
15
+ hidden_size: Hidden size.
16
+ block_id: Block index.
17
+ num_blocks: Number of blocks in the architecture.
18
+
19
+ """
20
+
21
+ def __init__(
22
+ self, size: int, hidden_size: int, block_id: int, dropout_rate: float, num_blocks: int
23
+ ) -> None:
24
+ """Construct a FeedForward object."""
25
+ super().__init__()
26
+
27
+ self.time_shift = torch.nn.ZeroPad2d((0, 0, 1, -1))
28
+
29
+ self.time_mix_key = torch.nn.Parameter(torch.empty(1, 1, size))
30
+ self.time_mix_receptance = torch.nn.Parameter(torch.empty(1, 1, size))
31
+
32
+ self.proj_key = torch.nn.Linear(size, hidden_size, bias=True)
33
+ self.proj_value = torch.nn.Linear(hidden_size, size, bias=True)
34
+ self.proj_receptance = torch.nn.Linear(size, size, bias=True)
35
+
36
+ self.block_id = block_id
37
+
38
+ self.reset_parameters(size, block_id, num_blocks)
39
+ self.dropout = torch.nn.Dropout(p=dropout_rate)
40
+
41
+ def reset_parameters(self, size: int, block_id: int, num_blocks: int) -> None:
42
+ """Reset module parameters.
43
+
44
+ Args:
45
+ size: Block size.
46
+ block_id: Block index.
47
+ num_blocks: Number of blocks in the architecture.
48
+
49
+ """
50
+ ratio_1_to_almost0 = 1.0 - (block_id / num_blocks)
51
+
52
+ time_weight = torch.ones(1, 1, size)
53
+
54
+ for i in range(size):
55
+ time_weight[0, 0, i] = i / size
56
+
57
+ with torch.no_grad():
58
+ self.time_mix_key.data = torch.pow(time_weight, ratio_1_to_almost0)
59
+ self.time_mix_receptance.data = torch.pow(time_weight, ratio_1_to_almost0)
60
+
61
+ def forward(
62
+ self, x: torch.Tensor, state: Optional[List[torch.Tensor]] = None
63
+ ) -> Tuple[torch.Tensor, Optional[List[torch.Tensor]]]:
64
+ """Compute channel mixing.
65
+
66
+ Args:
67
+ x: FeedForward input sequences. (B, U, size)
68
+ state: Decoder hidden state. [5 x (B, 1, size, N)]
69
+
70
+ Returns:
71
+ x: FeedForward output sequences. (B, U, size)
72
+ state: Decoder hidden state. [5 x (B, 1, size, N)]
73
+
74
+ """
75
+ shifted_x = self.time_shift(x) if state is None else state[0][..., self.block_id]
76
+
77
+ key = x * self.time_mix_key + shifted_x * (1 - self.time_mix_key)
78
+ receptance = x * self.time_mix_receptance + shifted_x * (1 - self.time_mix_receptance)
79
+
80
+ key = torch.square(torch.relu(self.proj_key(key)))
81
+ value = self.proj_value(self.dropout(key))
82
+ receptance = torch.sigmoid(self.proj_receptance(receptance))
83
+
84
+ if state is not None:
85
+ state[0][..., self.block_id] = x
86
+
87
+ x = receptance * value
88
+
89
+ return x, state
funasr/models/rwkv_bat/rwkv_subsampling.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import math
7
+ import torch
8
+ from typing import Optional, Tuple, Union
9
+ from funasr.models.transformer.utils.nets_utils import pad_to_len
10
+
11
+
12
+ class TooShortUttError(Exception):
13
+ """Raised when the utt is too short for subsampling.
14
+
15
+ Args:
16
+ message (str): Message for error catch
17
+ actual_size (int): the short size that cannot pass the subsampling
18
+ limit (int): the limit size for subsampling
19
+
20
+ """
21
+
22
+ def __init__(self, message, actual_size, limit):
23
+ """Construct a TooShortUttError for error handler."""
24
+ super().__init__(message)
25
+ self.actual_size = actual_size
26
+ self.limit = limit
27
+
28
+
29
+ def check_short_utt(ins, size):
30
+ """Check if the utterance is too short for subsampling."""
31
+ if isinstance(ins, Conv2dSubsampling2) and size < 3:
32
+ return True, 3
33
+ if isinstance(ins, Conv2dSubsampling) and size < 7:
34
+ return True, 7
35
+ if isinstance(ins, Conv2dSubsampling6) and size < 11:
36
+ return True, 11
37
+ if isinstance(ins, Conv2dSubsampling8) and size < 15:
38
+ return True, 15
39
+ return False, -1
40
+
41
+
42
+ class RWKVConvInput(torch.nn.Module):
43
+ """Streaming ConvInput module definition.
44
+ Args:
45
+ input_size: Input size.
46
+ conv_size: Convolution size.
47
+ subsampling_factor: Subsampling factor.
48
+ output_size: Block output dimension.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ input_size: int,
54
+ conv_size: Union[int, Tuple],
55
+ subsampling_factor: int = 4,
56
+ conv_kernel_size: int = 3,
57
+ output_size: Optional[int] = None,
58
+ ) -> None:
59
+ """Construct a ConvInput object."""
60
+ super().__init__()
61
+ if subsampling_factor == 1:
62
+ conv_size1, conv_size2, conv_size3 = conv_size
63
+
64
+ self.conv = torch.nn.Sequential(
65
+ torch.nn.Conv2d(
66
+ 1, conv_size1, conv_kernel_size, stride=1, padding=(conv_kernel_size - 1) // 2
67
+ ),
68
+ torch.nn.ReLU(),
69
+ torch.nn.Conv2d(
70
+ conv_size1,
71
+ conv_size1,
72
+ conv_kernel_size,
73
+ stride=[1, 2],
74
+ padding=(conv_kernel_size - 1) // 2,
75
+ ),
76
+ torch.nn.ReLU(),
77
+ torch.nn.Conv2d(
78
+ conv_size1,
79
+ conv_size2,
80
+ conv_kernel_size,
81
+ stride=1,
82
+ padding=(conv_kernel_size - 1) // 2,
83
+ ),
84
+ torch.nn.ReLU(),
85
+ torch.nn.Conv2d(
86
+ conv_size2,
87
+ conv_size2,
88
+ conv_kernel_size,
89
+ stride=[1, 2],
90
+ padding=(conv_kernel_size - 1) // 2,
91
+ ),
92
+ torch.nn.ReLU(),
93
+ torch.nn.Conv2d(
94
+ conv_size2,
95
+ conv_size3,
96
+ conv_kernel_size,
97
+ stride=1,
98
+ padding=(conv_kernel_size - 1) // 2,
99
+ ),
100
+ torch.nn.ReLU(),
101
+ torch.nn.Conv2d(
102
+ conv_size3,
103
+ conv_size3,
104
+ conv_kernel_size,
105
+ stride=[1, 2],
106
+ padding=(conv_kernel_size - 1) // 2,
107
+ ),
108
+ torch.nn.ReLU(),
109
+ )
110
+
111
+ output_proj = conv_size3 * ((input_size // 2) // 2)
112
+
113
+ self.subsampling_factor = 1
114
+
115
+ self.stride_1 = 1
116
+
117
+ self.create_new_mask = self.create_new_vgg_mask
118
+
119
+ else:
120
+ conv_size1, conv_size2, conv_size3 = conv_size
121
+
122
+ kernel_1 = int(subsampling_factor / 2)
123
+
124
+ self.conv = torch.nn.Sequential(
125
+ torch.nn.Conv2d(
126
+ 1, conv_size1, conv_kernel_size, stride=1, padding=(conv_kernel_size - 1) // 2
127
+ ),
128
+ torch.nn.ReLU(),
129
+ torch.nn.Conv2d(
130
+ conv_size1,
131
+ conv_size1,
132
+ conv_kernel_size,
133
+ stride=[kernel_1, 2],
134
+ padding=(conv_kernel_size - 1) // 2,
135
+ ),
136
+ torch.nn.ReLU(),
137
+ torch.nn.Conv2d(
138
+ conv_size1,
139
+ conv_size2,
140
+ conv_kernel_size,
141
+ stride=1,
142
+ padding=(conv_kernel_size - 1) // 2,
143
+ ),
144
+ torch.nn.ReLU(),
145
+ torch.nn.Conv2d(
146
+ conv_size2,
147
+ conv_size2,
148
+ conv_kernel_size,
149
+ stride=[2, 2],
150
+ padding=(conv_kernel_size - 1) // 2,
151
+ ),
152
+ torch.nn.ReLU(),
153
+ torch.nn.Conv2d(
154
+ conv_size2,
155
+ conv_size3,
156
+ conv_kernel_size,
157
+ stride=1,
158
+ padding=(conv_kernel_size - 1) // 2,
159
+ ),
160
+ torch.nn.ReLU(),
161
+ torch.nn.Conv2d(
162
+ conv_size3,
163
+ conv_size3,
164
+ conv_kernel_size,
165
+ stride=1,
166
+ padding=(conv_kernel_size - 1) // 2,
167
+ ),
168
+ torch.nn.ReLU(),
169
+ )
170
+
171
+ output_proj = conv_size3 * ((input_size // 2) // 2)
172
+
173
+ self.subsampling_factor = subsampling_factor
174
+
175
+ self.create_new_mask = self.create_new_vgg_mask
176
+
177
+ self.stride_1 = kernel_1
178
+
179
+ self.min_frame_length = 7
180
+
181
+ if output_size is not None:
182
+ self.output = torch.nn.Linear(output_proj, output_size)
183
+ self.output_size = output_size
184
+ else:
185
+ self.output = None
186
+ self.output_size = output_proj
187
+
188
+ def forward(
189
+ self, x: torch.Tensor, mask: Optional[torch.Tensor], chunk_size: Optional[torch.Tensor]
190
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
191
+ """Encode input sequences.
192
+ Args:
193
+ x: ConvInput input sequences. (B, T, D_feats)
194
+ mask: Mask of input sequences. (B, 1, T)
195
+ Returns:
196
+ x: ConvInput output sequences. (B, sub(T), D_out)
197
+ mask: Mask of output sequences. (B, 1, sub(T))
198
+ """
199
+ if mask is not None:
200
+ mask = self.create_new_mask(mask)
201
+ olens = max(mask.eq(0).sum(1))
202
+
203
+ b, t, f = x.size()
204
+ x = x.unsqueeze(1) # (b. 1. t. f)
205
+
206
+ if chunk_size is not None:
207
+ max_input_length = int(
208
+ chunk_size
209
+ * self.subsampling_factor
210
+ * (math.ceil(float(t) / (chunk_size * self.subsampling_factor)))
211
+ )
212
+ x = map(lambda inputs: pad_to_len(inputs, max_input_length, 1), x)
213
+ x = list(x)
214
+ x = torch.stack(x, dim=0)
215
+ N_chunks = max_input_length // (chunk_size * self.subsampling_factor)
216
+ x = x.view(b * N_chunks, 1, chunk_size * self.subsampling_factor, f)
217
+
218
+ x = self.conv(x)
219
+
220
+ _, c, _, f = x.size()
221
+ if chunk_size is not None:
222
+ x = x.transpose(1, 2).contiguous().view(b, -1, c * f)[:, :olens, :]
223
+ else:
224
+ x = x.transpose(1, 2).contiguous().view(b, -1, c * f)
225
+
226
+ if self.output is not None:
227
+ x = self.output(x)
228
+
229
+ return x, mask[:, :olens][:, : x.size(1)]
230
+
231
+ def create_new_vgg_mask(self, mask: torch.Tensor) -> torch.Tensor:
232
+ """Create a new mask for VGG output sequences.
233
+ Args:
234
+ mask: Mask of input sequences. (B, T)
235
+ Returns:
236
+ mask: Mask of output sequences. (B, sub(T))
237
+ """
238
+ if self.subsampling_factor > 1:
239
+ return mask[:, ::2][:, :: self.stride_1]
240
+ else:
241
+ return mask
242
+
243
+ def get_size_before_subsampling(self, size: int) -> int:
244
+ """Return the original size before subsampling for a given size.
245
+ Args:
246
+ size: Number of frames after subsampling.
247
+ Returns:
248
+ : Number of frames before subsampling.
249
+ """
250
+ return size * self.subsampling_factor
funasr/models/rwkv_bat/template.yaml ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # network architecture
2
+ model: Transducer
3
+ model_conf:
4
+ auxiliary_ctc_weight: 0.0
5
+
6
+ # encoder
7
+ encoder: RWKVEncoder
8
+ encoder_conf:
9
+ kernel: 3
10
+ subsampling_factor: 4
11
+ output_size: 512
12
+ num_blocks: 18
13
+ time_reduction_factor: 2
14
+ att_dropout_rate: 0.1
15
+ ffn_dropout_rate: 0.1
16
+ dropout_rate: 0.1
17
+
18
+ # decoder (prediction network)
19
+ decoder: rnnt_decoder
20
+ decoder_conf:
21
+ embed_size: 512
22
+ hidden_size: 512
23
+ embed_dropout_rate: 0.1
24
+ dropout_rate: 0.1
25
+ use_embed_mask: false
26
+
27
+ # joint network
28
+ joint_network: joint_network
29
+ joint_network_conf:
30
+ joint_space_size: 512
31
+
32
+ frontend: WavFrontend
33
+ frontend_conf:
34
+ fs: 16000
35
+ window: hamming
36
+ n_mels: 80
37
+ frame_length: 25
38
+ frame_shift: 10
39
+ lfr_m: 1
40
+ lfr_n: 1
41
+ upsacle_samples: true
42
+
43
+ specaug: SpecAugLFR
44
+ specaug_conf:
45
+ apply_time_warp: false
46
+ time_warp_window: 5
47
+ time_warp_mode: bicubic
48
+ apply_freq_mask: true
49
+ freq_mask_width_range:
50
+ - 0
51
+ - 30
52
+ lfr_rate: 6
53
+ num_freq_mask: 1
54
+ apply_time_mask: true
55
+ time_mask_width_range:
56
+ - 0
57
+ - 12
58
+ num_time_mask: 1
59
+
60
+ tokenizer: CharTokenizer
61
+ tokenizer_conf:
62
+ unk_symbol: <unk>
63
+ split_with_space: true
64
+
funasr/models/sa_asr/__init__.py ADDED
File without changes
funasr/models/sa_asr/attention.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ """Multi-Head Attention layer definition."""
6
+
7
+ import math
8
+
9
+ import numpy
10
+ import torch
11
+ from torch import nn
12
+ from typing import Optional, Tuple
13
+
14
+ import torch.nn.functional as F
15
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
16
+ import funasr.models.lora.layers as lora
17
+
18
+
19
+ class CosineDistanceAttention(nn.Module):
20
+ """Compute Cosine Distance between spk decoder output and speaker profile
21
+ Args:
22
+ profile_path: speaker profile file path (.npy file)
23
+ """
24
+
25
+ def __init__(self):
26
+ super().__init__()
27
+ self.softmax = nn.Softmax(dim=-1)
28
+
29
+ def forward(self, spk_decoder_out, profile, profile_lens=None):
30
+ """
31
+ Args:
32
+ spk_decoder_out(torch.Tensor):(B, L, D)
33
+ spk_profiles(torch.Tensor):(B, N, D)
34
+ """
35
+ x = spk_decoder_out.unsqueeze(2) # (B, L, 1, D)
36
+ if profile_lens is not None:
37
+
38
+ mask = (make_pad_mask(profile_lens)[:, None, :]).to(profile.device)
39
+ min_value = float(numpy.finfo(torch.tensor(0, dtype=x.dtype).numpy().dtype).min)
40
+ weights_not_softmax = F.cosine_similarity(x, profile.unsqueeze(1), dim=-1).masked_fill(
41
+ mask, min_value
42
+ )
43
+ weights = self.softmax(weights_not_softmax).masked_fill(mask, 0.0) # (B, L, N)
44
+ else:
45
+ x = x[:, -1:, :, :]
46
+ weights_not_softmax = F.cosine_similarity(x, profile.unsqueeze(1).to(x.device), dim=-1)
47
+ weights = self.softmax(weights_not_softmax) # (B, 1, N)
48
+ spk_embedding = torch.matmul(weights, profile.to(weights.device)) # (B, L, D)
49
+
50
+ return spk_embedding, weights
funasr/models/sa_asr/beam_search_sa_asr.py ADDED
@@ -0,0 +1,540 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Beam search module."""
2
+
3
+ from itertools import chain
4
+ import logging
5
+ from typing import Any
6
+ from typing import Dict
7
+ from typing import List
8
+ from typing import NamedTuple
9
+ from typing import Tuple
10
+ from typing import Union
11
+
12
+ import torch
13
+
14
+ from funasr.metrics import end_detect
15
+ from funasr.models.transformer.scorers.scorer_interface import PartialScorerInterface
16
+ from funasr.models.transformer.scorers.scorer_interface import ScorerInterface
17
+ from funasr.models.decoder.abs_decoder import AbsDecoder
18
+
19
+
20
+ class Hypothesis(NamedTuple):
21
+ """Hypothesis data type."""
22
+
23
+ yseq: torch.Tensor
24
+ spk_weigths: List
25
+ score: Union[float, torch.Tensor] = 0
26
+ scores: Dict[str, Union[float, torch.Tensor]] = dict()
27
+ states: Dict[str, Any] = dict()
28
+
29
+ def asdict(self) -> dict:
30
+ """Convert data to JSON-friendly dict."""
31
+ return self._replace(
32
+ yseq=self.yseq.tolist(),
33
+ score=float(self.score),
34
+ scores={k: float(v) for k, v in self.scores.items()},
35
+ )._asdict()
36
+
37
+
38
+ class BeamSearch(torch.nn.Module):
39
+ """Beam search implementation."""
40
+
41
+ def __init__(
42
+ self,
43
+ scorers: Dict[str, ScorerInterface],
44
+ weights: Dict[str, float],
45
+ beam_size: int,
46
+ vocab_size: int,
47
+ sos: int,
48
+ eos: int,
49
+ token_list: List[str] = None,
50
+ pre_beam_ratio: float = 1.5,
51
+ pre_beam_score_key: str = None,
52
+ ):
53
+ """Initialize beam search.
54
+
55
+ Args:
56
+ scorers (dict[str, ScorerInterface]): Dict of decoder modules
57
+ e.g., Decoder, CTCPrefixScorer, LM
58
+ The scorer will be ignored if it is `None`
59
+ weights (dict[str, float]): Dict of weights for each scorers
60
+ The scorer will be ignored if its weight is 0
61
+ beam_size (int): The number of hypotheses kept during search
62
+ vocab_size (int): The number of vocabulary
63
+ sos (int): Start of sequence id
64
+ eos (int): End of sequence id
65
+ token_list (list[str]): List of tokens for debug log
66
+ pre_beam_score_key (str): key of scores to perform pre-beam search
67
+ pre_beam_ratio (float): beam size in the pre-beam search
68
+ will be `int(pre_beam_ratio * beam_size)`
69
+
70
+ """
71
+ super().__init__()
72
+ # set scorers
73
+ self.weights = weights
74
+ self.scorers = dict()
75
+ self.full_scorers = dict()
76
+ self.part_scorers = dict()
77
+ # this module dict is required for recursive cast
78
+ # `self.to(device, dtype)` in `recog.py`
79
+ self.nn_dict = torch.nn.ModuleDict()
80
+ for k, v in scorers.items():
81
+ w = weights.get(k, 0)
82
+ if w == 0 or v is None:
83
+ continue
84
+ assert isinstance(
85
+ v, ScorerInterface
86
+ ), f"{k} ({type(v)}) does not implement ScorerInterface"
87
+ self.scorers[k] = v
88
+ if isinstance(v, PartialScorerInterface):
89
+ self.part_scorers[k] = v
90
+ else:
91
+ self.full_scorers[k] = v
92
+ if isinstance(v, torch.nn.Module):
93
+ self.nn_dict[k] = v
94
+
95
+ # set configurations
96
+ self.sos = sos
97
+ self.eos = eos
98
+ self.token_list = token_list
99
+ self.pre_beam_size = int(pre_beam_ratio * beam_size)
100
+ self.beam_size = beam_size
101
+ self.n_vocab = vocab_size
102
+ if (
103
+ pre_beam_score_key is not None
104
+ and pre_beam_score_key != "full"
105
+ and pre_beam_score_key not in self.full_scorers
106
+ ):
107
+ raise KeyError(f"{pre_beam_score_key} is not found in {self.full_scorers}")
108
+ self.pre_beam_score_key = pre_beam_score_key
109
+ self.do_pre_beam = (
110
+ self.pre_beam_score_key is not None
111
+ and self.pre_beam_size < self.n_vocab
112
+ and len(self.part_scorers) > 0
113
+ )
114
+
115
+ def init_hyp(self, x: torch.Tensor) -> List[Hypothesis]:
116
+ """Get an initial hypothesis data.
117
+
118
+ Args:
119
+ x (torch.Tensor): The encoder output feature
120
+
121
+ Returns:
122
+ Hypothesis: The initial hypothesis.
123
+
124
+ """
125
+ init_states = dict()
126
+ init_scores = dict()
127
+ for k, d in self.scorers.items():
128
+ init_states[k] = d.init_state(x)
129
+ init_scores[k] = 0.0
130
+ return [
131
+ Hypothesis(
132
+ score=0.0,
133
+ scores=init_scores,
134
+ states=init_states,
135
+ yseq=torch.tensor([self.sos], device=x.device),
136
+ spk_weigths=[],
137
+ )
138
+ ]
139
+
140
+ @staticmethod
141
+ def append_token(xs: torch.Tensor, x: int) -> torch.Tensor:
142
+ """Append new token to prefix tokens.
143
+
144
+ Args:
145
+ xs (torch.Tensor): The prefix token
146
+ x (int): The new token to append
147
+
148
+ Returns:
149
+ torch.Tensor: New tensor contains: xs + [x] with xs.dtype and xs.device
150
+
151
+ """
152
+ x = torch.tensor([x], dtype=xs.dtype, device=xs.device)
153
+ return torch.cat((xs, x))
154
+
155
+ def score_full(
156
+ self,
157
+ hyp: Hypothesis,
158
+ asr_enc: torch.Tensor,
159
+ spk_enc: torch.Tensor,
160
+ profile: torch.Tensor,
161
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
162
+ """Score new hypothesis by `self.full_scorers`.
163
+
164
+ Args:
165
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
166
+ x (torch.Tensor): Corresponding input feature
167
+
168
+ Returns:
169
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
170
+ score dict of `hyp` that has string keys of `self.full_scorers`
171
+ and tensor score values of shape: `(self.n_vocab,)`,
172
+ and state dict that has string keys
173
+ and state values of `self.full_scorers`
174
+
175
+ """
176
+ scores = dict()
177
+ states = dict()
178
+ for k, d in self.full_scorers.items():
179
+ if isinstance(d, AbsDecoder):
180
+ scores[k], spk_weigths, states[k] = d.score(
181
+ hyp.yseq, hyp.states[k], asr_enc, spk_enc, profile
182
+ )
183
+ else:
184
+ scores[k], states[k] = d.score(hyp.yseq, hyp.states[k], asr_enc)
185
+ return scores, spk_weigths, states
186
+
187
+ def score_partial(
188
+ self,
189
+ hyp: Hypothesis,
190
+ ids: torch.Tensor,
191
+ asr_enc: torch.Tensor,
192
+ spk_enc: torch.Tensor,
193
+ profile: torch.Tensor,
194
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
195
+ """Score new hypothesis by `self.part_scorers`.
196
+
197
+ Args:
198
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
199
+ ids (torch.Tensor): 1D tensor of new partial tokens to score
200
+ x (torch.Tensor): Corresponding input feature
201
+
202
+ Returns:
203
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
204
+ score dict of `hyp` that has string keys of `self.part_scorers`
205
+ and tensor score values of shape: `(len(ids),)`,
206
+ and state dict that has string keys
207
+ and state values of `self.part_scorers`
208
+
209
+ """
210
+ scores = dict()
211
+ states = dict()
212
+ for k, d in self.part_scorers.items():
213
+ if isinstance(d, AbsDecoder):
214
+ scores[k], states[k] = d.score_partial(
215
+ hyp.yseq, ids, hyp.states[k], asr_enc, spk_enc, profile
216
+ )
217
+ else:
218
+ scores[k], states[k] = d.score_partial(hyp.yseq, ids, hyp.states[k], asr_enc)
219
+ return scores, states
220
+
221
+ def beam(
222
+ self, weighted_scores: torch.Tensor, ids: torch.Tensor
223
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
224
+ """Compute topk full token ids and partial token ids.
225
+
226
+ Args:
227
+ weighted_scores (torch.Tensor): The weighted sum scores for each tokens.
228
+ Its shape is `(self.n_vocab,)`.
229
+ ids (torch.Tensor): The partial token ids to compute topk
230
+
231
+ Returns:
232
+ Tuple[torch.Tensor, torch.Tensor]:
233
+ The topk full token ids and partial token ids.
234
+ Their shapes are `(self.beam_size,)`
235
+
236
+ """
237
+ # no pre beam performed
238
+ if weighted_scores.size(0) == ids.size(0):
239
+ top_ids = weighted_scores.topk(self.beam_size)[1]
240
+ return top_ids, top_ids
241
+
242
+ # mask pruned in pre-beam not to select in topk
243
+ tmp = weighted_scores[ids]
244
+ weighted_scores[:] = -float("inf")
245
+ weighted_scores[ids] = tmp
246
+ top_ids = weighted_scores.topk(self.beam_size)[1]
247
+ local_ids = weighted_scores[ids].topk(self.beam_size)[1]
248
+ return top_ids, local_ids
249
+
250
+ @staticmethod
251
+ def merge_scores(
252
+ prev_scores: Dict[str, float],
253
+ next_full_scores: Dict[str, torch.Tensor],
254
+ full_idx: int,
255
+ next_part_scores: Dict[str, torch.Tensor],
256
+ part_idx: int,
257
+ ) -> Dict[str, torch.Tensor]:
258
+ """Merge scores for new hypothesis.
259
+
260
+ Args:
261
+ prev_scores (Dict[str, float]):
262
+ The previous hypothesis scores by `self.scorers`
263
+ next_full_scores (Dict[str, torch.Tensor]): scores by `self.full_scorers`
264
+ full_idx (int): The next token id for `next_full_scores`
265
+ next_part_scores (Dict[str, torch.Tensor]):
266
+ scores of partial tokens by `self.part_scorers`
267
+ part_idx (int): The new token id for `next_part_scores`
268
+
269
+ Returns:
270
+ Dict[str, torch.Tensor]: The new score dict.
271
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
272
+ Its values are scalar tensors by the scorers.
273
+
274
+ """
275
+ new_scores = dict()
276
+ for k, v in next_full_scores.items():
277
+ new_scores[k] = prev_scores[k] + v[full_idx]
278
+ for k, v in next_part_scores.items():
279
+ new_scores[k] = prev_scores[k] + v[part_idx]
280
+ return new_scores
281
+
282
+ def merge_states(self, states: Any, part_states: Any, part_idx: int) -> Any:
283
+ """Merge states for new hypothesis.
284
+
285
+ Args:
286
+ states: states of `self.full_scorers`
287
+ part_states: states of `self.part_scorers`
288
+ part_idx (int): The new token id for `part_scores`
289
+
290
+ Returns:
291
+ Dict[str, torch.Tensor]: The new score dict.
292
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
293
+ Its values are states of the scorers.
294
+
295
+ """
296
+ new_states = dict()
297
+ for k, v in states.items():
298
+ new_states[k] = v
299
+ for k, d in self.part_scorers.items():
300
+ new_states[k] = d.select_state(part_states[k], part_idx)
301
+ return new_states
302
+
303
+ def search(
304
+ self,
305
+ running_hyps: List[Hypothesis],
306
+ asr_enc: torch.Tensor,
307
+ spk_enc: torch.Tensor,
308
+ profile: torch.Tensor,
309
+ ) -> List[Hypothesis]:
310
+ """Search new tokens for running hypotheses and encoded speech x.
311
+
312
+ Args:
313
+ running_hyps (List[Hypothesis]): Running hypotheses on beam
314
+ x (torch.Tensor): Encoded speech feature (T, D)
315
+
316
+ Returns:
317
+ List[Hypotheses]: Best sorted hypotheses
318
+
319
+ """
320
+ # import ipdb;ipdb.set_trace()
321
+ best_hyps = []
322
+ part_ids = torch.arange(self.n_vocab, device=asr_enc.device) # no pre-beam
323
+ for hyp in running_hyps:
324
+ # scoring
325
+ weighted_scores = torch.zeros(self.n_vocab, dtype=asr_enc.dtype, device=asr_enc.device)
326
+ scores, spk_weigths, states = self.score_full(hyp, asr_enc, spk_enc, profile)
327
+ for k in self.full_scorers:
328
+ weighted_scores += self.weights[k] * scores[k]
329
+ # partial scoring
330
+ if self.do_pre_beam:
331
+ pre_beam_scores = (
332
+ weighted_scores
333
+ if self.pre_beam_score_key == "full"
334
+ else scores[self.pre_beam_score_key]
335
+ )
336
+ part_ids = torch.topk(pre_beam_scores, self.pre_beam_size)[1]
337
+ part_scores, part_states = self.score_partial(hyp, part_ids, asr_enc, spk_enc, profile)
338
+ for k in self.part_scorers:
339
+ weighted_scores[part_ids] += self.weights[k] * part_scores[k]
340
+ # add previous hyp score
341
+ weighted_scores += hyp.score
342
+
343
+ # update hyps
344
+ for j, part_j in zip(*self.beam(weighted_scores, part_ids)):
345
+ # will be (2 x beam at most)
346
+ best_hyps.append(
347
+ Hypothesis(
348
+ score=weighted_scores[j],
349
+ yseq=self.append_token(hyp.yseq, j),
350
+ scores=self.merge_scores(hyp.scores, scores, j, part_scores, part_j),
351
+ states=self.merge_states(states, part_states, part_j),
352
+ spk_weigths=hyp.spk_weigths + [spk_weigths],
353
+ )
354
+ )
355
+
356
+ # sort and prune 2 x beam -> beam
357
+ best_hyps = sorted(best_hyps, key=lambda x: x.score, reverse=True)[
358
+ : min(len(best_hyps), self.beam_size)
359
+ ]
360
+ return best_hyps
361
+
362
+ def forward(
363
+ self,
364
+ asr_enc: torch.Tensor,
365
+ spk_enc: torch.Tensor,
366
+ profile: torch.Tensor,
367
+ maxlenratio: float = 0.0,
368
+ minlenratio: float = 0.0,
369
+ ) -> List[Hypothesis]:
370
+ """Perform beam search.
371
+
372
+ Args:
373
+ x (torch.Tensor): Encoded speech feature (T, D)
374
+ maxlenratio (float): Input length ratio to obtain max output length.
375
+ If maxlenratio=0.0 (default), it uses a end-detect function
376
+ to automatically find maximum hypothesis lengths
377
+ minlenratio (float): Input length ratio to obtain min output length.
378
+
379
+ Returns:
380
+ list[Hypothesis]: N-best decoding results
381
+
382
+ """
383
+ # import ipdb;ipdb.set_trace()
384
+ # set length bounds
385
+ if maxlenratio == 0:
386
+ maxlen = asr_enc.shape[0]
387
+ else:
388
+ maxlen = max(1, int(maxlenratio * asr_enc.size(0)))
389
+ minlen = int(minlenratio * asr_enc.size(0))
390
+ logging.info("decoder input length: " + str(asr_enc.shape[0]))
391
+ logging.info("max output length: " + str(maxlen))
392
+ logging.info("min output length: " + str(minlen))
393
+
394
+ # main loop of prefix search
395
+ running_hyps = self.init_hyp(asr_enc)
396
+ ended_hyps = []
397
+ for i in range(maxlen):
398
+ logging.debug("position " + str(i))
399
+ best = self.search(running_hyps, asr_enc, spk_enc, profile)
400
+ # import pdb;pdb.set_trace()
401
+ # post process of one iteration
402
+ running_hyps = self.post_process(i, maxlen, maxlenratio, best, ended_hyps)
403
+ # end detection
404
+ if maxlenratio == 0.0 and end_detect([h.asdict() for h in ended_hyps], i):
405
+ logging.info(f"end detected at {i}")
406
+ break
407
+ if len(running_hyps) == 0:
408
+ logging.info("no hypothesis. Finish decoding.")
409
+ break
410
+ else:
411
+ logging.debug(f"remained hypotheses: {len(running_hyps)}")
412
+
413
+ nbest_hyps = sorted(ended_hyps, key=lambda x: x.score, reverse=True)
414
+ # check the number of hypotheses reaching to eos
415
+ if len(nbest_hyps) == 0:
416
+ logging.warning(
417
+ "there is no N-best results, perform recognition " "again with smaller minlenratio."
418
+ )
419
+ return (
420
+ []
421
+ if minlenratio < 0.1
422
+ else self.forward(
423
+ asr_enc, spk_enc, profile, maxlenratio, max(0.0, minlenratio - 0.1)
424
+ )
425
+ )
426
+
427
+ # report the best result
428
+ best = nbest_hyps[0]
429
+ for k, v in best.scores.items():
430
+ logging.info(f"{v:6.2f} * {self.weights[k]:3} = {v * self.weights[k]:6.2f} for {k}")
431
+ logging.info(f"total log probability: {best.score:.2f}")
432
+ logging.info(f"normalized log probability: {best.score / len(best.yseq):.2f}")
433
+ logging.info(f"total number of ended hypotheses: {len(nbest_hyps)}")
434
+ if self.token_list is not None:
435
+ logging.info(
436
+ "best hypo: " + "".join([self.token_list[x] for x in best.yseq[1:-1]]) + "\n"
437
+ )
438
+ return nbest_hyps
439
+
440
+ def post_process(
441
+ self,
442
+ i: int,
443
+ maxlen: int,
444
+ maxlenratio: float,
445
+ running_hyps: List[Hypothesis],
446
+ ended_hyps: List[Hypothesis],
447
+ ) -> List[Hypothesis]:
448
+ """Perform post-processing of beam search iterations.
449
+
450
+ Args:
451
+ i (int): The length of hypothesis tokens.
452
+ maxlen (int): The maximum length of tokens in beam search.
453
+ maxlenratio (int): The maximum length ratio in beam search.
454
+ running_hyps (List[Hypothesis]): The running hypotheses in beam search.
455
+ ended_hyps (List[Hypothesis]): The ended hypotheses in beam search.
456
+
457
+ Returns:
458
+ List[Hypothesis]: The new running hypotheses.
459
+
460
+ """
461
+ logging.debug(f"the number of running hypotheses: {len(running_hyps)}")
462
+ if self.token_list is not None:
463
+ logging.debug(
464
+ "best hypo: " + "".join([self.token_list[x] for x in running_hyps[0].yseq[1:]])
465
+ )
466
+ # add eos in the final loop to avoid that there are no ended hyps
467
+ if i == maxlen - 1:
468
+ logging.info("adding <eos> in the last position in the loop")
469
+ running_hyps = [
470
+ h._replace(yseq=self.append_token(h.yseq, self.eos)) for h in running_hyps
471
+ ]
472
+
473
+ # add ended hypotheses to a final list, and removed them from current hypotheses
474
+ # (this will be a problem, number of hyps < beam)
475
+ remained_hyps = []
476
+ for hyp in running_hyps:
477
+ if hyp.yseq[-1] == self.eos:
478
+ # e.g., Word LM needs to add final <eos> score
479
+ for k, d in chain(self.full_scorers.items(), self.part_scorers.items()):
480
+ s = d.final_score(hyp.states[k])
481
+ hyp.scores[k] += s
482
+ hyp = hyp._replace(score=hyp.score + self.weights[k] * s)
483
+ ended_hyps.append(hyp)
484
+ else:
485
+ remained_hyps.append(hyp)
486
+ return remained_hyps
487
+
488
+
489
+ def beam_search(
490
+ x: torch.Tensor,
491
+ sos: int,
492
+ eos: int,
493
+ beam_size: int,
494
+ vocab_size: int,
495
+ scorers: Dict[str, ScorerInterface],
496
+ weights: Dict[str, float],
497
+ token_list: List[str] = None,
498
+ maxlenratio: float = 0.0,
499
+ minlenratio: float = 0.0,
500
+ pre_beam_ratio: float = 1.5,
501
+ pre_beam_score_key: str = "full",
502
+ ) -> list:
503
+ """Perform beam search with scorers.
504
+
505
+ Args:
506
+ x (torch.Tensor): Encoded speech feature (T, D)
507
+ sos (int): Start of sequence id
508
+ eos (int): End of sequence id
509
+ beam_size (int): The number of hypotheses kept during search
510
+ vocab_size (int): The number of vocabulary
511
+ scorers (dict[str, ScorerInterface]): Dict of decoder modules
512
+ e.g., Decoder, CTCPrefixScorer, LM
513
+ The scorer will be ignored if it is `None`
514
+ weights (dict[str, float]): Dict of weights for each scorers
515
+ The scorer will be ignored if its weight is 0
516
+ token_list (list[str]): List of tokens for debug log
517
+ maxlenratio (float): Input length ratio to obtain max output length.
518
+ If maxlenratio=0.0 (default), it uses a end-detect function
519
+ to automatically find maximum hypothesis lengths
520
+ minlenratio (float): Input length ratio to obtain min output length.
521
+ pre_beam_score_key (str): key of scores to perform pre-beam search
522
+ pre_beam_ratio (float): beam size in the pre-beam search
523
+ will be `int(pre_beam_ratio * beam_size)`
524
+
525
+ Returns:
526
+ list: N-best decoding results
527
+
528
+ """
529
+ ret = BeamSearch(
530
+ scorers,
531
+ weights,
532
+ beam_size=beam_size,
533
+ vocab_size=vocab_size,
534
+ pre_beam_ratio=pre_beam_ratio,
535
+ pre_beam_score_key=pre_beam_score_key,
536
+ sos=sos,
537
+ eos=eos,
538
+ token_list=token_list,
539
+ ).forward(x=x, maxlenratio=maxlenratio, minlenratio=minlenratio)
540
+ return [h.asdict() for h in ret]
funasr/models/sa_asr/e2e_sa_asr.py ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright ESPnet (https://github.com/espnet/espnet). All Rights Reserved.
2
+ # Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3
+
4
+ import logging
5
+ from contextlib import contextmanager
6
+ from distutils.version import LooseVersion
7
+ from typing import Dict
8
+ from typing import List
9
+ from typing import Optional
10
+ from typing import Tuple
11
+ from typing import Union
12
+
13
+ import torch
14
+ import torch.nn.functional as F
15
+
16
+ from funasr.layers.abs_normalize import AbsNormalize
17
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss, NllLoss # noqa: H301
18
+ from funasr.models.ctc import CTC
19
+ from funasr.models.decoder.abs_decoder import AbsDecoder
20
+ from funasr.models.encoder.abs_encoder import AbsEncoder
21
+ from funasr.frontends.abs_frontend import AbsFrontend
22
+ from funasr.models.postencoder.abs_postencoder import AbsPostEncoder
23
+ from funasr.models.preencoder.abs_preencoder import AbsPreEncoder
24
+ from funasr.models.specaug.abs_specaug import AbsSpecAug
25
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
26
+ from funasr.metrics import ErrorCalculator
27
+ from funasr.metrics.compute_acc import th_accuracy
28
+ from funasr.train_utils.device_funcs import force_gatherable
29
+ from funasr.models.base_model import FunASRModel
30
+
31
+ if LooseVersion(torch.__version__) >= LooseVersion("1.6.0"):
32
+ from torch.cuda.amp import autocast
33
+ else:
34
+ # Nothing to do if torch<1.6.0
35
+ @contextmanager
36
+ def autocast(enabled=True):
37
+ yield
38
+
39
+
40
+ class SAASRModel(FunASRModel):
41
+ """CTC-attention hybrid Encoder-Decoder model"""
42
+
43
+ def __init__(
44
+ self,
45
+ vocab_size: int,
46
+ max_spk_num: int,
47
+ token_list: Union[Tuple[str, ...], List[str]],
48
+ frontend: Optional[AbsFrontend],
49
+ specaug: Optional[AbsSpecAug],
50
+ normalize: Optional[AbsNormalize],
51
+ asr_encoder: AbsEncoder,
52
+ spk_encoder: torch.nn.Module,
53
+ decoder: AbsDecoder,
54
+ ctc: CTC,
55
+ spk_weight: float = 0.5,
56
+ ctc_weight: float = 0.5,
57
+ interctc_weight: float = 0.0,
58
+ ignore_id: int = -1,
59
+ lsm_weight: float = 0.0,
60
+ length_normalized_loss: bool = False,
61
+ report_cer: bool = True,
62
+ report_wer: bool = True,
63
+ sym_space: str = "<space>",
64
+ sym_blank: str = "<blank>",
65
+ extract_feats_in_collect_stats: bool = True,
66
+ ):
67
+ assert 0.0 <= ctc_weight <= 1.0, ctc_weight
68
+ assert 0.0 <= interctc_weight < 1.0, interctc_weight
69
+
70
+ super().__init__()
71
+ # note that eos is the same as sos (equivalent ID)
72
+ self.blank_id = 0
73
+ self.sos = 1
74
+ self.eos = 2
75
+ self.vocab_size = vocab_size
76
+ self.max_spk_num = max_spk_num
77
+ self.ignore_id = ignore_id
78
+ self.spk_weight = spk_weight
79
+ self.ctc_weight = ctc_weight
80
+ self.interctc_weight = interctc_weight
81
+ self.token_list = token_list.copy()
82
+
83
+ self.frontend = frontend
84
+ self.specaug = specaug
85
+ self.normalize = normalize
86
+ self.asr_encoder = asr_encoder
87
+ self.spk_encoder = spk_encoder
88
+
89
+ if not hasattr(self.asr_encoder, "interctc_use_conditioning"):
90
+ self.asr_encoder.interctc_use_conditioning = False
91
+ if self.asr_encoder.interctc_use_conditioning:
92
+ self.asr_encoder.conditioning_layer = torch.nn.Linear(
93
+ vocab_size, self.asr_encoder.output_size()
94
+ )
95
+
96
+ self.error_calculator = None
97
+
98
+ # we set self.decoder = None in the CTC mode since
99
+ # self.decoder parameters were never used and PyTorch complained
100
+ # and threw an Exception in the multi-GPU experiment.
101
+ # thanks Jeff Farris for pointing out the issue.
102
+ if ctc_weight == 1.0:
103
+ self.decoder = None
104
+ else:
105
+ self.decoder = decoder
106
+
107
+ self.criterion_att = LabelSmoothingLoss(
108
+ size=vocab_size,
109
+ padding_idx=ignore_id,
110
+ smoothing=lsm_weight,
111
+ normalize_length=length_normalized_loss,
112
+ )
113
+
114
+ self.criterion_spk = NllLoss(
115
+ size=max_spk_num,
116
+ padding_idx=ignore_id,
117
+ normalize_length=length_normalized_loss,
118
+ )
119
+
120
+ if report_cer or report_wer:
121
+ self.error_calculator = ErrorCalculator(
122
+ token_list, sym_space, sym_blank, report_cer, report_wer
123
+ )
124
+
125
+ if ctc_weight == 0.0:
126
+ self.ctc = None
127
+ else:
128
+ self.ctc = ctc
129
+
130
+ self.extract_feats_in_collect_stats = extract_feats_in_collect_stats
131
+
132
+ def forward(
133
+ self,
134
+ speech: torch.Tensor,
135
+ speech_lengths: torch.Tensor,
136
+ text: torch.Tensor,
137
+ text_lengths: torch.Tensor,
138
+ profile: torch.Tensor,
139
+ profile_lengths: torch.Tensor,
140
+ text_id: torch.Tensor,
141
+ text_id_lengths: torch.Tensor,
142
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
143
+ """Frontend + Encoder + Decoder + Calc loss
144
+
145
+ Args:
146
+ speech: (Batch, Length, ...)
147
+ speech_lengths: (Batch, )
148
+ text: (Batch, Length)
149
+ text_lengths: (Batch,)
150
+ profile: (Batch, Length, Dim)
151
+ profile_lengths: (Batch,)
152
+ """
153
+ assert text_lengths.dim() == 1, text_lengths.shape
154
+ # Check that batch_size is unified
155
+ assert (
156
+ speech.shape[0] == speech_lengths.shape[0] == text.shape[0] == text_lengths.shape[0]
157
+ ), (speech.shape, speech_lengths.shape, text.shape, text_lengths.shape)
158
+ batch_size = speech.shape[0]
159
+
160
+ # for data-parallel
161
+ text = text[:, : text_lengths.max()]
162
+
163
+ # 1. Encoder
164
+ asr_encoder_out, encoder_out_lens, spk_encoder_out = self.encode(speech, speech_lengths)
165
+ intermediate_outs = None
166
+ if isinstance(asr_encoder_out, tuple):
167
+ intermediate_outs = asr_encoder_out[1]
168
+ asr_encoder_out = asr_encoder_out[0]
169
+
170
+ loss_att, loss_spk, acc_att, acc_spk, cer_att, wer_att = None, None, None, None, None, None
171
+ loss_ctc, cer_ctc = None, None
172
+ stats = dict()
173
+
174
+ # 1. CTC branch
175
+ if self.ctc_weight != 0.0:
176
+ loss_ctc, cer_ctc = self._calc_ctc_loss(
177
+ asr_encoder_out, encoder_out_lens, text, text_lengths
178
+ )
179
+
180
+ # Intermediate CTC (optional)
181
+ loss_interctc = 0.0
182
+ if self.interctc_weight != 0.0 and intermediate_outs is not None:
183
+ for layer_idx, intermediate_out in intermediate_outs:
184
+ # we assume intermediate_out has the same length & padding
185
+ # as those of encoder_out
186
+ loss_ic, cer_ic = self._calc_ctc_loss(
187
+ intermediate_out, encoder_out_lens, text, text_lengths
188
+ )
189
+ loss_interctc = loss_interctc + loss_ic
190
+
191
+ # Collect Intermedaite CTC stats
192
+ stats["loss_interctc_layer{}".format(layer_idx)] = (
193
+ loss_ic.detach() if loss_ic is not None else None
194
+ )
195
+ stats["cer_interctc_layer{}".format(layer_idx)] = cer_ic
196
+
197
+ loss_interctc = loss_interctc / len(intermediate_outs)
198
+
199
+ # calculate whole encoder loss
200
+ loss_ctc = (1 - self.interctc_weight) * loss_ctc + self.interctc_weight * loss_interctc
201
+
202
+ # 2b. Attention decoder branch
203
+ if self.ctc_weight != 1.0:
204
+ loss_att, loss_spk, acc_att, acc_spk, cer_att, wer_att = self._calc_att_loss(
205
+ asr_encoder_out,
206
+ spk_encoder_out,
207
+ encoder_out_lens,
208
+ text,
209
+ text_lengths,
210
+ profile,
211
+ profile_lengths,
212
+ text_id,
213
+ text_id_lengths,
214
+ )
215
+
216
+ # 3. CTC-Att loss definition
217
+ if self.ctc_weight == 0.0:
218
+ loss_asr = loss_att
219
+ elif self.ctc_weight == 1.0:
220
+ loss_asr = loss_ctc
221
+ else:
222
+ loss_asr = self.ctc_weight * loss_ctc + (1 - self.ctc_weight) * loss_att
223
+
224
+ if self.spk_weight == 0.0:
225
+ loss = loss_asr
226
+ else:
227
+ loss = self.spk_weight * loss_spk + (1 - self.spk_weight) * loss_asr
228
+
229
+ stats = dict(
230
+ loss=loss.detach(),
231
+ loss_asr=loss_asr.detach(),
232
+ loss_att=loss_att.detach() if loss_att is not None else None,
233
+ loss_ctc=loss_ctc.detach() if loss_ctc is not None else None,
234
+ loss_spk=loss_spk.detach() if loss_spk is not None else None,
235
+ acc=acc_att,
236
+ acc_spk=acc_spk,
237
+ cer=cer_att,
238
+ wer=wer_att,
239
+ cer_ctc=cer_ctc,
240
+ )
241
+
242
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
243
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
244
+ return loss, stats, weight
245
+
246
+ def collect_feats(
247
+ self,
248
+ speech: torch.Tensor,
249
+ speech_lengths: torch.Tensor,
250
+ text: torch.Tensor,
251
+ text_lengths: torch.Tensor,
252
+ ) -> Dict[str, torch.Tensor]:
253
+ if self.extract_feats_in_collect_stats:
254
+ feats, feats_lengths = self._extract_feats(speech, speech_lengths)
255
+ else:
256
+ # Generate dummy stats if extract_feats_in_collect_stats is False
257
+ logging.warning(
258
+ "Generating dummy stats for feats and feats_lengths, "
259
+ "because encoder_conf.extract_feats_in_collect_stats is "
260
+ f"{self.extract_feats_in_collect_stats}"
261
+ )
262
+ feats, feats_lengths = speech, speech_lengths
263
+ return {"feats": feats, "feats_lengths": feats_lengths}
264
+
265
+ def encode(
266
+ self, speech: torch.Tensor, speech_lengths: torch.Tensor
267
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
268
+ """Frontend + Encoder. Note that this method is used by asr_inference.py
269
+
270
+ Args:
271
+ speech: (Batch, Length, ...)
272
+ speech_lengths: (Batch, )
273
+ """
274
+ with autocast(False):
275
+ # 1. Extract feats
276
+ feats, feats_lengths = self._extract_feats(speech, speech_lengths)
277
+
278
+ # 2. Data augmentation
279
+ feats_raw = feats.clone()
280
+ if self.specaug is not None and self.training:
281
+ feats, feats_lengths = self.specaug(feats, feats_lengths)
282
+
283
+ # 3. Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
284
+ if self.normalize is not None:
285
+ feats, feats_lengths = self.normalize(feats, feats_lengths)
286
+
287
+ # 4. Forward encoder
288
+ # feats: (Batch, Length, Dim)
289
+ # -> encoder_out: (Batch, Length2, Dim2)
290
+ if self.asr_encoder.interctc_use_conditioning:
291
+ encoder_out, encoder_out_lens, _ = self.asr_encoder(feats, feats_lengths, ctc=self.ctc)
292
+ else:
293
+ encoder_out, encoder_out_lens, _ = self.asr_encoder(feats, feats_lengths)
294
+ intermediate_outs = None
295
+ if isinstance(encoder_out, tuple):
296
+ intermediate_outs = encoder_out[1]
297
+ encoder_out = encoder_out[0]
298
+
299
+ encoder_out_spk_ori = self.spk_encoder(feats_raw, feats_lengths)[0]
300
+ # import ipdb;ipdb.set_trace()
301
+ if encoder_out_spk_ori.size(1) != encoder_out.size(1):
302
+ encoder_out_spk = F.interpolate(
303
+ encoder_out_spk_ori.transpose(-2, -1), size=(encoder_out.size(1)), mode="nearest"
304
+ ).transpose(-2, -1)
305
+ else:
306
+ encoder_out_spk = encoder_out_spk_ori
307
+
308
+ assert encoder_out.size(0) == speech.size(0), (
309
+ encoder_out.size(),
310
+ speech.size(0),
311
+ )
312
+ assert encoder_out.size(1) <= encoder_out_lens.max(), (
313
+ encoder_out.size(),
314
+ encoder_out_lens.max(),
315
+ )
316
+ assert encoder_out_spk.size(0) == speech.size(0), (
317
+ encoder_out_spk.size(),
318
+ speech.size(0),
319
+ )
320
+
321
+ if intermediate_outs is not None:
322
+ return (encoder_out, intermediate_outs), encoder_out_lens, encoder_out_spk
323
+
324
+ return encoder_out, encoder_out_lens, encoder_out_spk
325
+
326
+ def _extract_feats(
327
+ self, speech: torch.Tensor, speech_lengths: torch.Tensor
328
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
329
+ assert speech_lengths.dim() == 1, speech_lengths.shape
330
+
331
+ # for data-parallel
332
+ speech = speech[:, : speech_lengths.max()]
333
+
334
+ if self.frontend is not None:
335
+ # Frontend
336
+ # e.g. STFT and Feature extract
337
+ # data_loader may send time-domain signal in this case
338
+ # speech (Batch, NSamples) -> feats: (Batch, NFrames, Dim)
339
+ feats, feats_lengths = self.frontend(speech, speech_lengths)
340
+ else:
341
+ # No frontend and no feature extract
342
+ feats, feats_lengths = speech, speech_lengths
343
+ return feats, feats_lengths
344
+
345
+ def nll(
346
+ self,
347
+ encoder_out: torch.Tensor,
348
+ encoder_out_lens: torch.Tensor,
349
+ ys_pad: torch.Tensor,
350
+ ys_pad_lens: torch.Tensor,
351
+ ) -> torch.Tensor:
352
+ """Compute negative log likelihood(nll) from transformer-decoder
353
+
354
+ Normally, this function is called in batchify_nll.
355
+
356
+ Args:
357
+ encoder_out: (Batch, Length, Dim)
358
+ encoder_out_lens: (Batch,)
359
+ ys_pad: (Batch, Length)
360
+ ys_pad_lens: (Batch,)
361
+ """
362
+ ys_in_pad, ys_out_pad = add_sos_eos(ys_pad, self.sos, self.eos, self.ignore_id)
363
+ ys_in_lens = ys_pad_lens + 1
364
+
365
+ # 1. Forward decoder
366
+ decoder_out, _ = self.decoder(
367
+ encoder_out, encoder_out_lens, ys_in_pad, ys_in_lens
368
+ ) # [batch, seqlen, dim]
369
+ batch_size = decoder_out.size(0)
370
+ decoder_num_class = decoder_out.size(2)
371
+ # nll: negative log-likelihood
372
+ nll = torch.nn.functional.cross_entropy(
373
+ decoder_out.view(-1, decoder_num_class),
374
+ ys_out_pad.view(-1),
375
+ ignore_index=self.ignore_id,
376
+ reduction="none",
377
+ )
378
+ nll = nll.view(batch_size, -1)
379
+ nll = nll.sum(dim=1)
380
+ assert nll.size(0) == batch_size
381
+ return nll
382
+
383
+ def batchify_nll(
384
+ self,
385
+ encoder_out: torch.Tensor,
386
+ encoder_out_lens: torch.Tensor,
387
+ ys_pad: torch.Tensor,
388
+ ys_pad_lens: torch.Tensor,
389
+ batch_size: int = 100,
390
+ ):
391
+ """Compute negative log likelihood(nll) from transformer-decoder
392
+
393
+ To avoid OOM, this fuction seperate the input into batches.
394
+ Then call nll for each batch and combine and return results.
395
+ Args:
396
+ encoder_out: (Batch, Length, Dim)
397
+ encoder_out_lens: (Batch,)
398
+ ys_pad: (Batch, Length)
399
+ ys_pad_lens: (Batch,)
400
+ batch_size: int, samples each batch contain when computing nll,
401
+ you may change this to avoid OOM or increase
402
+ GPU memory usage
403
+ """
404
+ total_num = encoder_out.size(0)
405
+ if total_num <= batch_size:
406
+ nll = self.nll(encoder_out, encoder_out_lens, ys_pad, ys_pad_lens)
407
+ else:
408
+ nll = []
409
+ start_idx = 0
410
+ while True:
411
+ end_idx = min(start_idx + batch_size, total_num)
412
+ batch_encoder_out = encoder_out[start_idx:end_idx, :, :]
413
+ batch_encoder_out_lens = encoder_out_lens[start_idx:end_idx]
414
+ batch_ys_pad = ys_pad[start_idx:end_idx, :]
415
+ batch_ys_pad_lens = ys_pad_lens[start_idx:end_idx]
416
+ batch_nll = self.nll(
417
+ batch_encoder_out,
418
+ batch_encoder_out_lens,
419
+ batch_ys_pad,
420
+ batch_ys_pad_lens,
421
+ )
422
+ nll.append(batch_nll)
423
+ start_idx = end_idx
424
+ if start_idx == total_num:
425
+ break
426
+ nll = torch.cat(nll)
427
+ assert nll.size(0) == total_num
428
+ return nll
429
+
430
+ def _calc_att_loss(
431
+ self,
432
+ asr_encoder_out: torch.Tensor,
433
+ spk_encoder_out: torch.Tensor,
434
+ encoder_out_lens: torch.Tensor,
435
+ ys_pad: torch.Tensor,
436
+ ys_pad_lens: torch.Tensor,
437
+ profile: torch.Tensor,
438
+ profile_lens: torch.Tensor,
439
+ text_id: torch.Tensor,
440
+ text_id_lengths: torch.Tensor,
441
+ ):
442
+ ys_in_pad, ys_out_pad = add_sos_eos(ys_pad, self.sos, self.eos, self.ignore_id)
443
+ ys_in_lens = ys_pad_lens + 1
444
+
445
+ # 1. Forward decoder
446
+ decoder_out, weights_no_pad, _ = self.decoder(
447
+ asr_encoder_out,
448
+ spk_encoder_out,
449
+ encoder_out_lens,
450
+ ys_in_pad,
451
+ ys_in_lens,
452
+ profile,
453
+ profile_lens,
454
+ )
455
+
456
+ spk_num_no_pad = weights_no_pad.size(-1)
457
+ pad = (0, self.max_spk_num - spk_num_no_pad)
458
+ weights = F.pad(weights_no_pad, pad, mode="constant", value=0)
459
+
460
+ # pre_id=weights.argmax(-1)
461
+ # pre_text=decoder_out.argmax(-1)
462
+ # id_mask=(pre_id==text_id).to(dtype=text_id.dtype)
463
+ # pre_text_mask=pre_text*id_mask+1-id_mask #相同的地方不变,不同的地方设为1(<unk>)
464
+ # padding_mask= ys_out_pad != self.ignore_id
465
+ # numerator = torch.sum(pre_text_mask.masked_select(padding_mask) == ys_out_pad.masked_select(padding_mask))
466
+ # denominator = torch.sum(padding_mask)
467
+ # sd_acc = float(numerator) / float(denominator)
468
+
469
+ # 2. Compute attention loss
470
+ loss_att = self.criterion_att(decoder_out, ys_out_pad)
471
+ loss_spk = self.criterion_spk(torch.log(weights), text_id)
472
+
473
+ acc_spk = th_accuracy(
474
+ weights.view(-1, self.max_spk_num),
475
+ text_id,
476
+ ignore_label=self.ignore_id,
477
+ )
478
+ acc_att = th_accuracy(
479
+ decoder_out.view(-1, self.vocab_size),
480
+ ys_out_pad,
481
+ ignore_label=self.ignore_id,
482
+ )
483
+
484
+ # Compute cer/wer using attention-decoder
485
+ if self.training or self.error_calculator is None:
486
+ cer_att, wer_att = None, None
487
+ else:
488
+ ys_hat = decoder_out.argmax(dim=-1)
489
+ cer_att, wer_att = self.error_calculator(ys_hat.cpu(), ys_pad.cpu())
490
+
491
+ return loss_att, loss_spk, acc_att, acc_spk, cer_att, wer_att
492
+
493
+ def _calc_ctc_loss(
494
+ self,
495
+ encoder_out: torch.Tensor,
496
+ encoder_out_lens: torch.Tensor,
497
+ ys_pad: torch.Tensor,
498
+ ys_pad_lens: torch.Tensor,
499
+ ):
500
+ # Calc CTC loss
501
+ loss_ctc = self.ctc(encoder_out, encoder_out_lens, ys_pad, ys_pad_lens)
502
+
503
+ # Calc CER using CTC
504
+ cer_ctc = None
505
+ if not self.training and self.error_calculator is not None:
506
+ ys_hat = self.ctc.argmax(encoder_out).data
507
+ cer_ctc = self.error_calculator(ys_hat.cpu(), ys_pad.cpu(), is_ctc=True)
508
+ return loss_ctc, cer_ctc
funasr/models/sa_asr/transformer_decoder.py ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2019 Shigeki Karita
2
+ # Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3
+
4
+ """Decoder definition."""
5
+ from typing import Any
6
+ from typing import List
7
+ from typing import Sequence
8
+ from typing import Tuple
9
+
10
+ import torch
11
+ from torch import nn
12
+
13
+
14
+ from funasr.models.transformer.attention import MultiHeadedAttention
15
+ from funasr.models.sa_asr.attention import CosineDistanceAttention
16
+ from funasr.models.transformer.utils.dynamic_conv import DynamicConvolution
17
+ from funasr.models.transformer.utils.dynamic_conv2d import DynamicConvolution2D
18
+ from funasr.models.transformer.embedding import PositionalEncoding
19
+ from funasr.models.transformer.layer_norm import LayerNorm
20
+ from funasr.models.transformer.utils.lightconv import LightweightConvolution
21
+ from funasr.models.transformer.utils.lightconv2d import LightweightConvolution2D
22
+ from funasr.models.transformer.utils.mask import subsequent_mask
23
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
24
+ from funasr.models.transformer.positionwise_feed_forward import (
25
+ PositionwiseFeedForward, # noqa: H301
26
+ )
27
+ from funasr.models.transformer.utils.repeat import repeat
28
+ from funasr.models.transformer.scorers.scorer_interface import BatchScorerInterface
29
+
30
+ from funasr.register import tables
31
+
32
+
33
+ class DecoderLayer(nn.Module):
34
+ """Single decoder layer module.
35
+
36
+ Args:
37
+ size (int): Input dimension.
38
+ self_attn (torch.nn.Module): Self-attention module instance.
39
+ `MultiHeadedAttention` instance can be used as the argument.
40
+ src_attn (torch.nn.Module): Self-attention module instance.
41
+ `MultiHeadedAttention` instance can be used as the argument.
42
+ feed_forward (torch.nn.Module): Feed-forward module instance.
43
+ `PositionwiseFeedForward`, `MultiLayeredConv1d`, or `Conv1dLinear` instance
44
+ can be used as the argument.
45
+ dropout_rate (float): Dropout rate.
46
+ normalize_before (bool): Whether to use layer_norm before the first block.
47
+ concat_after (bool): Whether to concat attention layer's input and output.
48
+ if True, additional linear will be applied.
49
+ i.e. x -> x + linear(concat(x, att(x)))
50
+ if False, no additional linear will be applied. i.e. x -> x + att(x)
51
+
52
+
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ size,
58
+ self_attn,
59
+ src_attn,
60
+ feed_forward,
61
+ dropout_rate,
62
+ normalize_before=True,
63
+ concat_after=False,
64
+ ):
65
+ """Construct an DecoderLayer object."""
66
+ super(DecoderLayer, self).__init__()
67
+ self.size = size
68
+ self.self_attn = self_attn
69
+ self.src_attn = src_attn
70
+ self.feed_forward = feed_forward
71
+ self.norm1 = LayerNorm(size)
72
+ self.norm2 = LayerNorm(size)
73
+ self.norm3 = LayerNorm(size)
74
+ self.dropout = nn.Dropout(dropout_rate)
75
+ self.normalize_before = normalize_before
76
+ self.concat_after = concat_after
77
+ if self.concat_after:
78
+ self.concat_linear1 = nn.Linear(size + size, size)
79
+ self.concat_linear2 = nn.Linear(size + size, size)
80
+
81
+ def forward(self, tgt, tgt_mask, memory, memory_mask, cache=None):
82
+ """Compute decoded features.
83
+
84
+ Args:
85
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
86
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
87
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
88
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
89
+ cache (List[torch.Tensor]): List of cached tensors.
90
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
91
+
92
+ Returns:
93
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
94
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
95
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
96
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
97
+
98
+ """
99
+ residual = tgt
100
+ if self.normalize_before:
101
+ tgt = self.norm1(tgt)
102
+
103
+ if cache is None:
104
+ tgt_q = tgt
105
+ tgt_q_mask = tgt_mask
106
+ else:
107
+ # compute only the last frame query keeping dim: max_time_out -> 1
108
+ assert cache.shape == (
109
+ tgt.shape[0],
110
+ tgt.shape[1] - 1,
111
+ self.size,
112
+ ), f"{cache.shape} == {(tgt.shape[0], tgt.shape[1] - 1, self.size)}"
113
+ tgt_q = tgt[:, -1:, :]
114
+ residual = residual[:, -1:, :]
115
+ tgt_q_mask = None
116
+ if tgt_mask is not None:
117
+ tgt_q_mask = tgt_mask[:, -1:, :]
118
+
119
+ if self.concat_after:
120
+ tgt_concat = torch.cat((tgt_q, self.self_attn(tgt_q, tgt, tgt, tgt_q_mask)), dim=-1)
121
+ x = residual + self.concat_linear1(tgt_concat)
122
+ else:
123
+ x = residual + self.dropout(self.self_attn(tgt_q, tgt, tgt, tgt_q_mask))
124
+ if not self.normalize_before:
125
+ x = self.norm1(x)
126
+
127
+ residual = x
128
+ if self.normalize_before:
129
+ x = self.norm2(x)
130
+ if self.concat_after:
131
+ x_concat = torch.cat((x, self.src_attn(x, memory, memory, memory_mask)), dim=-1)
132
+ x = residual + self.concat_linear2(x_concat)
133
+ else:
134
+ x = residual + self.dropout(self.src_attn(x, memory, memory, memory_mask))
135
+ if not self.normalize_before:
136
+ x = self.norm2(x)
137
+
138
+ residual = x
139
+ if self.normalize_before:
140
+ x = self.norm3(x)
141
+ x = residual + self.dropout(self.feed_forward(x))
142
+ if not self.normalize_before:
143
+ x = self.norm3(x)
144
+
145
+ if cache is not None:
146
+ x = torch.cat([cache, x], dim=1)
147
+
148
+ return x, tgt_mask, memory, memory_mask
149
+
150
+
151
+ class BaseTransformerDecoder(nn.Module, BatchScorerInterface):
152
+ """Base class of Transfomer decoder module.
153
+
154
+ Args:
155
+ vocab_size: output dim
156
+ encoder_output_size: dimension of attention
157
+ attention_heads: the number of heads of multi head attention
158
+ linear_units: the number of units of position-wise feed forward
159
+ num_blocks: the number of decoder blocks
160
+ dropout_rate: dropout rate
161
+ self_attention_dropout_rate: dropout rate for attention
162
+ input_layer: input layer type
163
+ use_output_layer: whether to use output layer
164
+ pos_enc_class: PositionalEncoding or ScaledPositionalEncoding
165
+ normalize_before: whether to use layer_norm before the first block
166
+ concat_after: whether to concat attention layer's input and output
167
+ if True, additional linear will be applied.
168
+ i.e. x -> x + linear(concat(x, att(x)))
169
+ if False, no additional linear will be applied.
170
+ i.e. x -> x + att(x)
171
+ """
172
+
173
+ def __init__(
174
+ self,
175
+ vocab_size: int,
176
+ encoder_output_size: int,
177
+ dropout_rate: float = 0.1,
178
+ positional_dropout_rate: float = 0.1,
179
+ input_layer: str = "embed",
180
+ use_output_layer: bool = True,
181
+ pos_enc_class=PositionalEncoding,
182
+ normalize_before: bool = True,
183
+ ):
184
+ super().__init__()
185
+ attention_dim = encoder_output_size
186
+
187
+ if input_layer == "embed":
188
+ self.embed = torch.nn.Sequential(
189
+ torch.nn.Embedding(vocab_size, attention_dim),
190
+ pos_enc_class(attention_dim, positional_dropout_rate),
191
+ )
192
+ elif input_layer == "linear":
193
+ self.embed = torch.nn.Sequential(
194
+ torch.nn.Linear(vocab_size, attention_dim),
195
+ torch.nn.LayerNorm(attention_dim),
196
+ torch.nn.Dropout(dropout_rate),
197
+ torch.nn.ReLU(),
198
+ pos_enc_class(attention_dim, positional_dropout_rate),
199
+ )
200
+ else:
201
+ raise ValueError(f"only 'embed' or 'linear' is supported: {input_layer}")
202
+
203
+ self.normalize_before = normalize_before
204
+ if self.normalize_before:
205
+ self.after_norm = LayerNorm(attention_dim)
206
+ if use_output_layer:
207
+ self.output_layer = torch.nn.Linear(attention_dim, vocab_size)
208
+ else:
209
+ self.output_layer = None
210
+
211
+ # Must set by the inheritance
212
+ self.decoders = None
213
+
214
+ def forward(
215
+ self,
216
+ hs_pad: torch.Tensor,
217
+ hlens: torch.Tensor,
218
+ ys_in_pad: torch.Tensor,
219
+ ys_in_lens: torch.Tensor,
220
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
221
+ """Forward decoder.
222
+
223
+ Args:
224
+ hs_pad: encoded memory, float32 (batch, maxlen_in, feat)
225
+ hlens: (batch)
226
+ ys_in_pad:
227
+ input token ids, int64 (batch, maxlen_out)
228
+ if input_layer == "embed"
229
+ input tensor (batch, maxlen_out, #mels) in the other cases
230
+ ys_in_lens: (batch)
231
+ Returns:
232
+ (tuple): tuple containing:
233
+
234
+ x: decoded token score before softmax (batch, maxlen_out, token)
235
+ if use_output_layer is True,
236
+ olens: (batch, )
237
+ """
238
+ tgt = ys_in_pad
239
+ # tgt_mask: (B, 1, L)
240
+ tgt_mask = (~make_pad_mask(ys_in_lens)[:, None, :]).to(tgt.device)
241
+ # m: (1, L, L)
242
+ m = subsequent_mask(tgt_mask.size(-1), device=tgt_mask.device).unsqueeze(0)
243
+ # tgt_mask: (B, L, L)
244
+ tgt_mask = tgt_mask & m
245
+
246
+ memory = hs_pad
247
+ memory_mask = (~make_pad_mask(hlens, maxlen=memory.size(1)))[:, None, :].to(memory.device)
248
+ # Padding for Longformer
249
+ if memory_mask.shape[-1] != memory.shape[1]:
250
+ padlen = memory.shape[1] - memory_mask.shape[-1]
251
+ memory_mask = torch.nn.functional.pad(memory_mask, (0, padlen), "constant", False)
252
+
253
+ x = self.embed(tgt)
254
+ x, tgt_mask, memory, memory_mask = self.decoders(x, tgt_mask, memory, memory_mask)
255
+ if self.normalize_before:
256
+ x = self.after_norm(x)
257
+ if self.output_layer is not None:
258
+ x = self.output_layer(x)
259
+
260
+ olens = tgt_mask.sum(1)
261
+ return x, olens
262
+
263
+ def forward_one_step(
264
+ self,
265
+ tgt: torch.Tensor,
266
+ tgt_mask: torch.Tensor,
267
+ memory: torch.Tensor,
268
+ cache: List[torch.Tensor] = None,
269
+ ) -> Tuple[torch.Tensor, List[torch.Tensor]]:
270
+ """Forward one step.
271
+
272
+ Args:
273
+ tgt: input token ids, int64 (batch, maxlen_out)
274
+ tgt_mask: input token mask, (batch, maxlen_out)
275
+ dtype=torch.uint8 in PyTorch 1.2-
276
+ dtype=torch.bool in PyTorch 1.2+ (include 1.2)
277
+ memory: encoded memory, float32 (batch, maxlen_in, feat)
278
+ cache: cached output list of (batch, max_time_out-1, size)
279
+ Returns:
280
+ y, cache: NN output value and cache per `self.decoders`.
281
+ y.shape` is (batch, maxlen_out, token)
282
+ """
283
+ x = self.embed(tgt)
284
+ if cache is None:
285
+ cache = [None] * len(self.decoders)
286
+ new_cache = []
287
+ for c, decoder in zip(cache, self.decoders):
288
+ x, tgt_mask, memory, memory_mask = decoder(x, tgt_mask, memory, None, cache=c)
289
+ new_cache.append(x)
290
+
291
+ if self.normalize_before:
292
+ y = self.after_norm(x[:, -1])
293
+ else:
294
+ y = x[:, -1]
295
+ if self.output_layer is not None:
296
+ y = torch.log_softmax(self.output_layer(y), dim=-1)
297
+
298
+ return y, new_cache
299
+
300
+ def score(self, ys, state, x):
301
+ """Score."""
302
+ ys_mask = subsequent_mask(len(ys), device=x.device).unsqueeze(0)
303
+ logp, state = self.forward_one_step(ys.unsqueeze(0), ys_mask, x.unsqueeze(0), cache=state)
304
+ return logp.squeeze(0), state
305
+
306
+ def batch_score(
307
+ self, ys: torch.Tensor, states: List[Any], xs: torch.Tensor
308
+ ) -> Tuple[torch.Tensor, List[Any]]:
309
+ """Score new token batch.
310
+
311
+ Args:
312
+ ys (torch.Tensor): torch.int64 prefix tokens (n_batch, ylen).
313
+ states (List[Any]): Scorer states for prefix tokens.
314
+ xs (torch.Tensor):
315
+ The encoder feature that generates ys (n_batch, xlen, n_feat).
316
+
317
+ Returns:
318
+ tuple[torch.Tensor, List[Any]]: Tuple of
319
+ batchfied scores for next token with shape of `(n_batch, n_vocab)`
320
+ and next state list for ys.
321
+
322
+ """
323
+ # merge states
324
+ n_batch = len(ys)
325
+ n_layers = len(self.decoders)
326
+ if states[0] is None:
327
+ batch_state = None
328
+ else:
329
+ # transpose state of [batch, layer] into [layer, batch]
330
+ batch_state = [
331
+ torch.stack([states[b][i] for b in range(n_batch)]) for i in range(n_layers)
332
+ ]
333
+
334
+ # batch decoding
335
+ ys_mask = subsequent_mask(ys.size(-1), device=xs.device).unsqueeze(0)
336
+ logp, states = self.forward_one_step(ys, ys_mask, xs, cache=batch_state)
337
+
338
+ # transpose state of [layer, batch] into [batch, layer]
339
+ state_list = [[states[i][b] for i in range(n_layers)] for b in range(n_batch)]
340
+ return logp, state_list
341
+
342
+
343
+ @tables.register("decoder_classes", "TransformerDecoder")
344
+ class TransformerDecoder(BaseTransformerDecoder):
345
+ def __init__(
346
+ self,
347
+ vocab_size: int,
348
+ encoder_output_size: int,
349
+ attention_heads: int = 4,
350
+ linear_units: int = 2048,
351
+ num_blocks: int = 6,
352
+ dropout_rate: float = 0.1,
353
+ positional_dropout_rate: float = 0.1,
354
+ self_attention_dropout_rate: float = 0.0,
355
+ src_attention_dropout_rate: float = 0.0,
356
+ input_layer: str = "embed",
357
+ use_output_layer: bool = True,
358
+ pos_enc_class=PositionalEncoding,
359
+ normalize_before: bool = True,
360
+ concat_after: bool = False,
361
+ ):
362
+ super().__init__(
363
+ vocab_size=vocab_size,
364
+ encoder_output_size=encoder_output_size,
365
+ dropout_rate=dropout_rate,
366
+ positional_dropout_rate=positional_dropout_rate,
367
+ input_layer=input_layer,
368
+ use_output_layer=use_output_layer,
369
+ pos_enc_class=pos_enc_class,
370
+ normalize_before=normalize_before,
371
+ )
372
+
373
+ attention_dim = encoder_output_size
374
+ self.decoders = repeat(
375
+ num_blocks,
376
+ lambda lnum: DecoderLayer(
377
+ attention_dim,
378
+ MultiHeadedAttention(attention_heads, attention_dim, self_attention_dropout_rate),
379
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
380
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
381
+ dropout_rate,
382
+ normalize_before,
383
+ concat_after,
384
+ ),
385
+ )
386
+
387
+
388
+ @tables.register("decoder_classes", "ParaformerDecoderSAN")
389
+ class ParaformerDecoderSAN(BaseTransformerDecoder):
390
+ """
391
+ Author: Speech Lab of DAMO Academy, Alibaba Group
392
+ Paraformer: Fast and Accurate Parallel Transformer for Non-autoregressive End-to-End Speech Recognition
393
+ https://arxiv.org/abs/2006.01713
394
+ """
395
+
396
+ def __init__(
397
+ self,
398
+ vocab_size: int,
399
+ encoder_output_size: int,
400
+ attention_heads: int = 4,
401
+ linear_units: int = 2048,
402
+ num_blocks: int = 6,
403
+ dropout_rate: float = 0.1,
404
+ positional_dropout_rate: float = 0.1,
405
+ self_attention_dropout_rate: float = 0.0,
406
+ src_attention_dropout_rate: float = 0.0,
407
+ input_layer: str = "embed",
408
+ use_output_layer: bool = True,
409
+ pos_enc_class=PositionalEncoding,
410
+ normalize_before: bool = True,
411
+ concat_after: bool = False,
412
+ embeds_id: int = -1,
413
+ ):
414
+ super().__init__(
415
+ vocab_size=vocab_size,
416
+ encoder_output_size=encoder_output_size,
417
+ dropout_rate=dropout_rate,
418
+ positional_dropout_rate=positional_dropout_rate,
419
+ input_layer=input_layer,
420
+ use_output_layer=use_output_layer,
421
+ pos_enc_class=pos_enc_class,
422
+ normalize_before=normalize_before,
423
+ )
424
+
425
+ attention_dim = encoder_output_size
426
+ self.decoders = repeat(
427
+ num_blocks,
428
+ lambda lnum: DecoderLayer(
429
+ attention_dim,
430
+ MultiHeadedAttention(attention_heads, attention_dim, self_attention_dropout_rate),
431
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
432
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
433
+ dropout_rate,
434
+ normalize_before,
435
+ concat_after,
436
+ ),
437
+ )
438
+ self.embeds_id = embeds_id
439
+ self.attention_dim = attention_dim
440
+
441
+ def forward(
442
+ self,
443
+ hs_pad: torch.Tensor,
444
+ hlens: torch.Tensor,
445
+ ys_in_pad: torch.Tensor,
446
+ ys_in_lens: torch.Tensor,
447
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
448
+ """Forward decoder.
449
+
450
+ Args:
451
+ hs_pad: encoded memory, float32 (batch, maxlen_in, feat)
452
+ hlens: (batch)
453
+ ys_in_pad:
454
+ input token ids, int64 (batch, maxlen_out)
455
+ if input_layer == "embed"
456
+ input tensor (batch, maxlen_out, #mels) in the other cases
457
+ ys_in_lens: (batch)
458
+ Returns:
459
+ (tuple): tuple containing:
460
+
461
+ x: decoded token score before softmax (batch, maxlen_out, token)
462
+ if use_output_layer is True,
463
+ olens: (batch, )
464
+ """
465
+ tgt = ys_in_pad
466
+ tgt_mask = (~make_pad_mask(ys_in_lens)[:, None, :]).to(tgt.device)
467
+
468
+ memory = hs_pad
469
+ memory_mask = (~make_pad_mask(hlens, maxlen=memory.size(1)))[:, None, :].to(memory.device)
470
+ # Padding for Longformer
471
+ if memory_mask.shape[-1] != memory.shape[1]:
472
+ padlen = memory.shape[1] - memory_mask.shape[-1]
473
+ memory_mask = torch.nn.functional.pad(memory_mask, (0, padlen), "constant", False)
474
+
475
+ # x = self.embed(tgt)
476
+ x = tgt
477
+ embeds_outputs = None
478
+ for layer_id, decoder in enumerate(self.decoders):
479
+ x, tgt_mask, memory, memory_mask = decoder(x, tgt_mask, memory, memory_mask)
480
+ if layer_id == self.embeds_id:
481
+ embeds_outputs = x
482
+ if self.normalize_before:
483
+ x = self.after_norm(x)
484
+ if self.output_layer is not None:
485
+ x = self.output_layer(x)
486
+
487
+ olens = tgt_mask.sum(1)
488
+ if embeds_outputs is not None:
489
+ return x, olens, embeds_outputs
490
+ else:
491
+ return x, olens
492
+
493
+
494
+ @tables.register("decoder_classes", "LightweightConvolutionTransformerDecoder")
495
+ class LightweightConvolutionTransformerDecoder(BaseTransformerDecoder):
496
+ def __init__(
497
+ self,
498
+ vocab_size: int,
499
+ encoder_output_size: int,
500
+ attention_heads: int = 4,
501
+ linear_units: int = 2048,
502
+ num_blocks: int = 6,
503
+ dropout_rate: float = 0.1,
504
+ positional_dropout_rate: float = 0.1,
505
+ self_attention_dropout_rate: float = 0.0,
506
+ src_attention_dropout_rate: float = 0.0,
507
+ input_layer: str = "embed",
508
+ use_output_layer: bool = True,
509
+ pos_enc_class=PositionalEncoding,
510
+ normalize_before: bool = True,
511
+ concat_after: bool = False,
512
+ conv_wshare: int = 4,
513
+ conv_kernel_length: Sequence[int] = (11, 11, 11, 11, 11, 11),
514
+ conv_usebias: int = False,
515
+ ):
516
+ if len(conv_kernel_length) != num_blocks:
517
+ raise ValueError(
518
+ "conv_kernel_length must have equal number of values to num_blocks: "
519
+ f"{len(conv_kernel_length)} != {num_blocks}"
520
+ )
521
+ super().__init__(
522
+ vocab_size=vocab_size,
523
+ encoder_output_size=encoder_output_size,
524
+ dropout_rate=dropout_rate,
525
+ positional_dropout_rate=positional_dropout_rate,
526
+ input_layer=input_layer,
527
+ use_output_layer=use_output_layer,
528
+ pos_enc_class=pos_enc_class,
529
+ normalize_before=normalize_before,
530
+ )
531
+
532
+ attention_dim = encoder_output_size
533
+ self.decoders = repeat(
534
+ num_blocks,
535
+ lambda lnum: DecoderLayer(
536
+ attention_dim,
537
+ LightweightConvolution(
538
+ wshare=conv_wshare,
539
+ n_feat=attention_dim,
540
+ dropout_rate=self_attention_dropout_rate,
541
+ kernel_size=conv_kernel_length[lnum],
542
+ use_kernel_mask=True,
543
+ use_bias=conv_usebias,
544
+ ),
545
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
546
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
547
+ dropout_rate,
548
+ normalize_before,
549
+ concat_after,
550
+ ),
551
+ )
552
+
553
+
554
+ @tables.register("decoder_classes", "LightweightConvolution2DTransformerDecoder")
555
+ class LightweightConvolution2DTransformerDecoder(BaseTransformerDecoder):
556
+ def __init__(
557
+ self,
558
+ vocab_size: int,
559
+ encoder_output_size: int,
560
+ attention_heads: int = 4,
561
+ linear_units: int = 2048,
562
+ num_blocks: int = 6,
563
+ dropout_rate: float = 0.1,
564
+ positional_dropout_rate: float = 0.1,
565
+ self_attention_dropout_rate: float = 0.0,
566
+ src_attention_dropout_rate: float = 0.0,
567
+ input_layer: str = "embed",
568
+ use_output_layer: bool = True,
569
+ pos_enc_class=PositionalEncoding,
570
+ normalize_before: bool = True,
571
+ concat_after: bool = False,
572
+ conv_wshare: int = 4,
573
+ conv_kernel_length: Sequence[int] = (11, 11, 11, 11, 11, 11),
574
+ conv_usebias: int = False,
575
+ ):
576
+ if len(conv_kernel_length) != num_blocks:
577
+ raise ValueError(
578
+ "conv_kernel_length must have equal number of values to num_blocks: "
579
+ f"{len(conv_kernel_length)} != {num_blocks}"
580
+ )
581
+ super().__init__(
582
+ vocab_size=vocab_size,
583
+ encoder_output_size=encoder_output_size,
584
+ dropout_rate=dropout_rate,
585
+ positional_dropout_rate=positional_dropout_rate,
586
+ input_layer=input_layer,
587
+ use_output_layer=use_output_layer,
588
+ pos_enc_class=pos_enc_class,
589
+ normalize_before=normalize_before,
590
+ )
591
+
592
+ attention_dim = encoder_output_size
593
+ self.decoders = repeat(
594
+ num_blocks,
595
+ lambda lnum: DecoderLayer(
596
+ attention_dim,
597
+ LightweightConvolution2D(
598
+ wshare=conv_wshare,
599
+ n_feat=attention_dim,
600
+ dropout_rate=self_attention_dropout_rate,
601
+ kernel_size=conv_kernel_length[lnum],
602
+ use_kernel_mask=True,
603
+ use_bias=conv_usebias,
604
+ ),
605
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
606
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
607
+ dropout_rate,
608
+ normalize_before,
609
+ concat_after,
610
+ ),
611
+ )
612
+
613
+
614
+ @tables.register("decoder_classes", "DynamicConvolutionTransformerDecoder")
615
+ class DynamicConvolutionTransformerDecoder(BaseTransformerDecoder):
616
+ def __init__(
617
+ self,
618
+ vocab_size: int,
619
+ encoder_output_size: int,
620
+ attention_heads: int = 4,
621
+ linear_units: int = 2048,
622
+ num_blocks: int = 6,
623
+ dropout_rate: float = 0.1,
624
+ positional_dropout_rate: float = 0.1,
625
+ self_attention_dropout_rate: float = 0.0,
626
+ src_attention_dropout_rate: float = 0.0,
627
+ input_layer: str = "embed",
628
+ use_output_layer: bool = True,
629
+ pos_enc_class=PositionalEncoding,
630
+ normalize_before: bool = True,
631
+ concat_after: bool = False,
632
+ conv_wshare: int = 4,
633
+ conv_kernel_length: Sequence[int] = (11, 11, 11, 11, 11, 11),
634
+ conv_usebias: int = False,
635
+ ):
636
+ if len(conv_kernel_length) != num_blocks:
637
+ raise ValueError(
638
+ "conv_kernel_length must have equal number of values to num_blocks: "
639
+ f"{len(conv_kernel_length)} != {num_blocks}"
640
+ )
641
+ super().__init__(
642
+ vocab_size=vocab_size,
643
+ encoder_output_size=encoder_output_size,
644
+ dropout_rate=dropout_rate,
645
+ positional_dropout_rate=positional_dropout_rate,
646
+ input_layer=input_layer,
647
+ use_output_layer=use_output_layer,
648
+ pos_enc_class=pos_enc_class,
649
+ normalize_before=normalize_before,
650
+ )
651
+ attention_dim = encoder_output_size
652
+
653
+ self.decoders = repeat(
654
+ num_blocks,
655
+ lambda lnum: DecoderLayer(
656
+ attention_dim,
657
+ DynamicConvolution(
658
+ wshare=conv_wshare,
659
+ n_feat=attention_dim,
660
+ dropout_rate=self_attention_dropout_rate,
661
+ kernel_size=conv_kernel_length[lnum],
662
+ use_kernel_mask=True,
663
+ use_bias=conv_usebias,
664
+ ),
665
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
666
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
667
+ dropout_rate,
668
+ normalize_before,
669
+ concat_after,
670
+ ),
671
+ )
672
+
673
+
674
+ @tables.register("decoder_classes", "DynamicConvolution2DTransformerDecoder")
675
+ class DynamicConvolution2DTransformerDecoder(BaseTransformerDecoder):
676
+ def __init__(
677
+ self,
678
+ vocab_size: int,
679
+ encoder_output_size: int,
680
+ attention_heads: int = 4,
681
+ linear_units: int = 2048,
682
+ num_blocks: int = 6,
683
+ dropout_rate: float = 0.1,
684
+ positional_dropout_rate: float = 0.1,
685
+ self_attention_dropout_rate: float = 0.0,
686
+ src_attention_dropout_rate: float = 0.0,
687
+ input_layer: str = "embed",
688
+ use_output_layer: bool = True,
689
+ pos_enc_class=PositionalEncoding,
690
+ normalize_before: bool = True,
691
+ concat_after: bool = False,
692
+ conv_wshare: int = 4,
693
+ conv_kernel_length: Sequence[int] = (11, 11, 11, 11, 11, 11),
694
+ conv_usebias: int = False,
695
+ ):
696
+ if len(conv_kernel_length) != num_blocks:
697
+ raise ValueError(
698
+ "conv_kernel_length must have equal number of values to num_blocks: "
699
+ f"{len(conv_kernel_length)} != {num_blocks}"
700
+ )
701
+ super().__init__(
702
+ vocab_size=vocab_size,
703
+ encoder_output_size=encoder_output_size,
704
+ dropout_rate=dropout_rate,
705
+ positional_dropout_rate=positional_dropout_rate,
706
+ input_layer=input_layer,
707
+ use_output_layer=use_output_layer,
708
+ pos_enc_class=pos_enc_class,
709
+ normalize_before=normalize_before,
710
+ )
711
+ attention_dim = encoder_output_size
712
+
713
+ self.decoders = repeat(
714
+ num_blocks,
715
+ lambda lnum: DecoderLayer(
716
+ attention_dim,
717
+ DynamicConvolution2D(
718
+ wshare=conv_wshare,
719
+ n_feat=attention_dim,
720
+ dropout_rate=self_attention_dropout_rate,
721
+ kernel_size=conv_kernel_length[lnum],
722
+ use_kernel_mask=True,
723
+ use_bias=conv_usebias,
724
+ ),
725
+ MultiHeadedAttention(attention_heads, attention_dim, src_attention_dropout_rate),
726
+ PositionwiseFeedForward(attention_dim, linear_units, dropout_rate),
727
+ dropout_rate,
728
+ normalize_before,
729
+ concat_after,
730
+ ),
731
+ )
funasr/models/sanm/__init__.py ADDED
File without changes
funasr/models/sanm/attention.py ADDED
@@ -0,0 +1,903 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ # Copyright 2019 Shigeki Karita
5
+ # Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
6
+
7
+ """Multi-Head Attention layer definition."""
8
+
9
+ import math
10
+
11
+ import numpy
12
+ import torch
13
+ from torch import nn
14
+ from typing import Optional, Tuple
15
+
16
+ import torch.nn.functional as F
17
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
18
+ import funasr.models.lora.layers as lora
19
+
20
+
21
+ def preprocess_for_attn(x, mask, cache, pad_fn, kernel_size):
22
+ x = x * mask
23
+ x = x.transpose(1, 2)
24
+ if cache is None:
25
+ x = pad_fn(x)
26
+ else:
27
+ x = torch.cat((cache, x), dim=2)
28
+ cache = x[:, :, -(kernel_size - 1) :]
29
+ return x, cache
30
+
31
+
32
+ torch_version = tuple([int(i) for i in torch.__version__.split(".")[:2]])
33
+ if torch_version >= (1, 8):
34
+ import torch.fx
35
+
36
+ torch.fx.wrap("preprocess_for_attn")
37
+
38
+
39
+ class MultiHeadedAttention(nn.Module):
40
+ """Multi-Head Attention layer.
41
+
42
+ Args:
43
+ n_head (int): The number of heads.
44
+ n_feat (int): The number of features.
45
+ dropout_rate (float): Dropout rate.
46
+
47
+ """
48
+
49
+ def __init__(self, n_head, n_feat, dropout_rate):
50
+ """Construct an MultiHeadedAttention object."""
51
+ super(MultiHeadedAttention, self).__init__()
52
+ assert n_feat % n_head == 0
53
+ # We assume d_v always equals d_k
54
+ self.d_k = n_feat // n_head
55
+ self.h = n_head
56
+ self.linear_q = nn.Linear(n_feat, n_feat)
57
+ self.linear_k = nn.Linear(n_feat, n_feat)
58
+ self.linear_v = nn.Linear(n_feat, n_feat)
59
+ self.linear_out = nn.Linear(n_feat, n_feat)
60
+ self.attn = None
61
+ self.dropout = nn.Dropout(p=dropout_rate)
62
+
63
+ def forward_qkv(self, query, key, value):
64
+ """Transform query, key and value.
65
+
66
+ Args:
67
+ query (torch.Tensor): Query tensor (#batch, time1, size).
68
+ key (torch.Tensor): Key tensor (#batch, time2, size).
69
+ value (torch.Tensor): Value tensor (#batch, time2, size).
70
+
71
+ Returns:
72
+ torch.Tensor: Transformed query tensor (#batch, n_head, time1, d_k).
73
+ torch.Tensor: Transformed key tensor (#batch, n_head, time2, d_k).
74
+ torch.Tensor: Transformed value tensor (#batch, n_head, time2, d_k).
75
+
76
+ """
77
+ n_batch = query.size(0)
78
+ q = self.linear_q(query).view(n_batch, -1, self.h, self.d_k)
79
+ k = self.linear_k(key).view(n_batch, -1, self.h, self.d_k)
80
+ v = self.linear_v(value).view(n_batch, -1, self.h, self.d_k)
81
+ q = q.transpose(1, 2) # (batch, head, time1, d_k)
82
+ k = k.transpose(1, 2) # (batch, head, time2, d_k)
83
+ v = v.transpose(1, 2) # (batch, head, time2, d_k)
84
+
85
+ return q, k, v
86
+
87
+ def forward_attention(self, value, scores, mask):
88
+ """Compute attention context vector.
89
+
90
+ Args:
91
+ value (torch.Tensor): Transformed value (#batch, n_head, time2, d_k).
92
+ scores (torch.Tensor): Attention score (#batch, n_head, time1, time2).
93
+ mask (torch.Tensor): Mask (#batch, 1, time2) or (#batch, time1, time2).
94
+
95
+ Returns:
96
+ torch.Tensor: Transformed value (#batch, time1, d_model)
97
+ weighted by the attention score (#batch, time1, time2).
98
+
99
+ """
100
+ n_batch = value.size(0)
101
+ if mask is not None:
102
+ mask = mask.unsqueeze(1).eq(0) # (batch, 1, *, time2)
103
+ min_value = -float(
104
+ "inf"
105
+ ) # float(numpy.finfo(torch.tensor(0, dtype=scores.dtype).numpy().dtype).min)
106
+ scores = scores.masked_fill(mask, min_value)
107
+ attn = torch.softmax(scores, dim=-1).masked_fill(
108
+ mask, 0.0
109
+ ) # (batch, head, time1, time2)
110
+ else:
111
+ attn = torch.softmax(scores, dim=-1) # (batch, head, time1, time2)
112
+
113
+ p_attn = self.dropout(attn)
114
+ x = torch.matmul(p_attn, value) # (batch, head, time1, d_k)
115
+ x = (
116
+ x.transpose(1, 2).contiguous().view(n_batch, -1, self.h * self.d_k)
117
+ ) # (batch, time1, d_model)
118
+
119
+ return self.linear_out(x) # (batch, time1, d_model)
120
+
121
+ def forward(self, query, key, value, mask):
122
+ """Compute scaled dot product attention.
123
+
124
+ Args:
125
+ query (torch.Tensor): Query tensor (#batch, time1, size).
126
+ key (torch.Tensor): Key tensor (#batch, time2, size).
127
+ value (torch.Tensor): Value tensor (#batch, time2, size).
128
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
129
+ (#batch, time1, time2).
130
+
131
+ Returns:
132
+ torch.Tensor: Output tensor (#batch, time1, d_model).
133
+
134
+ """
135
+ q, k, v = self.forward_qkv(query, key, value)
136
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
137
+ return self.forward_attention(v, scores, mask)
138
+
139
+
140
+ class MultiHeadedAttentionSANM(nn.Module):
141
+ """Multi-Head Attention layer.
142
+
143
+ Args:
144
+ n_head (int): The number of heads.
145
+ n_feat (int): The number of features.
146
+ dropout_rate (float): Dropout rate.
147
+
148
+ """
149
+
150
+ def __init__(
151
+ self,
152
+ n_head,
153
+ in_feat,
154
+ n_feat,
155
+ dropout_rate,
156
+ kernel_size,
157
+ sanm_shfit=0,
158
+ lora_list=None,
159
+ lora_rank=8,
160
+ lora_alpha=16,
161
+ lora_dropout=0.1,
162
+ ):
163
+ """Construct an MultiHeadedAttention object."""
164
+ super().__init__()
165
+ assert n_feat % n_head == 0
166
+ # We assume d_v always equals d_k
167
+ self.d_k = n_feat // n_head
168
+ self.h = n_head
169
+ # self.linear_q = nn.Linear(n_feat, n_feat)
170
+ # self.linear_k = nn.Linear(n_feat, n_feat)
171
+ # self.linear_v = nn.Linear(n_feat, n_feat)
172
+ if lora_list is not None:
173
+ if "o" in lora_list:
174
+ self.linear_out = lora.Linear(
175
+ n_feat, n_feat, r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout
176
+ )
177
+ else:
178
+ self.linear_out = nn.Linear(n_feat, n_feat)
179
+ lora_qkv_list = ["q" in lora_list, "k" in lora_list, "v" in lora_list]
180
+ if lora_qkv_list == [False, False, False]:
181
+ self.linear_q_k_v = nn.Linear(in_feat, n_feat * 3)
182
+ else:
183
+ self.linear_q_k_v = lora.MergedLinear(
184
+ in_feat,
185
+ n_feat * 3,
186
+ r=lora_rank,
187
+ lora_alpha=lora_alpha,
188
+ lora_dropout=lora_dropout,
189
+ enable_lora=lora_qkv_list,
190
+ )
191
+ else:
192
+ self.linear_out = nn.Linear(n_feat, n_feat)
193
+ self.linear_q_k_v = nn.Linear(in_feat, n_feat * 3)
194
+ attn = None
195
+ self.dropout = nn.Dropout(p=dropout_rate)
196
+
197
+ self.fsmn_block = nn.Conv1d(
198
+ n_feat, n_feat, kernel_size, stride=1, padding=0, groups=n_feat, bias=False
199
+ )
200
+ # padding
201
+ left_padding = (kernel_size - 1) // 2
202
+ if sanm_shfit > 0:
203
+ left_padding = left_padding + sanm_shfit
204
+ right_padding = kernel_size - 1 - left_padding
205
+ self.pad_fn = nn.ConstantPad1d((left_padding, right_padding), 0.0)
206
+
207
+ def forward_fsmn(self, inputs, mask, mask_shfit_chunk=None):
208
+ b, t, d = inputs.size()
209
+ if mask is not None:
210
+ mask = torch.reshape(mask, (b, -1, 1))
211
+ if mask_shfit_chunk is not None:
212
+ mask = mask * mask_shfit_chunk
213
+ inputs = inputs * mask
214
+
215
+ x = inputs.transpose(1, 2)
216
+ x = self.pad_fn(x)
217
+ x = self.fsmn_block(x)
218
+ x = x.transpose(1, 2)
219
+ x += inputs
220
+ x = self.dropout(x)
221
+ if mask is not None:
222
+ x = x * mask
223
+ return x
224
+
225
+ def forward_qkv(self, x):
226
+ """Transform query, key and value.
227
+
228
+ Args:
229
+ query (torch.Tensor): Query tensor (#batch, time1, size).
230
+ key (torch.Tensor): Key tensor (#batch, time2, size).
231
+ value (torch.Tensor): Value tensor (#batch, time2, size).
232
+
233
+ Returns:
234
+ torch.Tensor: Transformed query tensor (#batch, n_head, time1, d_k).
235
+ torch.Tensor: Transformed key tensor (#batch, n_head, time2, d_k).
236
+ torch.Tensor: Transformed value tensor (#batch, n_head, time2, d_k).
237
+
238
+ """
239
+ b, t, d = x.size()
240
+ q_k_v = self.linear_q_k_v(x)
241
+ q, k, v = torch.split(q_k_v, int(self.h * self.d_k), dim=-1)
242
+ q_h = torch.reshape(q, (b, t, self.h, self.d_k)).transpose(
243
+ 1, 2
244
+ ) # (batch, head, time1, d_k)
245
+ k_h = torch.reshape(k, (b, t, self.h, self.d_k)).transpose(
246
+ 1, 2
247
+ ) # (batch, head, time2, d_k)
248
+ v_h = torch.reshape(v, (b, t, self.h, self.d_k)).transpose(
249
+ 1, 2
250
+ ) # (batch, head, time2, d_k)
251
+
252
+ return q_h, k_h, v_h, v
253
+
254
+ def forward_attention(self, value, scores, mask, mask_att_chunk_encoder=None):
255
+ """Compute attention context vector.
256
+
257
+ Args:
258
+ value (torch.Tensor): Transformed value (#batch, n_head, time2, d_k).
259
+ scores (torch.Tensor): Attention score (#batch, n_head, time1, time2).
260
+ mask (torch.Tensor): Mask (#batch, 1, time2) or (#batch, time1, time2).
261
+
262
+ Returns:
263
+ torch.Tensor: Transformed value (#batch, time1, d_model)
264
+ weighted by the attention score (#batch, time1, time2).
265
+
266
+ """
267
+ n_batch = value.size(0)
268
+ if mask is not None:
269
+ if mask_att_chunk_encoder is not None:
270
+ mask = mask * mask_att_chunk_encoder
271
+
272
+ mask = mask.unsqueeze(1).eq(0) # (batch, 1, *, time2)
273
+
274
+ min_value = -float(
275
+ "inf"
276
+ ) # float(numpy.finfo(torch.tensor(0, dtype=scores.dtype).numpy().dtype).min)
277
+ scores = scores.masked_fill(mask, min_value)
278
+ attn = torch.softmax(scores, dim=-1).masked_fill(
279
+ mask, 0.0
280
+ ) # (batch, head, time1, time2)
281
+ else:
282
+ attn = torch.softmax(scores, dim=-1) # (batch, head, time1, time2)
283
+
284
+ p_attn = self.dropout(attn)
285
+ x = torch.matmul(p_attn, value) # (batch, head, time1, d_k)
286
+ x = (
287
+ x.transpose(1, 2).contiguous().view(n_batch, -1, self.h * self.d_k)
288
+ ) # (batch, time1, d_model)
289
+
290
+ return self.linear_out(x) # (batch, time1, d_model)
291
+
292
+ def forward(self, x, mask, mask_shfit_chunk=None, mask_att_chunk_encoder=None):
293
+ """Compute scaled dot product attention.
294
+
295
+ Args:
296
+ query (torch.Tensor): Query tensor (#batch, time1, size).
297
+ key (torch.Tensor): Key tensor (#batch, time2, size).
298
+ value (torch.Tensor): Value tensor (#batch, time2, size).
299
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
300
+ (#batch, time1, time2).
301
+
302
+ Returns:
303
+ torch.Tensor: Output tensor (#batch, time1, d_model).
304
+
305
+ """
306
+ q_h, k_h, v_h, v = self.forward_qkv(x)
307
+ fsmn_memory = self.forward_fsmn(v, mask, mask_shfit_chunk)
308
+ q_h = q_h * self.d_k ** (-0.5)
309
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
310
+ att_outs = self.forward_attention(v_h, scores, mask, mask_att_chunk_encoder)
311
+ return att_outs + fsmn_memory
312
+
313
+ def forward_chunk(self, x, cache=None, chunk_size=None, look_back=0):
314
+ """Compute scaled dot product attention.
315
+
316
+ Args:
317
+ query (torch.Tensor): Query tensor (#batch, time1, size).
318
+ key (torch.Tensor): Key tensor (#batch, time2, size).
319
+ value (torch.Tensor): Value tensor (#batch, time2, size).
320
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
321
+ (#batch, time1, time2).
322
+
323
+ Returns:
324
+ torch.Tensor: Output tensor (#batch, time1, d_model).
325
+
326
+ """
327
+ q_h, k_h, v_h, v = self.forward_qkv(x)
328
+ if chunk_size is not None and look_back > 0 or look_back == -1:
329
+ if cache is not None:
330
+ k_h_stride = k_h[:, :, : -(chunk_size[2]), :]
331
+ v_h_stride = v_h[:, :, : -(chunk_size[2]), :]
332
+ k_h = torch.cat((cache["k"], k_h), dim=2)
333
+ v_h = torch.cat((cache["v"], v_h), dim=2)
334
+
335
+ cache["k"] = torch.cat((cache["k"], k_h_stride), dim=2)
336
+ cache["v"] = torch.cat((cache["v"], v_h_stride), dim=2)
337
+ if look_back != -1:
338
+ cache["k"] = cache["k"][:, :, -(look_back * chunk_size[1]) :, :]
339
+ cache["v"] = cache["v"][:, :, -(look_back * chunk_size[1]) :, :]
340
+ else:
341
+ cache_tmp = {
342
+ "k": k_h[:, :, : -(chunk_size[2]), :],
343
+ "v": v_h[:, :, : -(chunk_size[2]), :],
344
+ }
345
+ cache = cache_tmp
346
+ fsmn_memory = self.forward_fsmn(v, None)
347
+ q_h = q_h * self.d_k ** (-0.5)
348
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
349
+ att_outs = self.forward_attention(v_h, scores, None)
350
+ return att_outs + fsmn_memory, cache
351
+
352
+
353
+ class MultiHeadedAttentionSANMExport(nn.Module):
354
+ def __init__(self, model):
355
+ super().__init__()
356
+ self.d_k = model.d_k
357
+ self.h = model.h
358
+ self.linear_out = model.linear_out
359
+ self.linear_q_k_v = model.linear_q_k_v
360
+ self.fsmn_block = model.fsmn_block
361
+ self.pad_fn = model.pad_fn
362
+
363
+ self.attn = None
364
+ self.all_head_size = self.h * self.d_k
365
+
366
+ def forward(self, x, mask):
367
+ mask_3d_btd, mask_4d_bhlt = mask
368
+ q_h, k_h, v_h, v = self.forward_qkv(x)
369
+ fsmn_memory = self.forward_fsmn(v, mask_3d_btd)
370
+ q_h = q_h * self.d_k ** (-0.5)
371
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
372
+ att_outs = self.forward_attention(v_h, scores, mask_4d_bhlt)
373
+ return att_outs + fsmn_memory
374
+
375
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
376
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
377
+ x = x.view(new_x_shape)
378
+ return x.permute(0, 2, 1, 3)
379
+
380
+ def forward_qkv(self, x):
381
+ q_k_v = self.linear_q_k_v(x)
382
+ q, k, v = torch.split(q_k_v, int(self.h * self.d_k), dim=-1)
383
+ q_h = self.transpose_for_scores(q)
384
+ k_h = self.transpose_for_scores(k)
385
+ v_h = self.transpose_for_scores(v)
386
+ return q_h, k_h, v_h, v
387
+
388
+ def forward_fsmn(self, inputs, mask):
389
+ # b, t, d = inputs.size()
390
+ # mask = torch.reshape(mask, (b, -1, 1))
391
+ inputs = inputs * mask
392
+ x = inputs.transpose(1, 2)
393
+ x = self.pad_fn(x)
394
+ x = self.fsmn_block(x)
395
+ x = x.transpose(1, 2)
396
+ x = x + inputs
397
+ x = x * mask
398
+ return x
399
+
400
+ def forward_attention(self, value, scores, mask):
401
+ scores = scores + mask
402
+
403
+ attn = torch.softmax(scores, dim=-1)
404
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
405
+
406
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
407
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
408
+ context_layer = context_layer.view(new_context_layer_shape)
409
+ return self.linear_out(context_layer) # (batch, time1, d_model)
410
+
411
+
412
+ class MultiHeadedAttentionSANMExport(nn.Module):
413
+ def __init__(self, model):
414
+ super().__init__()
415
+ self.d_k = model.d_k
416
+ self.h = model.h
417
+ self.linear_out = model.linear_out
418
+ self.linear_q_k_v = model.linear_q_k_v
419
+ self.fsmn_block = model.fsmn_block
420
+ self.pad_fn = model.pad_fn
421
+
422
+ self.attn = None
423
+ self.all_head_size = self.h * self.d_k
424
+
425
+ def forward(self, x, mask):
426
+ mask_3d_btd, mask_4d_bhlt = mask
427
+ q_h, k_h, v_h, v = self.forward_qkv(x)
428
+ fsmn_memory = self.forward_fsmn(v, mask_3d_btd)
429
+ q_h = q_h * self.d_k ** (-0.5)
430
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
431
+ att_outs = self.forward_attention(v_h, scores, mask_4d_bhlt)
432
+ return att_outs + fsmn_memory
433
+
434
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
435
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
436
+ x = x.view(new_x_shape)
437
+ return x.permute(0, 2, 1, 3)
438
+
439
+ def forward_qkv(self, x):
440
+ q_k_v = self.linear_q_k_v(x)
441
+ q, k, v = torch.split(q_k_v, int(self.h * self.d_k), dim=-1)
442
+ q_h = self.transpose_for_scores(q)
443
+ k_h = self.transpose_for_scores(k)
444
+ v_h = self.transpose_for_scores(v)
445
+ return q_h, k_h, v_h, v
446
+
447
+ def forward_fsmn(self, inputs, mask):
448
+ # b, t, d = inputs.size()
449
+ # mask = torch.reshape(mask, (b, -1, 1))
450
+ inputs = inputs * mask
451
+ x = inputs.transpose(1, 2)
452
+ x = self.pad_fn(x)
453
+ x = self.fsmn_block(x)
454
+ x = x.transpose(1, 2)
455
+ x = x + inputs
456
+ x = x * mask
457
+ return x
458
+
459
+ def forward_attention(self, value, scores, mask):
460
+ scores = scores + mask
461
+
462
+ attn = torch.softmax(scores, dim=-1)
463
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
464
+
465
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
466
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
467
+ context_layer = context_layer.view(new_context_layer_shape)
468
+ return self.linear_out(context_layer) # (batch, time1, d_model)
469
+
470
+
471
+ class MultiHeadedAttentionSANMDecoder(nn.Module):
472
+ """Multi-Head Attention layer.
473
+
474
+ Args:
475
+ n_head (int): The number of heads.
476
+ n_feat (int): The number of features.
477
+ dropout_rate (float): Dropout rate.
478
+
479
+ """
480
+
481
+ def __init__(self, n_feat, dropout_rate, kernel_size, sanm_shfit=0):
482
+ """Construct an MultiHeadedAttention object."""
483
+ super().__init__()
484
+
485
+ self.dropout = nn.Dropout(p=dropout_rate)
486
+
487
+ self.fsmn_block = nn.Conv1d(
488
+ n_feat, n_feat, kernel_size, stride=1, padding=0, groups=n_feat, bias=False
489
+ )
490
+ # padding
491
+ # padding
492
+ left_padding = (kernel_size - 1) // 2
493
+ if sanm_shfit > 0:
494
+ left_padding = left_padding + sanm_shfit
495
+ right_padding = kernel_size - 1 - left_padding
496
+ self.pad_fn = nn.ConstantPad1d((left_padding, right_padding), 0.0)
497
+ self.kernel_size = kernel_size
498
+
499
+ def forward(self, inputs, mask, cache=None, mask_shfit_chunk=None):
500
+ """
501
+ :param x: (#batch, time1, size).
502
+ :param mask: Mask tensor (#batch, 1, time)
503
+ :return:
504
+ """
505
+ # print("in fsmn, inputs", inputs.size())
506
+ b, t, d = inputs.size()
507
+ # logging.info(
508
+ # "mask: {}".format(mask.size()))
509
+ if mask is not None:
510
+ mask = torch.reshape(mask, (b, -1, 1))
511
+ # logging.info("in fsmn, mask: {}, {}".format(mask.size(), mask[0:100:50, :, :]))
512
+ if mask_shfit_chunk is not None:
513
+ # logging.info("in fsmn, mask_fsmn: {}, {}".format(mask_shfit_chunk.size(), mask_shfit_chunk[0:100:50, :, :]))
514
+ mask = mask * mask_shfit_chunk
515
+ # logging.info("in fsmn, mask_after_fsmn: {}, {}".format(mask.size(), mask[0:100:50, :, :]))
516
+ # print("in fsmn, mask", mask.size())
517
+ # print("in fsmn, inputs", inputs.size())
518
+ inputs = inputs * mask
519
+
520
+ x = inputs.transpose(1, 2)
521
+ b, d, t = x.size()
522
+ if cache is None:
523
+ # print("in fsmn, cache is None, x", x.size())
524
+
525
+ x = self.pad_fn(x)
526
+ if not self.training:
527
+ cache = x
528
+ else:
529
+ # print("in fsmn, cache is not None, x", x.size())
530
+ # x = torch.cat((x, cache), dim=2)[:, :, :-1]
531
+ # if t < self.kernel_size:
532
+ # x = self.pad_fn(x)
533
+ x = torch.cat((cache[:, :, 1:], x), dim=2)
534
+ x = x[:, :, -(self.kernel_size + t - 1) :]
535
+ # print("in fsmn, cache is not None, x_cat", x.size())
536
+ cache = x
537
+ x = self.fsmn_block(x)
538
+ x = x.transpose(1, 2)
539
+ # print("in fsmn, fsmn_out", x.size())
540
+ if x.size(1) != inputs.size(1):
541
+ inputs = inputs[:, -1, :]
542
+
543
+ x = x + inputs
544
+ x = self.dropout(x)
545
+ if mask is not None:
546
+ x = x * mask
547
+ return x, cache
548
+
549
+
550
+ class MultiHeadedAttentionSANMDecoderExport(nn.Module):
551
+ def __init__(self, model):
552
+ super().__init__()
553
+ self.fsmn_block = model.fsmn_block
554
+ self.pad_fn = model.pad_fn
555
+ self.kernel_size = model.kernel_size
556
+ self.attn = None
557
+
558
+ def forward(self, inputs, mask, cache=None):
559
+ x, cache = preprocess_for_attn(inputs, mask, cache, self.pad_fn, self.kernel_size)
560
+ x = self.fsmn_block(x)
561
+ x = x.transpose(1, 2)
562
+
563
+ x = x + inputs
564
+ x = x * mask
565
+ return x, cache
566
+
567
+
568
+ class MultiHeadedAttentionCrossAtt(nn.Module):
569
+ """Multi-Head Attention layer.
570
+
571
+ Args:
572
+ n_head (int): The number of heads.
573
+ n_feat (int): The number of features.
574
+ dropout_rate (float): Dropout rate.
575
+
576
+ """
577
+
578
+ def __init__(
579
+ self,
580
+ n_head,
581
+ n_feat,
582
+ dropout_rate,
583
+ lora_list=None,
584
+ lora_rank=8,
585
+ lora_alpha=16,
586
+ lora_dropout=0.1,
587
+ encoder_output_size=None,
588
+ ):
589
+ """Construct an MultiHeadedAttention object."""
590
+ super().__init__()
591
+ assert n_feat % n_head == 0
592
+ # We assume d_v always equals d_k
593
+ self.d_k = n_feat // n_head
594
+ self.h = n_head
595
+ if lora_list is not None:
596
+ if "q" in lora_list:
597
+ self.linear_q = lora.Linear(
598
+ n_feat, n_feat, r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout
599
+ )
600
+ else:
601
+ self.linear_q = nn.Linear(n_feat, n_feat)
602
+ lora_kv_list = ["k" in lora_list, "v" in lora_list]
603
+ if lora_kv_list == [False, False]:
604
+ self.linear_k_v = nn.Linear(
605
+ n_feat if encoder_output_size is None else encoder_output_size, n_feat * 2
606
+ )
607
+ else:
608
+ self.linear_k_v = lora.MergedLinear(
609
+ n_feat if encoder_output_size is None else encoder_output_size,
610
+ n_feat * 2,
611
+ r=lora_rank,
612
+ lora_alpha=lora_alpha,
613
+ lora_dropout=lora_dropout,
614
+ enable_lora=lora_kv_list,
615
+ )
616
+ if "o" in lora_list:
617
+ self.linear_out = lora.Linear(
618
+ n_feat, n_feat, r=lora_rank, lora_alpha=lora_alpha, lora_dropout=lora_dropout
619
+ )
620
+ else:
621
+ self.linear_out = nn.Linear(n_feat, n_feat)
622
+ else:
623
+ self.linear_q = nn.Linear(n_feat, n_feat)
624
+ self.linear_k_v = nn.Linear(
625
+ n_feat if encoder_output_size is None else encoder_output_size, n_feat * 2
626
+ )
627
+ self.linear_out = nn.Linear(n_feat, n_feat)
628
+ self.attn = None
629
+ self.dropout = nn.Dropout(p=dropout_rate)
630
+
631
+ def forward_qkv(self, x, memory):
632
+ """Transform query, key and value.
633
+
634
+ Args:
635
+ query (torch.Tensor): Query tensor (#batch, time1, size).
636
+ key (torch.Tensor): Key tensor (#batch, time2, size).
637
+ value (torch.Tensor): Value tensor (#batch, time2, size).
638
+
639
+ Returns:
640
+ torch.Tensor: Transformed query tensor (#batch, n_head, time1, d_k).
641
+ torch.Tensor: Transformed key tensor (#batch, n_head, time2, d_k).
642
+ torch.Tensor: Transformed value tensor (#batch, n_head, time2, d_k).
643
+
644
+ """
645
+
646
+ # print("in forward_qkv, x", x.size())
647
+ b = x.size(0)
648
+ q = self.linear_q(x)
649
+ q_h = torch.reshape(q, (b, -1, self.h, self.d_k)).transpose(
650
+ 1, 2
651
+ ) # (batch, head, time1, d_k)
652
+
653
+ k_v = self.linear_k_v(memory)
654
+ k, v = torch.split(k_v, int(self.h * self.d_k), dim=-1)
655
+ k_h = torch.reshape(k, (b, -1, self.h, self.d_k)).transpose(
656
+ 1, 2
657
+ ) # (batch, head, time2, d_k)
658
+ v_h = torch.reshape(v, (b, -1, self.h, self.d_k)).transpose(
659
+ 1, 2
660
+ ) # (batch, head, time2, d_k)
661
+
662
+ return q_h, k_h, v_h
663
+
664
+ def forward_attention(self, value, scores, mask, ret_attn=False):
665
+ """Compute attention context vector.
666
+
667
+ Args:
668
+ value (torch.Tensor): Transformed value (#batch, n_head, time2, d_k).
669
+ scores (torch.Tensor): Attention score (#batch, n_head, time1, time2).
670
+ mask (torch.Tensor): Mask (#batch, 1, time2) or (#batch, time1, time2).
671
+
672
+ Returns:
673
+ torch.Tensor: Transformed value (#batch, time1, d_model)
674
+ weighted by the attention score (#batch, time1, time2).
675
+
676
+ """
677
+ n_batch = value.size(0)
678
+ if mask is not None:
679
+ mask = mask.unsqueeze(1).eq(0) # (batch, 1, *, time2)
680
+ min_value = -float(
681
+ "inf"
682
+ ) # float(numpy.finfo(torch.tensor(0, dtype=scores.dtype).numpy().dtype).min)
683
+ # logging.info(
684
+ # "scores: {}, mask_size: {}".format(scores.size(), mask.size()))
685
+ scores = scores.masked_fill(mask, min_value)
686
+ attn = torch.softmax(scores, dim=-1).masked_fill(
687
+ mask, 0.0
688
+ ) # (batch, head, time1, time2)
689
+ else:
690
+ attn = torch.softmax(scores, dim=-1) # (batch, head, time1, time2)
691
+ p_attn = self.dropout(attn)
692
+ x = torch.matmul(p_attn, value) # (batch, head, time1, d_k)
693
+ x = (
694
+ x.transpose(1, 2).contiguous().view(n_batch, -1, self.h * self.d_k)
695
+ ) # (batch, time1, d_model)
696
+ if ret_attn:
697
+ return self.linear_out(x), attn # (batch, time1, d_model)
698
+ return self.linear_out(x) # (batch, time1, d_model)
699
+
700
+ def forward(self, x, memory, memory_mask, ret_attn=False):
701
+ """Compute scaled dot product attention.
702
+
703
+ Args:
704
+ query (torch.Tensor): Query tensor (#batch, time1, size).
705
+ key (torch.Tensor): Key tensor (#batch, time2, size).
706
+ value (torch.Tensor): Value tensor (#batch, time2, size).
707
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
708
+ (#batch, time1, time2).
709
+
710
+ Returns:
711
+ torch.Tensor: Output tensor (#batch, time1, d_model).
712
+
713
+ """
714
+ q_h, k_h, v_h = self.forward_qkv(x, memory)
715
+ q_h = q_h * self.d_k ** (-0.5)
716
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
717
+ return self.forward_attention(v_h, scores, memory_mask, ret_attn=ret_attn)
718
+
719
+ def forward_chunk(self, x, memory, cache=None, chunk_size=None, look_back=0):
720
+ """Compute scaled dot product attention.
721
+
722
+ Args:
723
+ query (torch.Tensor): Query tensor (#batch, time1, size).
724
+ key (torch.Tensor): Key tensor (#batch, time2, size).
725
+ value (torch.Tensor): Value tensor (#batch, time2, size).
726
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
727
+ (#batch, time1, time2).
728
+
729
+ Returns:
730
+ torch.Tensor: Output tensor (#batch, time1, d_model).
731
+
732
+ """
733
+ q_h, k_h, v_h = self.forward_qkv(x, memory)
734
+ if chunk_size is not None and look_back > 0:
735
+ if cache is not None:
736
+ k_h = torch.cat((cache["k"], k_h), dim=2)
737
+ v_h = torch.cat((cache["v"], v_h), dim=2)
738
+ cache["k"] = k_h[:, :, -(look_back * chunk_size[1]) :, :]
739
+ cache["v"] = v_h[:, :, -(look_back * chunk_size[1]) :, :]
740
+ else:
741
+ cache_tmp = {
742
+ "k": k_h[:, :, -(look_back * chunk_size[1]) :, :],
743
+ "v": v_h[:, :, -(look_back * chunk_size[1]) :, :],
744
+ }
745
+ cache = cache_tmp
746
+ q_h = q_h * self.d_k ** (-0.5)
747
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
748
+ return self.forward_attention(v_h, scores, None), cache
749
+
750
+
751
+ class MultiHeadedAttentionCrossAttExport(nn.Module):
752
+ def __init__(self, model):
753
+ super().__init__()
754
+ self.d_k = model.d_k
755
+ self.h = model.h
756
+ self.linear_q = model.linear_q
757
+ self.linear_k_v = model.linear_k_v
758
+ self.linear_out = model.linear_out
759
+ self.attn = None
760
+ self.all_head_size = self.h * self.d_k
761
+
762
+ def forward(self, x, memory, memory_mask, ret_attn=False):
763
+ q, k, v = self.forward_qkv(x, memory)
764
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
765
+ return self.forward_attention(v, scores, memory_mask, ret_attn)
766
+
767
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
768
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
769
+ x = x.view(new_x_shape)
770
+ return x.permute(0, 2, 1, 3)
771
+
772
+ def forward_qkv(self, x, memory):
773
+ q = self.linear_q(x)
774
+
775
+ k_v = self.linear_k_v(memory)
776
+ k, v = torch.split(k_v, int(self.h * self.d_k), dim=-1)
777
+ q = self.transpose_for_scores(q)
778
+ k = self.transpose_for_scores(k)
779
+ v = self.transpose_for_scores(v)
780
+ return q, k, v
781
+
782
+ def forward_attention(self, value, scores, mask, ret_attn):
783
+ scores = scores + mask.to(scores.device)
784
+
785
+ attn = torch.softmax(scores, dim=-1)
786
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
787
+
788
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
789
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
790
+ context_layer = context_layer.view(new_context_layer_shape)
791
+ if ret_attn:
792
+ return self.linear_out(context_layer), attn
793
+ return self.linear_out(context_layer) # (batch, time1, d_model)
794
+
795
+
796
+ class MultiHeadSelfAttention(nn.Module):
797
+ """Multi-Head Attention layer.
798
+
799
+ Args:
800
+ n_head (int): The number of heads.
801
+ n_feat (int): The number of features.
802
+ dropout_rate (float): Dropout rate.
803
+
804
+ """
805
+
806
+ def __init__(self, n_head, in_feat, n_feat, dropout_rate):
807
+ """Construct an MultiHeadedAttention object."""
808
+ super(MultiHeadSelfAttention, self).__init__()
809
+ assert n_feat % n_head == 0
810
+ # We assume d_v always equals d_k
811
+ self.d_k = n_feat // n_head
812
+ self.h = n_head
813
+ self.linear_out = nn.Linear(n_feat, n_feat)
814
+ self.linear_q_k_v = nn.Linear(in_feat, n_feat * 3)
815
+ self.attn = None
816
+ self.dropout = nn.Dropout(p=dropout_rate)
817
+
818
+ def forward_qkv(self, x):
819
+ """Transform query, key and value.
820
+
821
+ Args:
822
+ query (torch.Tensor): Query tensor (#batch, time1, size).
823
+ key (torch.Tensor): Key tensor (#batch, time2, size).
824
+ value (torch.Tensor): Value tensor (#batch, time2, size).
825
+
826
+ Returns:
827
+ torch.Tensor: Transformed query tensor (#batch, n_head, time1, d_k).
828
+ torch.Tensor: Transformed key tensor (#batch, n_head, time2, d_k).
829
+ torch.Tensor: Transformed value tensor (#batch, n_head, time2, d_k).
830
+
831
+ """
832
+ b, t, d = x.size()
833
+ q_k_v = self.linear_q_k_v(x)
834
+ q, k, v = torch.split(q_k_v, int(self.h * self.d_k), dim=-1)
835
+ q_h = torch.reshape(q, (b, t, self.h, self.d_k)).transpose(
836
+ 1, 2
837
+ ) # (batch, head, time1, d_k)
838
+ k_h = torch.reshape(k, (b, t, self.h, self.d_k)).transpose(
839
+ 1, 2
840
+ ) # (batch, head, time2, d_k)
841
+ v_h = torch.reshape(v, (b, t, self.h, self.d_k)).transpose(
842
+ 1, 2
843
+ ) # (batch, head, time2, d_k)
844
+
845
+ return q_h, k_h, v_h, v
846
+
847
+ def forward_attention(self, value, scores, mask, mask_att_chunk_encoder=None):
848
+ """Compute attention context vector.
849
+
850
+ Args:
851
+ value (torch.Tensor): Transformed value (#batch, n_head, time2, d_k).
852
+ scores (torch.Tensor): Attention score (#batch, n_head, time1, time2).
853
+ mask (torch.Tensor): Mask (#batch, 1, time2) or (#batch, time1, time2).
854
+
855
+ Returns:
856
+ torch.Tensor: Transformed value (#batch, time1, d_model)
857
+ weighted by the attention score (#batch, time1, time2).
858
+
859
+ """
860
+ n_batch = value.size(0)
861
+ if mask is not None:
862
+ if mask_att_chunk_encoder is not None:
863
+ mask = mask * mask_att_chunk_encoder
864
+
865
+ mask = mask.unsqueeze(1).eq(0) # (batch, 1, *, time2)
866
+
867
+ min_value = -float(
868
+ "inf"
869
+ ) # float(numpy.finfo(torch.tensor(0, dtype=scores.dtype).numpy().dtype).min)
870
+ scores = scores.masked_fill(mask, min_value)
871
+ attn = torch.softmax(scores, dim=-1).masked_fill(
872
+ mask, 0.0
873
+ ) # (batch, head, time1, time2)
874
+ else:
875
+ attn = torch.softmax(scores, dim=-1) # (batch, head, time1, time2)
876
+
877
+ p_attn = self.dropout(attn)
878
+ x = torch.matmul(p_attn, value) # (batch, head, time1, d_k)
879
+ x = (
880
+ x.transpose(1, 2).contiguous().view(n_batch, -1, self.h * self.d_k)
881
+ ) # (batch, time1, d_model)
882
+
883
+ return self.linear_out(x) # (batch, time1, d_model)
884
+
885
+ def forward(self, x, mask, mask_att_chunk_encoder=None):
886
+ """Compute scaled dot product attention.
887
+
888
+ Args:
889
+ query (torch.Tensor): Query tensor (#batch, time1, size).
890
+ key (torch.Tensor): Key tensor (#batch, time2, size).
891
+ value (torch.Tensor): Value tensor (#batch, time2, size).
892
+ mask (torch.Tensor): Mask tensor (#batch, 1, time2) or
893
+ (#batch, time1, time2).
894
+
895
+ Returns:
896
+ torch.Tensor: Output tensor (#batch, time1, d_model).
897
+
898
+ """
899
+ q_h, k_h, v_h, v = self.forward_qkv(x)
900
+ q_h = q_h * self.d_k ** (-0.5)
901
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
902
+ att_outs = self.forward_attention(v_h, scores, mask, mask_att_chunk_encoder)
903
+ return att_outs
funasr/models/sanm/decoder.py ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ from typing import List
7
+ from typing import Tuple
8
+ import logging
9
+ import torch
10
+ import torch.nn as nn
11
+ import numpy as np
12
+
13
+ from funasr.models.scama import utils as myutils
14
+ from funasr.models.transformer.decoder import BaseTransformerDecoder
15
+
16
+ from funasr.models.sanm.attention import (
17
+ MultiHeadedAttentionSANMDecoder,
18
+ MultiHeadedAttentionCrossAtt,
19
+ )
20
+ from funasr.models.transformer.embedding import PositionalEncoding
21
+ from funasr.models.transformer.layer_norm import LayerNorm
22
+ from funasr.models.sanm.positionwise_feed_forward import PositionwiseFeedForwardDecoderSANM
23
+ from funasr.models.transformer.utils.repeat import repeat
24
+
25
+ from funasr.register import tables
26
+
27
+
28
+ class DecoderLayerSANM(nn.Module):
29
+ """Single decoder layer module.
30
+
31
+ Args:
32
+ size (int): Input dimension.
33
+ self_attn (torch.nn.Module): Self-attention module instance.
34
+ `MultiHeadedAttention` instance can be used as the argument.
35
+ src_attn (torch.nn.Module): Self-attention module instance.
36
+ `MultiHeadedAttention` instance can be used as the argument.
37
+ feed_forward (torch.nn.Module): Feed-forward module instance.
38
+ `PositionwiseFeedForward`, `MultiLayeredConv1d`, or `Conv1dLinear` instance
39
+ can be used as the argument.
40
+ dropout_rate (float): Dropout rate.
41
+ normalize_before (bool): Whether to use layer_norm before the first block.
42
+ concat_after (bool): Whether to concat attention layer's input and output.
43
+ if True, additional linear will be applied.
44
+ i.e. x -> x + linear(concat(x, att(x)))
45
+ if False, no additional linear will be applied. i.e. x -> x + att(x)
46
+
47
+
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ size,
53
+ self_attn,
54
+ src_attn,
55
+ feed_forward,
56
+ dropout_rate,
57
+ normalize_before=True,
58
+ concat_after=False,
59
+ ):
60
+ """Construct an DecoderLayer object."""
61
+ super(DecoderLayerSANM, self).__init__()
62
+ self.size = size
63
+ self.self_attn = self_attn
64
+ self.src_attn = src_attn
65
+ self.feed_forward = feed_forward
66
+ self.norm1 = LayerNorm(size)
67
+ if self_attn is not None:
68
+ self.norm2 = LayerNorm(size)
69
+ if src_attn is not None:
70
+ self.norm3 = LayerNorm(size)
71
+ self.dropout = nn.Dropout(dropout_rate)
72
+ self.normalize_before = normalize_before
73
+ self.concat_after = concat_after
74
+ if self.concat_after:
75
+ self.concat_linear1 = nn.Linear(size + size, size)
76
+ self.concat_linear2 = nn.Linear(size + size, size)
77
+
78
+ def forward(self, tgt, tgt_mask, memory, memory_mask=None, cache=None):
79
+ """Compute decoded features.
80
+
81
+ Args:
82
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
83
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
84
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
85
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
86
+ cache (List[torch.Tensor]): List of cached tensors.
87
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
88
+
89
+ Returns:
90
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
91
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
92
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
93
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
94
+
95
+ """
96
+ # tgt = self.dropout(tgt)
97
+ residual = tgt
98
+ if self.normalize_before:
99
+ tgt = self.norm1(tgt)
100
+ tgt = self.feed_forward(tgt)
101
+
102
+ x = tgt
103
+ if self.self_attn:
104
+ if self.normalize_before:
105
+ tgt = self.norm2(tgt)
106
+ x, _ = self.self_attn(tgt, tgt_mask)
107
+ x = residual + self.dropout(x)
108
+
109
+ if self.src_attn is not None:
110
+ residual = x
111
+ if self.normalize_before:
112
+ x = self.norm3(x)
113
+
114
+ x = residual + self.dropout(self.src_attn(x, memory, memory_mask))
115
+
116
+ return x, tgt_mask, memory, memory_mask, cache
117
+
118
+ def forward_one_step(self, tgt, tgt_mask, memory, memory_mask=None, cache=None):
119
+ """Compute decoded features.
120
+
121
+ Args:
122
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
123
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
124
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
125
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
126
+ cache (List[torch.Tensor]): List of cached tensors.
127
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
128
+
129
+ Returns:
130
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
131
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
132
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
133
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
134
+
135
+ """
136
+ # tgt = self.dropout(tgt)
137
+ residual = tgt
138
+ if self.normalize_before:
139
+ tgt = self.norm1(tgt)
140
+ tgt = self.feed_forward(tgt)
141
+
142
+ x = tgt
143
+ if self.self_attn:
144
+ if self.normalize_before:
145
+ tgt = self.norm2(tgt)
146
+ if self.training:
147
+ cache = None
148
+ x, cache = self.self_attn(tgt, tgt_mask, cache=cache)
149
+ x = residual + self.dropout(x)
150
+
151
+ if self.src_attn is not None:
152
+ residual = x
153
+ if self.normalize_before:
154
+ x = self.norm3(x)
155
+
156
+ x = residual + self.dropout(self.src_attn(x, memory, memory_mask))
157
+
158
+ return x, tgt_mask, memory, memory_mask, cache
159
+
160
+ def forward_chunk(
161
+ self, tgt, memory, fsmn_cache=None, opt_cache=None, chunk_size=None, look_back=0
162
+ ):
163
+ """Compute decoded features.
164
+
165
+ Args:
166
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
167
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
168
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
169
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
170
+ cache (List[torch.Tensor]): List of cached tensors.
171
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
172
+
173
+ Returns:
174
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
175
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
176
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
177
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
178
+
179
+ """
180
+ residual = tgt
181
+ if self.normalize_before:
182
+ tgt = self.norm1(tgt)
183
+ tgt = self.feed_forward(tgt)
184
+
185
+ x = tgt
186
+ if self.self_attn:
187
+ if self.normalize_before:
188
+ tgt = self.norm2(tgt)
189
+ x, fsmn_cache = self.self_attn(tgt, None, fsmn_cache)
190
+ x = residual + self.dropout(x)
191
+
192
+ if self.src_attn is not None:
193
+ residual = x
194
+ if self.normalize_before:
195
+ x = self.norm3(x)
196
+
197
+ x, opt_cache = self.src_attn.forward_chunk(x, memory, opt_cache, chunk_size, look_back)
198
+ x = residual + x
199
+
200
+ return x, memory, fsmn_cache, opt_cache
201
+
202
+
203
+ @tables.register("decoder_classes", "FsmnDecoder")
204
+ class FsmnDecoder(BaseTransformerDecoder):
205
+ """
206
+ Author: Zhifu Gao, Shiliang Zhang, Ming Lei, Ian McLoughlin
207
+ San-m: Memory equipped self-attention for end-to-end speech recognition
208
+ https://arxiv.org/abs/2006.01713
209
+ """
210
+
211
+ def __init__(
212
+ self,
213
+ vocab_size: int,
214
+ encoder_output_size: int,
215
+ attention_heads: int = 4,
216
+ linear_units: int = 2048,
217
+ num_blocks: int = 6,
218
+ dropout_rate: float = 0.1,
219
+ positional_dropout_rate: float = 0.1,
220
+ self_attention_dropout_rate: float = 0.0,
221
+ src_attention_dropout_rate: float = 0.0,
222
+ input_layer: str = "embed",
223
+ use_output_layer: bool = True,
224
+ pos_enc_class=PositionalEncoding,
225
+ normalize_before: bool = True,
226
+ concat_after: bool = False,
227
+ att_layer_num: int = 6,
228
+ kernel_size: int = 21,
229
+ sanm_shfit: int = None,
230
+ concat_embeds: bool = False,
231
+ attention_dim: int = None,
232
+ tf2torch_tensor_name_prefix_torch: str = "decoder",
233
+ tf2torch_tensor_name_prefix_tf: str = "seq2seq/decoder",
234
+ embed_tensor_name_prefix_tf: str = None,
235
+ ):
236
+ super().__init__(
237
+ vocab_size=vocab_size,
238
+ encoder_output_size=encoder_output_size,
239
+ dropout_rate=dropout_rate,
240
+ positional_dropout_rate=positional_dropout_rate,
241
+ input_layer=input_layer,
242
+ use_output_layer=use_output_layer,
243
+ pos_enc_class=pos_enc_class,
244
+ normalize_before=normalize_before,
245
+ )
246
+ if attention_dim is None:
247
+ attention_dim = encoder_output_size
248
+
249
+ if input_layer == "embed":
250
+ self.embed = torch.nn.Sequential(
251
+ torch.nn.Embedding(vocab_size, attention_dim),
252
+ )
253
+ elif input_layer == "linear":
254
+ self.embed = torch.nn.Sequential(
255
+ torch.nn.Linear(vocab_size, attention_dim),
256
+ torch.nn.LayerNorm(attention_dim),
257
+ torch.nn.Dropout(dropout_rate),
258
+ torch.nn.ReLU(),
259
+ pos_enc_class(attention_dim, positional_dropout_rate),
260
+ )
261
+ else:
262
+ raise ValueError(f"only 'embed' or 'linear' is supported: {input_layer}")
263
+
264
+ self.normalize_before = normalize_before
265
+ if self.normalize_before:
266
+ self.after_norm = LayerNorm(attention_dim)
267
+ if use_output_layer:
268
+ self.output_layer = torch.nn.Linear(attention_dim, vocab_size)
269
+ else:
270
+ self.output_layer = None
271
+
272
+ self.att_layer_num = att_layer_num
273
+ self.num_blocks = num_blocks
274
+ if sanm_shfit is None:
275
+ sanm_shfit = (kernel_size - 1) // 2
276
+ self.decoders = repeat(
277
+ att_layer_num,
278
+ lambda lnum: DecoderLayerSANM(
279
+ attention_dim,
280
+ MultiHeadedAttentionSANMDecoder(
281
+ attention_dim, self_attention_dropout_rate, kernel_size, sanm_shfit=sanm_shfit
282
+ ),
283
+ MultiHeadedAttentionCrossAtt(
284
+ attention_heads,
285
+ attention_dim,
286
+ src_attention_dropout_rate,
287
+ encoder_output_size=encoder_output_size,
288
+ ),
289
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
290
+ dropout_rate,
291
+ normalize_before,
292
+ concat_after,
293
+ ),
294
+ )
295
+ if num_blocks - att_layer_num <= 0:
296
+ self.decoders2 = None
297
+ else:
298
+ self.decoders2 = repeat(
299
+ num_blocks - att_layer_num,
300
+ lambda lnum: DecoderLayerSANM(
301
+ attention_dim,
302
+ MultiHeadedAttentionSANMDecoder(
303
+ attention_dim,
304
+ self_attention_dropout_rate,
305
+ kernel_size,
306
+ sanm_shfit=sanm_shfit,
307
+ ),
308
+ None,
309
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
310
+ dropout_rate,
311
+ normalize_before,
312
+ concat_after,
313
+ ),
314
+ )
315
+
316
+ self.decoders3 = repeat(
317
+ 1,
318
+ lambda lnum: DecoderLayerSANM(
319
+ attention_dim,
320
+ None,
321
+ None,
322
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
323
+ dropout_rate,
324
+ normalize_before,
325
+ concat_after,
326
+ ),
327
+ )
328
+ if concat_embeds:
329
+ self.embed_concat_ffn = repeat(
330
+ 1,
331
+ lambda lnum: DecoderLayerSANM(
332
+ attention_dim + encoder_output_size,
333
+ None,
334
+ None,
335
+ PositionwiseFeedForwardDecoderSANM(
336
+ attention_dim + encoder_output_size,
337
+ linear_units,
338
+ dropout_rate,
339
+ adim=attention_dim,
340
+ ),
341
+ dropout_rate,
342
+ normalize_before,
343
+ concat_after,
344
+ ),
345
+ )
346
+ else:
347
+ self.embed_concat_ffn = None
348
+ self.concat_embeds = concat_embeds
349
+ self.tf2torch_tensor_name_prefix_torch = tf2torch_tensor_name_prefix_torch
350
+ self.tf2torch_tensor_name_prefix_tf = tf2torch_tensor_name_prefix_tf
351
+ self.embed_tensor_name_prefix_tf = embed_tensor_name_prefix_tf
352
+
353
+ def forward(
354
+ self,
355
+ hs_pad: torch.Tensor,
356
+ hlens: torch.Tensor,
357
+ ys_in_pad: torch.Tensor,
358
+ ys_in_lens: torch.Tensor,
359
+ chunk_mask: torch.Tensor = None,
360
+ pre_acoustic_embeds: torch.Tensor = None,
361
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
362
+ """Forward decoder.
363
+
364
+ Args:
365
+ hs_pad: encoded memory, float32 (batch, maxlen_in, feat)
366
+ hlens: (batch)
367
+ ys_in_pad:
368
+ input token ids, int64 (batch, maxlen_out)
369
+ if input_layer == "embed"
370
+ input tensor (batch, maxlen_out, #mels) in the other cases
371
+ ys_in_lens: (batch)
372
+ Returns:
373
+ (tuple): tuple containing:
374
+
375
+ x: decoded token score before softmax (batch, maxlen_out, token)
376
+ if use_output_layer is True,
377
+ olens: (batch, )
378
+ """
379
+ tgt = ys_in_pad
380
+ tgt_mask = myutils.sequence_mask(ys_in_lens, device=tgt.device)[:, :, None]
381
+
382
+ memory = hs_pad
383
+ memory_mask = myutils.sequence_mask(hlens, device=memory.device)[:, None, :]
384
+ if chunk_mask is not None:
385
+ memory_mask = memory_mask * chunk_mask
386
+ if tgt_mask.size(1) != memory_mask.size(1):
387
+ memory_mask = torch.cat((memory_mask, memory_mask[:, -2:-1, :]), dim=1)
388
+
389
+ x = self.embed(tgt)
390
+
391
+ if pre_acoustic_embeds is not None and self.concat_embeds:
392
+ x = torch.cat((x, pre_acoustic_embeds), dim=-1)
393
+ x, _, _, _, _ = self.embed_concat_ffn(x, None, None, None, None)
394
+
395
+ x, tgt_mask, memory, memory_mask, _ = self.decoders(x, tgt_mask, memory, memory_mask)
396
+ if self.decoders2 is not None:
397
+ x, tgt_mask, memory, memory_mask, _ = self.decoders2(x, tgt_mask, memory, memory_mask)
398
+ x, tgt_mask, memory, memory_mask, _ = self.decoders3(x, tgt_mask, memory, memory_mask)
399
+ if self.normalize_before:
400
+ x = self.after_norm(x)
401
+ if self.output_layer is not None:
402
+ x = self.output_layer(x)
403
+
404
+ olens = tgt_mask.sum(1)
405
+ return x, olens
406
+
407
+ def score(
408
+ self,
409
+ ys,
410
+ state,
411
+ x,
412
+ x_mask=None,
413
+ pre_acoustic_embeds: torch.Tensor = None,
414
+ ):
415
+ """Score."""
416
+ ys_mask = myutils.sequence_mask(
417
+ torch.tensor([len(ys)], dtype=torch.int32), device=x.device
418
+ )[:, :, None]
419
+ logp, state = self.forward_one_step(
420
+ ys.unsqueeze(0),
421
+ ys_mask,
422
+ x.unsqueeze(0),
423
+ memory_mask=x_mask,
424
+ pre_acoustic_embeds=pre_acoustic_embeds,
425
+ cache=state,
426
+ )
427
+ return logp.squeeze(0), state
428
+
429
+ def forward_one_step(
430
+ self,
431
+ tgt: torch.Tensor,
432
+ tgt_mask: torch.Tensor,
433
+ memory: torch.Tensor,
434
+ memory_mask: torch.Tensor = None,
435
+ pre_acoustic_embeds: torch.Tensor = None,
436
+ cache: List[torch.Tensor] = None,
437
+ ) -> Tuple[torch.Tensor, List[torch.Tensor]]:
438
+ """Forward one step.
439
+
440
+ Args:
441
+ tgt: input token ids, int64 (batch, maxlen_out)
442
+ tgt_mask: input token mask, (batch, maxlen_out)
443
+ dtype=torch.uint8 in PyTorch 1.2-
444
+ dtype=torch.bool in PyTorch 1.2+ (include 1.2)
445
+ memory: encoded memory, float32 (batch, maxlen_in, feat)
446
+ cache: cached output list of (batch, max_time_out-1, size)
447
+ Returns:
448
+ y, cache: NN output value and cache per `self.decoders`.
449
+ y.shape` is (batch, maxlen_out, token)
450
+ """
451
+
452
+ x = tgt[:, -1:]
453
+ tgt_mask = None
454
+ x = self.embed(x)
455
+
456
+ if pre_acoustic_embeds is not None and self.concat_embeds:
457
+ x = torch.cat((x, pre_acoustic_embeds), dim=-1)
458
+ x, _, _, _, _ = self.embed_concat_ffn(x, None, None, None, None)
459
+
460
+ if cache is None:
461
+ cache_layer_num = len(self.decoders)
462
+ if self.decoders2 is not None:
463
+ cache_layer_num += len(self.decoders2)
464
+ cache = [None] * cache_layer_num
465
+ new_cache = []
466
+ # for c, decoder in zip(cache, self.decoders):
467
+ for i in range(self.att_layer_num):
468
+ decoder = self.decoders[i]
469
+ c = cache[i]
470
+ x, tgt_mask, memory, memory_mask, c_ret = decoder.forward_one_step(
471
+ x, tgt_mask, memory, memory_mask, cache=c
472
+ )
473
+ new_cache.append(c_ret)
474
+
475
+ if self.num_blocks - self.att_layer_num >= 1:
476
+ for i in range(self.num_blocks - self.att_layer_num):
477
+ j = i + self.att_layer_num
478
+ decoder = self.decoders2[i]
479
+ c = cache[j]
480
+ x, tgt_mask, memory, memory_mask, c_ret = decoder.forward_one_step(
481
+ x, tgt_mask, memory, memory_mask, cache=c
482
+ )
483
+ new_cache.append(c_ret)
484
+
485
+ for decoder in self.decoders3:
486
+ x, tgt_mask, memory, memory_mask, _ = decoder.forward_one_step(
487
+ x, tgt_mask, memory, None, cache=None
488
+ )
489
+
490
+ if self.normalize_before:
491
+ y = self.after_norm(x[:, -1])
492
+ else:
493
+ y = x[:, -1]
494
+ if self.output_layer is not None:
495
+ y = self.output_layer(y)
496
+ y = torch.log_softmax(y, dim=-1)
497
+
498
+ return y, new_cache
funasr/models/sanm/encoder.py ADDED
@@ -0,0 +1,612 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ from typing import List
7
+ from typing import Optional
8
+ from typing import Sequence
9
+ from typing import Tuple
10
+ from typing import Union
11
+ import logging
12
+ import torch
13
+ import torch.nn as nn
14
+ import torch.nn.functional as F
15
+
16
+ import numpy as np
17
+ from funasr.train_utils.device_funcs import to_device
18
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
19
+ from funasr.models.sanm.attention import MultiHeadedAttention, MultiHeadedAttentionSANM
20
+ from funasr.models.transformer.embedding import (
21
+ SinusoidalPositionEncoder,
22
+ StreamSinusoidalPositionEncoder,
23
+ )
24
+ from funasr.models.transformer.layer_norm import LayerNorm
25
+ from funasr.models.transformer.utils.multi_layer_conv import Conv1dLinear
26
+ from funasr.models.transformer.utils.multi_layer_conv import MultiLayeredConv1d
27
+ from funasr.models.transformer.positionwise_feed_forward import (
28
+ PositionwiseFeedForward, # noqa: H301
29
+ )
30
+ from funasr.models.transformer.utils.repeat import repeat
31
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling
32
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling2
33
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling6
34
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling8
35
+ from funasr.models.transformer.utils.subsampling import TooShortUttError
36
+ from funasr.models.transformer.utils.subsampling import check_short_utt
37
+
38
+
39
+ from funasr.models.ctc.ctc import CTC
40
+
41
+ from funasr.register import tables
42
+
43
+
44
+ class EncoderLayerSANM(nn.Module):
45
+ def __init__(
46
+ self,
47
+ in_size,
48
+ size,
49
+ self_attn,
50
+ feed_forward,
51
+ dropout_rate,
52
+ normalize_before=True,
53
+ concat_after=False,
54
+ stochastic_depth_rate=0.0,
55
+ ):
56
+ """Construct an EncoderLayer object."""
57
+ super(EncoderLayerSANM, self).__init__()
58
+ self.self_attn = self_attn
59
+ self.feed_forward = feed_forward
60
+ self.norm1 = LayerNorm(in_size)
61
+ self.norm2 = LayerNorm(size)
62
+ self.dropout = nn.Dropout(dropout_rate)
63
+ self.in_size = in_size
64
+ self.size = size
65
+ self.normalize_before = normalize_before
66
+ self.concat_after = concat_after
67
+ if self.concat_after:
68
+ self.concat_linear = nn.Linear(size + size, size)
69
+ self.stochastic_depth_rate = stochastic_depth_rate
70
+ self.dropout_rate = dropout_rate
71
+
72
+ def forward(self, x, mask, cache=None, mask_shfit_chunk=None, mask_att_chunk_encoder=None):
73
+ """Compute encoded features.
74
+
75
+ Args:
76
+ x_input (torch.Tensor): Input tensor (#batch, time, size).
77
+ mask (torch.Tensor): Mask tensor for the input (#batch, time).
78
+ cache (torch.Tensor): Cache tensor of the input (#batch, time - 1, size).
79
+
80
+ Returns:
81
+ torch.Tensor: Output tensor (#batch, time, size).
82
+ torch.Tensor: Mask tensor (#batch, time).
83
+
84
+ """
85
+ skip_layer = False
86
+ # with stochastic depth, residual connection `x + f(x)` becomes
87
+ # `x <- x + 1 / (1 - p) * f(x)` at training time.
88
+ stoch_layer_coeff = 1.0
89
+ if self.training and self.stochastic_depth_rate > 0:
90
+ skip_layer = torch.rand(1).item() < self.stochastic_depth_rate
91
+ stoch_layer_coeff = 1.0 / (1 - self.stochastic_depth_rate)
92
+
93
+ if skip_layer:
94
+ if cache is not None:
95
+ x = torch.cat([cache, x], dim=1)
96
+ return x, mask
97
+
98
+ residual = x
99
+ if self.normalize_before:
100
+ x = self.norm1(x)
101
+
102
+ if self.concat_after:
103
+ x_concat = torch.cat(
104
+ (
105
+ x,
106
+ self.self_attn(
107
+ x,
108
+ mask,
109
+ mask_shfit_chunk=mask_shfit_chunk,
110
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
111
+ ),
112
+ ),
113
+ dim=-1,
114
+ )
115
+ if self.in_size == self.size:
116
+ x = residual + stoch_layer_coeff * self.concat_linear(x_concat)
117
+ else:
118
+ x = stoch_layer_coeff * self.concat_linear(x_concat)
119
+ else:
120
+ if self.in_size == self.size:
121
+ x = residual + stoch_layer_coeff * self.dropout(
122
+ self.self_attn(
123
+ x,
124
+ mask,
125
+ mask_shfit_chunk=mask_shfit_chunk,
126
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
127
+ )
128
+ )
129
+ else:
130
+ x = stoch_layer_coeff * self.dropout(
131
+ self.self_attn(
132
+ x,
133
+ mask,
134
+ mask_shfit_chunk=mask_shfit_chunk,
135
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
136
+ )
137
+ )
138
+ if not self.normalize_before:
139
+ x = self.norm1(x)
140
+
141
+ residual = x
142
+ if self.normalize_before:
143
+ x = self.norm2(x)
144
+ x = residual + stoch_layer_coeff * self.dropout(self.feed_forward(x))
145
+ if not self.normalize_before:
146
+ x = self.norm2(x)
147
+
148
+ return x, mask, cache, mask_shfit_chunk, mask_att_chunk_encoder
149
+
150
+ def forward_chunk(self, x, cache=None, chunk_size=None, look_back=0):
151
+ """Compute encoded features.
152
+
153
+ Args:
154
+ x_input (torch.Tensor): Input tensor (#batch, time, size).
155
+ mask (torch.Tensor): Mask tensor for the input (#batch, time).
156
+ cache (torch.Tensor): Cache tensor of the input (#batch, time - 1, size).
157
+
158
+ Returns:
159
+ torch.Tensor: Output tensor (#batch, time, size).
160
+ torch.Tensor: Mask tensor (#batch, time).
161
+
162
+ """
163
+
164
+ residual = x
165
+ if self.normalize_before:
166
+ x = self.norm1(x)
167
+
168
+ if self.in_size == self.size:
169
+ attn, cache = self.self_attn.forward_chunk(x, cache, chunk_size, look_back)
170
+ x = residual + attn
171
+ else:
172
+ x, cache = self.self_attn.forward_chunk(x, cache, chunk_size, look_back)
173
+
174
+ if not self.normalize_before:
175
+ x = self.norm1(x)
176
+
177
+ residual = x
178
+ if self.normalize_before:
179
+ x = self.norm2(x)
180
+ x = residual + self.feed_forward(x)
181
+ if not self.normalize_before:
182
+ x = self.norm2(x)
183
+
184
+ return x, cache
185
+
186
+
187
+ @tables.register("encoder_classes", "SANMEncoder")
188
+ class SANMEncoder(nn.Module):
189
+ """
190
+ Author: Zhifu Gao, Shiliang Zhang, Ming Lei, Ian McLoughlin
191
+ San-m: Memory equipped self-attention for end-to-end speech recognition
192
+ https://arxiv.org/abs/2006.01713
193
+ """
194
+
195
+ def __init__(
196
+ self,
197
+ input_size: int,
198
+ output_size: int = 256,
199
+ attention_heads: int = 4,
200
+ linear_units: int = 2048,
201
+ num_blocks: int = 6,
202
+ dropout_rate: float = 0.1,
203
+ positional_dropout_rate: float = 0.1,
204
+ attention_dropout_rate: float = 0.0,
205
+ input_layer: Optional[str] = "conv2d",
206
+ pos_enc_class=SinusoidalPositionEncoder,
207
+ normalize_before: bool = True,
208
+ concat_after: bool = False,
209
+ positionwise_layer_type: str = "linear",
210
+ positionwise_conv_kernel_size: int = 1,
211
+ padding_idx: int = -1,
212
+ interctc_layer_idx: List[int] = [],
213
+ interctc_use_conditioning: bool = False,
214
+ kernel_size: int = 11,
215
+ sanm_shfit: int = 0,
216
+ lora_list: List[str] = None,
217
+ lora_rank: int = 8,
218
+ lora_alpha: int = 16,
219
+ lora_dropout: float = 0.1,
220
+ selfattention_layer_type: str = "sanm",
221
+ tf2torch_tensor_name_prefix_torch: str = "encoder",
222
+ tf2torch_tensor_name_prefix_tf: str = "seq2seq/encoder",
223
+ ):
224
+ super().__init__()
225
+ self._output_size = output_size
226
+
227
+ if input_layer == "linear":
228
+ self.embed = torch.nn.Sequential(
229
+ torch.nn.Linear(input_size, output_size),
230
+ torch.nn.LayerNorm(output_size),
231
+ torch.nn.Dropout(dropout_rate),
232
+ torch.nn.ReLU(),
233
+ pos_enc_class(output_size, positional_dropout_rate),
234
+ )
235
+ elif input_layer == "conv2d":
236
+ self.embed = Conv2dSubsampling(input_size, output_size, dropout_rate)
237
+ elif input_layer == "conv2d2":
238
+ self.embed = Conv2dSubsampling2(input_size, output_size, dropout_rate)
239
+ elif input_layer == "conv2d6":
240
+ self.embed = Conv2dSubsampling6(input_size, output_size, dropout_rate)
241
+ elif input_layer == "conv2d8":
242
+ self.embed = Conv2dSubsampling8(input_size, output_size, dropout_rate)
243
+ elif input_layer == "embed":
244
+ self.embed = torch.nn.Sequential(
245
+ torch.nn.Embedding(input_size, output_size, padding_idx=padding_idx),
246
+ SinusoidalPositionEncoder(),
247
+ )
248
+ elif input_layer is None:
249
+ if input_size == output_size:
250
+ self.embed = None
251
+ else:
252
+ self.embed = torch.nn.Linear(input_size, output_size)
253
+ elif input_layer == "pe":
254
+ self.embed = SinusoidalPositionEncoder()
255
+ elif input_layer == "pe_online":
256
+ self.embed = StreamSinusoidalPositionEncoder()
257
+ else:
258
+ raise ValueError("unknown input_layer: " + input_layer)
259
+ self.normalize_before = normalize_before
260
+ if positionwise_layer_type == "linear":
261
+ positionwise_layer = PositionwiseFeedForward
262
+ positionwise_layer_args = (
263
+ output_size,
264
+ linear_units,
265
+ dropout_rate,
266
+ )
267
+ elif positionwise_layer_type == "conv1d":
268
+ positionwise_layer = MultiLayeredConv1d
269
+ positionwise_layer_args = (
270
+ output_size,
271
+ linear_units,
272
+ positionwise_conv_kernel_size,
273
+ dropout_rate,
274
+ )
275
+ elif positionwise_layer_type == "conv1d-linear":
276
+ positionwise_layer = Conv1dLinear
277
+ positionwise_layer_args = (
278
+ output_size,
279
+ linear_units,
280
+ positionwise_conv_kernel_size,
281
+ dropout_rate,
282
+ )
283
+ else:
284
+ raise NotImplementedError("Support only linear or conv1d.")
285
+
286
+ if selfattention_layer_type == "selfattn":
287
+ encoder_selfattn_layer = MultiHeadedAttention
288
+ encoder_selfattn_layer_args = (
289
+ attention_heads,
290
+ output_size,
291
+ attention_dropout_rate,
292
+ )
293
+
294
+ elif selfattention_layer_type == "sanm":
295
+ encoder_selfattn_layer = MultiHeadedAttentionSANM
296
+ encoder_selfattn_layer_args0 = (
297
+ attention_heads,
298
+ input_size,
299
+ output_size,
300
+ attention_dropout_rate,
301
+ kernel_size,
302
+ sanm_shfit,
303
+ lora_list,
304
+ lora_rank,
305
+ lora_alpha,
306
+ lora_dropout,
307
+ )
308
+
309
+ encoder_selfattn_layer_args = (
310
+ attention_heads,
311
+ output_size,
312
+ output_size,
313
+ attention_dropout_rate,
314
+ kernel_size,
315
+ sanm_shfit,
316
+ lora_list,
317
+ lora_rank,
318
+ lora_alpha,
319
+ lora_dropout,
320
+ )
321
+ self.encoders0 = repeat(
322
+ 1,
323
+ lambda lnum: EncoderLayerSANM(
324
+ input_size,
325
+ output_size,
326
+ encoder_selfattn_layer(*encoder_selfattn_layer_args0),
327
+ positionwise_layer(*positionwise_layer_args),
328
+ dropout_rate,
329
+ normalize_before,
330
+ concat_after,
331
+ ),
332
+ )
333
+
334
+ self.encoders = repeat(
335
+ num_blocks - 1,
336
+ lambda lnum: EncoderLayerSANM(
337
+ output_size,
338
+ output_size,
339
+ encoder_selfattn_layer(*encoder_selfattn_layer_args),
340
+ positionwise_layer(*positionwise_layer_args),
341
+ dropout_rate,
342
+ normalize_before,
343
+ concat_after,
344
+ ),
345
+ )
346
+ if self.normalize_before:
347
+ self.after_norm = LayerNorm(output_size)
348
+
349
+ self.interctc_layer_idx = interctc_layer_idx
350
+ if len(interctc_layer_idx) > 0:
351
+ assert 0 < min(interctc_layer_idx) and max(interctc_layer_idx) < num_blocks
352
+ self.interctc_use_conditioning = interctc_use_conditioning
353
+ self.conditioning_layer = None
354
+ self.dropout = nn.Dropout(dropout_rate)
355
+ self.tf2torch_tensor_name_prefix_torch = tf2torch_tensor_name_prefix_torch
356
+ self.tf2torch_tensor_name_prefix_tf = tf2torch_tensor_name_prefix_tf
357
+
358
+ def output_size(self) -> int:
359
+ return self._output_size
360
+
361
+ def forward(
362
+ self,
363
+ xs_pad: torch.Tensor,
364
+ ilens: torch.Tensor,
365
+ prev_states: torch.Tensor = None,
366
+ ctc: CTC = None,
367
+ ) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
368
+ """Embed positions in tensor.
369
+
370
+ Args:
371
+ xs_pad: input tensor (B, L, D)
372
+ ilens: input length (B)
373
+ prev_states: Not to be used now.
374
+ Returns:
375
+ position embedded tensor and mask
376
+ """
377
+ masks = (~make_pad_mask(ilens)[:, None, :]).to(xs_pad.device)
378
+ xs_pad = xs_pad * self.output_size() ** 0.5
379
+ if self.embed is None:
380
+ xs_pad = xs_pad
381
+ elif (
382
+ isinstance(self.embed, Conv2dSubsampling)
383
+ or isinstance(self.embed, Conv2dSubsampling2)
384
+ or isinstance(self.embed, Conv2dSubsampling6)
385
+ or isinstance(self.embed, Conv2dSubsampling8)
386
+ ):
387
+ short_status, limit_size = check_short_utt(self.embed, xs_pad.size(1))
388
+ if short_status:
389
+ raise TooShortUttError(
390
+ f"has {xs_pad.size(1)} frames and is too short for subsampling "
391
+ + f"(it needs more than {limit_size} frames), return empty results",
392
+ xs_pad.size(1),
393
+ limit_size,
394
+ )
395
+ xs_pad, masks = self.embed(xs_pad, masks)
396
+ else:
397
+ xs_pad = self.embed(xs_pad)
398
+
399
+ # xs_pad = self.dropout(xs_pad)
400
+ encoder_outs = self.encoders0(xs_pad, masks)
401
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
402
+ intermediate_outs = []
403
+ if len(self.interctc_layer_idx) == 0:
404
+ encoder_outs = self.encoders(xs_pad, masks)
405
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
406
+ else:
407
+ for layer_idx, encoder_layer in enumerate(self.encoders):
408
+ encoder_outs = encoder_layer(xs_pad, masks)
409
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
410
+
411
+ if layer_idx + 1 in self.interctc_layer_idx:
412
+ encoder_out = xs_pad
413
+
414
+ # intermediate outputs are also normalized
415
+ if self.normalize_before:
416
+ encoder_out = self.after_norm(encoder_out)
417
+
418
+ intermediate_outs.append((layer_idx + 1, encoder_out))
419
+
420
+ if self.interctc_use_conditioning:
421
+ ctc_out = ctc.softmax(encoder_out)
422
+ xs_pad = xs_pad + self.conditioning_layer(ctc_out)
423
+
424
+ if self.normalize_before:
425
+ xs_pad = self.after_norm(xs_pad)
426
+
427
+ olens = masks.squeeze(1).sum(1)
428
+ if len(intermediate_outs) > 0:
429
+ return (xs_pad, intermediate_outs), olens, None
430
+ return xs_pad, olens, None
431
+
432
+ def _add_overlap_chunk(self, feats: np.ndarray, cache: dict = {}):
433
+ if len(cache) == 0:
434
+ return feats
435
+ cache["feats"] = to_device(cache["feats"], device=feats.device)
436
+ overlap_feats = torch.cat((cache["feats"], feats), dim=1)
437
+ cache["feats"] = overlap_feats[:, -(cache["chunk_size"][0] + cache["chunk_size"][2]) :, :]
438
+ return overlap_feats
439
+
440
+ def forward_chunk(
441
+ self,
442
+ xs_pad: torch.Tensor,
443
+ ilens: torch.Tensor,
444
+ cache: dict = None,
445
+ ctc: CTC = None,
446
+ ):
447
+ xs_pad *= self.output_size() ** 0.5
448
+ if self.embed is None:
449
+ xs_pad = xs_pad
450
+ else:
451
+ xs_pad = self.embed(xs_pad, cache)
452
+ if cache["tail_chunk"]:
453
+ xs_pad = to_device(cache["feats"], device=xs_pad.device)
454
+ else:
455
+ xs_pad = self._add_overlap_chunk(xs_pad, cache)
456
+ encoder_outs = self.encoders0(xs_pad, None, None, None, None)
457
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
458
+ intermediate_outs = []
459
+ if len(self.interctc_layer_idx) == 0:
460
+ encoder_outs = self.encoders(xs_pad, None, None, None, None)
461
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
462
+ else:
463
+ for layer_idx, encoder_layer in enumerate(self.encoders):
464
+ encoder_outs = encoder_layer(xs_pad, None, None, None, None)
465
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
466
+ if layer_idx + 1 in self.interctc_layer_idx:
467
+ encoder_out = xs_pad
468
+
469
+ # intermediate outputs are also normalized
470
+ if self.normalize_before:
471
+ encoder_out = self.after_norm(encoder_out)
472
+
473
+ intermediate_outs.append((layer_idx + 1, encoder_out))
474
+
475
+ if self.interctc_use_conditioning:
476
+ ctc_out = ctc.softmax(encoder_out)
477
+ xs_pad = xs_pad + self.conditioning_layer(ctc_out)
478
+
479
+ if self.normalize_before:
480
+ xs_pad = self.after_norm(xs_pad)
481
+
482
+ if len(intermediate_outs) > 0:
483
+ return (xs_pad, intermediate_outs), None, None
484
+ return xs_pad, ilens, None
485
+
486
+
487
+ class EncoderLayerSANMExport(nn.Module):
488
+ def __init__(
489
+ self,
490
+ model,
491
+ ):
492
+ """Construct an EncoderLayer object."""
493
+ super().__init__()
494
+ self.self_attn = model.self_attn
495
+ self.feed_forward = model.feed_forward
496
+ self.norm1 = model.norm1
497
+ self.norm2 = model.norm2
498
+ self.in_size = model.in_size
499
+ self.size = model.size
500
+
501
+ def forward(self, x, mask):
502
+
503
+ residual = x
504
+ x = self.norm1(x)
505
+ x = self.self_attn(x, mask)
506
+ if self.in_size == self.size:
507
+ x = x + residual
508
+ residual = x
509
+ x = self.norm2(x)
510
+ x = self.feed_forward(x)
511
+ x = x + residual
512
+
513
+ return x, mask
514
+
515
+
516
+ @tables.register("encoder_classes", "SANMEncoderChunkOptExport")
517
+ @tables.register("encoder_classes", "SANMEncoderExport")
518
+ class SANMEncoderExport(nn.Module):
519
+ def __init__(
520
+ self,
521
+ model,
522
+ max_seq_len=512,
523
+ feats_dim=560,
524
+ model_name="encoder",
525
+ onnx: bool = True,
526
+ ctc_linear: nn.Module = None,
527
+ ):
528
+ super().__init__()
529
+ self.embed = model.embed
530
+ if isinstance(self.embed, StreamSinusoidalPositionEncoder):
531
+ self.embed = None
532
+ self.model = model
533
+ self.feats_dim = feats_dim
534
+ self._output_size = model._output_size
535
+
536
+ from funasr.utils.torch_function import sequence_mask
537
+
538
+ self.make_pad_mask = sequence_mask(max_seq_len, flip=False)
539
+
540
+ from funasr.models.sanm.attention import MultiHeadedAttentionSANMExport
541
+
542
+ if hasattr(model, "encoders0"):
543
+ for i, d in enumerate(self.model.encoders0):
544
+ if isinstance(d.self_attn, MultiHeadedAttentionSANM):
545
+ d.self_attn = MultiHeadedAttentionSANMExport(d.self_attn)
546
+ self.model.encoders0[i] = EncoderLayerSANMExport(d)
547
+
548
+ for i, d in enumerate(self.model.encoders):
549
+ if isinstance(d.self_attn, MultiHeadedAttentionSANM):
550
+ d.self_attn = MultiHeadedAttentionSANMExport(d.self_attn)
551
+ self.model.encoders[i] = EncoderLayerSANMExport(d)
552
+
553
+ self.model_name = model_name
554
+ self.num_heads = model.encoders[0].self_attn.h
555
+ self.hidden_size = model.encoders[0].self_attn.linear_out.out_features
556
+
557
+ self.ctc_linear = ctc_linear
558
+
559
+ def prepare_mask(self, mask):
560
+ mask_3d_btd = mask[:, :, None]
561
+ if len(mask.shape) == 2:
562
+ mask_4d_bhlt = 1 - mask[:, None, None, :]
563
+ elif len(mask.shape) == 3:
564
+ mask_4d_bhlt = 1 - mask[:, None, :]
565
+ mask_4d_bhlt = mask_4d_bhlt * -10000.0
566
+
567
+ return mask_3d_btd, mask_4d_bhlt
568
+
569
+ def forward(self, speech: torch.Tensor, speech_lengths: torch.Tensor, online: bool = False):
570
+ if not online:
571
+ speech = speech * self._output_size**0.5
572
+
573
+ mask = self.make_pad_mask(speech_lengths)
574
+ mask = self.prepare_mask(mask)
575
+ if self.embed is None:
576
+ xs_pad = speech
577
+ else:
578
+ xs_pad = self.embed(speech)
579
+
580
+ encoder_outs = self.model.encoders0(xs_pad, mask)
581
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
582
+
583
+ encoder_outs = self.model.encoders(xs_pad, mask)
584
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
585
+
586
+ xs_pad = self.model.after_norm(xs_pad)
587
+
588
+ if self.ctc_linear is not None:
589
+ xs_pad = self.ctc_linear(xs_pad)
590
+ xs_pad = F.softmax(xs_pad, dim=2)
591
+
592
+ return xs_pad, speech_lengths
593
+
594
+ def get_output_size(self):
595
+ return self.model.encoders[0].size
596
+
597
+ def get_dummy_inputs(self):
598
+ feats = torch.randn(1, 100, self.feats_dim)
599
+ return feats
600
+
601
+ def get_input_names(self):
602
+ return ["feats"]
603
+
604
+ def get_output_names(self):
605
+ return ["encoder_out", "encoder_out_lens", "predictor_weight"]
606
+
607
+ def get_dynamic_axes(self):
608
+ return {
609
+ "feats": {1: "feats_length"},
610
+ "encoder_out": {1: "enc_out_length"},
611
+ "predictor_weight": {1: "pre_out_length"},
612
+ }
funasr/models/sanm/model.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import logging
7
+
8
+ import torch
9
+
10
+ from funasr.models.transformer.model import Transformer
11
+ from funasr.register import tables
12
+
13
+
14
+ @tables.register("model_classes", "SANM")
15
+ class SANM(Transformer):
16
+ """
17
+ Author: Zhifu Gao, Shiliang Zhang, Ming Lei, Ian McLoughlin
18
+ San-m: Memory equipped self-attention for end-to-end speech recognition
19
+ https://arxiv.org/abs/2006.01713
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ *args,
25
+ **kwargs,
26
+ ):
27
+
28
+ super().__init__(*args, **kwargs)
funasr/models/sanm/multihead_att.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+
7
+
8
+ class MultiHeadedAttentionSANMExport(nn.Module):
9
+ def __init__(self, model):
10
+ super().__init__()
11
+ self.d_k = model.d_k
12
+ self.h = model.h
13
+ self.linear_out = model.linear_out
14
+ self.linear_q_k_v = model.linear_q_k_v
15
+ self.fsmn_block = model.fsmn_block
16
+ self.pad_fn = model.pad_fn
17
+
18
+ self.attn = None
19
+ self.all_head_size = self.h * self.d_k
20
+
21
+ def forward(self, x, mask):
22
+ mask_3d_btd, mask_4d_bhlt = mask
23
+ q_h, k_h, v_h, v = self.forward_qkv(x)
24
+ fsmn_memory = self.forward_fsmn(v, mask_3d_btd)
25
+ q_h = q_h * self.d_k ** (-0.5)
26
+ scores = torch.matmul(q_h, k_h.transpose(-2, -1))
27
+ att_outs = self.forward_attention(v_h, scores, mask_4d_bhlt)
28
+ return att_outs + fsmn_memory
29
+
30
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
31
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
32
+ x = x.view(new_x_shape)
33
+ return x.permute(0, 2, 1, 3)
34
+
35
+ def forward_qkv(self, x):
36
+ q_k_v = self.linear_q_k_v(x)
37
+ q, k, v = torch.split(q_k_v, int(self.h * self.d_k), dim=-1)
38
+ q_h = self.transpose_for_scores(q)
39
+ k_h = self.transpose_for_scores(k)
40
+ v_h = self.transpose_for_scores(v)
41
+ return q_h, k_h, v_h, v
42
+
43
+ def forward_fsmn(self, inputs, mask):
44
+ # b, t, d = inputs.size()
45
+ # mask = torch.reshape(mask, (b, -1, 1))
46
+ inputs = inputs * mask
47
+ x = inputs.transpose(1, 2)
48
+ x = self.pad_fn(x)
49
+ x = self.fsmn_block(x)
50
+ x = x.transpose(1, 2)
51
+ x = x + inputs
52
+ x = x * mask
53
+ return x
54
+
55
+ def forward_attention(self, value, scores, mask):
56
+ scores = scores + mask
57
+
58
+ attn = torch.softmax(scores, dim=-1)
59
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
60
+
61
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
62
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
63
+ context_layer = context_layer.view(new_context_layer_shape)
64
+ return self.linear_out(context_layer) # (batch, time1, d_model)
65
+
66
+
67
+ def preprocess_for_attn(x, mask, cache, pad_fn, kernel_size):
68
+ x = x * mask
69
+ x = x.transpose(1, 2)
70
+ if cache is None:
71
+ x = pad_fn(x)
72
+ else:
73
+ x = torch.cat((cache, x), dim=2)
74
+ cache = x[:, :, -(kernel_size - 1) :]
75
+ return x, cache
76
+
77
+
78
+ torch_version = tuple([int(i) for i in torch.__version__.split(".")[:2]])
79
+ if torch_version >= (1, 8):
80
+ import torch.fx
81
+
82
+ torch.fx.wrap("preprocess_for_attn")
83
+
84
+
85
+ class MultiHeadedAttentionSANMDecoderExport(nn.Module):
86
+ def __init__(self, model):
87
+ super().__init__()
88
+ self.fsmn_block = model.fsmn_block
89
+ self.pad_fn = model.pad_fn
90
+ self.kernel_size = model.kernel_size
91
+ self.attn = None
92
+
93
+ def forward(self, inputs, mask, cache=None):
94
+ x, cache = preprocess_for_attn(inputs, mask, cache, self.pad_fn, self.kernel_size)
95
+ x = self.fsmn_block(x)
96
+ x = x.transpose(1, 2)
97
+
98
+ x = x + inputs
99
+ x = x * mask
100
+ return x, cache
101
+
102
+
103
+ class MultiHeadedAttentionCrossAttExport(nn.Module):
104
+ def __init__(self, model):
105
+ super().__init__()
106
+ self.d_k = model.d_k
107
+ self.h = model.h
108
+ self.linear_q = model.linear_q
109
+ self.linear_k_v = model.linear_k_v
110
+ self.linear_out = model.linear_out
111
+ self.attn = None
112
+ self.all_head_size = self.h * self.d_k
113
+
114
+ def forward(self, x, memory, memory_mask):
115
+ q, k, v = self.forward_qkv(x, memory)
116
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
117
+ return self.forward_attention(v, scores, memory_mask)
118
+
119
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
120
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
121
+ x = x.view(new_x_shape)
122
+ return x.permute(0, 2, 1, 3)
123
+
124
+ def forward_qkv(self, x, memory):
125
+ q = self.linear_q(x)
126
+
127
+ k_v = self.linear_k_v(memory)
128
+ k, v = torch.split(k_v, int(self.h * self.d_k), dim=-1)
129
+ q = self.transpose_for_scores(q)
130
+ k = self.transpose_for_scores(k)
131
+ v = self.transpose_for_scores(v)
132
+ return q, k, v
133
+
134
+ def forward_attention(self, value, scores, mask):
135
+ scores = scores + mask
136
+
137
+ attn = torch.softmax(scores, dim=-1)
138
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
139
+
140
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
141
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
142
+ context_layer = context_layer.view(new_context_layer_shape)
143
+ return self.linear_out(context_layer) # (batch, time1, d_model)
144
+
145
+
146
+ class OnnxMultiHeadedAttention(nn.Module):
147
+ def __init__(self, model):
148
+ super().__init__()
149
+ self.d_k = model.d_k
150
+ self.h = model.h
151
+ self.linear_q = model.linear_q
152
+ self.linear_k = model.linear_k
153
+ self.linear_v = model.linear_v
154
+ self.linear_out = model.linear_out
155
+ self.attn = None
156
+ self.all_head_size = self.h * self.d_k
157
+
158
+ def forward(self, query, key, value, mask):
159
+ q, k, v = self.forward_qkv(query, key, value)
160
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
161
+ return self.forward_attention(v, scores, mask)
162
+
163
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
164
+ new_x_shape = x.size()[:-1] + (self.h, self.d_k)
165
+ x = x.view(new_x_shape)
166
+ return x.permute(0, 2, 1, 3)
167
+
168
+ def forward_qkv(self, query, key, value):
169
+ q = self.linear_q(query)
170
+ k = self.linear_k(key)
171
+ v = self.linear_v(value)
172
+ q = self.transpose_for_scores(q)
173
+ k = self.transpose_for_scores(k)
174
+ v = self.transpose_for_scores(v)
175
+ return q, k, v
176
+
177
+ def forward_attention(self, value, scores, mask):
178
+ scores = scores + mask
179
+
180
+ attn = torch.softmax(scores, dim=-1)
181
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
182
+
183
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
184
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
185
+ context_layer = context_layer.view(new_context_layer_shape)
186
+ return self.linear_out(context_layer) # (batch, time1, d_model)
187
+
188
+
189
+ class OnnxRelPosMultiHeadedAttention(OnnxMultiHeadedAttention):
190
+ def __init__(self, model):
191
+ super().__init__(model)
192
+ self.linear_pos = model.linear_pos
193
+ self.pos_bias_u = model.pos_bias_u
194
+ self.pos_bias_v = model.pos_bias_v
195
+
196
+ def forward(self, query, key, value, pos_emb, mask):
197
+ q, k, v = self.forward_qkv(query, key, value)
198
+ q = q.transpose(1, 2) # (batch, time1, head, d_k)
199
+
200
+ p = self.transpose_for_scores(self.linear_pos(pos_emb)) # (batch, head, time1, d_k)
201
+
202
+ # (batch, head, time1, d_k)
203
+ q_with_bias_u = (q + self.pos_bias_u).transpose(1, 2)
204
+ # (batch, head, time1, d_k)
205
+ q_with_bias_v = (q + self.pos_bias_v).transpose(1, 2)
206
+
207
+ # compute attention score
208
+ # first compute matrix a and matrix c
209
+ # as described in https://arxiv.org/abs/1901.02860 Section 3.3
210
+ # (batch, head, time1, time2)
211
+ matrix_ac = torch.matmul(q_with_bias_u, k.transpose(-2, -1))
212
+
213
+ # compute matrix b and matrix d
214
+ # (batch, head, time1, time1)
215
+ matrix_bd = torch.matmul(q_with_bias_v, p.transpose(-2, -1))
216
+ matrix_bd = self.rel_shift(matrix_bd)
217
+
218
+ scores = (matrix_ac + matrix_bd) / math.sqrt(self.d_k) # (batch, head, time1, time2)
219
+
220
+ return self.forward_attention(v, scores, mask)
221
+
222
+ def rel_shift(self, x):
223
+ zero_pad = torch.zeros((*x.size()[:3], 1), device=x.device, dtype=x.dtype)
224
+ x_padded = torch.cat([zero_pad, x], dim=-1)
225
+
226
+ x_padded = x_padded.view(*x.size()[:2], x.size(3) + 1, x.size(2))
227
+ x = x_padded[:, :, 1:].view_as(x)[
228
+ :, :, :, : x.size(-1) // 2 + 1
229
+ ] # only keep the positions from 0 to time2
230
+ return x
231
+
232
+ def forward_attention(self, value, scores, mask):
233
+ scores = scores + mask
234
+
235
+ attn = torch.softmax(scores, dim=-1)
236
+ context_layer = torch.matmul(attn, value) # (batch, head, time1, d_k)
237
+
238
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
239
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
240
+ context_layer = context_layer.view(new_context_layer_shape)
241
+ return self.linear_out(context_layer) # (batch, time1, d_model)
funasr/models/sanm/positionwise_feed_forward.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ """Positionwise feed forward layer definition."""
6
+
7
+ import torch
8
+
9
+ from funasr.models.transformer.layer_norm import LayerNorm
10
+
11
+
12
+ class PositionwiseFeedForwardDecoderSANM(torch.nn.Module):
13
+ """Positionwise feed forward layer.
14
+
15
+ Args:
16
+ idim (int): Input dimenstion.
17
+ hidden_units (int): The number of hidden units.
18
+ dropout_rate (float): Dropout rate.
19
+
20
+ """
21
+
22
+ def __init__(self, idim, hidden_units, dropout_rate, adim=None, activation=torch.nn.ReLU()):
23
+ """Construct an PositionwiseFeedForward object."""
24
+ super(PositionwiseFeedForwardDecoderSANM, self).__init__()
25
+ self.w_1 = torch.nn.Linear(idim, hidden_units)
26
+ self.w_2 = torch.nn.Linear(hidden_units, idim if adim is None else adim, bias=False)
27
+ self.dropout = torch.nn.Dropout(dropout_rate)
28
+ self.activation = activation
29
+ self.norm = LayerNorm(hidden_units)
30
+
31
+ def forward(self, x):
32
+ """Forward function."""
33
+ return self.w_2(self.norm(self.dropout(self.activation(self.w_1(x)))))
funasr/models/sanm/template.yaml ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is an example that demonstrates how to configure a model file.
2
+ # You can modify the configuration according to your own requirements.
3
+
4
+ # to print the register_table:
5
+ # from funasr.register import tables
6
+ # tables.print()
7
+
8
+ # network architecture
9
+ model: SANM
10
+ model_conf:
11
+ ctc_weight: 0.0
12
+ lsm_weight: 0.1
13
+ length_normalized_loss: true
14
+
15
+ # encoder
16
+ encoder: SANMEncoder
17
+ encoder_conf:
18
+ output_size: 512
19
+ attention_heads: 4
20
+ linear_units: 2048
21
+ num_blocks: 50
22
+ dropout_rate: 0.1
23
+ positional_dropout_rate: 0.1
24
+ attention_dropout_rate: 0.1
25
+ input_layer: pe
26
+ pos_enc_class: SinusoidalPositionEncoder
27
+ normalize_before: true
28
+ kernel_size: 11
29
+ sanm_shfit: 0
30
+ selfattention_layer_type: sanm
31
+
32
+ # decoder
33
+ decoder: FsmnDecoder
34
+ decoder_conf:
35
+ attention_heads: 4
36
+ linear_units: 2048
37
+ num_blocks: 16
38
+ dropout_rate: 0.1
39
+ positional_dropout_rate: 0.1
40
+ self_attention_dropout_rate: 0.1
41
+ src_attention_dropout_rate: 0.1
42
+ att_layer_num: 16
43
+ kernel_size: 11
44
+ sanm_shfit: 0
45
+
46
+
47
+
48
+ # frontend related
49
+ frontend: WavFrontend
50
+ frontend_conf:
51
+ fs: 16000
52
+ window: hamming
53
+ n_mels: 80
54
+ frame_length: 25
55
+ frame_shift: 10
56
+ lfr_m: 7
57
+ lfr_n: 6
58
+
59
+ specaug: SpecAugLFR
60
+ specaug_conf:
61
+ apply_time_warp: false
62
+ time_warp_window: 5
63
+ time_warp_mode: bicubic
64
+ apply_freq_mask: true
65
+ freq_mask_width_range:
66
+ - 0
67
+ - 30
68
+ lfr_rate: 6
69
+ num_freq_mask: 1
70
+ apply_time_mask: true
71
+ time_mask_width_range:
72
+ - 0
73
+ - 12
74
+ num_time_mask: 1
75
+
76
+ train_conf:
77
+ accum_grad: 1
78
+ grad_clip: 5
79
+ max_epoch: 150
80
+ val_scheduler_criterion:
81
+ - valid
82
+ - acc
83
+ best_model_criterion:
84
+ - - valid
85
+ - acc
86
+ - max
87
+ keep_nbest_models: 10
88
+ avg_nbest_model: 10
89
+ log_interval: 50
90
+
91
+ optim: adam
92
+ optim_conf:
93
+ lr: 0.0005
94
+ scheduler: warmuplr
95
+ scheduler_conf:
96
+ warmup_steps: 30000
97
+
98
+ dataset: AudioDataset
99
+ dataset_conf:
100
+ index_ds: IndexDSJsonl
101
+ batch_sampler: BatchSampler
102
+ batch_type: example # example or length
103
+ batch_size: 1 # if batch_type is example, batch_size is the numbers of samples; if length, batch_size is source_token_len+target_token_len;
104
+ max_token_length: 2048 # filter samples if source_token_len+target_token_len > max_token_length,
105
+ buffer_size: 500
106
+ shuffle: True
107
+ num_workers: 0
108
+
109
+ tokenizer: CharTokenizer
110
+ tokenizer_conf:
111
+ unk_symbol: <unk>
112
+ split_with_space: true
113
+
114
+
115
+ ctc_conf:
116
+ dropout_rate: 0.0
117
+ ctc_type: builtin
118
+ reduce: true
119
+ ignore_nan_grad: true
120
+
121
+ normalize: null
funasr/models/sanm_kws/__init__.py ADDED
File without changes
funasr/models/sanm_kws/export_meta.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import types
7
+ import copy
8
+ import torch
9
+ from funasr.register import tables
10
+
11
+
12
+ def export_rebuild_model(model, **kwargs):
13
+ # self.device = kwargs.get("device")
14
+ is_onnx = kwargs.get("type", "onnx") == "onnx"
15
+ encoder_class = tables.encoder_classes.get(kwargs["encoder"] + "Export")
16
+
17
+ if hasattr(model, "ctc"):
18
+ model.encoder = encoder_class(
19
+ model.encoder,
20
+ onnx=is_onnx,
21
+ feats_dim=kwargs.get("input_size", 560),
22
+ ctc_linear=model.ctc.ctc_lo
23
+ )
24
+ else:
25
+ assert False, print(model)
26
+ model.encoder = encoder_class(model.encoder, onnx=is_onnx, feats_dim=kwargs.get("input_size", 560))
27
+
28
+ # from funasr.utils.torch_function import sequence_mask
29
+ # model.make_pad_mask = sequence_mask(max_seq_len=None, flip=False)
30
+
31
+ encoder_model = copy.copy(model)
32
+
33
+ # encoder
34
+ encoder_model.forward = types.MethodType(export_encoder_forward, encoder_model)
35
+ encoder_model.export_dummy_inputs = types.MethodType(export_encoder_dummy_inputs, encoder_model)
36
+ encoder_model.export_input_names = types.MethodType(export_encoder_input_names, encoder_model)
37
+ encoder_model.export_output_names = types.MethodType(export_encoder_output_names, encoder_model)
38
+ encoder_model.export_dynamic_axes = types.MethodType(export_encoder_dynamic_axes, encoder_model)
39
+ encoder_model.export_name = types.MethodType(export_encoder_name, encoder_model)
40
+
41
+ return encoder_model
42
+
43
+
44
+ def export_encoder_forward(
45
+ self,
46
+ speech: torch.Tensor,
47
+ speech_lengths: torch.Tensor,
48
+ ):
49
+ # a. To device
50
+ batch = {
51
+ "speech": speech,
52
+ "speech_lengths": speech_lengths,
53
+ "online": True
54
+ }
55
+ # batch = to_device(batch, device=self.device)
56
+
57
+ encoder_out, encoder_out_len = self.encoder(**batch)
58
+ # mask = self.make_pad_mask(encoder_out_len)[:, None, :]
59
+ # alphas, _ = self.predictor.forward_cnn(enc, mask)
60
+
61
+ # return encoder_out, encoder_out_len, alphas
62
+ return encoder_out, encoder_out_len
63
+
64
+
65
+ def export_encoder_dummy_inputs(self):
66
+ speech = torch.randn(2, 30, 280)
67
+ speech_lengths = torch.tensor([6, 30], dtype=torch.int32)
68
+ return (speech, speech_lengths)
69
+
70
+
71
+ def export_encoder_input_names(self):
72
+ return ["speech", "speech_lengths"]
73
+
74
+
75
+ def export_encoder_output_names(self):
76
+ # return ["encoder_out", "encoder_out_len", "alphas"]
77
+ return ["encoder_out", "encoder_out_len"]
78
+
79
+
80
+ def export_encoder_dynamic_axes(self):
81
+ return {
82
+ "speech": {
83
+ 0: "batch_size", 1: "feats_length"
84
+ },
85
+ "speech_lengths": {
86
+ 0: "batch_size",
87
+ },
88
+ "encoder_out": {
89
+ 0: "batch_size", 1: "feats_length"
90
+ },
91
+ "encoder_out_len": {
92
+ 0: "batch_size",
93
+ },
94
+ }
95
+
96
+
97
+ def export_encoder_name(self):
98
+ return "encoder.onnx"
funasr/models/sanm_kws/model.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import time
7
+ import torch
8
+ import logging
9
+ from torch.cuda.amp import autocast
10
+ from typing import Union, Dict, List, Tuple, Optional
11
+
12
+ from funasr.register import tables
13
+ from funasr.models.ctc.ctc import CTC
14
+ from funasr.utils import postprocess_utils
15
+ from funasr.metrics.compute_acc import th_accuracy
16
+ from funasr.train_utils.device_funcs import to_device
17
+ from funasr.utils.datadir_writer import DatadirWriter
18
+ from funasr.models.paraformer.search import Hypothesis
19
+ from funasr.models.paraformer.cif_predictor import mae_loss
20
+ from funasr.train_utils.device_funcs import force_gatherable
21
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss
22
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
23
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
24
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
25
+
26
+
27
+ @tables.register("model_classes", "SanmKWS")
28
+ class SanmKWS(torch.nn.Module):
29
+ """
30
+ Author: Speech Lab of DAMO Academy, Alibaba Group
31
+ Paraformer: Fast and Accurate Parallel Transformer for Non-autoregressive End-to-End Speech Recognition
32
+ https://arxiv.org/abs/2206.08317
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ specaug: Optional[str] = None,
38
+ specaug_conf: Optional[Dict] = None,
39
+ normalize: str = None,
40
+ normalize_conf: Optional[Dict] = None,
41
+ encoder: str = None,
42
+ encoder_conf: Optional[Dict] = None,
43
+ ctc: str = None,
44
+ ctc_conf: Optional[Dict] = None,
45
+ ctc_weight: float = 1.0,
46
+ input_size: int = 360,
47
+ vocab_size: int = -1,
48
+ ignore_id: int = -1,
49
+ blank_id: int = 0,
50
+ sos: int = 1,
51
+ eos: int = 2,
52
+ **kwargs,
53
+ ):
54
+
55
+ super().__init__()
56
+
57
+ if specaug is not None:
58
+ specaug_class = tables.specaug_classes.get(specaug)
59
+ specaug = specaug_class(**specaug_conf)
60
+
61
+ if normalize is not None:
62
+ normalize_class = tables.normalize_classes.get(normalize)
63
+ normalize = normalize_class(**normalize_conf)
64
+
65
+ encoder_class = tables.encoder_classes.get(encoder)
66
+ encoder = encoder_class(input_size=input_size, **encoder_conf)
67
+ encoder_output_size = encoder.output_size()
68
+
69
+ if ctc_conf is None:
70
+ ctc_conf = {}
71
+ ctc = CTC(odim=vocab_size, encoder_output_size=encoder_output_size, **ctc_conf)
72
+
73
+ # note that eos is the same as sos (equivalent ID)
74
+ self.blank_id = blank_id
75
+ self.sos = sos if sos is not None else vocab_size - 1
76
+ self.eos = eos if eos is not None else vocab_size - 1
77
+ self.vocab_size = vocab_size
78
+ self.ignore_id = ignore_id
79
+ self.ctc_weight = ctc_weight
80
+ # self.token_list = token_list.copy()
81
+ #
82
+ # self.frontend = frontend
83
+ self.specaug = specaug
84
+ self.normalize = normalize
85
+ self.encoder = encoder
86
+
87
+ self.ctc = ctc
88
+ self.error_calculator = None
89
+
90
+ def forward(
91
+ self,
92
+ speech: torch.Tensor,
93
+ speech_lengths: torch.Tensor,
94
+ text: torch.Tensor,
95
+ text_lengths: torch.Tensor,
96
+ **kwargs,
97
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
98
+ """Encoder + Decoder + Calc loss
99
+ Args:
100
+ speech: (Batch, Length, ...)
101
+ speech_lengths: (Batch, )
102
+ text: (Batch, Length)
103
+ text_lengths: (Batch,)
104
+ """
105
+ if len(text_lengths.size()) > 1:
106
+ text_lengths = text_lengths[:, 0]
107
+ if len(speech_lengths.size()) > 1:
108
+ speech_lengths = speech_lengths[:, 0]
109
+ batch_size = speech.shape[0]
110
+
111
+ # Encoder
112
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
113
+
114
+ # decoder: CTC branch
115
+ loss_ctc, cer_ctc = self._calc_ctc_loss(
116
+ encoder_out, encoder_out_lens, text, text_lengths
117
+ )
118
+
119
+ # Collect CTC branch stats
120
+ stats = dict()
121
+ stats["loss_ctc"] = loss_ctc.detach() if loss_ctc is not None else None
122
+ stats["cer_ctc"] = cer_ctc
123
+ stats["cer"] = cer_ctc
124
+
125
+ loss = loss_ctc
126
+
127
+ stats["loss"] = torch.clone(loss.detach())
128
+ stats["batch_size"] = batch_size
129
+
130
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
131
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
132
+ return loss, stats, weight
133
+
134
+ def encode(
135
+ self,
136
+ speech: torch.Tensor,
137
+ speech_lengths: torch.Tensor,
138
+ **kwargs,
139
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
140
+ """Encoder. Note that this method is used by asr_inference.py
141
+ Args:
142
+ speech: (Batch, Length, ...)
143
+ speech_lengths: (Batch, )
144
+ ind: int
145
+ """
146
+ with autocast(False):
147
+ # Data augmentation
148
+ if self.specaug is not None and self.training:
149
+ speech, speech_lengths = self.specaug(speech, speech_lengths)
150
+
151
+ # Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
152
+ if self.normalize is not None:
153
+ speech, speech_lengths = self.normalize(speech, speech_lengths)
154
+
155
+ # Forward encoder
156
+ encoder_out, encoder_out_lens, _ = self.encoder(speech, speech_lengths)
157
+ if isinstance(encoder_out, tuple):
158
+ encoder_out = encoder_out[0]
159
+
160
+ return encoder_out, encoder_out_lens
161
+
162
+ def _calc_ctc_loss(
163
+ self,
164
+ encoder_out: torch.Tensor,
165
+ encoder_out_lens: torch.Tensor,
166
+ ys_pad: torch.Tensor,
167
+ ys_pad_lens: torch.Tensor,
168
+ ):
169
+ # Calc CTC loss
170
+ loss_ctc = self.ctc(encoder_out, encoder_out_lens, ys_pad, ys_pad_lens)
171
+
172
+ # Calc CER using CTC
173
+ cer_ctc = None
174
+ if not self.training and self.error_calculator is not None:
175
+ ys_hat = self.ctc.argmax(encoder_out).data
176
+ cer_ctc = self.error_calculator(ys_hat.cpu(), ys_pad.cpu(), is_ctc=True)
177
+ return loss_ctc, cer_ctc
178
+
179
+ def inference(
180
+ self,
181
+ data_in,
182
+ data_lengths=None,
183
+ key: list = None,
184
+ tokenizer=None,
185
+ frontend=None,
186
+ **kwargs,
187
+ ):
188
+ keywords = kwargs.get("keywords")
189
+ from funasr.utils.kws_utils import KwsCtcPrefixDecoder
190
+ self.kws_decoder = KwsCtcPrefixDecoder(
191
+ ctc=self.ctc,
192
+ keywords=keywords,
193
+ token_list=tokenizer.token_list,
194
+ seg_dict=tokenizer.seg_dict,
195
+ )
196
+
197
+ meta_data = {}
198
+ if (
199
+ isinstance(data_in, torch.Tensor) and kwargs.get("data_type", "sound") == "fbank"
200
+ ): # fbank
201
+ speech, speech_lengths = data_in, data_lengths
202
+ if len(speech.shape) < 3:
203
+ speech = speech[None, :, :]
204
+ if speech_lengths is not None:
205
+ speech_lengths = speech_lengths.squeeze(-1)
206
+ else:
207
+ speech_lengths = speech.shape[1]
208
+ else:
209
+ # extract fbank feats
210
+ time1 = time.perf_counter()
211
+ audio_sample_list = load_audio_text_image_video(
212
+ data_in,
213
+ fs=frontend.fs,
214
+ audio_fs=kwargs.get("fs", 16000),
215
+ data_type=kwargs.get("data_type", "sound"),
216
+ tokenizer=tokenizer,
217
+ )
218
+ time2 = time.perf_counter()
219
+ meta_data["load_data"] = f"{time2 - time1:0.3f}"
220
+ speech, speech_lengths = extract_fbank(
221
+ audio_sample_list, data_type=kwargs.get("data_type", "sound"), frontend=frontend
222
+ )
223
+ time3 = time.perf_counter()
224
+ meta_data["extract_feat"] = f"{time3 - time2:0.3f}"
225
+ meta_data["batch_data_time"] = (
226
+ speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000
227
+ )
228
+
229
+ speech = speech.to(device=kwargs["device"])
230
+ speech_lengths = speech_lengths.to(device=kwargs["device"])
231
+ # Encoder
232
+ if kwargs.get("fp16", False):
233
+ speech = speech.half()
234
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
235
+ if isinstance(encoder_out, tuple):
236
+ encoder_out = encoder_out[0]
237
+
238
+ results = []
239
+ if kwargs.get("output_dir") is not None:
240
+ if not hasattr(self, "writer"):
241
+ self.writer = DatadirWriter(kwargs.get("output_dir"))
242
+
243
+ for i in range(encoder_out.size(0)):
244
+ x = encoder_out[i, : encoder_out_lens[i], :]
245
+ detect_result = self.kws_decoder.decode(x)
246
+ is_deted, det_keyword, det_score = detect_result[0], detect_result[1], detect_result[2]
247
+
248
+ if is_deted:
249
+ self.writer["detect"][key[i]] = "detected " + det_keyword + " " + str(det_score)
250
+ det_info = "detected " + det_keyword + " " + str(det_score)
251
+ else:
252
+ self.writer["detect"][key[i]] = "rejected"
253
+ det_info = "rejected"
254
+
255
+ result_i = {"key": key[i], "text": det_info}
256
+ results.append(result_i)
257
+
258
+ return results, meta_data
259
+
260
+ def export(self, **kwargs):
261
+ from .export_meta import export_rebuild_model
262
+
263
+ if "max_seq_len" not in kwargs:
264
+ kwargs["max_seq_len"] = 512
265
+ models = export_rebuild_model(model=self, **kwargs)
266
+ return models
funasr/models/sanm_kws_streaming/__init__.py ADDED
File without changes
funasr/models/sanm_kws_streaming/export_meta.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import types
7
+ import copy
8
+ import torch
9
+ from funasr.register import tables
10
+
11
+
12
+ def export_rebuild_model(model, **kwargs):
13
+ # self.device = kwargs.get("device")
14
+ is_onnx = kwargs.get("type", "onnx") == "onnx"
15
+ encoder_class = tables.encoder_classes.get(kwargs["encoder"] + "Export")
16
+
17
+ if hasattr(model, "ctc"):
18
+ model.encoder = encoder_class(
19
+ model.encoder,
20
+ onnx=is_onnx,
21
+ feats_dim=kwargs.get("input_size", 560),
22
+ ctc_linear=model.ctc.ctc_lo
23
+ )
24
+ else:
25
+ assert False, print(model)
26
+ model.encoder = encoder_class(model.encoder, onnx=is_onnx, feats_dim=kwargs.get("input_size", 560))
27
+
28
+ # from funasr.utils.torch_function import sequence_mask
29
+ # model.make_pad_mask = sequence_mask(max_seq_len=None, flip=False)
30
+
31
+ encoder_model = copy.copy(model)
32
+
33
+ # encoder
34
+ encoder_model.forward = types.MethodType(export_encoder_forward, encoder_model)
35
+ encoder_model.export_dummy_inputs = types.MethodType(export_encoder_dummy_inputs, encoder_model)
36
+ encoder_model.export_input_names = types.MethodType(export_encoder_input_names, encoder_model)
37
+ encoder_model.export_output_names = types.MethodType(export_encoder_output_names, encoder_model)
38
+ encoder_model.export_dynamic_axes = types.MethodType(export_encoder_dynamic_axes, encoder_model)
39
+ encoder_model.export_name = types.MethodType(export_encoder_name, encoder_model)
40
+
41
+ return encoder_model
42
+
43
+
44
+ def export_encoder_forward(
45
+ self,
46
+ speech: torch.Tensor,
47
+ speech_lengths: torch.Tensor,
48
+ ):
49
+ # a. To device
50
+ batch = {
51
+ "speech": speech,
52
+ "speech_lengths": speech_lengths,
53
+ "online": True
54
+ }
55
+ # batch = to_device(batch, device=self.device)
56
+
57
+ encoder_out, encoder_out_len = self.encoder(**batch)
58
+ # mask = self.make_pad_mask(encoder_out_len)[:, None, :]
59
+ # alphas, _ = self.predictor.forward_cnn(enc, mask)
60
+
61
+ # return encoder_out, encoder_out_len, alphas
62
+ return encoder_out, encoder_out_len
63
+
64
+
65
+ def export_encoder_dummy_inputs(self):
66
+ speech = torch.randn(2, 30, 280)
67
+ speech_lengths = torch.tensor([6, 30], dtype=torch.int32)
68
+ return (speech, speech_lengths)
69
+
70
+
71
+ def export_encoder_input_names(self):
72
+ return ["speech", "speech_lengths"]
73
+
74
+
75
+ def export_encoder_output_names(self):
76
+ # return ["encoder_out", "encoder_out_len", "alphas"]
77
+ return ["encoder_out", "encoder_out_len"]
78
+
79
+
80
+ def export_encoder_dynamic_axes(self):
81
+ return {
82
+ "speech": {
83
+ 0: "batch_size", 1: "feats_length"
84
+ },
85
+ "speech_lengths": {
86
+ 0: "batch_size",
87
+ },
88
+ "encoder_out": {
89
+ 0: "batch_size", 1: "feats_length"
90
+ },
91
+ "encoder_out_len": {
92
+ 0: "batch_size",
93
+ },
94
+ }
95
+
96
+
97
+ def export_encoder_name(self):
98
+ return "encoder.onnx"
funasr/models/sanm_kws_streaming/model.py ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import time
7
+ import torch
8
+ import logging
9
+ from typing import Dict, Tuple
10
+ from contextlib import contextmanager
11
+ from distutils.version import LooseVersion
12
+
13
+ from funasr.register import tables
14
+ from funasr.models.ctc.ctc import CTC
15
+ from funasr.utils import postprocess_utils
16
+ from funasr.metrics.compute_acc import th_accuracy
17
+ from funasr.utils.datadir_writer import DatadirWriter
18
+ from funasr.models.sanm_kws.model import SanmKWS
19
+ from funasr.models.paraformer.search import Hypothesis
20
+ from funasr.models.paraformer.cif_predictor import mae_loss
21
+ from funasr.train_utils.device_funcs import force_gatherable
22
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss
23
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
24
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask, pad_list
25
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
26
+
27
+
28
+ if LooseVersion(torch.__version__) >= LooseVersion("1.6.0"):
29
+ from torch.cuda.amp import autocast
30
+ else:
31
+ # Nothing to do if torch<1.6.0
32
+ @contextmanager
33
+ def autocast(enabled=True):
34
+ yield
35
+
36
+
37
+ @tables.register("model_classes", "SanmKWSStreaming")
38
+ class SanmKWSStreaming(SanmKWS):
39
+ """
40
+ Author: Speech Lab of DAMO Academy, Alibaba Group
41
+ Paraformer: Fast and Accurate Parallel Transformer for Non-autoregressive End-to-End Speech Recognition
42
+ https://arxiv.org/abs/2206.08317
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ *args,
48
+ **kwargs,
49
+ ):
50
+ super().__init__(*args, **kwargs)
51
+
52
+ def forward(
53
+ self,
54
+ speech: torch.Tensor,
55
+ speech_lengths: torch.Tensor,
56
+ text: torch.Tensor,
57
+ text_lengths: torch.Tensor,
58
+ **kwargs,
59
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
60
+ """Encoder + Decoder + Calc loss
61
+ Args:
62
+ speech: (Batch, Length, ...)
63
+ speech_lengths: (Batch, )
64
+ text: (Batch, Length)
65
+ text_lengths: (Batch,)
66
+ """
67
+ decoding_ind = kwargs.get("decoding_ind")
68
+ if len(text_lengths.size()) > 1:
69
+ text_lengths = text_lengths[:, 0]
70
+ if len(speech_lengths.size()) > 1:
71
+ speech_lengths = speech_lengths[:, 0]
72
+
73
+ batch_size = speech.shape[0]
74
+
75
+ # Encoder
76
+ if hasattr(self.encoder, "overlap_chunk_cls"):
77
+ ind = self.encoder.overlap_chunk_cls.random_choice(self.training, decoding_ind)
78
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths, ind=ind)
79
+ else:
80
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
81
+
82
+ # decoder: CTC branch
83
+ if hasattr(self.encoder, "overlap_chunk_cls"):
84
+ encoder_out_ctc, encoder_out_lens_ctc = self.encoder.overlap_chunk_cls.remove_chunk(
85
+ encoder_out, encoder_out_lens, chunk_outs=None
86
+ )
87
+ else:
88
+ encoder_out_ctc, encoder_out_lens_ctc = encoder_out, encoder_out_lens
89
+
90
+ loss_ctc, cer_ctc = self._calc_ctc_loss(
91
+ encoder_out_ctc, encoder_out_lens_ctc, text, text_lengths
92
+ )
93
+
94
+ # Collect CTC branch stats
95
+ stats = dict()
96
+ stats["loss_ctc"] = loss_ctc.detach() if loss_ctc is not None else None
97
+ stats["cer_ctc"] = cer_ctc
98
+
99
+ loss = loss_ctc
100
+
101
+ stats["cer"] = cer_ctc
102
+ stats["loss"] = torch.clone(loss.detach())
103
+
104
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
105
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
106
+ return loss, stats, weight
107
+
108
+ def encode_chunk(
109
+ self,
110
+ speech: torch.Tensor,
111
+ speech_lengths: torch.Tensor,
112
+ cache: dict = None,
113
+ **kwargs,
114
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
115
+ """Frontend + Encoder. Note that this method is used by asr_inference.py
116
+ Args:
117
+ speech: (Batch, Length, ...)
118
+ speech_lengths: (Batch, )
119
+ ind: int
120
+ """
121
+ with autocast(False):
122
+ # Data augmentation
123
+ if self.specaug is not None and self.training:
124
+ speech, speech_lengths = self.specaug(speech, speech_lengths)
125
+
126
+ # Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
127
+ if self.normalize is not None:
128
+ speech, speech_lengths = self.normalize(speech, speech_lengths)
129
+
130
+ # Forward encoder
131
+ encoder_out, encoder_out_lens, _ = self.encoder.forward_chunk(
132
+ speech, speech_lengths, cache=cache["encoder"]
133
+ )
134
+
135
+ if isinstance(encoder_out, tuple):
136
+ encoder_out = encoder_out[0]
137
+
138
+ return encoder_out, torch.tensor([encoder_out.size(1)])
139
+
140
+ def init_cache(self, cache: dict = {}, **kwargs):
141
+ chunk_size = kwargs.get("chunk_size", [0, 10, 5])
142
+ encoder_chunk_look_back = kwargs.get("encoder_chunk_look_back", 0)
143
+ decoder_chunk_look_back = kwargs.get("decoder_chunk_look_back", 0)
144
+ batch_size = 1
145
+
146
+ enc_output_size = kwargs["encoder_conf"]["output_size"]
147
+ feats_dims = kwargs["frontend_conf"]["n_mels"] * kwargs["frontend_conf"]["lfr_m"]
148
+ cache_encoder = {
149
+ "start_idx": 0,
150
+ "cif_hidden": torch.zeros((batch_size, 1, enc_output_size)),
151
+ "cif_alphas": torch.zeros((batch_size, 1)),
152
+ "encoder_out": None,
153
+ "encoder_out_lens": None,
154
+ "chunk_size": chunk_size,
155
+ "encoder_chunk_look_back": encoder_chunk_look_back,
156
+ "last_chunk": False,
157
+ "opt": None,
158
+ "feats": torch.zeros((batch_size, chunk_size[0] + chunk_size[2], feats_dims)),
159
+ "tail_chunk": False,
160
+ }
161
+ cache["encoder"] = cache_encoder
162
+
163
+ cache_decoder = {
164
+ "decode_fsmn": None,
165
+ "decoder_chunk_look_back": decoder_chunk_look_back,
166
+ "opt": None,
167
+ "chunk_size": chunk_size,
168
+ }
169
+ cache["decoder"] = cache_decoder
170
+ cache["frontend"] = {}
171
+ cache["prev_samples"] = torch.empty(0)
172
+
173
+ return cache
174
+
175
+ def generate_chunk(
176
+ self,
177
+ speech,
178
+ speech_lengths=None,
179
+ key: list = None,
180
+ tokenizer=None,
181
+ frontend=None,
182
+ **kwargs,
183
+ ):
184
+ cache = kwargs.get("cache", {})
185
+ speech = speech.to(device=kwargs["device"])
186
+ speech_lengths = speech_lengths.to(device=kwargs["device"])
187
+
188
+ # Encoder
189
+ is_final = kwargs.get("is_final", False)
190
+ encoder_out, encoder_out_lens = self.encode_chunk(
191
+ speech, speech_lengths, cache=cache, is_final=is_final
192
+ )
193
+ if isinstance(encoder_out, tuple):
194
+ encoder_out = encoder_out[0]
195
+
196
+ chunk_size = cache["encoder"]["chunk_size"]
197
+ real_start_pos = chunk_size[0]
198
+
199
+ if encoder_out_lens[0] > chunk_size[0] + chunk_size[1] + chunk_size[2]:
200
+ assert False, print("impossible case 1 !")
201
+ if encoder_out_lens[0] == chunk_size[0] + chunk_size[1] + chunk_size[2]:
202
+ real_end_pos = chunk_size[0] + chunk_size[1]
203
+ elif encoder_out_lens[0] > chunk_size[0] + chunk_size[1]:
204
+ real_end_pos = chunk_size[0] + chunk_size[1]
205
+ elif encoder_out_lens[0] > chunk_size[0]:
206
+ real_end_pos = encoder_out_lens[0]
207
+ else:
208
+ assert False, print("impossible case 2 !")
209
+
210
+ encoder_out_accum = cache["encoder"]["encoder_out"]
211
+ if encoder_out_accum is not None:
212
+ encoder_out_accum = torch.cat((encoder_out_accum, encoder_out[:, real_start_pos:real_end_pos, :]), dim=1)
213
+ else:
214
+ encoder_out_accum = encoder_out[:, real_start_pos:real_end_pos, :]
215
+ cache["encoder"]["encoder_out"] = encoder_out_accum
216
+
217
+ if cache["encoder"]["encoder_out_lens"] is not None:
218
+ cache["encoder"]["encoder_out_lens"][0] += real_end_pos - real_start_pos
219
+ else:
220
+ cache["encoder"]["encoder_out_lens"] = encoder_out_lens
221
+ cache["encoder"]["encoder_out_lens"][0] = real_end_pos - real_start_pos
222
+
223
+ if is_final:
224
+ if kwargs.get("output_dir") is not None:
225
+ if not hasattr(self, "writer"):
226
+ self.writer = DatadirWriter(kwargs.get("output_dir"))
227
+
228
+ results = []
229
+ for i in range(encoder_out_accum.size(0)):
230
+ x = encoder_out_accum[i, : cache["encoder"]["encoder_out_lens"][i], :]
231
+ detect_result = self.kws_decoder.decode(x)
232
+ is_deted, det_keyword, det_score = detect_result[0], detect_result[1], detect_result[2]
233
+
234
+ if is_deted:
235
+ self.writer["detect"][key[i]] = "detected " + det_keyword + " " + str(det_score)
236
+ det_info = "detected " + det_keyword + " " + str(det_score)
237
+ else:
238
+ self.writer["detect"][key[i]] = "rejected"
239
+ det_info = "rejected"
240
+
241
+ result_i = {"key": key[i], "text": det_info}
242
+ results.append(result_i)
243
+
244
+ return results
245
+ else:
246
+ return None
247
+
248
+ def inference(
249
+ self,
250
+ data_in,
251
+ data_lengths=None,
252
+ key: list = None,
253
+ tokenizer=None,
254
+ frontend=None,
255
+ cache: dict = {},
256
+ **kwargs,
257
+ ):
258
+ keywords = kwargs.get("keywords")
259
+ from funasr.utils.kws_utils import KwsCtcPrefixDecoder
260
+ self.kws_decoder = KwsCtcPrefixDecoder(
261
+ ctc=self.ctc,
262
+ keywords=keywords,
263
+ token_list=tokenizer.token_list,
264
+ seg_dict=tokenizer.seg_dict,
265
+ )
266
+
267
+ meta_data = {}
268
+ chunk_size = kwargs["chunk_size"]
269
+ chunk_stride_samples = int(chunk_size[1] * 960) # 600ms
270
+ first_chunk_padding_samples = int(chunk_size[2] * 960) # 600ms
271
+
272
+ if len(cache) == 0:
273
+ self.init_cache(cache, **kwargs)
274
+
275
+ time1 = time.perf_counter()
276
+ cfg = {"is_final": kwargs.get("is_final", False)}
277
+ audio_sample_list = load_audio_text_image_video(
278
+ data_in,
279
+ fs=frontend.fs,
280
+ audio_fs=kwargs.get("fs", 16000),
281
+ data_type=kwargs.get("data_type", "sound"),
282
+ tokenizer=tokenizer,
283
+ cache=cfg,
284
+ )
285
+ _is_final = cfg["is_final"] # if data_in is a file or url, set is_final=True
286
+
287
+ time2 = time.perf_counter()
288
+ meta_data["load_data"] = f"{time2 - time1:0.3f}"
289
+ assert len(audio_sample_list) == 1, "batch_size must be set 1"
290
+
291
+ audio_sample = torch.cat((cache["prev_samples"], audio_sample_list[0]))
292
+
293
+ if len(audio_sample) < first_chunk_padding_samples:
294
+ print("key: {}, audio is too short for inference {}".format(key, len(audio_sample)))
295
+
296
+ audio_sample_pre = audio_sample[0 : first_chunk_padding_samples]
297
+ feat_pre, feat_pre_lengths = extract_fbank(
298
+ [audio_sample_pre],
299
+ data_type=kwargs.get("data_type", "sound"),
300
+ frontend=frontend,
301
+ cache=cache["frontend"],
302
+ is_final=False,
303
+ )
304
+
305
+ audio_sample = audio_sample[first_chunk_padding_samples:]
306
+ audio_chunks = int(len(audio_sample) // chunk_stride_samples)
307
+
308
+ for i in range(audio_chunks):
309
+ if i == 0:
310
+ cache["encoder"]["feats"][:, chunk_size[2] :, :] = feat_pre
311
+
312
+ kwargs["is_final"] = False
313
+ audio_sample_i = audio_sample[i * chunk_stride_samples : (i + 1) * chunk_stride_samples]
314
+
315
+ if kwargs["is_final"] and len(audio_sample_i) < 960:
316
+ cache["encoder"]["tail_chunk"] = True
317
+ speech = cache["encoder"]["feats"]
318
+ speech_lengths = torch.tensor([speech.shape[1]], dtype=torch.int64).to(
319
+ speech.device
320
+ )
321
+ else:
322
+ # extract fbank feats
323
+ speech, speech_lengths = extract_fbank(
324
+ [audio_sample_i],
325
+ data_type=kwargs.get("data_type", "sound"),
326
+ frontend=frontend,
327
+ cache=cache["frontend"],
328
+ is_final=kwargs["is_final"],
329
+ )
330
+ time3 = time.perf_counter()
331
+ meta_data["extract_feat"] = f"{time3 - time2:0.3f}"
332
+ meta_data["batch_data_time"] = (
333
+ speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000
334
+ )
335
+
336
+ results_chunk_i = self.generate_chunk(
337
+ speech,
338
+ speech_lengths,
339
+ key=key,
340
+ tokenizer=tokenizer,
341
+ cache=cache,
342
+ frontend=frontend,
343
+ **kwargs,
344
+ )
345
+
346
+ # results_chunk_i must be None when is_final=False
347
+ assert results_chunk_i is None
348
+
349
+ # process tail samples
350
+ tail_audio_sample = audio_sample[ audio_chunks * chunk_stride_samples: ]
351
+ if len(tail_audio_sample) < 960:
352
+ kwargs["is_final"] = _is_final
353
+ cache["encoder"]["tail_chunk"] = True
354
+ speech = cache["encoder"]["feats"]
355
+ speech_lengths = torch.tensor([speech.shape[1]], dtype=torch.int64).to(
356
+ speech.device
357
+ )
358
+ results_chunk_tail = self.generate_chunk(
359
+ speech,
360
+ speech_lengths,
361
+ key=key,
362
+ tokenizer=tokenizer,
363
+ cache=cache,
364
+ frontend=frontend,
365
+ **kwargs,
366
+ )
367
+ elif len(tail_audio_sample) <= first_chunk_padding_samples:
368
+ kwargs["is_final"] = _is_final
369
+ # extract fbank feats
370
+ # cache["encoder"]["tail_chunk"] = True # cannot be true
371
+ speech, speech_lengths = extract_fbank(
372
+ [ tail_audio_sample ],
373
+ data_type=kwargs.get("data_type", "sound"),
374
+ frontend=frontend,
375
+ cache=cache["frontend"],
376
+ is_final=kwargs["is_final"],
377
+ )
378
+ results_chunk_tail = self.generate_chunk(
379
+ speech,
380
+ speech_lengths,
381
+ key=key,
382
+ tokenizer=tokenizer,
383
+ cache=cache,
384
+ frontend=frontend,
385
+ **kwargs,
386
+ )
387
+ elif len(tail_audio_sample) > first_chunk_padding_samples and \
388
+ len(tail_audio_sample) < chunk_stride_samples:
389
+ kwargs["is_final"] = False
390
+ # extract fbank feats
391
+ speech, speech_lengths = extract_fbank(
392
+ [ tail_audio_sample ],
393
+ data_type=kwargs.get("data_type", "sound"),
394
+ frontend=frontend,
395
+ cache=cache["frontend"],
396
+ is_final=kwargs["is_final"],
397
+ )
398
+ results_chunk = self.generate_chunk(
399
+ speech,
400
+ speech_lengths,
401
+ key=key,
402
+ tokenizer=tokenizer,
403
+ cache=cache,
404
+ frontend=frontend,
405
+ **kwargs,
406
+ )
407
+ # results_chunk must be None when is_final=False
408
+ assert results_chunk is None
409
+
410
+ # push tail chunk
411
+ kwargs["is_final"] = _is_final
412
+ cache["encoder"]["tail_chunk"] = True
413
+ speech = cache["encoder"]["feats"]
414
+ speech_lengths = torch.tensor([speech.shape[1]], dtype=torch.int64).to(
415
+ speech.device
416
+ )
417
+ results_chunk_tail = self.generate_chunk(
418
+ speech,
419
+ speech_lengths,
420
+ key=key,
421
+ tokenizer=tokenizer,
422
+ cache=cache,
423
+ frontend=frontend,
424
+ **kwargs,
425
+ )
426
+
427
+ result = results_chunk_tail
428
+
429
+ if _is_final:
430
+ self.init_cache(cache, **kwargs)
431
+
432
+ if kwargs.get("output_dir"):
433
+ if not hasattr(self, "writer"):
434
+ self.writer = DatadirWriter(kwargs.get("output_dir"))
435
+
436
+ return result, meta_data
437
+
438
+ def export(self, **kwargs):
439
+ from .export_meta import export_rebuild_model
440
+
441
+ models = export_rebuild_model(model=self, **kwargs)
442
+ return models
funasr/models/scama/__init__.py ADDED
File without changes
funasr/models/scama/beam_search.py ADDED
@@ -0,0 +1,978 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Beam search module."""
2
+
3
+ from itertools import chain
4
+ import logging
5
+ from typing import Any
6
+ from typing import Dict
7
+ from typing import List
8
+ from typing import NamedTuple
9
+ from typing import Tuple
10
+ from typing import Union
11
+
12
+ import torch
13
+
14
+ from funasr.metrics.common import end_detect
15
+ from funasr.models.transformer.scorers.scorer_interface import PartialScorerInterface
16
+ from funasr.models.transformer.scorers.scorer_interface import ScorerInterface
17
+
18
+
19
+ class Hypothesis(NamedTuple):
20
+ """Hypothesis data type."""
21
+
22
+ yseq: torch.Tensor
23
+ score: Union[float, torch.Tensor] = 0
24
+ scores: Dict[str, Union[float, torch.Tensor]] = dict()
25
+ states: Dict[str, Any] = dict()
26
+
27
+ def asdict(self) -> dict:
28
+ """Convert data to JSON-friendly dict."""
29
+ return self._replace(
30
+ yseq=self.yseq.tolist(),
31
+ score=float(self.score),
32
+ scores={k: float(v) for k, v in self.scores.items()},
33
+ )._asdict()
34
+
35
+
36
+ class BeamSearchScama(torch.nn.Module):
37
+ """Beam search implementation."""
38
+
39
+ def __init__(
40
+ self,
41
+ scorers: Dict[str, ScorerInterface],
42
+ weights: Dict[str, float],
43
+ beam_size: int,
44
+ vocab_size: int,
45
+ sos: int,
46
+ eos: int,
47
+ token_list: List[str] = None,
48
+ pre_beam_ratio: float = 1.5,
49
+ pre_beam_score_key: str = None,
50
+ ):
51
+ """Initialize beam search.
52
+
53
+ Args:
54
+ scorers (dict[str, ScorerInterface]): Dict of decoder modules
55
+ e.g., Decoder, CTCPrefixScorer, LM
56
+ The scorer will be ignored if it is `None`
57
+ weights (dict[str, float]): Dict of weights for each scorers
58
+ The scorer will be ignored if its weight is 0
59
+ beam_size (int): The number of hypotheses kept during search
60
+ vocab_size (int): The number of vocabulary
61
+ sos (int): Start of sequence id
62
+ eos (int): End of sequence id
63
+ token_list (list[str]): List of tokens for debug log
64
+ pre_beam_score_key (str): key of scores to perform pre-beam search
65
+ pre_beam_ratio (float): beam size in the pre-beam search
66
+ will be `int(pre_beam_ratio * beam_size)`
67
+
68
+ """
69
+ super().__init__()
70
+ # set scorers
71
+ self.weights = weights
72
+ self.scorers = dict()
73
+ self.full_scorers = dict()
74
+ self.part_scorers = dict()
75
+ # this module dict is required for recursive cast
76
+ # `self.to(device, dtype)` in `recog.py`
77
+ self.nn_dict = torch.nn.ModuleDict()
78
+ for k, v in scorers.items():
79
+ w = weights.get(k, 0)
80
+ if w == 0 or v is None:
81
+ continue
82
+ assert isinstance(
83
+ v, ScorerInterface
84
+ ), f"{k} ({type(v)}) does not implement ScorerInterface"
85
+ self.scorers[k] = v
86
+ if isinstance(v, PartialScorerInterface):
87
+ self.part_scorers[k] = v
88
+ else:
89
+ self.full_scorers[k] = v
90
+ if isinstance(v, torch.nn.Module):
91
+ self.nn_dict[k] = v
92
+
93
+ # set configurations
94
+ self.sos = sos
95
+ self.eos = eos
96
+ self.token_list = token_list
97
+ self.pre_beam_size = int(pre_beam_ratio * beam_size)
98
+ self.beam_size = beam_size
99
+ self.n_vocab = vocab_size
100
+ if (
101
+ pre_beam_score_key is not None
102
+ and pre_beam_score_key != "full"
103
+ and pre_beam_score_key not in self.full_scorers
104
+ ):
105
+ raise KeyError(f"{pre_beam_score_key} is not found in {self.full_scorers}")
106
+ self.pre_beam_score_key = pre_beam_score_key
107
+ self.do_pre_beam = (
108
+ self.pre_beam_score_key is not None
109
+ and self.pre_beam_size < self.n_vocab
110
+ and len(self.part_scorers) > 0
111
+ )
112
+
113
+ def init_hyp(self, x: torch.Tensor) -> List[Hypothesis]:
114
+ """Get an initial hypothesis data.
115
+
116
+ Args:
117
+ x (torch.Tensor): The encoder output feature
118
+
119
+ Returns:
120
+ Hypothesis: The initial hypothesis.
121
+
122
+ """
123
+ init_states = dict()
124
+ init_scores = dict()
125
+ for k, d in self.scorers.items():
126
+ init_states[k] = d.init_state(x)
127
+ init_scores[k] = 0.0
128
+ return [
129
+ Hypothesis(
130
+ score=0.0,
131
+ scores=init_scores,
132
+ states=init_states,
133
+ yseq=torch.tensor([self.sos], device=x.device),
134
+ )
135
+ ]
136
+
137
+ @staticmethod
138
+ def append_token(xs: torch.Tensor, x: int) -> torch.Tensor:
139
+ """Append new token to prefix tokens.
140
+
141
+ Args:
142
+ xs (torch.Tensor): The prefix token
143
+ x (int): The new token to append
144
+
145
+ Returns:
146
+ torch.Tensor: New tensor contains: xs + [x] with xs.dtype and xs.device
147
+
148
+ """
149
+ x = torch.tensor([x], dtype=xs.dtype, device=xs.device)
150
+ return torch.cat((xs, x))
151
+
152
+ def score_full(
153
+ self,
154
+ hyp: Hypothesis,
155
+ x: torch.Tensor,
156
+ x_mask: torch.Tensor = None,
157
+ pre_acoustic_embeds: torch.Tensor = None,
158
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
159
+ """Score new hypothesis by `self.full_scorers`.
160
+
161
+ Args:
162
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
163
+ x (torch.Tensor): Corresponding input feature
164
+
165
+ Returns:
166
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
167
+ score dict of `hyp` that has string keys of `self.full_scorers`
168
+ and tensor score values of shape: `(self.n_vocab,)`,
169
+ and state dict that has string keys
170
+ and state values of `self.full_scorers`
171
+
172
+ """
173
+ scores = dict()
174
+ states = dict()
175
+ for k, d in self.full_scorers.items():
176
+ scores[k], states[k] = d.score(
177
+ hyp.yseq, hyp.states[k], x, x_mask=x_mask, pre_acoustic_embeds=pre_acoustic_embeds
178
+ )
179
+ return scores, states
180
+
181
+ def score_partial(
182
+ self, hyp: Hypothesis, ids: torch.Tensor, x: torch.Tensor
183
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
184
+ """Score new hypothesis by `self.part_scorers`.
185
+
186
+ Args:
187
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
188
+ ids (torch.Tensor): 1D tensor of new partial tokens to score
189
+ x (torch.Tensor): Corresponding input feature
190
+
191
+ Returns:
192
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
193
+ score dict of `hyp` that has string keys of `self.part_scorers`
194
+ and tensor score values of shape: `(len(ids),)`,
195
+ and state dict that has string keys
196
+ and state values of `self.part_scorers`
197
+
198
+ """
199
+ scores = dict()
200
+ states = dict()
201
+ for k, d in self.part_scorers.items():
202
+ scores[k], states[k] = d.score_partial(hyp.yseq, ids, hyp.states[k], x)
203
+ return scores, states
204
+
205
+ def beam(
206
+ self, weighted_scores: torch.Tensor, ids: torch.Tensor
207
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
208
+ """Compute topk full token ids and partial token ids.
209
+
210
+ Args:
211
+ weighted_scores (torch.Tensor): The weighted sum scores for each tokens.
212
+ Its shape is `(self.n_vocab,)`.
213
+ ids (torch.Tensor): The partial token ids to compute topk
214
+
215
+ Returns:
216
+ Tuple[torch.Tensor, torch.Tensor]:
217
+ The topk full token ids and partial token ids.
218
+ Their shapes are `(self.beam_size,)`
219
+
220
+ """
221
+ # no pre beam performed
222
+ if weighted_scores.size(0) == ids.size(0):
223
+ top_ids = weighted_scores.topk(self.beam_size)[1]
224
+ return top_ids, top_ids
225
+
226
+ # mask pruned in pre-beam not to select in topk
227
+ tmp = weighted_scores[ids]
228
+ weighted_scores[:] = -float("inf")
229
+ weighted_scores[ids] = tmp
230
+ top_ids = weighted_scores.topk(self.beam_size)[1]
231
+ local_ids = weighted_scores[ids].topk(self.beam_size)[1]
232
+ return top_ids, local_ids
233
+
234
+ @staticmethod
235
+ def merge_scores(
236
+ prev_scores: Dict[str, float],
237
+ next_full_scores: Dict[str, torch.Tensor],
238
+ full_idx: int,
239
+ next_part_scores: Dict[str, torch.Tensor],
240
+ part_idx: int,
241
+ ) -> Dict[str, torch.Tensor]:
242
+ """Merge scores for new hypothesis.
243
+
244
+ Args:
245
+ prev_scores (Dict[str, float]):
246
+ The previous hypothesis scores by `self.scorers`
247
+ next_full_scores (Dict[str, torch.Tensor]): scores by `self.full_scorers`
248
+ full_idx (int): The next token id for `next_full_scores`
249
+ next_part_scores (Dict[str, torch.Tensor]):
250
+ scores of partial tokens by `self.part_scorers`
251
+ part_idx (int): The new token id for `next_part_scores`
252
+
253
+ Returns:
254
+ Dict[str, torch.Tensor]: The new score dict.
255
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
256
+ Its values are scalar tensors by the scorers.
257
+
258
+ """
259
+ new_scores = dict()
260
+ for k, v in next_full_scores.items():
261
+ new_scores[k] = prev_scores[k] + v[full_idx]
262
+ for k, v in next_part_scores.items():
263
+ new_scores[k] = prev_scores[k] + v[part_idx]
264
+ return new_scores
265
+
266
+ def merge_states(self, states: Any, part_states: Any, part_idx: int) -> Any:
267
+ """Merge states for new hypothesis.
268
+
269
+ Args:
270
+ states: states of `self.full_scorers`
271
+ part_states: states of `self.part_scorers`
272
+ part_idx (int): The new token id for `part_scores`
273
+
274
+ Returns:
275
+ Dict[str, torch.Tensor]: The new score dict.
276
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
277
+ Its values are states of the scorers.
278
+
279
+ """
280
+ new_states = dict()
281
+ for k, v in states.items():
282
+ new_states[k] = v
283
+ for k, d in self.part_scorers.items():
284
+ new_states[k] = d.select_state(part_states[k], part_idx)
285
+ return new_states
286
+
287
+ def search(
288
+ self,
289
+ running_hyps: List[Hypothesis],
290
+ x: torch.Tensor,
291
+ x_mask: torch.Tensor = None,
292
+ pre_acoustic_embeds: torch.Tensor = None,
293
+ ) -> List[Hypothesis]:
294
+ """Search new tokens for running hypotheses and encoded speech x.
295
+
296
+ Args:
297
+ running_hyps (List[Hypothesis]): Running hypotheses on beam
298
+ x (torch.Tensor): Encoded speech feature (T, D)
299
+
300
+ Returns:
301
+ List[Hypotheses]: Best sorted hypotheses
302
+
303
+ """
304
+ best_hyps = []
305
+ part_ids = torch.arange(self.n_vocab, device=x.device) # no pre-beam
306
+ for hyp in running_hyps:
307
+ # scoring
308
+ weighted_scores = torch.zeros(self.n_vocab, dtype=x.dtype, device=x.device)
309
+ scores, states = self.score_full(
310
+ hyp, x, x_mask=x_mask, pre_acoustic_embeds=pre_acoustic_embeds
311
+ )
312
+ for k in self.full_scorers:
313
+ weighted_scores += self.weights[k] * scores[k]
314
+ # partial scoring
315
+ if self.do_pre_beam:
316
+ pre_beam_scores = (
317
+ weighted_scores
318
+ if self.pre_beam_score_key == "full"
319
+ else scores[self.pre_beam_score_key]
320
+ )
321
+ part_ids = torch.topk(pre_beam_scores, self.pre_beam_size)[1]
322
+ part_scores, part_states = self.score_partial(hyp, part_ids, x)
323
+ for k in self.part_scorers:
324
+ weighted_scores[part_ids] += self.weights[k] * part_scores[k]
325
+ # add previous hyp score
326
+ weighted_scores += hyp.score
327
+
328
+ # update hyps
329
+ for j, part_j in zip(*self.beam(weighted_scores, part_ids)):
330
+ # will be (2 x beam at most)
331
+ best_hyps.append(
332
+ Hypothesis(
333
+ score=weighted_scores[j],
334
+ yseq=self.append_token(hyp.yseq, j),
335
+ scores=self.merge_scores(hyp.scores, scores, j, part_scores, part_j),
336
+ states=self.merge_states(states, part_states, part_j),
337
+ )
338
+ )
339
+
340
+ # sort and prune 2 x beam -> beam
341
+ best_hyps = sorted(best_hyps, key=lambda x: x.score, reverse=True)[
342
+ : min(len(best_hyps), self.beam_size)
343
+ ]
344
+ return best_hyps
345
+
346
+ def forward(
347
+ self,
348
+ x: torch.Tensor,
349
+ scama_mask: torch.Tensor = None,
350
+ pre_acoustic_embeds: torch.Tensor = None,
351
+ maxlenratio: float = 0.0,
352
+ minlenratio: float = 0.0,
353
+ maxlen: int = None,
354
+ minlen: int = 0,
355
+ ) -> List[Hypothesis]:
356
+ """Perform beam search.
357
+
358
+ Args:
359
+ x (torch.Tensor): Encoded speech feature (T, D)
360
+ maxlenratio (float): Input length ratio to obtain max output length.
361
+ If maxlenratio=0.0 (default), it uses a end-detect function
362
+ to automatically find maximum hypothesis lengths
363
+ If maxlenratio<0.0, its absolute value is interpreted
364
+ as a constant max output length.
365
+ minlenratio (float): Input length ratio to obtain min output length.
366
+
367
+ Returns:
368
+ list[Hypothesis]: N-best decoding results
369
+
370
+ """
371
+ if maxlen is None:
372
+ # set length bounds
373
+ if maxlenratio == 0:
374
+ maxlen = x.shape[0]
375
+ elif maxlenratio < 0:
376
+ maxlen = -1 * int(maxlenratio)
377
+ else:
378
+ maxlen = max(1, int(maxlenratio * x.size(0)))
379
+ minlen = int(minlenratio * x.size(0))
380
+
381
+ logging.info("decoder input length: " + str(x.shape[0]))
382
+ logging.info("max output length: " + str(maxlen))
383
+ logging.info("min output length: " + str(minlen))
384
+
385
+ # main loop of prefix search
386
+ running_hyps = self.init_hyp(x)
387
+ ended_hyps = []
388
+ for i in range(maxlen):
389
+ logging.debug("position " + str(i))
390
+ mask_enc = None
391
+ if scama_mask is not None:
392
+ token_num_predictor = scama_mask.size(1)
393
+ token_id_slice = min(i, token_num_predictor - 1)
394
+ mask_enc = scama_mask[:, token_id_slice : token_id_slice + 1, :]
395
+ # if mask_enc.size(1) == 0:
396
+ # mask_enc = scama_mask[:, -2:-1, :]
397
+ # # mask_enc = torch.zeros_like(mask_enc)
398
+ pre_acoustic_embeds_cur = None
399
+ if pre_acoustic_embeds is not None:
400
+ b, t, d = pre_acoustic_embeds.size()
401
+ pad = torch.zeros((b, 1, d), dtype=pre_acoustic_embeds.dtype).to(
402
+ device=pre_acoustic_embeds.device
403
+ )
404
+ pre_acoustic_embeds = torch.cat((pre_acoustic_embeds, pad), dim=1)
405
+ token_id_slice = min(i, t)
406
+ pre_acoustic_embeds_cur = pre_acoustic_embeds[
407
+ :, token_id_slice : token_id_slice + 1, :
408
+ ]
409
+
410
+ best = self.search(
411
+ running_hyps, x, x_mask=mask_enc, pre_acoustic_embeds=pre_acoustic_embeds_cur
412
+ )
413
+ # post process of one iteration
414
+ running_hyps = self.post_process(i, maxlen, maxlenratio, best, ended_hyps)
415
+ # end detection
416
+ if maxlenratio == 0.0 and end_detect([h.asdict() for h in ended_hyps], i):
417
+ logging.info(f"end detected at {i}")
418
+ break
419
+ if len(running_hyps) == 0:
420
+ logging.info("no hypothesis. Finish decoding.")
421
+ break
422
+ else:
423
+ logging.debug(f"remained hypotheses: {len(running_hyps)}")
424
+
425
+ nbest_hyps = sorted(ended_hyps, key=lambda x: x.score, reverse=True)
426
+ # check the number of hypotheses reaching to eos
427
+ if len(nbest_hyps) == 0:
428
+ logging.warning(
429
+ "there is no N-best results, perform recognition " "again with smaller minlenratio."
430
+ )
431
+ return (
432
+ []
433
+ if minlenratio < 0.1
434
+ else self.forward(x, maxlenratio, max(0.0, minlenratio - 0.1))
435
+ )
436
+
437
+ # report the best result
438
+ for x in nbest_hyps:
439
+ yseq = "".join([self.token_list[x] for x in x.yseq])
440
+ logging.debug("nbest: y: {}, yseq: {}, score: {}".format(x.yseq, yseq, x.score))
441
+ best = nbest_hyps[0]
442
+ for k, v in best.scores.items():
443
+ logging.info(f"{v:6.2f} * {self.weights[k]:3} = {v * self.weights[k]:6.2f} for {k}")
444
+ logging.info(f"total log probability: {best.score:.2f}")
445
+ logging.info(f"normalized log probability: {best.score / len(best.yseq):.2f}")
446
+ logging.info(f"total number of ended hypotheses: {len(nbest_hyps)}")
447
+ if self.token_list is not None:
448
+ logging.info(
449
+ "best hypo: " + "".join([self.token_list[x] for x in best.yseq[1:-1]]) + "\n"
450
+ )
451
+ return nbest_hyps
452
+
453
+ def post_process(
454
+ self,
455
+ i: int,
456
+ maxlen: int,
457
+ maxlenratio: float,
458
+ running_hyps: List[Hypothesis],
459
+ ended_hyps: List[Hypothesis],
460
+ ) -> List[Hypothesis]:
461
+ """Perform post-processing of beam search iterations.
462
+
463
+ Args:
464
+ i (int): The length of hypothesis tokens.
465
+ maxlen (int): The maximum length of tokens in beam search.
466
+ maxlenratio (int): The maximum length ratio in beam search.
467
+ running_hyps (List[Hypothesis]): The running hypotheses in beam search.
468
+ ended_hyps (List[Hypothesis]): The ended hypotheses in beam search.
469
+
470
+ Returns:
471
+ List[Hypothesis]: The new running hypotheses.
472
+
473
+ """
474
+ logging.debug(f"the number of running hypotheses: {len(running_hyps)}")
475
+ if self.token_list is not None:
476
+ logging.debug(
477
+ "best hypo: " + "".join([self.token_list[x] for x in running_hyps[0].yseq[1:]])
478
+ )
479
+ # add eos in the final loop to avoid that there are no ended hyps
480
+ if i == maxlen - 1:
481
+ logging.info("adding <eos> in the last position in the loop")
482
+ running_hyps = [
483
+ h._replace(yseq=self.append_token(h.yseq, self.eos)) for h in running_hyps
484
+ ]
485
+
486
+ # add ended hypotheses to a final list, and removed them from current hypotheses
487
+ # (this will be a problem, number of hyps < beam)
488
+ remained_hyps = []
489
+ for hyp in running_hyps:
490
+ if hyp.yseq[-1] == self.eos:
491
+ # e.g., Word LM needs to add final <eos> score
492
+ for k, d in chain(self.full_scorers.items(), self.part_scorers.items()):
493
+ s = d.final_score(hyp.states[k])
494
+ hyp.scores[k] += s
495
+ hyp = hyp._replace(score=hyp.score + self.weights[k] * s)
496
+ ended_hyps.append(hyp)
497
+ else:
498
+ remained_hyps.append(hyp)
499
+ return remained_hyps
500
+
501
+
502
+ class BeamSearchScamaStreaming(torch.nn.Module):
503
+ """Beam search implementation."""
504
+
505
+ def __init__(
506
+ self,
507
+ scorers: Dict[str, ScorerInterface],
508
+ weights: Dict[str, float],
509
+ beam_size: int,
510
+ vocab_size: int,
511
+ sos: int,
512
+ eos: int,
513
+ token_list: List[str] = None,
514
+ pre_beam_ratio: float = 1.5,
515
+ pre_beam_score_key: str = None,
516
+ ):
517
+ """Initialize beam search.
518
+
519
+ Args:
520
+ scorers (dict[str, ScorerInterface]): Dict of decoder modules
521
+ e.g., Decoder, CTCPrefixScorer, LM
522
+ The scorer will be ignored if it is `None`
523
+ weights (dict[str, float]): Dict of weights for each scorers
524
+ The scorer will be ignored if its weight is 0
525
+ beam_size (int): The number of hypotheses kept during search
526
+ vocab_size (int): The number of vocabulary
527
+ sos (int): Start of sequence id
528
+ eos (int): End of sequence id
529
+ token_list (list[str]): List of tokens for debug log
530
+ pre_beam_score_key (str): key of scores to perform pre-beam search
531
+ pre_beam_ratio (float): beam size in the pre-beam search
532
+ will be `int(pre_beam_ratio * beam_size)`
533
+
534
+ """
535
+ super().__init__()
536
+ # set scorers
537
+ self.weights = weights
538
+ self.scorers = dict()
539
+ self.full_scorers = dict()
540
+ self.part_scorers = dict()
541
+ # this module dict is required for recursive cast
542
+ # `self.to(device, dtype)` in `recog.py`
543
+ self.nn_dict = torch.nn.ModuleDict()
544
+ for k, v in scorers.items():
545
+ w = weights.get(k, 0)
546
+ if w == 0 or v is None:
547
+ continue
548
+ assert isinstance(
549
+ v, ScorerInterface
550
+ ), f"{k} ({type(v)}) does not implement ScorerInterface"
551
+ self.scorers[k] = v
552
+ if isinstance(v, PartialScorerInterface):
553
+ self.part_scorers[k] = v
554
+ else:
555
+ self.full_scorers[k] = v
556
+ if isinstance(v, torch.nn.Module):
557
+ self.nn_dict[k] = v
558
+
559
+ # set configurations
560
+ self.sos = sos
561
+ self.eos = eos
562
+ self.token_list = token_list
563
+ self.pre_beam_size = int(pre_beam_ratio * beam_size)
564
+ self.beam_size = beam_size
565
+ self.n_vocab = vocab_size
566
+ if (
567
+ pre_beam_score_key is not None
568
+ and pre_beam_score_key != "full"
569
+ and pre_beam_score_key not in self.full_scorers
570
+ ):
571
+ raise KeyError(f"{pre_beam_score_key} is not found in {self.full_scorers}")
572
+ self.pre_beam_score_key = pre_beam_score_key
573
+ self.do_pre_beam = (
574
+ self.pre_beam_score_key is not None
575
+ and self.pre_beam_size < self.n_vocab
576
+ and len(self.part_scorers) > 0
577
+ )
578
+
579
+ def init_hyp(self, x) -> List[Hypothesis]:
580
+ """Get an initial hypothesis data.
581
+
582
+ Args:
583
+ x (torch.Tensor): The encoder output feature
584
+
585
+ Returns:
586
+ Hypothesis: The initial hypothesis.
587
+
588
+ """
589
+ init_states = dict()
590
+ init_scores = dict()
591
+ for k, d in self.scorers.items():
592
+ init_states[k] = d.init_state(x)
593
+ init_scores[k] = 0.0
594
+ return [
595
+ Hypothesis(
596
+ score=0.0,
597
+ scores=init_scores,
598
+ states=init_states,
599
+ yseq=torch.tensor([self.sos], device=x.device),
600
+ )
601
+ ]
602
+
603
+ @staticmethod
604
+ def append_token(xs: torch.Tensor, x: int) -> torch.Tensor:
605
+ """Append new token to prefix tokens.
606
+
607
+ Args:
608
+ xs (torch.Tensor): The prefix token
609
+ x (int): The new token to append
610
+
611
+ Returns:
612
+ torch.Tensor: New tensor contains: xs + [x] with xs.dtype and xs.device
613
+
614
+ """
615
+ x = torch.tensor([x], dtype=xs.dtype, device=xs.device)
616
+ return torch.cat((xs, x))
617
+
618
+ def score_full(
619
+ self,
620
+ hyp: Hypothesis,
621
+ x: torch.Tensor,
622
+ x_mask: torch.Tensor = None,
623
+ pre_acoustic_embeds: torch.Tensor = None,
624
+ cache: dict = {},
625
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
626
+ """Score new hypothesis by `self.full_scorers`.
627
+
628
+ Args:
629
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
630
+ x (torch.Tensor): Corresponding input feature
631
+
632
+ Returns:
633
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
634
+ score dict of `hyp` that has string keys of `self.full_scorers`
635
+ and tensor score values of shape: `(self.n_vocab,)`,
636
+ and state dict that has string keys
637
+ and state values of `self.full_scorers`
638
+
639
+ """
640
+ scores = dict()
641
+ states = dict()
642
+ for k, d in self.full_scorers.items():
643
+ scores[k], states[k] = d.score(
644
+ hyp.yseq,
645
+ hyp.states[k],
646
+ x,
647
+ x_mask=x_mask,
648
+ pre_acoustic_embeds=pre_acoustic_embeds,
649
+ cache=cache,
650
+ )
651
+ return scores, states
652
+
653
+ def score_partial(
654
+ self, hyp: Hypothesis, ids: torch.Tensor, x: torch.Tensor
655
+ ) -> Tuple[Dict[str, torch.Tensor], Dict[str, Any]]:
656
+ """Score new hypothesis by `self.part_scorers`.
657
+
658
+ Args:
659
+ hyp (Hypothesis): Hypothesis with prefix tokens to score
660
+ ids (torch.Tensor): 1D tensor of new partial tokens to score
661
+ x (torch.Tensor): Corresponding input feature
662
+
663
+ Returns:
664
+ Tuple[Dict[str, torch.Tensor], Dict[str, Any]]: Tuple of
665
+ score dict of `hyp` that has string keys of `self.part_scorers`
666
+ and tensor score values of shape: `(len(ids),)`,
667
+ and state dict that has string keys
668
+ and state values of `self.part_scorers`
669
+
670
+ """
671
+ scores = dict()
672
+ states = dict()
673
+ for k, d in self.part_scorers.items():
674
+ scores[k], states[k] = d.score_partial(hyp.yseq, ids, hyp.states[k], x)
675
+ return scores, states
676
+
677
+ def beam(
678
+ self, weighted_scores: torch.Tensor, ids: torch.Tensor
679
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
680
+ """Compute topk full token ids and partial token ids.
681
+
682
+ Args:
683
+ weighted_scores (torch.Tensor): The weighted sum scores for each tokens.
684
+ Its shape is `(self.n_vocab,)`.
685
+ ids (torch.Tensor): The partial token ids to compute topk
686
+
687
+ Returns:
688
+ Tuple[torch.Tensor, torch.Tensor]:
689
+ The topk full token ids and partial token ids.
690
+ Their shapes are `(self.beam_size,)`
691
+
692
+ """
693
+ # no pre beam performed
694
+ if weighted_scores.size(0) == ids.size(0):
695
+ top_ids = weighted_scores.topk(self.beam_size)[1]
696
+ return top_ids, top_ids
697
+
698
+ # mask pruned in pre-beam not to select in topk
699
+ tmp = weighted_scores[ids]
700
+ weighted_scores[:] = -float("inf")
701
+ weighted_scores[ids] = tmp
702
+ top_ids = weighted_scores.topk(self.beam_size)[1]
703
+ local_ids = weighted_scores[ids].topk(self.beam_size)[1]
704
+ return top_ids, local_ids
705
+
706
+ @staticmethod
707
+ def merge_scores(
708
+ prev_scores: Dict[str, float],
709
+ next_full_scores: Dict[str, torch.Tensor],
710
+ full_idx: int,
711
+ next_part_scores: Dict[str, torch.Tensor],
712
+ part_idx: int,
713
+ ) -> Dict[str, torch.Tensor]:
714
+ """Merge scores for new hypothesis.
715
+
716
+ Args:
717
+ prev_scores (Dict[str, float]):
718
+ The previous hypothesis scores by `self.scorers`
719
+ next_full_scores (Dict[str, torch.Tensor]): scores by `self.full_scorers`
720
+ full_idx (int): The next token id for `next_full_scores`
721
+ next_part_scores (Dict[str, torch.Tensor]):
722
+ scores of partial tokens by `self.part_scorers`
723
+ part_idx (int): The new token id for `next_part_scores`
724
+
725
+ Returns:
726
+ Dict[str, torch.Tensor]: The new score dict.
727
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
728
+ Its values are scalar tensors by the scorers.
729
+
730
+ """
731
+ new_scores = dict()
732
+ for k, v in next_full_scores.items():
733
+ new_scores[k] = prev_scores[k] + v[full_idx]
734
+ for k, v in next_part_scores.items():
735
+ new_scores[k] = prev_scores[k] + v[part_idx]
736
+ return new_scores
737
+
738
+ def merge_states(self, states: Any, part_states: Any, part_idx: int) -> Any:
739
+ """Merge states for new hypothesis.
740
+
741
+ Args:
742
+ states: states of `self.full_scorers`
743
+ part_states: states of `self.part_scorers`
744
+ part_idx (int): The new token id for `part_scores`
745
+
746
+ Returns:
747
+ Dict[str, torch.Tensor]: The new score dict.
748
+ Its keys are names of `self.full_scorers` and `self.part_scorers`.
749
+ Its values are states of the scorers.
750
+
751
+ """
752
+ new_states = dict()
753
+ for k, v in states.items():
754
+ new_states[k] = v
755
+ for k, d in self.part_scorers.items():
756
+ new_states[k] = d.select_state(part_states[k], part_idx)
757
+ return new_states
758
+
759
+ def search(
760
+ self,
761
+ running_hyps: List[Hypothesis],
762
+ x: torch.Tensor,
763
+ x_mask: torch.Tensor = None,
764
+ pre_acoustic_embeds: torch.Tensor = None,
765
+ cache: dict = {},
766
+ ) -> List[Hypothesis]:
767
+ """Search new tokens for running hypotheses and encoded speech x.
768
+
769
+ Args:
770
+ running_hyps (List[Hypothesis]): Running hypotheses on beam
771
+ x (torch.Tensor): Encoded speech feature (T, D)
772
+
773
+ Returns:
774
+ List[Hypotheses]: Best sorted hypotheses
775
+
776
+ """
777
+ best_hyps = []
778
+ part_ids = torch.arange(self.n_vocab, device=x.device) # no pre-beam
779
+ for hyp in running_hyps:
780
+ # scoring
781
+ weighted_scores = torch.zeros(self.n_vocab, dtype=x.dtype, device=x.device)
782
+ scores, states = self.score_full(
783
+ hyp, x, x_mask=x_mask, pre_acoustic_embeds=pre_acoustic_embeds, cache=cache
784
+ )
785
+ for k in self.full_scorers:
786
+ weighted_scores += self.weights[k] * scores[k]
787
+ # partial scoring
788
+ if self.do_pre_beam:
789
+ pre_beam_scores = (
790
+ weighted_scores
791
+ if self.pre_beam_score_key == "full"
792
+ else scores[self.pre_beam_score_key]
793
+ )
794
+ part_ids = torch.topk(pre_beam_scores, self.pre_beam_size)[1]
795
+ part_scores, part_states = self.score_partial(hyp, part_ids, x)
796
+ for k in self.part_scorers:
797
+ weighted_scores[part_ids] += self.weights[k] * part_scores[k]
798
+ # add previous hyp score
799
+ weighted_scores += hyp.score
800
+
801
+ # update hyps
802
+ for j, part_j in zip(*self.beam(weighted_scores, part_ids)):
803
+ # will be (2 x beam at most)
804
+ best_hyps.append(
805
+ Hypothesis(
806
+ score=weighted_scores[j],
807
+ yseq=self.append_token(hyp.yseq, j),
808
+ scores=self.merge_scores(hyp.scores, scores, j, part_scores, part_j),
809
+ states=self.merge_states(states, part_states, part_j),
810
+ )
811
+ )
812
+
813
+ # sort and prune 2 x beam -> beam
814
+ best_hyps = sorted(best_hyps, key=lambda x: x.score, reverse=True)[
815
+ : min(len(best_hyps), self.beam_size)
816
+ ]
817
+ return best_hyps
818
+
819
+ def forward(
820
+ self,
821
+ x: torch.Tensor,
822
+ scama_mask: torch.Tensor = None,
823
+ pre_acoustic_embeds: torch.Tensor = None,
824
+ maxlenratio: float = 0.0,
825
+ minlenratio: float = 0.0,
826
+ maxlen: int = None,
827
+ minlen: int = 0,
828
+ cache: dict = {},
829
+ ) -> List[Hypothesis]:
830
+ """Perform beam search.
831
+
832
+ Args:
833
+ x (torch.Tensor): Encoded speech feature (T, D)
834
+ maxlenratio (float): Input length ratio to obtain max output length.
835
+ If maxlenratio=0.0 (default), it uses a end-detect function
836
+ to automatically find maximum hypothesis lengths
837
+ If maxlenratio<0.0, its absolute value is interpreted
838
+ as a constant max output length.
839
+ minlenratio (float): Input length ratio to obtain min output length.
840
+
841
+ Returns:
842
+ list[Hypothesis]: N-best decoding results
843
+
844
+ """
845
+ if maxlen is None:
846
+ # set length bounds
847
+ if maxlenratio == 0:
848
+ maxlen = x.shape[0]
849
+ elif maxlenratio < 0:
850
+ maxlen = -1 * int(maxlenratio)
851
+ else:
852
+ maxlen = max(1, int(maxlenratio * x.size(0)))
853
+ minlen = int(minlenratio * x.size(0))
854
+
855
+ logging.info("decoder input length: " + str(x.shape[0]))
856
+ logging.info("max output length: " + str(maxlen))
857
+ logging.info("min output length: " + str(minlen))
858
+
859
+ # main loop of prefix search
860
+ # running_hyps = self.init_hyp(x)
861
+ running_hyps = cache["running_hyps"]
862
+ ended_hyps = []
863
+ for i in range(maxlen):
864
+ logging.debug("position " + str(i))
865
+ mask_enc = None
866
+ # if scama_mask is not None:
867
+ # token_num_predictor = scama_mask.size(1)
868
+ # token_id_slice = min(i, token_num_predictor-1)
869
+ # mask_enc = scama_mask[:, token_id_slice:token_id_slice+1, :]
870
+ # # if mask_enc.size(1) == 0:
871
+ # # mask_enc = scama_mask[:, -2:-1, :]
872
+ # # # mask_enc = torch.zeros_like(mask_enc)
873
+ pre_acoustic_embeds_cur = None
874
+ if pre_acoustic_embeds is not None:
875
+ b, t, d = pre_acoustic_embeds.size()
876
+ pad = torch.zeros((b, 1, d), dtype=pre_acoustic_embeds.dtype).to(
877
+ device=pre_acoustic_embeds.device
878
+ )
879
+ pre_acoustic_embeds = torch.cat((pre_acoustic_embeds, pad), dim=1)
880
+ token_id_slice = min(i, t)
881
+ pre_acoustic_embeds_cur = pre_acoustic_embeds[
882
+ :, token_id_slice : token_id_slice + 1, :
883
+ ]
884
+
885
+ best = self.search(
886
+ running_hyps,
887
+ x,
888
+ x_mask=mask_enc,
889
+ pre_acoustic_embeds=pre_acoustic_embeds_cur,
890
+ cache=cache["decoder"],
891
+ )
892
+ # post process of one iteration
893
+ running_hyps = self.post_process(i, maxlen, maxlenratio, best, ended_hyps)
894
+ # end detection
895
+ if maxlenratio == 0.0 and end_detect([h.asdict() for h in ended_hyps], i):
896
+ logging.info(f"end detected at {i}")
897
+ break
898
+ if len(running_hyps) == 0:
899
+ logging.info("no hypothesis. Finish decoding.")
900
+ break
901
+ else:
902
+ logging.debug(f"remained hypotheses: {len(running_hyps)}")
903
+
904
+ nbest_hyps = sorted(ended_hyps, key=lambda x: x.score, reverse=True)
905
+ # check the number of hypotheses reaching to eos
906
+ if len(nbest_hyps) == 0:
907
+ logging.warning(
908
+ "there is no N-best results, perform recognition " "again with smaller minlenratio."
909
+ )
910
+ return (
911
+ []
912
+ if minlenratio < 0.1
913
+ else self.forward(x, maxlenratio, max(0.0, minlenratio - 0.1))
914
+ )
915
+
916
+ # report the best result
917
+ for x in nbest_hyps:
918
+ yseq = "".join([self.token_list[x] for x in x.yseq])
919
+ logging.debug("nbest: y: {}, yseq: {}, score: {}".format(x.yseq, yseq, x.score))
920
+ best = nbest_hyps[0]
921
+ for k, v in best.scores.items():
922
+ logging.info(f"{v:6.2f} * {self.weights[k]:3} = {v * self.weights[k]:6.2f} for {k}")
923
+ logging.info(f"total log probability: {best.score:.2f}")
924
+ logging.info(f"normalized log probability: {best.score / len(best.yseq):.2f}")
925
+ logging.info(f"total number of ended hypotheses: {len(nbest_hyps)}")
926
+ if self.token_list is not None:
927
+ logging.info(
928
+ "best hypo: " + "".join([self.token_list[x] for x in best.yseq[1:-1]]) + "\n"
929
+ )
930
+ return nbest_hyps
931
+
932
+ def post_process(
933
+ self,
934
+ i: int,
935
+ maxlen: int,
936
+ maxlenratio: float,
937
+ running_hyps: List[Hypothesis],
938
+ ended_hyps: List[Hypothesis],
939
+ ) -> List[Hypothesis]:
940
+ """Perform post-processing of beam search iterations.
941
+
942
+ Args:
943
+ i (int): The length of hypothesis tokens.
944
+ maxlen (int): The maximum length of tokens in beam search.
945
+ maxlenratio (int): The maximum length ratio in beam search.
946
+ running_hyps (List[Hypothesis]): The running hypotheses in beam search.
947
+ ended_hyps (List[Hypothesis]): The ended hypotheses in beam search.
948
+
949
+ Returns:
950
+ List[Hypothesis]: The new running hypotheses.
951
+
952
+ """
953
+ logging.debug(f"the number of running hypotheses: {len(running_hyps)}")
954
+ if self.token_list is not None:
955
+ logging.debug(
956
+ "best hypo: " + "".join([self.token_list[x] for x in running_hyps[0].yseq[1:]])
957
+ )
958
+ # add eos in the final loop to avoid that there are no ended hyps
959
+ if i == maxlen - 1:
960
+ logging.info("adding <eos> in the last position in the loop")
961
+ running_hyps = [
962
+ h._replace(yseq=self.append_token(h.yseq, self.eos)) for h in running_hyps
963
+ ]
964
+
965
+ # add ended hypotheses to a final list, and removed them from current hypotheses
966
+ # (this will be a problem, number of hyps < beam)
967
+ remained_hyps = []
968
+ for hyp in running_hyps:
969
+ if hyp.yseq[-1] == self.eos:
970
+ # e.g., Word LM needs to add final <eos> score
971
+ for k, d in chain(self.full_scorers.items(), self.part_scorers.items()):
972
+ s = d.final_score(hyp.states[k])
973
+ hyp.scores[k] += s
974
+ hyp = hyp._replace(score=hyp.score + self.weights[k] * s)
975
+ ended_hyps.append(hyp)
976
+ else:
977
+ remained_hyps.append(hyp)
978
+ return remained_hyps
funasr/models/scama/chunk_utilis.py ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import numpy as np
4
+ import torch.nn.functional as F
5
+
6
+ from funasr.models.scama.utils import sequence_mask
7
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
8
+
9
+
10
+ class overlap_chunk:
11
+ """
12
+ Author: Speech Lab of DAMO Academy, Alibaba Group
13
+ San-m: Memory equipped self-attention for end-to-end speech recognition
14
+ https://arxiv.org/abs/2006.01713
15
+
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ chunk_size: tuple = (16,),
21
+ stride: tuple = (10,),
22
+ pad_left: tuple = (0,),
23
+ encoder_att_look_back_factor: tuple = (1,),
24
+ shfit_fsmn: int = 0,
25
+ decoder_att_look_back_factor: tuple = (1,),
26
+ ):
27
+
28
+ pad_left = self.check_chunk_size_args(chunk_size, pad_left)
29
+ encoder_att_look_back_factor = self.check_chunk_size_args(
30
+ chunk_size, encoder_att_look_back_factor
31
+ )
32
+ decoder_att_look_back_factor = self.check_chunk_size_args(
33
+ chunk_size, decoder_att_look_back_factor
34
+ )
35
+ (
36
+ self.chunk_size,
37
+ self.stride,
38
+ self.pad_left,
39
+ self.encoder_att_look_back_factor,
40
+ self.decoder_att_look_back_factor,
41
+ ) = (
42
+ chunk_size,
43
+ stride,
44
+ pad_left,
45
+ encoder_att_look_back_factor,
46
+ decoder_att_look_back_factor,
47
+ )
48
+ self.shfit_fsmn = shfit_fsmn
49
+ self.x_add_mask = None
50
+ self.x_rm_mask = None
51
+ self.x_len = None
52
+ self.mask_shfit_chunk = None
53
+ self.mask_chunk_predictor = None
54
+ self.mask_att_chunk_encoder = None
55
+ self.mask_shift_att_chunk_decoder = None
56
+ self.chunk_outs = None
57
+ (
58
+ self.chunk_size_cur,
59
+ self.stride_cur,
60
+ self.pad_left_cur,
61
+ self.encoder_att_look_back_factor_cur,
62
+ self.chunk_size_pad_shift_cur,
63
+ ) = (None, None, None, None, None)
64
+
65
+ def check_chunk_size_args(self, chunk_size, x):
66
+ if len(x) < len(chunk_size):
67
+ x = [x[0] for i in chunk_size]
68
+ return x
69
+
70
+ def get_chunk_size(self, ind: int = 0):
71
+ # with torch.no_grad:
72
+ chunk_size, stride, pad_left, encoder_att_look_back_factor, decoder_att_look_back_factor = (
73
+ self.chunk_size[ind],
74
+ self.stride[ind],
75
+ self.pad_left[ind],
76
+ self.encoder_att_look_back_factor[ind],
77
+ self.decoder_att_look_back_factor[ind],
78
+ )
79
+ (
80
+ self.chunk_size_cur,
81
+ self.stride_cur,
82
+ self.pad_left_cur,
83
+ self.encoder_att_look_back_factor_cur,
84
+ self.chunk_size_pad_shift_cur,
85
+ self.decoder_att_look_back_factor_cur,
86
+ ) = (
87
+ chunk_size,
88
+ stride,
89
+ pad_left,
90
+ encoder_att_look_back_factor,
91
+ chunk_size + self.shfit_fsmn,
92
+ decoder_att_look_back_factor,
93
+ )
94
+ return (
95
+ self.chunk_size_cur,
96
+ self.stride_cur,
97
+ self.pad_left_cur,
98
+ self.encoder_att_look_back_factor_cur,
99
+ self.chunk_size_pad_shift_cur,
100
+ )
101
+
102
+ def random_choice(self, training=True, decoding_ind=None):
103
+ chunk_num = len(self.chunk_size)
104
+ ind = 0
105
+ if training and chunk_num > 1:
106
+ ind = torch.randint(0, chunk_num, ()).cpu().item()
107
+ if not training and decoding_ind is not None:
108
+ ind = int(decoding_ind)
109
+
110
+ return ind
111
+
112
+ def gen_chunk_mask(self, x_len, ind=0, num_units=1, num_units_predictor=1):
113
+
114
+ with torch.no_grad():
115
+ x_len = x_len.cpu().numpy()
116
+ x_len_max = x_len.max()
117
+
118
+ chunk_size, stride, pad_left, encoder_att_look_back_factor, chunk_size_pad_shift = (
119
+ self.get_chunk_size(ind)
120
+ )
121
+ shfit_fsmn = self.shfit_fsmn
122
+ pad_right = chunk_size - stride - pad_left
123
+
124
+ chunk_num_batch = np.ceil(x_len / stride).astype(np.int32)
125
+ x_len_chunk = (
126
+ (chunk_num_batch - 1) * chunk_size_pad_shift
127
+ + shfit_fsmn
128
+ + pad_left
129
+ + 0
130
+ + x_len
131
+ - (chunk_num_batch - 1) * stride
132
+ )
133
+ x_len_chunk = x_len_chunk.astype(x_len.dtype)
134
+ x_len_chunk_max = x_len_chunk.max()
135
+
136
+ chunk_num = int(math.ceil(x_len_max / stride))
137
+ dtype = np.int32
138
+ max_len_for_x_mask_tmp = max(chunk_size, x_len_max + pad_left)
139
+ x_add_mask = np.zeros([0, max_len_for_x_mask_tmp], dtype=dtype)
140
+ x_rm_mask = np.zeros([max_len_for_x_mask_tmp, 0], dtype=dtype)
141
+ mask_shfit_chunk = np.zeros([0, num_units], dtype=dtype)
142
+ mask_chunk_predictor = np.zeros([0, num_units_predictor], dtype=dtype)
143
+ mask_shift_att_chunk_decoder = np.zeros([0, 1], dtype=dtype)
144
+ mask_att_chunk_encoder = np.zeros([0, chunk_num * chunk_size_pad_shift], dtype=dtype)
145
+ for chunk_ids in range(chunk_num):
146
+ # x_mask add
147
+ fsmn_padding = np.zeros((shfit_fsmn, max_len_for_x_mask_tmp), dtype=dtype)
148
+ x_mask_cur = np.diag(np.ones(chunk_size, dtype=np.float32))
149
+ x_mask_pad_left = np.zeros((chunk_size, chunk_ids * stride), dtype=dtype)
150
+ x_mask_pad_right = np.zeros((chunk_size, max_len_for_x_mask_tmp), dtype=dtype)
151
+ x_cur_pad = np.concatenate([x_mask_pad_left, x_mask_cur, x_mask_pad_right], axis=1)
152
+ x_cur_pad = x_cur_pad[:chunk_size, :max_len_for_x_mask_tmp]
153
+ x_add_mask_fsmn = np.concatenate([fsmn_padding, x_cur_pad], axis=0)
154
+ x_add_mask = np.concatenate([x_add_mask, x_add_mask_fsmn], axis=0)
155
+
156
+ # x_mask rm
157
+ fsmn_padding = np.zeros((max_len_for_x_mask_tmp, shfit_fsmn), dtype=dtype)
158
+ padding_mask_left = np.zeros((max_len_for_x_mask_tmp, pad_left), dtype=dtype)
159
+ padding_mask_right = np.zeros((max_len_for_x_mask_tmp, pad_right), dtype=dtype)
160
+ x_mask_cur = np.diag(np.ones(stride, dtype=dtype))
161
+ x_mask_cur_pad_top = np.zeros((chunk_ids * stride, stride), dtype=dtype)
162
+ x_mask_cur_pad_bottom = np.zeros((max_len_for_x_mask_tmp, stride), dtype=dtype)
163
+ x_rm_mask_cur = np.concatenate(
164
+ [x_mask_cur_pad_top, x_mask_cur, x_mask_cur_pad_bottom], axis=0
165
+ )
166
+ x_rm_mask_cur = x_rm_mask_cur[:max_len_for_x_mask_tmp, :stride]
167
+ x_rm_mask_cur_fsmn = np.concatenate(
168
+ [fsmn_padding, padding_mask_left, x_rm_mask_cur, padding_mask_right], axis=1
169
+ )
170
+ x_rm_mask = np.concatenate([x_rm_mask, x_rm_mask_cur_fsmn], axis=1)
171
+
172
+ # fsmn_padding_mask
173
+ pad_shfit_mask = np.zeros([shfit_fsmn, num_units], dtype=dtype)
174
+ ones_1 = np.ones([chunk_size, num_units], dtype=dtype)
175
+ mask_shfit_chunk_cur = np.concatenate([pad_shfit_mask, ones_1], axis=0)
176
+ mask_shfit_chunk = np.concatenate([mask_shfit_chunk, mask_shfit_chunk_cur], axis=0)
177
+
178
+ # predictor mask
179
+ zeros_1 = np.zeros([shfit_fsmn + pad_left, num_units_predictor], dtype=dtype)
180
+ ones_2 = np.ones([stride, num_units_predictor], dtype=dtype)
181
+ zeros_3 = np.zeros(
182
+ [chunk_size - stride - pad_left, num_units_predictor], dtype=dtype
183
+ )
184
+ ones_zeros = np.concatenate([ones_2, zeros_3], axis=0)
185
+ mask_chunk_predictor_cur = np.concatenate([zeros_1, ones_zeros], axis=0)
186
+ mask_chunk_predictor = np.concatenate(
187
+ [mask_chunk_predictor, mask_chunk_predictor_cur], axis=0
188
+ )
189
+
190
+ # encoder att mask
191
+ zeros_1_top = np.zeros([shfit_fsmn, chunk_num * chunk_size_pad_shift], dtype=dtype)
192
+
193
+ zeros_2_num = max(chunk_ids - encoder_att_look_back_factor, 0)
194
+ zeros_2 = np.zeros([chunk_size, zeros_2_num * chunk_size_pad_shift], dtype=dtype)
195
+
196
+ encoder_att_look_back_num = max(chunk_ids - zeros_2_num, 0)
197
+ zeros_2_left = np.zeros([chunk_size, shfit_fsmn], dtype=dtype)
198
+ ones_2_mid = np.ones([stride, stride], dtype=dtype)
199
+ zeros_2_bottom = np.zeros([chunk_size - stride, stride], dtype=dtype)
200
+ zeros_2_right = np.zeros([chunk_size, chunk_size - stride], dtype=dtype)
201
+ ones_2 = np.concatenate([ones_2_mid, zeros_2_bottom], axis=0)
202
+ ones_2 = np.concatenate([zeros_2_left, ones_2, zeros_2_right], axis=1)
203
+ ones_2 = np.tile(ones_2, [1, encoder_att_look_back_num])
204
+
205
+ zeros_3_left = np.zeros([chunk_size, shfit_fsmn], dtype=dtype)
206
+ ones_3_right = np.ones([chunk_size, chunk_size], dtype=dtype)
207
+ ones_3 = np.concatenate([zeros_3_left, ones_3_right], axis=1)
208
+
209
+ zeros_remain_num = max(chunk_num - 1 - chunk_ids, 0)
210
+ zeros_remain = np.zeros(
211
+ [chunk_size, zeros_remain_num * chunk_size_pad_shift], dtype=dtype
212
+ )
213
+
214
+ ones2_bottom = np.concatenate([zeros_2, ones_2, ones_3, zeros_remain], axis=1)
215
+ mask_att_chunk_encoder_cur = np.concatenate([zeros_1_top, ones2_bottom], axis=0)
216
+ mask_att_chunk_encoder = np.concatenate(
217
+ [mask_att_chunk_encoder, mask_att_chunk_encoder_cur], axis=0
218
+ )
219
+
220
+ # decoder fsmn_shift_att_mask
221
+ zeros_1 = np.zeros([shfit_fsmn, 1])
222
+ ones_1 = np.ones([chunk_size, 1])
223
+ mask_shift_att_chunk_decoder_cur = np.concatenate([zeros_1, ones_1], axis=0)
224
+ mask_shift_att_chunk_decoder = np.concatenate(
225
+ [mask_shift_att_chunk_decoder, mask_shift_att_chunk_decoder_cur], axis=0
226
+ )
227
+
228
+ self.x_add_mask = x_add_mask[:x_len_chunk_max, : x_len_max + pad_left]
229
+ self.x_len_chunk = x_len_chunk
230
+ self.x_rm_mask = x_rm_mask[:x_len_max, :x_len_chunk_max]
231
+ self.x_len = x_len
232
+ self.mask_shfit_chunk = mask_shfit_chunk[:x_len_chunk_max, :]
233
+ self.mask_chunk_predictor = mask_chunk_predictor[:x_len_chunk_max, :]
234
+ self.mask_att_chunk_encoder = mask_att_chunk_encoder[:x_len_chunk_max, :x_len_chunk_max]
235
+ self.mask_shift_att_chunk_decoder = mask_shift_att_chunk_decoder[:x_len_chunk_max, :]
236
+ self.chunk_outs = (
237
+ self.x_add_mask,
238
+ self.x_len_chunk,
239
+ self.x_rm_mask,
240
+ self.x_len,
241
+ self.mask_shfit_chunk,
242
+ self.mask_chunk_predictor,
243
+ self.mask_att_chunk_encoder,
244
+ self.mask_shift_att_chunk_decoder,
245
+ )
246
+
247
+ return self.chunk_outs
248
+
249
+ def split_chunk(self, x, x_len, chunk_outs):
250
+ """
251
+ :param x: (b, t, d)
252
+ :param x_length: (b)
253
+ :param ind: int
254
+ :return:
255
+ """
256
+ x = x[:, : x_len.max(), :]
257
+ b, t, d = x.size()
258
+ x_len_mask = (~make_pad_mask(x_len, maxlen=t)).to(x.device)
259
+ x *= x_len_mask[:, :, None]
260
+
261
+ x_add_mask = self.get_x_add_mask(chunk_outs, x.device, dtype=x.dtype)
262
+ x_len_chunk = self.get_x_len_chunk(chunk_outs, x_len.device, dtype=x_len.dtype)
263
+ pad = (0, 0, self.pad_left_cur, 0)
264
+ x = F.pad(x, pad, "constant", 0.0)
265
+ b, t, d = x.size()
266
+ x = torch.transpose(x, 1, 0)
267
+ x = torch.reshape(x, [t, -1])
268
+ x_chunk = torch.mm(x_add_mask, x)
269
+ x_chunk = torch.reshape(x_chunk, [-1, b, d]).transpose(1, 0)
270
+
271
+ return x_chunk, x_len_chunk
272
+
273
+ def remove_chunk(self, x_chunk, x_len_chunk, chunk_outs):
274
+ x_chunk = x_chunk[:, : x_len_chunk.max(), :]
275
+ b, t, d = x_chunk.size()
276
+ x_len_chunk_mask = (~make_pad_mask(x_len_chunk, maxlen=t)).to(x_chunk.device)
277
+ x_chunk *= x_len_chunk_mask[:, :, None]
278
+
279
+ x_rm_mask = self.get_x_rm_mask(chunk_outs, x_chunk.device, dtype=x_chunk.dtype)
280
+ x_len = self.get_x_len(chunk_outs, x_len_chunk.device, dtype=x_len_chunk.dtype)
281
+ x_chunk = torch.transpose(x_chunk, 1, 0)
282
+ x_chunk = torch.reshape(x_chunk, [t, -1])
283
+ x = torch.mm(x_rm_mask, x_chunk)
284
+ x = torch.reshape(x, [-1, b, d]).transpose(1, 0)
285
+
286
+ return x, x_len
287
+
288
+ def get_x_add_mask(self, chunk_outs=None, device="cpu", idx=0, dtype=torch.float32):
289
+ with torch.no_grad():
290
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
291
+ x = torch.from_numpy(x).type(dtype).to(device)
292
+ return x
293
+
294
+ def get_x_len_chunk(self, chunk_outs=None, device="cpu", idx=1, dtype=torch.float32):
295
+ with torch.no_grad():
296
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
297
+ x = torch.from_numpy(x).type(dtype).to(device)
298
+ return x
299
+
300
+ def get_x_rm_mask(self, chunk_outs=None, device="cpu", idx=2, dtype=torch.float32):
301
+ with torch.no_grad():
302
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
303
+ x = torch.from_numpy(x).type(dtype).to(device)
304
+ return x
305
+
306
+ def get_x_len(self, chunk_outs=None, device="cpu", idx=3, dtype=torch.float32):
307
+ with torch.no_grad():
308
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
309
+ x = torch.from_numpy(x).type(dtype).to(device)
310
+ return x
311
+
312
+ def get_mask_shfit_chunk(
313
+ self, chunk_outs=None, device="cpu", batch_size=1, num_units=1, idx=4, dtype=torch.float32
314
+ ):
315
+ with torch.no_grad():
316
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
317
+ x = np.tile(
318
+ x[
319
+ None,
320
+ :,
321
+ :,
322
+ ],
323
+ [batch_size, 1, num_units],
324
+ )
325
+ x = torch.from_numpy(x).type(dtype).to(device)
326
+ return x
327
+
328
+ def get_mask_chunk_predictor(
329
+ self, chunk_outs=None, device="cpu", batch_size=1, num_units=1, idx=5, dtype=torch.float32
330
+ ):
331
+ with torch.no_grad():
332
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
333
+ x = np.tile(
334
+ x[
335
+ None,
336
+ :,
337
+ :,
338
+ ],
339
+ [batch_size, 1, num_units],
340
+ )
341
+ x = torch.from_numpy(x).type(dtype).to(device)
342
+ return x
343
+
344
+ def get_mask_att_chunk_encoder(
345
+ self, chunk_outs=None, device="cpu", batch_size=1, idx=6, dtype=torch.float32
346
+ ):
347
+ with torch.no_grad():
348
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
349
+ x = np.tile(
350
+ x[
351
+ None,
352
+ :,
353
+ :,
354
+ ],
355
+ [batch_size, 1, 1],
356
+ )
357
+ x = torch.from_numpy(x).type(dtype).to(device)
358
+ return x
359
+
360
+ def get_mask_shift_att_chunk_decoder(
361
+ self, chunk_outs=None, device="cpu", batch_size=1, idx=7, dtype=torch.float32
362
+ ):
363
+ with torch.no_grad():
364
+ x = chunk_outs[idx] if chunk_outs is not None else self.chunk_outs[idx]
365
+ x = np.tile(x[None, None, :, 0], [batch_size, 1, 1])
366
+ x = torch.from_numpy(x).type(dtype).to(device)
367
+ return x
368
+
369
+
370
+ def build_scama_mask_for_cross_attention_decoder(
371
+ predictor_alignments: torch.Tensor,
372
+ encoder_sequence_length: torch.Tensor,
373
+ chunk_size: int = 5,
374
+ encoder_chunk_size: int = 5,
375
+ attention_chunk_center_bias: int = 0,
376
+ attention_chunk_size: int = 1,
377
+ attention_chunk_type: str = "chunk",
378
+ step=None,
379
+ predictor_mask_chunk_hopping: torch.Tensor = None,
380
+ decoder_att_look_back_factor: int = 1,
381
+ mask_shift_att_chunk_decoder: torch.Tensor = None,
382
+ target_length: torch.Tensor = None,
383
+ is_training=True,
384
+ dtype: torch.dtype = torch.float32,
385
+ ):
386
+ with torch.no_grad():
387
+ device = predictor_alignments.device
388
+ batch_size, chunk_num = predictor_alignments.size()
389
+ maximum_encoder_length = encoder_sequence_length.max().item()
390
+ int_type = predictor_alignments.dtype
391
+ if not is_training:
392
+ target_length = predictor_alignments.sum(dim=-1).type(encoder_sequence_length.dtype)
393
+ maximum_target_length = target_length.max()
394
+ predictor_alignments_cumsum = torch.cumsum(predictor_alignments, dim=1)
395
+ predictor_alignments_cumsum = predictor_alignments_cumsum[:, None, :].repeat(
396
+ 1, maximum_target_length, 1
397
+ )
398
+
399
+ index = torch.ones([batch_size, maximum_target_length], dtype=int_type).to(device)
400
+ index = torch.cumsum(index, dim=1)
401
+ index = index[:, :, None].repeat(1, 1, chunk_num)
402
+
403
+ index_div = torch.floor(torch.divide(predictor_alignments_cumsum, index)).type(int_type)
404
+ index_div_bool_zeros = index_div == 0
405
+ index_div_bool_zeros_count = torch.sum(index_div_bool_zeros.type(int_type), dim=-1) + 1
406
+
407
+ index_div_bool_zeros_count = torch.clip(index_div_bool_zeros_count, min=1, max=chunk_num)
408
+
409
+ index_div_bool_zeros_count *= chunk_size
410
+ index_div_bool_zeros_count += attention_chunk_center_bias
411
+ index_div_bool_zeros_count = torch.clip(
412
+ index_div_bool_zeros_count - 1, min=0, max=maximum_encoder_length
413
+ )
414
+ index_div_bool_zeros_count_ori = index_div_bool_zeros_count
415
+
416
+ index_div_bool_zeros_count = (
417
+ torch.floor(index_div_bool_zeros_count / encoder_chunk_size) + 1
418
+ ) * encoder_chunk_size
419
+ max_len_chunk = math.ceil(maximum_encoder_length / encoder_chunk_size) * encoder_chunk_size
420
+
421
+ mask_flip, mask_flip2 = None, None
422
+ if attention_chunk_size is not None:
423
+ index_div_bool_zeros_count_beg = index_div_bool_zeros_count - attention_chunk_size
424
+ index_div_bool_zeros_count_beg = torch.clip(
425
+ index_div_bool_zeros_count_beg, 0, max_len_chunk
426
+ )
427
+ index_div_bool_zeros_count_beg_mask = sequence_mask(
428
+ index_div_bool_zeros_count_beg, maxlen=max_len_chunk, dtype=int_type, device=device
429
+ )
430
+ mask_flip = 1 - index_div_bool_zeros_count_beg_mask
431
+ attention_chunk_size2 = attention_chunk_size * (decoder_att_look_back_factor + 1)
432
+ index_div_bool_zeros_count_beg = index_div_bool_zeros_count - attention_chunk_size2
433
+
434
+ index_div_bool_zeros_count_beg = torch.clip(
435
+ index_div_bool_zeros_count_beg, 0, max_len_chunk
436
+ )
437
+ index_div_bool_zeros_count_beg_mask = sequence_mask(
438
+ index_div_bool_zeros_count_beg, maxlen=max_len_chunk, dtype=int_type, device=device
439
+ )
440
+ mask_flip2 = 1 - index_div_bool_zeros_count_beg_mask
441
+
442
+ mask = sequence_mask(
443
+ index_div_bool_zeros_count, maxlen=max_len_chunk, dtype=dtype, device=device
444
+ )
445
+
446
+ if predictor_mask_chunk_hopping is not None:
447
+ b, k, t = mask.size()
448
+ predictor_mask_chunk_hopping = predictor_mask_chunk_hopping[:, None, :, 0].repeat(
449
+ 1, k, 1
450
+ )
451
+
452
+ mask_mask_flip = mask
453
+ if mask_flip is not None:
454
+ mask_mask_flip = mask_flip * mask
455
+
456
+ def _fn():
457
+ mask_sliced = mask[:b, :k, encoder_chunk_size:t]
458
+ zero_pad_right = torch.zeros(
459
+ [b, k, encoder_chunk_size], dtype=mask_sliced.dtype
460
+ ).to(device)
461
+ mask_sliced = torch.cat([mask_sliced, zero_pad_right], dim=2)
462
+ _, _, tt = predictor_mask_chunk_hopping.size()
463
+ pad_right_p = max_len_chunk - tt
464
+ predictor_mask_chunk_hopping_pad = torch.nn.functional.pad(
465
+ predictor_mask_chunk_hopping, [0, pad_right_p], "constant", 0
466
+ )
467
+ masked = mask_sliced * predictor_mask_chunk_hopping_pad
468
+
469
+ mask_true = mask_mask_flip + masked
470
+ return mask_true
471
+
472
+ mask = _fn() if t > chunk_size else mask_mask_flip
473
+
474
+ if mask_flip2 is not None:
475
+ mask *= mask_flip2
476
+
477
+ mask_target = sequence_mask(
478
+ target_length, maxlen=maximum_target_length, dtype=mask.dtype, device=device
479
+ )
480
+ mask = mask[:, :maximum_target_length, :] * mask_target[:, :, None]
481
+
482
+ mask_len = sequence_mask(
483
+ encoder_sequence_length, maxlen=maximum_encoder_length, dtype=mask.dtype, device=device
484
+ )
485
+ mask = mask[:, :, :maximum_encoder_length] * mask_len[:, None, :]
486
+
487
+ if attention_chunk_type == "full":
488
+ mask = torch.ones_like(mask).to(device)
489
+ if mask_shift_att_chunk_decoder is not None:
490
+ mask = mask * mask_shift_att_chunk_decoder
491
+ mask = mask[:, :maximum_target_length, :maximum_encoder_length].type(dtype).to(device)
492
+
493
+ return mask
funasr/models/scama/decoder.py ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ from typing import List
7
+ from typing import Tuple
8
+ import logging
9
+ import torch
10
+ import torch.nn as nn
11
+ import numpy as np
12
+
13
+ from funasr.models.scama import utils as myutils
14
+ from funasr.models.transformer.decoder import BaseTransformerDecoder
15
+
16
+ from funasr.models.sanm.attention import (
17
+ MultiHeadedAttentionSANMDecoder,
18
+ MultiHeadedAttentionCrossAtt,
19
+ )
20
+ from funasr.models.transformer.embedding import PositionalEncoding
21
+ from funasr.models.transformer.layer_norm import LayerNorm
22
+ from funasr.models.sanm.positionwise_feed_forward import PositionwiseFeedForwardDecoderSANM
23
+ from funasr.models.transformer.utils.repeat import repeat
24
+
25
+ from funasr.register import tables
26
+
27
+
28
+ class DecoderLayerSANM(nn.Module):
29
+ """Single decoder layer module.
30
+
31
+ Args:
32
+ size (int): Input dimension.
33
+ self_attn (torch.nn.Module): Self-attention module instance.
34
+ `MultiHeadedAttention` instance can be used as the argument.
35
+ src_attn (torch.nn.Module): Self-attention module instance.
36
+ `MultiHeadedAttention` instance can be used as the argument.
37
+ feed_forward (torch.nn.Module): Feed-forward module instance.
38
+ `PositionwiseFeedForward`, `MultiLayeredConv1d`, or `Conv1dLinear` instance
39
+ can be used as the argument.
40
+ dropout_rate (float): Dropout rate.
41
+ normalize_before (bool): Whether to use layer_norm before the first block.
42
+ concat_after (bool): Whether to concat attention layer's input and output.
43
+ if True, additional linear will be applied.
44
+ i.e. x -> x + linear(concat(x, att(x)))
45
+ if False, no additional linear will be applied. i.e. x -> x + att(x)
46
+
47
+
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ size,
53
+ self_attn,
54
+ src_attn,
55
+ feed_forward,
56
+ dropout_rate,
57
+ normalize_before=True,
58
+ concat_after=False,
59
+ ):
60
+ """Construct an DecoderLayer object."""
61
+ super(DecoderLayerSANM, self).__init__()
62
+ self.size = size
63
+ self.self_attn = self_attn
64
+ self.src_attn = src_attn
65
+ self.feed_forward = feed_forward
66
+ self.norm1 = LayerNorm(size)
67
+ if self_attn is not None:
68
+ self.norm2 = LayerNorm(size)
69
+ if src_attn is not None:
70
+ self.norm3 = LayerNorm(size)
71
+ self.dropout = nn.Dropout(dropout_rate)
72
+ self.normalize_before = normalize_before
73
+ self.concat_after = concat_after
74
+ if self.concat_after:
75
+ self.concat_linear1 = nn.Linear(size + size, size)
76
+ self.concat_linear2 = nn.Linear(size + size, size)
77
+
78
+ def forward(self, tgt, tgt_mask, memory, memory_mask=None, cache=None):
79
+ """Compute decoded features.
80
+
81
+ Args:
82
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
83
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
84
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
85
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
86
+ cache (List[torch.Tensor]): List of cached tensors.
87
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
88
+
89
+ Returns:
90
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
91
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
92
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
93
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
94
+
95
+ """
96
+ # tgt = self.dropout(tgt)
97
+ residual = tgt
98
+ if self.normalize_before:
99
+ tgt = self.norm1(tgt)
100
+ tgt = self.feed_forward(tgt)
101
+
102
+ x = tgt
103
+ if self.self_attn:
104
+ if self.normalize_before:
105
+ tgt = self.norm2(tgt)
106
+ x, _ = self.self_attn(tgt, tgt_mask)
107
+ x = residual + self.dropout(x)
108
+
109
+ if self.src_attn is not None:
110
+ residual = x
111
+ if self.normalize_before:
112
+ x = self.norm3(x)
113
+
114
+ x = residual + self.dropout(self.src_attn(x, memory, memory_mask))
115
+
116
+ return x, tgt_mask, memory, memory_mask, cache
117
+
118
+ def forward_one_step(self, tgt, tgt_mask, memory, memory_mask=None, cache=None):
119
+ """Compute decoded features.
120
+
121
+ Args:
122
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
123
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
124
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
125
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
126
+ cache (List[torch.Tensor]): List of cached tensors.
127
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
128
+
129
+ Returns:
130
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
131
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
132
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
133
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
134
+
135
+ """
136
+ # tgt = self.dropout(tgt)
137
+ residual = tgt
138
+ if self.normalize_before:
139
+ tgt = self.norm1(tgt)
140
+ tgt = self.feed_forward(tgt)
141
+
142
+ x = tgt
143
+ if self.self_attn:
144
+ if self.normalize_before:
145
+ tgt = self.norm2(tgt)
146
+ if self.training:
147
+ cache = None
148
+ x, cache = self.self_attn(tgt, tgt_mask, cache=cache)
149
+ x = residual + self.dropout(x)
150
+
151
+ if self.src_attn is not None:
152
+ residual = x
153
+ if self.normalize_before:
154
+ x = self.norm3(x)
155
+
156
+ x = residual + self.dropout(self.src_attn(x, memory, memory_mask))
157
+
158
+ return x, tgt_mask, memory, memory_mask, cache
159
+
160
+ def forward_chunk(
161
+ self, tgt, memory, fsmn_cache=None, opt_cache=None, chunk_size=None, look_back=0
162
+ ):
163
+ """Compute decoded features.
164
+
165
+ Args:
166
+ tgt (torch.Tensor): Input tensor (#batch, maxlen_out, size).
167
+ tgt_mask (torch.Tensor): Mask for input tensor (#batch, maxlen_out).
168
+ memory (torch.Tensor): Encoded memory, float32 (#batch, maxlen_in, size).
169
+ memory_mask (torch.Tensor): Encoded memory mask (#batch, maxlen_in).
170
+ cache (List[torch.Tensor]): List of cached tensors.
171
+ Each tensor shape should be (#batch, maxlen_out - 1, size).
172
+
173
+ Returns:
174
+ torch.Tensor: Output tensor(#batch, maxlen_out, size).
175
+ torch.Tensor: Mask for output tensor (#batch, maxlen_out).
176
+ torch.Tensor: Encoded memory (#batch, maxlen_in, size).
177
+ torch.Tensor: Encoded memory mask (#batch, maxlen_in).
178
+
179
+ """
180
+ residual = tgt
181
+ if self.normalize_before:
182
+ tgt = self.norm1(tgt)
183
+ tgt = self.feed_forward(tgt)
184
+
185
+ x = tgt
186
+ if self.self_attn:
187
+ if self.normalize_before:
188
+ tgt = self.norm2(tgt)
189
+ x, fsmn_cache = self.self_attn(tgt, None, fsmn_cache)
190
+ x = residual + self.dropout(x)
191
+
192
+ if self.src_attn is not None:
193
+ residual = x
194
+ if self.normalize_before:
195
+ x = self.norm3(x)
196
+
197
+ x, opt_cache = self.src_attn.forward_chunk(x, memory, opt_cache, chunk_size, look_back)
198
+ x = residual + x
199
+
200
+ return x, memory, fsmn_cache, opt_cache
201
+
202
+
203
+ @tables.register("decoder_classes", "FsmnDecoderSCAMAOpt")
204
+ class FsmnDecoderSCAMAOpt(BaseTransformerDecoder):
205
+ """
206
+ Author: Shiliang Zhang, Zhifu Gao, Haoneng Luo, Ming Lei, Jie Gao, Zhijie Yan, Lei Xie
207
+ SCAMA: Streaming chunk-aware multihead attention for online end-to-end speech recognition
208
+ https://arxiv.org/abs/2006.01712
209
+ """
210
+
211
+ def __init__(
212
+ self,
213
+ vocab_size: int,
214
+ encoder_output_size: int,
215
+ attention_heads: int = 4,
216
+ linear_units: int = 2048,
217
+ num_blocks: int = 6,
218
+ dropout_rate: float = 0.1,
219
+ positional_dropout_rate: float = 0.1,
220
+ self_attention_dropout_rate: float = 0.0,
221
+ src_attention_dropout_rate: float = 0.0,
222
+ input_layer: str = "embed",
223
+ use_output_layer: bool = True,
224
+ pos_enc_class=PositionalEncoding,
225
+ normalize_before: bool = True,
226
+ concat_after: bool = False,
227
+ att_layer_num: int = 6,
228
+ kernel_size: int = 21,
229
+ sanm_shfit: int = None,
230
+ concat_embeds: bool = False,
231
+ attention_dim: int = None,
232
+ tf2torch_tensor_name_prefix_torch: str = "decoder",
233
+ tf2torch_tensor_name_prefix_tf: str = "seq2seq/decoder",
234
+ embed_tensor_name_prefix_tf: str = None,
235
+ ):
236
+ super().__init__(
237
+ vocab_size=vocab_size,
238
+ encoder_output_size=encoder_output_size,
239
+ dropout_rate=dropout_rate,
240
+ positional_dropout_rate=positional_dropout_rate,
241
+ input_layer=input_layer,
242
+ use_output_layer=use_output_layer,
243
+ pos_enc_class=pos_enc_class,
244
+ normalize_before=normalize_before,
245
+ )
246
+ if attention_dim is None:
247
+ attention_dim = encoder_output_size
248
+
249
+ if input_layer == "embed":
250
+ self.embed = torch.nn.Sequential(
251
+ torch.nn.Embedding(vocab_size, attention_dim),
252
+ )
253
+ elif input_layer == "linear":
254
+ self.embed = torch.nn.Sequential(
255
+ torch.nn.Linear(vocab_size, attention_dim),
256
+ torch.nn.LayerNorm(attention_dim),
257
+ torch.nn.Dropout(dropout_rate),
258
+ torch.nn.ReLU(),
259
+ pos_enc_class(attention_dim, positional_dropout_rate),
260
+ )
261
+ else:
262
+ raise ValueError(f"only 'embed' or 'linear' is supported: {input_layer}")
263
+
264
+ self.normalize_before = normalize_before
265
+ if self.normalize_before:
266
+ self.after_norm = LayerNorm(attention_dim)
267
+ if use_output_layer:
268
+ self.output_layer = torch.nn.Linear(attention_dim, vocab_size)
269
+ else:
270
+ self.output_layer = None
271
+
272
+ self.att_layer_num = att_layer_num
273
+ self.num_blocks = num_blocks
274
+ if sanm_shfit is None:
275
+ sanm_shfit = (kernel_size - 1) // 2
276
+ self.decoders = repeat(
277
+ att_layer_num,
278
+ lambda lnum: DecoderLayerSANM(
279
+ attention_dim,
280
+ MultiHeadedAttentionSANMDecoder(
281
+ attention_dim, self_attention_dropout_rate, kernel_size, sanm_shfit=sanm_shfit
282
+ ),
283
+ MultiHeadedAttentionCrossAtt(
284
+ attention_heads,
285
+ attention_dim,
286
+ src_attention_dropout_rate,
287
+ encoder_output_size=encoder_output_size,
288
+ ),
289
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
290
+ dropout_rate,
291
+ normalize_before,
292
+ concat_after,
293
+ ),
294
+ )
295
+ if num_blocks - att_layer_num <= 0:
296
+ self.decoders2 = None
297
+ else:
298
+ self.decoders2 = repeat(
299
+ num_blocks - att_layer_num,
300
+ lambda lnum: DecoderLayerSANM(
301
+ attention_dim,
302
+ MultiHeadedAttentionSANMDecoder(
303
+ attention_dim,
304
+ self_attention_dropout_rate,
305
+ kernel_size,
306
+ sanm_shfit=sanm_shfit,
307
+ ),
308
+ None,
309
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
310
+ dropout_rate,
311
+ normalize_before,
312
+ concat_after,
313
+ ),
314
+ )
315
+
316
+ self.decoders3 = repeat(
317
+ 1,
318
+ lambda lnum: DecoderLayerSANM(
319
+ attention_dim,
320
+ None,
321
+ None,
322
+ PositionwiseFeedForwardDecoderSANM(attention_dim, linear_units, dropout_rate),
323
+ dropout_rate,
324
+ normalize_before,
325
+ concat_after,
326
+ ),
327
+ )
328
+ if concat_embeds:
329
+ self.embed_concat_ffn = repeat(
330
+ 1,
331
+ lambda lnum: DecoderLayerSANM(
332
+ attention_dim + encoder_output_size,
333
+ None,
334
+ None,
335
+ PositionwiseFeedForwardDecoderSANM(
336
+ attention_dim + encoder_output_size,
337
+ linear_units,
338
+ dropout_rate,
339
+ adim=attention_dim,
340
+ ),
341
+ dropout_rate,
342
+ normalize_before,
343
+ concat_after,
344
+ ),
345
+ )
346
+ else:
347
+ self.embed_concat_ffn = None
348
+ self.concat_embeds = concat_embeds
349
+ self.tf2torch_tensor_name_prefix_torch = tf2torch_tensor_name_prefix_torch
350
+ self.tf2torch_tensor_name_prefix_tf = tf2torch_tensor_name_prefix_tf
351
+ self.embed_tensor_name_prefix_tf = embed_tensor_name_prefix_tf
352
+
353
+ def forward(
354
+ self,
355
+ hs_pad: torch.Tensor,
356
+ hlens: torch.Tensor,
357
+ ys_in_pad: torch.Tensor,
358
+ ys_in_lens: torch.Tensor,
359
+ chunk_mask: torch.Tensor = None,
360
+ pre_acoustic_embeds: torch.Tensor = None,
361
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
362
+ """Forward decoder.
363
+
364
+ Args:
365
+ hs_pad: encoded memory, float32 (batch, maxlen_in, feat)
366
+ hlens: (batch)
367
+ ys_in_pad:
368
+ input token ids, int64 (batch, maxlen_out)
369
+ if input_layer == "embed"
370
+ input tensor (batch, maxlen_out, #mels) in the other cases
371
+ ys_in_lens: (batch)
372
+ Returns:
373
+ (tuple): tuple containing:
374
+
375
+ x: decoded token score before softmax (batch, maxlen_out, token)
376
+ if use_output_layer is True,
377
+ olens: (batch, )
378
+ """
379
+ tgt = ys_in_pad
380
+ tgt_mask = myutils.sequence_mask(ys_in_lens, device=tgt.device)[:, :, None]
381
+
382
+ memory = hs_pad
383
+ memory_mask = myutils.sequence_mask(hlens, device=memory.device)[:, None, :]
384
+ if chunk_mask is not None:
385
+ memory_mask = memory_mask * chunk_mask
386
+ if tgt_mask.size(1) != memory_mask.size(1):
387
+ memory_mask = torch.cat((memory_mask, memory_mask[:, -2:-1, :]), dim=1)
388
+
389
+ x = self.embed(tgt)
390
+
391
+ if pre_acoustic_embeds is not None and self.concat_embeds:
392
+ x = torch.cat((x, pre_acoustic_embeds), dim=-1)
393
+ x, _, _, _, _ = self.embed_concat_ffn(x, None, None, None, None)
394
+
395
+ x, tgt_mask, memory, memory_mask, _ = self.decoders(x, tgt_mask, memory, memory_mask)
396
+ if self.decoders2 is not None:
397
+ x, tgt_mask, memory, memory_mask, _ = self.decoders2(x, tgt_mask, memory, memory_mask)
398
+ x, tgt_mask, memory, memory_mask, _ = self.decoders3(x, tgt_mask, memory, memory_mask)
399
+ if self.normalize_before:
400
+ x = self.after_norm(x)
401
+ if self.output_layer is not None:
402
+ x = self.output_layer(x)
403
+
404
+ olens = tgt_mask.sum(1)
405
+ return x, olens
406
+
407
+ def score(
408
+ self,
409
+ ys,
410
+ state,
411
+ x,
412
+ x_mask=None,
413
+ pre_acoustic_embeds: torch.Tensor = None,
414
+ ):
415
+ """Score."""
416
+ ys_mask = myutils.sequence_mask(
417
+ torch.tensor([len(ys)], dtype=torch.int32), device=x.device
418
+ )[:, :, None]
419
+ logp, state = self.forward_one_step(
420
+ ys.unsqueeze(0),
421
+ ys_mask,
422
+ x.unsqueeze(0),
423
+ memory_mask=x_mask,
424
+ pre_acoustic_embeds=pre_acoustic_embeds,
425
+ cache=state,
426
+ )
427
+ return logp.squeeze(0), state
428
+
429
+ def forward_one_step(
430
+ self,
431
+ tgt: torch.Tensor,
432
+ tgt_mask: torch.Tensor,
433
+ memory: torch.Tensor,
434
+ memory_mask: torch.Tensor = None,
435
+ pre_acoustic_embeds: torch.Tensor = None,
436
+ cache: List[torch.Tensor] = None,
437
+ ) -> Tuple[torch.Tensor, List[torch.Tensor]]:
438
+ """Forward one step.
439
+
440
+ Args:
441
+ tgt: input token ids, int64 (batch, maxlen_out)
442
+ tgt_mask: input token mask, (batch, maxlen_out)
443
+ dtype=torch.uint8 in PyTorch 1.2-
444
+ dtype=torch.bool in PyTorch 1.2+ (include 1.2)
445
+ memory: encoded memory, float32 (batch, maxlen_in, feat)
446
+ cache: cached output list of (batch, max_time_out-1, size)
447
+ Returns:
448
+ y, cache: NN output value and cache per `self.decoders`.
449
+ y.shape` is (batch, maxlen_out, token)
450
+ """
451
+
452
+ x = tgt[:, -1:]
453
+ tgt_mask = None
454
+ x = self.embed(x)
455
+
456
+ if pre_acoustic_embeds is not None and self.concat_embeds:
457
+ x = torch.cat((x, pre_acoustic_embeds), dim=-1)
458
+ x, _, _, _, _ = self.embed_concat_ffn(x, None, None, None, None)
459
+
460
+ if cache is None:
461
+ cache_layer_num = len(self.decoders)
462
+ if self.decoders2 is not None:
463
+ cache_layer_num += len(self.decoders2)
464
+ cache = [None] * cache_layer_num
465
+ new_cache = []
466
+ # for c, decoder in zip(cache, self.decoders):
467
+ for i in range(self.att_layer_num):
468
+ decoder = self.decoders[i]
469
+ c = cache[i]
470
+ x, tgt_mask, memory, memory_mask, c_ret = decoder.forward_one_step(
471
+ x, tgt_mask, memory, memory_mask, cache=c
472
+ )
473
+ new_cache.append(c_ret)
474
+
475
+ if self.num_blocks - self.att_layer_num >= 1:
476
+ for i in range(self.num_blocks - self.att_layer_num):
477
+ j = i + self.att_layer_num
478
+ decoder = self.decoders2[i]
479
+ c = cache[j]
480
+ x, tgt_mask, memory, memory_mask, c_ret = decoder.forward_one_step(
481
+ x, tgt_mask, memory, memory_mask, cache=c
482
+ )
483
+ new_cache.append(c_ret)
484
+
485
+ for decoder in self.decoders3:
486
+ x, tgt_mask, memory, memory_mask, _ = decoder.forward_one_step(
487
+ x, tgt_mask, memory, None, cache=None
488
+ )
489
+
490
+ if self.normalize_before:
491
+ y = self.after_norm(x[:, -1])
492
+ else:
493
+ y = x[:, -1]
494
+ if self.output_layer is not None:
495
+ y = self.output_layer(y)
496
+ y = torch.log_softmax(y, dim=-1)
497
+
498
+ return y, new_cache
funasr/models/scama/encoder.py ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ from typing import List
7
+ from typing import Optional
8
+ from typing import Sequence
9
+ from typing import Tuple
10
+ from typing import Union
11
+ import logging
12
+ import torch
13
+ import torch.nn as nn
14
+ import torch.nn.functional as F
15
+ from funasr.models.scama.chunk_utilis import overlap_chunk
16
+ import numpy as np
17
+ from funasr.train_utils.device_funcs import to_device
18
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask
19
+ from funasr.models.sanm.attention import MultiHeadedAttention, MultiHeadedAttentionSANM
20
+ from funasr.models.transformer.embedding import (
21
+ SinusoidalPositionEncoder,
22
+ StreamSinusoidalPositionEncoder,
23
+ )
24
+ from funasr.models.transformer.layer_norm import LayerNorm
25
+ from funasr.models.transformer.utils.multi_layer_conv import Conv1dLinear
26
+ from funasr.models.transformer.utils.multi_layer_conv import MultiLayeredConv1d
27
+ from funasr.models.transformer.positionwise_feed_forward import (
28
+ PositionwiseFeedForward, # noqa: H301
29
+ )
30
+ from funasr.models.transformer.utils.repeat import repeat
31
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling
32
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling2
33
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling6
34
+ from funasr.models.transformer.utils.subsampling import Conv2dSubsampling8
35
+ from funasr.models.transformer.utils.subsampling import TooShortUttError
36
+ from funasr.models.transformer.utils.subsampling import check_short_utt
37
+ from funasr.models.transformer.utils.mask import subsequent_mask, vad_mask
38
+
39
+ from funasr.models.ctc.ctc import CTC
40
+
41
+ from funasr.register import tables
42
+
43
+
44
+ class EncoderLayerSANM(nn.Module):
45
+ def __init__(
46
+ self,
47
+ in_size,
48
+ size,
49
+ self_attn,
50
+ feed_forward,
51
+ dropout_rate,
52
+ normalize_before=True,
53
+ concat_after=False,
54
+ stochastic_depth_rate=0.0,
55
+ ):
56
+ """Construct an EncoderLayer object."""
57
+ super(EncoderLayerSANM, self).__init__()
58
+ self.self_attn = self_attn
59
+ self.feed_forward = feed_forward
60
+ self.norm1 = LayerNorm(in_size)
61
+ self.norm2 = LayerNorm(size)
62
+ self.dropout = nn.Dropout(dropout_rate)
63
+ self.in_size = in_size
64
+ self.size = size
65
+ self.normalize_before = normalize_before
66
+ self.concat_after = concat_after
67
+ if self.concat_after:
68
+ self.concat_linear = nn.Linear(size + size, size)
69
+ self.stochastic_depth_rate = stochastic_depth_rate
70
+ self.dropout_rate = dropout_rate
71
+
72
+ def forward(self, x, mask, cache=None, mask_shfit_chunk=None, mask_att_chunk_encoder=None):
73
+ """Compute encoded features.
74
+
75
+ Args:
76
+ x_input (torch.Tensor): Input tensor (#batch, time, size).
77
+ mask (torch.Tensor): Mask tensor for the input (#batch, time).
78
+ cache (torch.Tensor): Cache tensor of the input (#batch, time - 1, size).
79
+
80
+ Returns:
81
+ torch.Tensor: Output tensor (#batch, time, size).
82
+ torch.Tensor: Mask tensor (#batch, time).
83
+
84
+ """
85
+ skip_layer = False
86
+ # with stochastic depth, residual connection `x + f(x)` becomes
87
+ # `x <- x + 1 / (1 - p) * f(x)` at training time.
88
+ stoch_layer_coeff = 1.0
89
+ if self.training and self.stochastic_depth_rate > 0:
90
+ skip_layer = torch.rand(1).item() < self.stochastic_depth_rate
91
+ stoch_layer_coeff = 1.0 / (1 - self.stochastic_depth_rate)
92
+
93
+ if skip_layer:
94
+ if cache is not None:
95
+ x = torch.cat([cache, x], dim=1)
96
+ return x, mask
97
+
98
+ residual = x
99
+ if self.normalize_before:
100
+ x = self.norm1(x)
101
+
102
+ if self.concat_after:
103
+ x_concat = torch.cat(
104
+ (
105
+ x,
106
+ self.self_attn(
107
+ x,
108
+ mask,
109
+ mask_shfit_chunk=mask_shfit_chunk,
110
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
111
+ ),
112
+ ),
113
+ dim=-1,
114
+ )
115
+ if self.in_size == self.size:
116
+ x = residual + stoch_layer_coeff * self.concat_linear(x_concat)
117
+ else:
118
+ x = stoch_layer_coeff * self.concat_linear(x_concat)
119
+ else:
120
+ if self.in_size == self.size:
121
+ x = residual + stoch_layer_coeff * self.dropout(
122
+ self.self_attn(
123
+ x,
124
+ mask,
125
+ mask_shfit_chunk=mask_shfit_chunk,
126
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
127
+ )
128
+ )
129
+ else:
130
+ x = stoch_layer_coeff * self.dropout(
131
+ self.self_attn(
132
+ x,
133
+ mask,
134
+ mask_shfit_chunk=mask_shfit_chunk,
135
+ mask_att_chunk_encoder=mask_att_chunk_encoder,
136
+ )
137
+ )
138
+ if not self.normalize_before:
139
+ x = self.norm1(x)
140
+
141
+ residual = x
142
+ if self.normalize_before:
143
+ x = self.norm2(x)
144
+ x = residual + stoch_layer_coeff * self.dropout(self.feed_forward(x))
145
+ if not self.normalize_before:
146
+ x = self.norm2(x)
147
+
148
+ return x, mask, cache, mask_shfit_chunk, mask_att_chunk_encoder
149
+
150
+ def forward_chunk(self, x, cache=None, chunk_size=None, look_back=0):
151
+ """Compute encoded features.
152
+
153
+ Args:
154
+ x_input (torch.Tensor): Input tensor (#batch, time, size).
155
+ mask (torch.Tensor): Mask tensor for the input (#batch, time).
156
+ cache (torch.Tensor): Cache tensor of the input (#batch, time - 1, size).
157
+
158
+ Returns:
159
+ torch.Tensor: Output tensor (#batch, time, size).
160
+ torch.Tensor: Mask tensor (#batch, time).
161
+
162
+ """
163
+
164
+ residual = x
165
+ if self.normalize_before:
166
+ x = self.norm1(x)
167
+
168
+ if self.in_size == self.size:
169
+ attn, cache = self.self_attn.forward_chunk(x, cache, chunk_size, look_back)
170
+ x = residual + attn
171
+ else:
172
+ x, cache = self.self_attn.forward_chunk(x, cache, chunk_size, look_back)
173
+
174
+ if not self.normalize_before:
175
+ x = self.norm1(x)
176
+
177
+ residual = x
178
+ if self.normalize_before:
179
+ x = self.norm2(x)
180
+ x = residual + self.feed_forward(x)
181
+ if not self.normalize_before:
182
+ x = self.norm2(x)
183
+
184
+ return x, cache
185
+
186
+
187
+ @tables.register("encoder_classes", "SANMEncoderChunkOpt")
188
+ class SANMEncoderChunkOpt(nn.Module):
189
+ """
190
+ Author: Shiliang Zhang, Zhifu Gao, Haoneng Luo, Ming Lei, Jie Gao, Zhijie Yan, Lei Xie
191
+ SCAMA: Streaming chunk-aware multihead attention for online end-to-end speech recognition
192
+ https://arxiv.org/abs/2006.01712
193
+ """
194
+
195
+ def __init__(
196
+ self,
197
+ input_size: int,
198
+ output_size: int = 256,
199
+ attention_heads: int = 4,
200
+ linear_units: int = 2048,
201
+ num_blocks: int = 6,
202
+ dropout_rate: float = 0.1,
203
+ positional_dropout_rate: float = 0.1,
204
+ attention_dropout_rate: float = 0.0,
205
+ input_layer: Optional[str] = "conv2d",
206
+ pos_enc_class=SinusoidalPositionEncoder,
207
+ normalize_before: bool = True,
208
+ concat_after: bool = False,
209
+ positionwise_layer_type: str = "linear",
210
+ positionwise_conv_kernel_size: int = 1,
211
+ padding_idx: int = -1,
212
+ interctc_layer_idx: List[int] = [],
213
+ interctc_use_conditioning: bool = False,
214
+ kernel_size: int = 11,
215
+ sanm_shfit: int = 0,
216
+ selfattention_layer_type: str = "sanm",
217
+ chunk_size: Union[int, Sequence[int]] = (16,),
218
+ stride: Union[int, Sequence[int]] = (10,),
219
+ pad_left: Union[int, Sequence[int]] = (0,),
220
+ encoder_att_look_back_factor: Union[int, Sequence[int]] = (1,),
221
+ decoder_att_look_back_factor: Union[int, Sequence[int]] = (1,),
222
+ tf2torch_tensor_name_prefix_torch: str = "encoder",
223
+ tf2torch_tensor_name_prefix_tf: str = "seq2seq/encoder",
224
+ ):
225
+ super().__init__()
226
+ self._output_size = output_size
227
+
228
+ if input_layer == "linear":
229
+ self.embed = torch.nn.Sequential(
230
+ torch.nn.Linear(input_size, output_size),
231
+ torch.nn.LayerNorm(output_size),
232
+ torch.nn.Dropout(dropout_rate),
233
+ torch.nn.ReLU(),
234
+ pos_enc_class(output_size, positional_dropout_rate),
235
+ )
236
+ elif input_layer == "conv2d":
237
+ self.embed = Conv2dSubsampling(input_size, output_size, dropout_rate)
238
+ elif input_layer == "conv2d2":
239
+ self.embed = Conv2dSubsampling2(input_size, output_size, dropout_rate)
240
+ elif input_layer == "conv2d6":
241
+ self.embed = Conv2dSubsampling6(input_size, output_size, dropout_rate)
242
+ elif input_layer == "conv2d8":
243
+ self.embed = Conv2dSubsampling8(input_size, output_size, dropout_rate)
244
+ elif input_layer == "embed":
245
+ self.embed = torch.nn.Sequential(
246
+ torch.nn.Embedding(input_size, output_size, padding_idx=padding_idx),
247
+ pos_enc_class(output_size, positional_dropout_rate),
248
+ )
249
+ elif input_layer is None:
250
+ if input_size == output_size:
251
+ self.embed = None
252
+ else:
253
+ self.embed = torch.nn.Linear(input_size, output_size)
254
+ elif input_layer == "pe":
255
+ self.embed = SinusoidalPositionEncoder()
256
+ elif input_layer == "pe_online":
257
+ self.embed = StreamSinusoidalPositionEncoder()
258
+ else:
259
+ raise ValueError("unknown input_layer: " + input_layer)
260
+ self.normalize_before = normalize_before
261
+ if positionwise_layer_type == "linear":
262
+ positionwise_layer = PositionwiseFeedForward
263
+ positionwise_layer_args = (
264
+ output_size,
265
+ linear_units,
266
+ dropout_rate,
267
+ )
268
+ elif positionwise_layer_type == "conv1d":
269
+ positionwise_layer = MultiLayeredConv1d
270
+ positionwise_layer_args = (
271
+ output_size,
272
+ linear_units,
273
+ positionwise_conv_kernel_size,
274
+ dropout_rate,
275
+ )
276
+ elif positionwise_layer_type == "conv1d-linear":
277
+ positionwise_layer = Conv1dLinear
278
+ positionwise_layer_args = (
279
+ output_size,
280
+ linear_units,
281
+ positionwise_conv_kernel_size,
282
+ dropout_rate,
283
+ )
284
+ else:
285
+ raise NotImplementedError("Support only linear or conv1d.")
286
+
287
+ if selfattention_layer_type == "selfattn":
288
+ encoder_selfattn_layer = MultiHeadedAttention
289
+ encoder_selfattn_layer_args = (
290
+ attention_heads,
291
+ output_size,
292
+ attention_dropout_rate,
293
+ )
294
+ elif selfattention_layer_type == "sanm":
295
+ encoder_selfattn_layer = MultiHeadedAttentionSANM
296
+ encoder_selfattn_layer_args0 = (
297
+ attention_heads,
298
+ input_size,
299
+ output_size,
300
+ attention_dropout_rate,
301
+ kernel_size,
302
+ sanm_shfit,
303
+ )
304
+
305
+ encoder_selfattn_layer_args = (
306
+ attention_heads,
307
+ output_size,
308
+ output_size,
309
+ attention_dropout_rate,
310
+ kernel_size,
311
+ sanm_shfit,
312
+ )
313
+ self.encoders0 = repeat(
314
+ 1,
315
+ lambda lnum: EncoderLayerSANM(
316
+ input_size,
317
+ output_size,
318
+ encoder_selfattn_layer(*encoder_selfattn_layer_args0),
319
+ positionwise_layer(*positionwise_layer_args),
320
+ dropout_rate,
321
+ normalize_before,
322
+ concat_after,
323
+ ),
324
+ )
325
+
326
+ self.encoders = repeat(
327
+ num_blocks - 1,
328
+ lambda lnum: EncoderLayerSANM(
329
+ output_size,
330
+ output_size,
331
+ encoder_selfattn_layer(*encoder_selfattn_layer_args),
332
+ positionwise_layer(*positionwise_layer_args),
333
+ dropout_rate,
334
+ normalize_before,
335
+ concat_after,
336
+ ),
337
+ )
338
+ if self.normalize_before:
339
+ self.after_norm = LayerNorm(output_size)
340
+
341
+ self.interctc_layer_idx = interctc_layer_idx
342
+ if len(interctc_layer_idx) > 0:
343
+ assert 0 < min(interctc_layer_idx) and max(interctc_layer_idx) < num_blocks
344
+ self.interctc_use_conditioning = interctc_use_conditioning
345
+ self.conditioning_layer = None
346
+ shfit_fsmn = (kernel_size - 1) // 2
347
+ self.overlap_chunk_cls = overlap_chunk(
348
+ chunk_size=chunk_size,
349
+ stride=stride,
350
+ pad_left=pad_left,
351
+ shfit_fsmn=shfit_fsmn,
352
+ encoder_att_look_back_factor=encoder_att_look_back_factor,
353
+ decoder_att_look_back_factor=decoder_att_look_back_factor,
354
+ )
355
+ self.tf2torch_tensor_name_prefix_torch = tf2torch_tensor_name_prefix_torch
356
+ self.tf2torch_tensor_name_prefix_tf = tf2torch_tensor_name_prefix_tf
357
+
358
+ def output_size(self) -> int:
359
+ return self._output_size
360
+
361
+ def forward(
362
+ self,
363
+ xs_pad: torch.Tensor,
364
+ ilens: torch.Tensor,
365
+ prev_states: torch.Tensor = None,
366
+ ctc: CTC = None,
367
+ ind: int = 0,
368
+ ) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
369
+ """Embed positions in tensor.
370
+
371
+ Args:
372
+ xs_pad: input tensor (B, L, D)
373
+ ilens: input length (B)
374
+ prev_states: Not to be used now.
375
+ Returns:
376
+ position embedded tensor and mask
377
+ """
378
+ masks = (~make_pad_mask(ilens)[:, None, :]).to(xs_pad.device)
379
+ xs_pad *= self.output_size() ** 0.5
380
+ if self.embed is None:
381
+ xs_pad = xs_pad
382
+ elif (
383
+ isinstance(self.embed, Conv2dSubsampling)
384
+ or isinstance(self.embed, Conv2dSubsampling2)
385
+ or isinstance(self.embed, Conv2dSubsampling6)
386
+ or isinstance(self.embed, Conv2dSubsampling8)
387
+ ):
388
+ short_status, limit_size = check_short_utt(self.embed, xs_pad.size(1))
389
+ if short_status:
390
+ raise TooShortUttError(
391
+ f"has {xs_pad.size(1)} frames and is too short for subsampling "
392
+ + f"(it needs more than {limit_size} frames), return empty results",
393
+ xs_pad.size(1),
394
+ limit_size,
395
+ )
396
+ xs_pad, masks = self.embed(xs_pad, masks)
397
+ else:
398
+ xs_pad = self.embed(xs_pad)
399
+
400
+ mask_shfit_chunk, mask_att_chunk_encoder = None, None
401
+ if self.overlap_chunk_cls is not None:
402
+ ilens = masks.squeeze(1).sum(1)
403
+ chunk_outs = self.overlap_chunk_cls.gen_chunk_mask(ilens, ind)
404
+ xs_pad, ilens = self.overlap_chunk_cls.split_chunk(xs_pad, ilens, chunk_outs=chunk_outs)
405
+ masks = (~make_pad_mask(ilens)[:, None, :]).to(xs_pad.device)
406
+ mask_shfit_chunk = self.overlap_chunk_cls.get_mask_shfit_chunk(
407
+ chunk_outs, xs_pad.device, xs_pad.size(0), dtype=xs_pad.dtype
408
+ )
409
+ mask_att_chunk_encoder = self.overlap_chunk_cls.get_mask_att_chunk_encoder(
410
+ chunk_outs, xs_pad.device, xs_pad.size(0), dtype=xs_pad.dtype
411
+ )
412
+
413
+ encoder_outs = self.encoders0(xs_pad, masks, None, mask_shfit_chunk, mask_att_chunk_encoder)
414
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
415
+ intermediate_outs = []
416
+ if len(self.interctc_layer_idx) == 0:
417
+ encoder_outs = self.encoders(
418
+ xs_pad, masks, None, mask_shfit_chunk, mask_att_chunk_encoder
419
+ )
420
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
421
+ else:
422
+ for layer_idx, encoder_layer in enumerate(self.encoders):
423
+ encoder_outs = encoder_layer(
424
+ xs_pad, masks, None, mask_shfit_chunk, mask_att_chunk_encoder
425
+ )
426
+ xs_pad, masks = encoder_outs[0], encoder_outs[1]
427
+ if layer_idx + 1 in self.interctc_layer_idx:
428
+ encoder_out = xs_pad
429
+
430
+ # intermediate outputs are also normalized
431
+ if self.normalize_before:
432
+ encoder_out = self.after_norm(encoder_out)
433
+
434
+ intermediate_outs.append((layer_idx + 1, encoder_out))
435
+
436
+ if self.interctc_use_conditioning:
437
+ ctc_out = ctc.softmax(encoder_out)
438
+ xs_pad = xs_pad + self.conditioning_layer(ctc_out)
439
+
440
+ if self.normalize_before:
441
+ xs_pad = self.after_norm(xs_pad)
442
+
443
+ olens = masks.squeeze(1).sum(1)
444
+ if len(intermediate_outs) > 0:
445
+ return (xs_pad, intermediate_outs), olens, None
446
+ return xs_pad, olens, None
447
+
448
+ def _add_overlap_chunk(self, feats: np.ndarray, cache: dict = {}):
449
+ if len(cache) == 0:
450
+ return feats
451
+ cache["feats"] = to_device(cache["feats"], device=feats.device)
452
+ overlap_feats = torch.cat((cache["feats"], feats), dim=1)
453
+ cache["feats"] = overlap_feats[:, -(cache["chunk_size"][0] + cache["chunk_size"][2]) :, :]
454
+ return overlap_feats
455
+
456
+ def forward_chunk(
457
+ self,
458
+ xs_pad: torch.Tensor,
459
+ ilens: torch.Tensor,
460
+ cache: dict = None,
461
+ **kwargs,
462
+ ):
463
+ is_final = kwargs.get("is_final", False)
464
+ xs_pad *= self.output_size() ** 0.5
465
+ if self.embed is None:
466
+ xs_pad = xs_pad
467
+ else:
468
+ xs_pad = self.embed(xs_pad, cache)
469
+ if cache["tail_chunk"]:
470
+ xs_pad = to_device(cache["feats"], device=xs_pad.device)
471
+ else:
472
+ xs_pad = self._add_overlap_chunk(xs_pad, cache)
473
+ if cache["opt"] is None:
474
+ cache_layer_num = len(self.encoders0) + len(self.encoders)
475
+ new_cache = [None] * cache_layer_num
476
+ else:
477
+ new_cache = cache["opt"]
478
+
479
+ for layer_idx, encoder_layer in enumerate(self.encoders0):
480
+ encoder_outs = encoder_layer.forward_chunk(
481
+ xs_pad, new_cache[layer_idx], cache["chunk_size"], cache["encoder_chunk_look_back"]
482
+ )
483
+ xs_pad, new_cache[0] = encoder_outs[0], encoder_outs[1]
484
+
485
+ for layer_idx, encoder_layer in enumerate(self.encoders):
486
+ encoder_outs = encoder_layer.forward_chunk(
487
+ xs_pad,
488
+ new_cache[layer_idx + len(self.encoders0)],
489
+ cache["chunk_size"],
490
+ cache["encoder_chunk_look_back"],
491
+ )
492
+ xs_pad, new_cache[layer_idx + len(self.encoders0)] = encoder_outs[0], encoder_outs[1]
493
+
494
+ if self.normalize_before:
495
+ xs_pad = self.after_norm(xs_pad)
496
+ if cache["encoder_chunk_look_back"] > 0 or cache["encoder_chunk_look_back"] == -1:
497
+ cache["opt"] = new_cache
498
+
499
+ return xs_pad, ilens, None
funasr/models/scama/model.py ADDED
@@ -0,0 +1,739 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import time
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.functional as F
10
+ import logging
11
+ from typing import Dict, Tuple
12
+ from contextlib import contextmanager
13
+ from distutils.version import LooseVersion
14
+
15
+ from funasr.register import tables
16
+ from funasr.models.ctc.ctc import CTC
17
+ from funasr.utils import postprocess_utils
18
+ from funasr.metrics.compute_acc import th_accuracy
19
+ from funasr.utils.datadir_writer import DatadirWriter
20
+ from funasr.models.paraformer.model import Paraformer
21
+ from funasr.models.paraformer.search import Hypothesis
22
+ from funasr.models.paraformer.cif_predictor import mae_loss
23
+ from funasr.train_utils.device_funcs import force_gatherable
24
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss
25
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
26
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask, pad_list
27
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
28
+ from funasr.models.scama.utils import sequence_mask
29
+
30
+ if LooseVersion(torch.__version__) >= LooseVersion("1.6.0"):
31
+ from torch.cuda.amp import autocast
32
+ else:
33
+ # Nothing to do if torch<1.6.0
34
+ @contextmanager
35
+ def autocast(enabled=True):
36
+ yield
37
+
38
+
39
+ @tables.register("model_classes", "SCAMA")
40
+ class SCAMA(nn.Module):
41
+ """
42
+ Author: Shiliang Zhang, Zhifu Gao, Haoneng Luo, Ming Lei, Jie Gao, Zhijie Yan, Lei Xie
43
+ SCAMA: Streaming chunk-aware multihead attention for online end-to-end speech recognition
44
+ https://arxiv.org/abs/2006.01712
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ specaug: str = None,
50
+ specaug_conf: dict = None,
51
+ normalize: str = None,
52
+ normalize_conf: dict = None,
53
+ encoder: str = None,
54
+ encoder_conf: dict = None,
55
+ decoder: str = None,
56
+ decoder_conf: dict = None,
57
+ ctc: str = None,
58
+ ctc_conf: dict = None,
59
+ ctc_weight: float = 0.5,
60
+ predictor: str = None,
61
+ predictor_conf: dict = None,
62
+ predictor_bias: int = 0,
63
+ predictor_weight: float = 0.0,
64
+ input_size: int = 80,
65
+ vocab_size: int = -1,
66
+ ignore_id: int = -1,
67
+ blank_id: int = 0,
68
+ sos: int = 1,
69
+ eos: int = 2,
70
+ lsm_weight: float = 0.0,
71
+ length_normalized_loss: bool = False,
72
+ share_embedding: bool = False,
73
+ **kwargs,
74
+ ):
75
+
76
+ super().__init__()
77
+
78
+ if specaug is not None:
79
+ specaug_class = tables.specaug_classes.get(specaug)
80
+ specaug = specaug_class(**specaug_conf)
81
+
82
+ if normalize is not None:
83
+ normalize_class = tables.normalize_classes.get(normalize)
84
+ normalize = normalize_class(**normalize_conf)
85
+
86
+ encoder_class = tables.encoder_classes.get(encoder)
87
+ encoder = encoder_class(input_size=input_size, **encoder_conf)
88
+ encoder_output_size = encoder.output_size()
89
+
90
+ decoder_class = tables.decoder_classes.get(decoder)
91
+ decoder = decoder_class(
92
+ vocab_size=vocab_size,
93
+ encoder_output_size=encoder_output_size,
94
+ **decoder_conf,
95
+ )
96
+ if ctc_weight > 0.0:
97
+
98
+ if ctc_conf is None:
99
+ ctc_conf = {}
100
+
101
+ ctc = CTC(odim=vocab_size, encoder_output_size=encoder_output_size, **ctc_conf)
102
+
103
+ predictor_class = tables.predictor_classes.get(predictor)
104
+ predictor = predictor_class(**predictor_conf)
105
+
106
+ # note that eos is the same as sos (equivalent ID)
107
+ self.blank_id = blank_id
108
+ self.sos = sos if sos is not None else vocab_size - 1
109
+ self.eos = eos if eos is not None else vocab_size - 1
110
+ self.vocab_size = vocab_size
111
+ self.ignore_id = ignore_id
112
+ self.ctc_weight = ctc_weight
113
+
114
+ self.specaug = specaug
115
+ self.normalize = normalize
116
+
117
+ self.encoder = encoder
118
+
119
+ if ctc_weight == 1.0:
120
+ self.decoder = None
121
+ else:
122
+ self.decoder = decoder
123
+
124
+ self.criterion_att = LabelSmoothingLoss(
125
+ size=vocab_size,
126
+ padding_idx=ignore_id,
127
+ smoothing=lsm_weight,
128
+ normalize_length=length_normalized_loss,
129
+ )
130
+
131
+ if ctc_weight == 0.0:
132
+ self.ctc = None
133
+ else:
134
+ self.ctc = ctc
135
+
136
+ self.predictor = predictor
137
+ self.predictor_weight = predictor_weight
138
+ self.predictor_bias = predictor_bias
139
+
140
+ self.criterion_pre = mae_loss(normalize_length=length_normalized_loss)
141
+
142
+ self.share_embedding = share_embedding
143
+ if self.share_embedding:
144
+ self.decoder.embed = None
145
+
146
+ self.length_normalized_loss = length_normalized_loss
147
+ self.beam_search = None
148
+ self.error_calculator = None
149
+
150
+ if self.encoder.overlap_chunk_cls is not None:
151
+ from funasr.models.scama.chunk_utilis import (
152
+ build_scama_mask_for_cross_attention_decoder,
153
+ )
154
+
155
+ self.build_scama_mask_for_cross_attention_decoder_fn = (
156
+ build_scama_mask_for_cross_attention_decoder
157
+ )
158
+ self.decoder_attention_chunk_type = kwargs.get("decoder_attention_chunk_type", "chunk")
159
+
160
+ def forward(
161
+ self,
162
+ speech: torch.Tensor,
163
+ speech_lengths: torch.Tensor,
164
+ text: torch.Tensor,
165
+ text_lengths: torch.Tensor,
166
+ **kwargs,
167
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
168
+ """Encoder + Decoder + Calc loss
169
+ Args:
170
+ speech: (Batch, Length, ...)
171
+ speech_lengths: (Batch, )
172
+ text: (Batch, Length)
173
+ text_lengths: (Batch,)
174
+ """
175
+
176
+ decoding_ind = kwargs.get("decoding_ind")
177
+ if len(text_lengths.size()) > 1:
178
+ text_lengths = text_lengths[:, 0]
179
+ if len(speech_lengths.size()) > 1:
180
+ speech_lengths = speech_lengths[:, 0]
181
+
182
+ batch_size = speech.shape[0]
183
+
184
+ # Encoder
185
+ ind = self.encoder.overlap_chunk_cls.random_choice(self.training, decoding_ind)
186
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths, ind=ind)
187
+
188
+ loss_ctc, cer_ctc = None, None
189
+ loss_pre = None
190
+ stats = dict()
191
+
192
+ # decoder: CTC branch
193
+
194
+ if self.ctc_weight > 0.0:
195
+
196
+ encoder_out_ctc, encoder_out_lens_ctc = self.encoder.overlap_chunk_cls.remove_chunk(
197
+ encoder_out, encoder_out_lens, chunk_outs=None
198
+ )
199
+
200
+ loss_ctc, cer_ctc = self._calc_ctc_loss(
201
+ encoder_out_ctc, encoder_out_lens_ctc, text, text_lengths
202
+ )
203
+ # Collect CTC branch stats
204
+ stats["loss_ctc"] = loss_ctc.detach() if loss_ctc is not None else None
205
+ stats["cer_ctc"] = cer_ctc
206
+
207
+ # decoder: Attention decoder branch
208
+ loss_att, acc_att, cer_att, wer_att, loss_pre = self._calc_att_predictor_loss(
209
+ encoder_out, encoder_out_lens, text, text_lengths
210
+ )
211
+
212
+ # 3. CTC-Att loss definition
213
+ if self.ctc_weight == 0.0:
214
+ loss = loss_att + loss_pre * self.predictor_weight
215
+ else:
216
+ loss = (
217
+ self.ctc_weight * loss_ctc
218
+ + (1 - self.ctc_weight) * loss_att
219
+ + loss_pre * self.predictor_weight
220
+ )
221
+
222
+ # Collect Attn branch stats
223
+ stats["loss_att"] = loss_att.detach() if loss_att is not None else None
224
+ stats["acc"] = acc_att
225
+ stats["cer"] = cer_att
226
+ stats["wer"] = wer_att
227
+ stats["loss_pre"] = loss_pre.detach().cpu() if loss_pre is not None else None
228
+
229
+ stats["loss"] = torch.clone(loss.detach())
230
+
231
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
232
+ if self.length_normalized_loss:
233
+ batch_size = (text_lengths + self.predictor_bias).sum()
234
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
235
+ return loss, stats, weight
236
+
237
+ def encode(
238
+ self,
239
+ speech: torch.Tensor,
240
+ speech_lengths: torch.Tensor,
241
+ **kwargs,
242
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
243
+ """Encoder. Note that this method is used by asr_inference.py
244
+ Args:
245
+ speech: (Batch, Length, ...)
246
+ speech_lengths: (Batch, )
247
+ ind: int
248
+ """
249
+ with autocast(False):
250
+
251
+ # Data augmentation
252
+ if self.specaug is not None and self.training:
253
+ speech, speech_lengths = self.specaug(speech, speech_lengths)
254
+
255
+ # Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
256
+ if self.normalize is not None:
257
+ speech, speech_lengths = self.normalize(speech, speech_lengths)
258
+
259
+ # Forward encoder
260
+ encoder_out, encoder_out_lens, _ = self.encoder(speech, speech_lengths)
261
+ if isinstance(encoder_out, tuple):
262
+ encoder_out = encoder_out[0]
263
+
264
+ return encoder_out, encoder_out_lens
265
+
266
+ def encode_chunk(
267
+ self,
268
+ speech: torch.Tensor,
269
+ speech_lengths: torch.Tensor,
270
+ cache: dict = None,
271
+ **kwargs,
272
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
273
+ """Frontend + Encoder. Note that this method is used by asr_inference.py
274
+ Args:
275
+ speech: (Batch, Length, ...)
276
+ speech_lengths: (Batch, )
277
+ ind: int
278
+ """
279
+ with autocast(False):
280
+
281
+ # Data augmentation
282
+ if self.specaug is not None and self.training:
283
+ speech, speech_lengths = self.specaug(speech, speech_lengths)
284
+
285
+ # Normalization for feature: e.g. Global-CMVN, Utterance-CMVN
286
+ if self.normalize is not None:
287
+ speech, speech_lengths = self.normalize(speech, speech_lengths)
288
+
289
+ # Forward encoder
290
+ encoder_out, encoder_out_lens, _ = self.encoder.forward_chunk(
291
+ speech, speech_lengths, cache=cache["encoder"]
292
+ )
293
+ if isinstance(encoder_out, tuple):
294
+ encoder_out = encoder_out[0]
295
+
296
+ return encoder_out, torch.tensor([encoder_out.size(1)])
297
+
298
+ def calc_predictor_chunk(self, encoder_out, encoder_out_lens, cache=None, **kwargs):
299
+ is_final = kwargs.get("is_final", False)
300
+
301
+ return self.predictor.forward_chunk(encoder_out, cache["encoder"], is_final=is_final)
302
+
303
+ def _calc_att_predictor_loss(
304
+ self,
305
+ encoder_out: torch.Tensor,
306
+ encoder_out_lens: torch.Tensor,
307
+ ys_pad: torch.Tensor,
308
+ ys_pad_lens: torch.Tensor,
309
+ ):
310
+ ys_in_pad, ys_out_pad = add_sos_eos(ys_pad, self.sos, self.eos, self.ignore_id)
311
+ ys_in_lens = ys_pad_lens + 1
312
+
313
+ encoder_out_mask = sequence_mask(
314
+ encoder_out_lens,
315
+ maxlen=encoder_out.size(1),
316
+ dtype=encoder_out.dtype,
317
+ device=encoder_out.device,
318
+ )[:, None, :]
319
+ mask_chunk_predictor = None
320
+ if self.encoder.overlap_chunk_cls is not None:
321
+ mask_chunk_predictor = self.encoder.overlap_chunk_cls.get_mask_chunk_predictor(
322
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
323
+ )
324
+ mask_shfit_chunk = self.encoder.overlap_chunk_cls.get_mask_shfit_chunk(
325
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
326
+ )
327
+ encoder_out = encoder_out * mask_shfit_chunk
328
+ pre_acoustic_embeds, pre_token_length, pre_alphas, _ = self.predictor(
329
+ encoder_out,
330
+ ys_out_pad,
331
+ encoder_out_mask,
332
+ ignore_id=self.ignore_id,
333
+ mask_chunk_predictor=mask_chunk_predictor,
334
+ target_label_length=ys_in_lens,
335
+ )
336
+ predictor_alignments, predictor_alignments_len = self.predictor.gen_frame_alignments(
337
+ pre_alphas, encoder_out_lens
338
+ )
339
+
340
+ encoder_chunk_size = self.encoder.overlap_chunk_cls.chunk_size_pad_shift_cur
341
+ attention_chunk_center_bias = 0
342
+ attention_chunk_size = encoder_chunk_size
343
+ decoder_att_look_back_factor = (
344
+ self.encoder.overlap_chunk_cls.decoder_att_look_back_factor_cur
345
+ )
346
+ mask_shift_att_chunk_decoder = (
347
+ self.encoder.overlap_chunk_cls.get_mask_shift_att_chunk_decoder(
348
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
349
+ )
350
+ )
351
+ scama_mask = self.build_scama_mask_for_cross_attention_decoder_fn(
352
+ predictor_alignments=predictor_alignments,
353
+ encoder_sequence_length=encoder_out_lens,
354
+ chunk_size=1,
355
+ encoder_chunk_size=encoder_chunk_size,
356
+ attention_chunk_center_bias=attention_chunk_center_bias,
357
+ attention_chunk_size=attention_chunk_size,
358
+ attention_chunk_type=self.decoder_attention_chunk_type,
359
+ step=None,
360
+ predictor_mask_chunk_hopping=mask_chunk_predictor,
361
+ decoder_att_look_back_factor=decoder_att_look_back_factor,
362
+ mask_shift_att_chunk_decoder=mask_shift_att_chunk_decoder,
363
+ target_length=ys_in_lens,
364
+ is_training=self.training,
365
+ )
366
+
367
+ # try:
368
+ # 1. Forward decoder
369
+ decoder_out, _ = self.decoder(
370
+ encoder_out,
371
+ encoder_out_lens,
372
+ ys_in_pad,
373
+ ys_in_lens,
374
+ chunk_mask=scama_mask,
375
+ pre_acoustic_embeds=pre_acoustic_embeds,
376
+ )
377
+
378
+ # 2. Compute attention loss
379
+ loss_att = self.criterion_att(decoder_out, ys_out_pad)
380
+ acc_att = th_accuracy(
381
+ decoder_out.view(-1, self.vocab_size),
382
+ ys_out_pad,
383
+ ignore_label=self.ignore_id,
384
+ )
385
+ # predictor loss
386
+ loss_pre = self.criterion_pre(ys_in_lens.type_as(pre_token_length), pre_token_length)
387
+ # Compute cer/wer using attention-decoder
388
+ if self.training or self.error_calculator is None:
389
+ cer_att, wer_att = None, None
390
+ else:
391
+ ys_hat = decoder_out.argmax(dim=-1)
392
+ cer_att, wer_att = self.error_calculator(ys_hat.cpu(), ys_pad.cpu())
393
+
394
+ return loss_att, acc_att, cer_att, wer_att, loss_pre
395
+
396
+ def calc_predictor_mask(
397
+ self,
398
+ encoder_out: torch.Tensor,
399
+ encoder_out_lens: torch.Tensor,
400
+ ys_pad: torch.Tensor = None,
401
+ ys_pad_lens: torch.Tensor = None,
402
+ ):
403
+ # ys_in_pad, ys_out_pad = add_sos_eos(ys_pad, self.sos, self.eos, self.ignore_id)
404
+ # ys_in_lens = ys_pad_lens + 1
405
+ ys_out_pad, ys_in_lens = None, None
406
+
407
+ encoder_out_mask = sequence_mask(
408
+ encoder_out_lens,
409
+ maxlen=encoder_out.size(1),
410
+ dtype=encoder_out.dtype,
411
+ device=encoder_out.device,
412
+ )[:, None, :]
413
+ mask_chunk_predictor = None
414
+
415
+ mask_chunk_predictor = self.encoder.overlap_chunk_cls.get_mask_chunk_predictor(
416
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
417
+ )
418
+ mask_shfit_chunk = self.encoder.overlap_chunk_cls.get_mask_shfit_chunk(
419
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
420
+ )
421
+ encoder_out = encoder_out * mask_shfit_chunk
422
+ pre_acoustic_embeds, pre_token_length, pre_alphas, _ = self.predictor(
423
+ encoder_out,
424
+ ys_out_pad,
425
+ encoder_out_mask,
426
+ ignore_id=self.ignore_id,
427
+ mask_chunk_predictor=mask_chunk_predictor,
428
+ target_label_length=ys_in_lens,
429
+ )
430
+ predictor_alignments, predictor_alignments_len = self.predictor.gen_frame_alignments(
431
+ pre_alphas, encoder_out_lens
432
+ )
433
+
434
+ encoder_chunk_size = self.encoder.overlap_chunk_cls.chunk_size_pad_shift_cur
435
+ attention_chunk_center_bias = 0
436
+ attention_chunk_size = encoder_chunk_size
437
+ decoder_att_look_back_factor = (
438
+ self.encoder.overlap_chunk_cls.decoder_att_look_back_factor_cur
439
+ )
440
+ mask_shift_att_chunk_decoder = (
441
+ self.encoder.overlap_chunk_cls.get_mask_shift_att_chunk_decoder(
442
+ None, device=encoder_out.device, batch_size=encoder_out.size(0)
443
+ )
444
+ )
445
+ scama_mask = self.build_scama_mask_for_cross_attention_decoder_fn(
446
+ predictor_alignments=predictor_alignments,
447
+ encoder_sequence_length=encoder_out_lens,
448
+ chunk_size=1,
449
+ encoder_chunk_size=encoder_chunk_size,
450
+ attention_chunk_center_bias=attention_chunk_center_bias,
451
+ attention_chunk_size=attention_chunk_size,
452
+ attention_chunk_type=self.decoder_attention_chunk_type,
453
+ step=None,
454
+ predictor_mask_chunk_hopping=mask_chunk_predictor,
455
+ decoder_att_look_back_factor=decoder_att_look_back_factor,
456
+ mask_shift_att_chunk_decoder=mask_shift_att_chunk_decoder,
457
+ target_length=ys_in_lens,
458
+ is_training=self.training,
459
+ )
460
+
461
+ return (
462
+ pre_acoustic_embeds,
463
+ pre_token_length,
464
+ predictor_alignments,
465
+ predictor_alignments_len,
466
+ scama_mask,
467
+ )
468
+
469
+ def init_beam_search(
470
+ self,
471
+ **kwargs,
472
+ ):
473
+
474
+ from funasr.models.scama.beam_search import BeamSearchScamaStreaming
475
+
476
+ from funasr.models.transformer.scorers.ctc import CTCPrefixScorer
477
+ from funasr.models.transformer.scorers.length_bonus import LengthBonus
478
+
479
+ # 1. Build ASR model
480
+ scorers = {}
481
+
482
+ if self.ctc != None:
483
+ ctc = CTCPrefixScorer(ctc=self.ctc, eos=self.eos)
484
+ scorers.update(ctc=ctc)
485
+ token_list = kwargs.get("token_list")
486
+ scorers.update(
487
+ decoder=self.decoder,
488
+ length_bonus=LengthBonus(len(token_list)),
489
+ )
490
+
491
+ # 3. Build ngram model
492
+ # ngram is not supported now
493
+ ngram = None
494
+ scorers["ngram"] = ngram
495
+
496
+ weights = dict(
497
+ decoder=1.0 - kwargs.get("decoding_ctc_weight", 0.0),
498
+ ctc=kwargs.get("decoding_ctc_weight", 0.0),
499
+ lm=kwargs.get("lm_weight", 0.0),
500
+ ngram=kwargs.get("ngram_weight", 0.0),
501
+ length_bonus=kwargs.get("penalty", 0.0),
502
+ )
503
+
504
+ beam_search = BeamSearchScamaStreaming(
505
+ beam_size=kwargs.get("beam_size", 2),
506
+ weights=weights,
507
+ scorers=scorers,
508
+ sos=self.sos,
509
+ eos=self.eos,
510
+ vocab_size=len(token_list),
511
+ token_list=token_list,
512
+ pre_beam_score_key=None if self.ctc_weight == 1.0 else "full",
513
+ )
514
+ # beam_search.to(device=kwargs.get("device", "cpu"), dtype=getattr(torch, kwargs.get("dtype", "float32"))).eval()
515
+ # for scorer in scorers.values():
516
+ # if isinstance(scorer, torch.nn.Module):
517
+ # scorer.to(device=kwargs.get("device", "cpu"), dtype=getattr(torch, kwargs.get("dtype", "float32"))).eval()
518
+ self.beam_search = beam_search
519
+
520
+ def generate_chunk(
521
+ self,
522
+ speech,
523
+ speech_lengths=None,
524
+ key: list = None,
525
+ tokenizer=None,
526
+ frontend=None,
527
+ **kwargs,
528
+ ):
529
+ cache = kwargs.get("cache", {})
530
+ speech = speech.to(device=kwargs["device"])
531
+ speech_lengths = speech_lengths.to(device=kwargs["device"])
532
+
533
+ # Encoder
534
+ encoder_out, encoder_out_lens = self.encode_chunk(
535
+ speech, speech_lengths, cache=cache, is_final=kwargs.get("is_final", False)
536
+ )
537
+ if isinstance(encoder_out, tuple):
538
+ encoder_out = encoder_out[0]
539
+ if "running_hyps" not in cache:
540
+ running_hyps = self.beam_search.init_hyp(encoder_out)
541
+ cache["running_hyps"] = running_hyps
542
+
543
+ # predictor
544
+ predictor_outs = self.calc_predictor_chunk(
545
+ encoder_out,
546
+ encoder_out_lens,
547
+ cache=cache,
548
+ is_final=kwargs.get("is_final", False),
549
+ )
550
+ pre_acoustic_embeds, pre_token_length, alphas, pre_peak_index = (
551
+ predictor_outs[0],
552
+ predictor_outs[1],
553
+ predictor_outs[2],
554
+ predictor_outs[3],
555
+ )
556
+ pre_token_length = pre_token_length.round().long()
557
+
558
+ if torch.max(pre_token_length) < 1:
559
+ return []
560
+ maxlen = minlen = pre_token_length
561
+ if kwargs.get("is_final", False):
562
+ maxlen += kwargs.get("token_num_relax", 5)
563
+ minlen = max(0, minlen - kwargs.get("token_num_relax", 5))
564
+ # c. Passed the encoder result and the beam search
565
+ nbest_hyps = self.beam_search(
566
+ x=encoder_out[0],
567
+ scama_mask=None,
568
+ pre_acoustic_embeds=pre_acoustic_embeds,
569
+ maxlen=int(maxlen),
570
+ minlen=int(minlen),
571
+ cache=cache,
572
+ )
573
+
574
+ cache["running_hyps"] = nbest_hyps
575
+ nbest_hyps = nbest_hyps[: self.nbest]
576
+
577
+ results = []
578
+ for hyp in nbest_hyps:
579
+ # assert isinstance(hyp, (Hypothesis)), type(hyp)
580
+
581
+ # remove sos/eos and get results
582
+ last_pos = -1
583
+ if isinstance(hyp.yseq, list):
584
+ token_int = hyp.yseq[1:last_pos]
585
+ else:
586
+ token_int = hyp.yseq[1:last_pos].tolist()
587
+
588
+ # remove blank symbol id, which is assumed to be 0
589
+ token_int = list(
590
+ filter(
591
+ lambda x: x != self.eos and x != self.sos and x != self.blank_id, token_int
592
+ )
593
+ )
594
+
595
+ # Change integer-ids to tokens
596
+ token = tokenizer.ids2tokens(token_int)
597
+ # text = tokenizer.tokens2text(token)
598
+
599
+ result_i = token
600
+
601
+ results.extend(result_i)
602
+
603
+ return results
604
+
605
+ def init_cache(self, cache: dict = {}, **kwargs):
606
+ device = kwargs.get("device", "cuda")
607
+
608
+ chunk_size = kwargs.get("chunk_size", [0, 10, 5])
609
+ encoder_chunk_look_back = kwargs.get("encoder_chunk_look_back", 0)
610
+ decoder_chunk_look_back = kwargs.get("decoder_chunk_look_back", 0)
611
+ batch_size = 1
612
+
613
+ enc_output_size = kwargs["encoder_conf"]["output_size"]
614
+ feats_dims = kwargs["frontend_conf"]["n_mels"] * kwargs["frontend_conf"]["lfr_m"]
615
+
616
+ cache_encoder = {
617
+ "start_idx": 0,
618
+ "cif_hidden": torch.zeros((batch_size, 1, enc_output_size)).to(device=device),
619
+ "cif_alphas": torch.zeros((batch_size, 1)).to(device=device),
620
+ "chunk_size": chunk_size,
621
+ "encoder_chunk_look_back": encoder_chunk_look_back,
622
+ "last_chunk": False,
623
+ "opt": None,
624
+ "feats": torch.zeros((batch_size, chunk_size[0] + chunk_size[2], feats_dims)).to(
625
+ device=device
626
+ ),
627
+ "tail_chunk": False,
628
+ }
629
+ cache["encoder"] = cache_encoder
630
+
631
+ cache_decoder = {
632
+ "decode_fsmn": None,
633
+ "decoder_chunk_look_back": decoder_chunk_look_back,
634
+ "opt": None,
635
+ "chunk_size": chunk_size,
636
+ }
637
+ cache["decoder"] = cache_decoder
638
+ cache["frontend"] = {}
639
+
640
+ cache["prev_samples"] = torch.empty(0).to(device=device)
641
+
642
+ return cache
643
+
644
+ def inference(
645
+ self,
646
+ data_in,
647
+ data_lengths=None,
648
+ key: list = None,
649
+ tokenizer=None,
650
+ frontend=None,
651
+ cache: dict = {},
652
+ **kwargs,
653
+ ):
654
+
655
+ # init beamsearch
656
+ is_use_ctc = kwargs.get("decoding_ctc_weight", 0.0) > 0.00001 and self.ctc != None
657
+ is_use_lm = (
658
+ kwargs.get("lm_weight", 0.0) > 0.00001 and kwargs.get("lm_file", None) is not None
659
+ )
660
+
661
+ if self.beam_search is None:
662
+
663
+ logging.info("enable beam_search")
664
+ self.init_beam_search(**kwargs)
665
+ self.nbest = kwargs.get("nbest", 1)
666
+
667
+ if len(cache) == 0:
668
+ self.init_cache(cache, **kwargs)
669
+
670
+ meta_data = {}
671
+ chunk_size = kwargs.get("chunk_size", [0, 10, 5])
672
+ chunk_stride_samples = int(chunk_size[1] * 960) # 600ms
673
+
674
+ time1 = time.perf_counter()
675
+ cfg = {"is_final": kwargs.get("is_final", False)}
676
+ audio_sample_list = load_audio_text_image_video(
677
+ data_in,
678
+ fs=frontend.fs,
679
+ audio_fs=kwargs.get("fs", 16000),
680
+ data_type=kwargs.get("data_type", "sound"),
681
+ tokenizer=tokenizer,
682
+ cache=cfg,
683
+ )
684
+ _is_final = cfg["is_final"] # if data_in is a file or url, set is_final=True
685
+
686
+ time2 = time.perf_counter()
687
+ meta_data["load_data"] = f"{time2 - time1:0.3f}"
688
+ assert len(audio_sample_list) == 1, "batch_size must be set 1"
689
+
690
+ audio_sample = torch.cat((cache["prev_samples"], audio_sample_list[0]))
691
+
692
+ n = int(len(audio_sample) // chunk_stride_samples + int(_is_final))
693
+ m = int(len(audio_sample) % chunk_stride_samples * (1 - int(_is_final)))
694
+ tokens = []
695
+ for i in range(n):
696
+ kwargs["is_final"] = _is_final and i == n - 1
697
+ audio_sample_i = audio_sample[i * chunk_stride_samples : (i + 1) * chunk_stride_samples]
698
+
699
+ # extract fbank feats
700
+ speech, speech_lengths = extract_fbank(
701
+ [audio_sample_i],
702
+ data_type=kwargs.get("data_type", "sound"),
703
+ frontend=frontend,
704
+ cache=cache["frontend"],
705
+ is_final=kwargs["is_final"],
706
+ )
707
+ time3 = time.perf_counter()
708
+ meta_data["extract_feat"] = f"{time3 - time2:0.3f}"
709
+ meta_data["batch_data_time"] = (
710
+ speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000
711
+ )
712
+
713
+ tokens_i = self.generate_chunk(
714
+ speech,
715
+ speech_lengths,
716
+ key=key,
717
+ tokenizer=tokenizer,
718
+ cache=cache,
719
+ frontend=frontend,
720
+ **kwargs,
721
+ )
722
+ tokens.extend(tokens_i)
723
+
724
+ text_postprocessed, _ = postprocess_utils.sentence_postprocess(tokens)
725
+
726
+ result_i = {"key": key[0], "text": text_postprocessed}
727
+ result = [result_i]
728
+
729
+ cache["prev_samples"] = audio_sample[:-m]
730
+ if _is_final:
731
+ self.init_cache(cache, **kwargs)
732
+
733
+ if kwargs.get("output_dir"):
734
+ writer = DatadirWriter(kwargs.get("output_dir"))
735
+ ibest_writer = writer[f"{1}best_recog"]
736
+ ibest_writer["token"][key[0]] = " ".join(tokens)
737
+ ibest_writer["text"][key[0]] = text_postprocessed
738
+
739
+ return result, meta_data
funasr/models/scama/template.yaml ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is an example that demonstrates how to configure a model file.
2
+ # You can modify the configuration according to your own requirements.
3
+
4
+ # to print the register_table:
5
+ # from funasr.register import tables
6
+ # tables.print()
7
+
8
+ # network architecture
9
+ model: SCAMA
10
+ model_conf:
11
+ ctc_weight: 0.0
12
+ lsm_weight: 0.1
13
+ length_normalized_loss: true
14
+
15
+ # encoder
16
+ encoder: SANMEncoderChunkOpt
17
+ encoder_conf:
18
+ output_size: 512
19
+ attention_heads: 4
20
+ linear_units: 2048
21
+ num_blocks: 50
22
+ dropout_rate: 0.1
23
+ positional_dropout_rate: 0.1
24
+ attention_dropout_rate: 0.1
25
+ input_layer: pe
26
+ pos_enc_class: SinusoidalPositionEncoder
27
+ normalize_before: true
28
+ kernel_size: 11
29
+ sanm_shfit: 0
30
+ selfattention_layer_type: sanm
31
+
32
+ # decoder
33
+ decoder: FsmnDecoderSCAMAOpt
34
+ decoder_conf:
35
+ attention_heads: 4
36
+ linear_units: 2048
37
+ num_blocks: 16
38
+ dropout_rate: 0.1
39
+ positional_dropout_rate: 0.1
40
+ self_attention_dropout_rate: 0.1
41
+ src_attention_dropout_rate: 0.1
42
+ att_layer_num: 16
43
+ kernel_size: 11
44
+ sanm_shfit: 0
45
+
46
+ predictor: CifPredictorV2
47
+ predictor_conf:
48
+ idim: 512
49
+ threshold: 1.0
50
+ l_order: 1
51
+ r_order: 1
52
+ tail_threshold: 0.45
53
+
54
+ # frontend related
55
+ frontend: WavFrontend
56
+ frontend_conf:
57
+ fs: 16000
58
+ window: hamming
59
+ n_mels: 80
60
+ frame_length: 25
61
+ frame_shift: 10
62
+ lfr_m: 7
63
+ lfr_n: 6
64
+
65
+ specaug: SpecAugLFR
66
+ specaug_conf:
67
+ apply_time_warp: false
68
+ time_warp_window: 5
69
+ time_warp_mode: bicubic
70
+ apply_freq_mask: true
71
+ freq_mask_width_range:
72
+ - 0
73
+ - 30
74
+ lfr_rate: 6
75
+ num_freq_mask: 1
76
+ apply_time_mask: true
77
+ time_mask_width_range:
78
+ - 0
79
+ - 12
80
+ num_time_mask: 1
81
+
82
+ train_conf:
83
+ accum_grad: 1
84
+ grad_clip: 5
85
+ max_epoch: 150
86
+ val_scheduler_criterion:
87
+ - valid
88
+ - acc
89
+ best_model_criterion:
90
+ - - valid
91
+ - acc
92
+ - max
93
+ keep_nbest_models: 10
94
+ avg_nbest_model: 10
95
+ log_interval: 50
96
+
97
+ optim: adam
98
+ optim_conf:
99
+ lr: 0.0005
100
+ scheduler: warmuplr
101
+ scheduler_conf:
102
+ warmup_steps: 30000
103
+
104
+ dataset: AudioDataset
105
+ dataset_conf:
106
+ index_ds: IndexDSJsonl
107
+ batch_sampler: BatchSampler
108
+ batch_type: example # example or length
109
+ batch_size: 1 # if batch_type is example, batch_size is the numbers of samples; if length, batch_size is source_token_len+target_token_len;
110
+ max_token_length: 2048 # filter samples if source_token_len+target_token_len > max_token_length,
111
+ buffer_size: 500
112
+ shuffle: True
113
+ num_workers: 0
114
+
115
+ tokenizer: CharTokenizer
116
+ tokenizer_conf:
117
+ unk_symbol: <unk>
118
+ split_with_space: true
119
+
120
+
121
+ ctc_conf:
122
+ dropout_rate: 0.0
123
+ ctc_type: builtin
124
+ reduce: true
125
+ ignore_nan_grad: true
126
+
127
+ normalize: null
funasr/models/scama/utils.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import yaml
3
+ import torch
4
+ import numpy as np
5
+ from torch.nn import functional as F
6
+
7
+
8
+ def sequence_mask(lengths, maxlen=None, dtype=torch.float32, device=None):
9
+ if maxlen is None:
10
+ maxlen = lengths.max()
11
+ row_vector = torch.arange(0, maxlen, 1).to(lengths.device)
12
+ matrix = torch.unsqueeze(lengths, dim=-1)
13
+ mask = row_vector < matrix
14
+ mask = mask.detach()
15
+
16
+ return mask.type(dtype).to(device) if device is not None else mask.type(dtype)
17
+
18
+
19
+ def apply_cmvn(inputs, mvn):
20
+ device = inputs.device
21
+ dtype = inputs.dtype
22
+ frame, dim = inputs.shape
23
+ meams = np.tile(mvn[0:1, :dim], (frame, 1))
24
+ vars = np.tile(mvn[1:2, :dim], (frame, 1))
25
+ inputs -= torch.from_numpy(meams).type(dtype).to(device)
26
+ inputs *= torch.from_numpy(vars).type(dtype).to(device)
27
+
28
+ return inputs.type(torch.float32)
29
+
30
+
31
+ def drop_and_add(
32
+ inputs: torch.Tensor,
33
+ outputs: torch.Tensor,
34
+ training: bool,
35
+ dropout_rate: float = 0.1,
36
+ stoch_layer_coeff: float = 1.0,
37
+ ):
38
+
39
+ outputs = F.dropout(outputs, p=dropout_rate, training=training, inplace=True)
40
+ outputs *= stoch_layer_coeff
41
+
42
+ input_dim = inputs.size(-1)
43
+ output_dim = outputs.size(-1)
44
+
45
+ if input_dim == output_dim:
46
+ outputs += inputs
47
+ return outputs
48
+
49
+
50
+ def proc_tf_vocab(vocab_path):
51
+ with open(vocab_path, encoding="utf-8") as f:
52
+ token_list = [line.rstrip() for line in f]
53
+ if "<unk>" not in token_list:
54
+ token_list.append("<unk>")
55
+ return token_list
56
+
57
+
58
+ def gen_config_for_tfmodel(config_path, vocab_path, output_dir):
59
+ token_list = proc_tf_vocab(vocab_path)
60
+ with open(config_path, encoding="utf-8") as f:
61
+ config = yaml.safe_load(f)
62
+
63
+ config["token_list"] = token_list
64
+
65
+ if not os.path.exists(output_dir):
66
+ os.makedirs(output_dir)
67
+
68
+ with open(os.path.join(output_dir, "config.yaml"), "w", encoding="utf-8") as f:
69
+ yaml_no_alias_safe_dump(config, f, indent=4, sort_keys=False)
70
+
71
+
72
+ class NoAliasSafeDumper(yaml.SafeDumper):
73
+ # Disable anchor/alias in yaml because looks ugly
74
+ def ignore_aliases(self, data):
75
+ return True
76
+
77
+
78
+ def yaml_no_alias_safe_dump(data, stream=None, **kwargs):
79
+ """Safe-dump in yaml with no anchor/alias"""
80
+ return yaml.dump(data, stream, allow_unicode=True, Dumper=NoAliasSafeDumper, **kwargs)
81
+
82
+
83
+ if __name__ == "__main__":
84
+ import sys
85
+
86
+ config_path = sys.argv[1]
87
+ vocab_path = sys.argv[2]
88
+ output_dir = sys.argv[3]
89
+ gen_config_for_tfmodel(config_path, vocab_path, output_dir)
funasr/models/seaco_paraformer/__init__.py ADDED
File without changes
funasr/models/seaco_paraformer/export_meta.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import torch
7
+
8
+ from funasr.register import tables
9
+
10
+
11
+ class ContextualEmbedderExport(torch.nn.Module):
12
+ def __init__(
13
+ self,
14
+ model,
15
+ max_seq_len=512,
16
+ feats_dim=560,
17
+ **kwargs,
18
+ ):
19
+ super().__init__()
20
+ self.embedding = model.decoder.embed # model.bias_embed
21
+ model.bias_encoder.batch_first = False
22
+ self.bias_encoder = model.bias_encoder
23
+
24
+ def forward(self, hotword):
25
+ hotword = self.embedding(hotword).transpose(0, 1) # batch second
26
+ hw_embed, (_, _) = self.bias_encoder(hotword)
27
+ return hw_embed
28
+
29
+ def export_dummy_inputs(self):
30
+ hotword = torch.tensor(
31
+ [
32
+ [10, 11, 12, 13, 14, 10, 11, 12, 13, 14],
33
+ [100, 101, 0, 0, 0, 0, 0, 0, 0, 0],
34
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
35
+ [10, 11, 12, 13, 14, 10, 11, 12, 13, 14],
36
+ [100, 101, 0, 0, 0, 0, 0, 0, 0, 0],
37
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
38
+ ],
39
+ dtype=torch.int32,
40
+ )
41
+ # hotword_length = torch.tensor([10, 2, 1], dtype=torch.int32)
42
+ return hotword
43
+
44
+ def export_input_names(self):
45
+ return ["hotword"]
46
+
47
+ def export_output_names(self):
48
+ return ["hw_embed"]
49
+
50
+ def export_dynamic_axes(self):
51
+ return {
52
+ "hotword": {
53
+ 0: "num_hotwords",
54
+ },
55
+ "hw_embed": {
56
+ 1: "num_hotwords",
57
+ },
58
+ }
59
+
60
+ def export_name(self):
61
+ return "model_eb.onnx"
62
+
63
+
64
+ def export_rebuild_model(model, **kwargs):
65
+ model.device = kwargs.get("device")
66
+ is_onnx = kwargs.get("type", "onnx") == "onnx"
67
+ encoder_class = tables.encoder_classes.get(kwargs["encoder"] + "Export")
68
+ model.encoder = encoder_class(model.encoder, onnx=is_onnx)
69
+
70
+ predictor_class = tables.predictor_classes.get(kwargs["predictor"] + "Export")
71
+ model.predictor = predictor_class(model.predictor, onnx=is_onnx)
72
+
73
+ # before decoder convert into export class
74
+ embedder_class = ContextualEmbedderExport
75
+ embedder_model = embedder_class(model, onnx=is_onnx)
76
+
77
+ decoder_class = tables.decoder_classes.get(kwargs["decoder"] + "Export")
78
+ model.decoder = decoder_class(model.decoder, onnx=is_onnx)
79
+
80
+ seaco_decoder_class = tables.decoder_classes.get(kwargs["seaco_decoder"] + "Export")
81
+ model.seaco_decoder = seaco_decoder_class(model.seaco_decoder, onnx=is_onnx)
82
+
83
+ from funasr.utils.torch_function import sequence_mask
84
+
85
+ model.make_pad_mask = sequence_mask(kwargs["max_seq_len"], flip=False)
86
+
87
+ from funasr.utils.torch_function import sequence_mask
88
+
89
+ model.make_pad_mask = sequence_mask(kwargs["max_seq_len"], flip=False)
90
+ model.feats_dim = 560
91
+ model.NOBIAS = 8377
92
+
93
+ import copy
94
+ import types
95
+
96
+ backbone_model = copy.copy(model)
97
+
98
+ # backbone
99
+ backbone_model.forward = types.MethodType(export_backbone_forward, backbone_model)
100
+ backbone_model.export_dummy_inputs = types.MethodType(
101
+ export_backbone_dummy_inputs, backbone_model
102
+ )
103
+ backbone_model.export_input_names = types.MethodType(
104
+ export_backbone_input_names, backbone_model
105
+ )
106
+ backbone_model.export_output_names = types.MethodType(
107
+ export_backbone_output_names, backbone_model
108
+ )
109
+ backbone_model.export_dynamic_axes = types.MethodType(
110
+ export_backbone_dynamic_axes, backbone_model
111
+ )
112
+
113
+ embedder_model.export_name = "model_eb"
114
+ backbone_model.export_name = "model"
115
+
116
+ return backbone_model, embedder_model
117
+
118
+
119
+ def export_backbone_forward(
120
+ self,
121
+ speech: torch.Tensor,
122
+ speech_lengths: torch.Tensor,
123
+ bias_embed: torch.Tensor,
124
+ # lmbd: float,
125
+ ):
126
+ # a. To device
127
+ batch = {"speech": speech, "speech_lengths": speech_lengths}
128
+
129
+ enc, enc_len = self.encoder(**batch)
130
+ mask = self.make_pad_mask(enc_len)[:, None, :]
131
+ pre_acoustic_embeds, pre_token_length, alphas, pre_peak_index = self.predictor(enc, mask)
132
+ pre_token_length = pre_token_length.floor().type(torch.int32)
133
+
134
+ decoder_out, decoder_hidden, _ = self.decoder(
135
+ enc, enc_len, pre_acoustic_embeds, pre_token_length, return_hidden=True, return_both=True
136
+ )
137
+ decoder_out = torch.log_softmax(decoder_out, dim=-1)
138
+ # seaco forward
139
+ B, N, D = bias_embed.shape
140
+ _contextual_length = torch.ones(B) * N
141
+
142
+ # ASF
143
+ hotword_scores = self.seaco_decoder.forward_asf6(
144
+ bias_embed, _contextual_length, decoder_hidden, pre_token_length
145
+ )
146
+ hotword_scores = hotword_scores[0].sum(0).sum(0)
147
+ # _ = self.decoder2(bias_embed, _contextual_length, decoder_hidden, pre_token_length)
148
+ # hotword_scores = self.decoder2.model.decoders[-1].attn_mat[0][0].sum(0).sum(0)
149
+ dec_filter = torch.sort(hotword_scores, descending=True)[1][:51]
150
+ contextual_info = bias_embed[:, dec_filter]
151
+ num_hot_word = contextual_info.shape[1]
152
+ _contextual_length = torch.Tensor([num_hot_word]).int().repeat(B).to(enc.device)
153
+
154
+ # again
155
+ cif_attended, _ = self.seaco_decoder(
156
+ contextual_info, _contextual_length, pre_acoustic_embeds, pre_token_length
157
+ )
158
+ dec_attended, _ = self.seaco_decoder(
159
+ contextual_info, _contextual_length, decoder_hidden, pre_token_length
160
+ )
161
+ merged = cif_attended + dec_attended
162
+ dha_output = self.hotword_output_layer(merged)
163
+ dha_pred = torch.log_softmax(dha_output, dim=-1)
164
+ # merging logits
165
+ dha_ids = dha_pred.max(-1)[-1]
166
+ dha_mask = (dha_ids == self.NOBIAS).int().unsqueeze(-1)
167
+ decoder_out = decoder_out * dha_mask + dha_pred * (1 - dha_mask)
168
+
169
+ # get predicted timestamps
170
+ us_alphas, us_cif_peak = self.predictor.get_upsample_timestmap(enc, mask, pre_token_length)
171
+
172
+ return decoder_out, pre_token_length, us_alphas, us_cif_peak
173
+
174
+
175
+ def export_backbone_dummy_inputs(self):
176
+ speech = torch.randn(2, 30, self.feats_dim)
177
+ speech_lengths = torch.tensor([15, 30], dtype=torch.int32)
178
+ bias_embed = torch.randn(2, 1, 512)
179
+ return (speech, speech_lengths, bias_embed)
180
+
181
+
182
+ def export_backbone_input_names(self):
183
+ return ["speech", "speech_lengths", "bias_embed"]
184
+
185
+
186
+ def export_backbone_output_names(self):
187
+ return ["logits", "token_num", "us_alphas", "us_cif_peak"]
188
+
189
+
190
+ def export_backbone_dynamic_axes(self):
191
+ return {
192
+ "speech": {0: "batch_size", 1: "feats_length"},
193
+ "speech_lengths": {
194
+ 0: "batch_size",
195
+ },
196
+ "bias_embed": {0: "batch_size", 1: "num_hotwords"},
197
+ "logits": {0: "batch_size", 1: "logits_length"},
198
+ "pre_acoustic_embeds": {1: "feats_length1"},
199
+ "us_alphas": {0: "batch_size", 1: "alphas_length"},
200
+ "us_cif_peak": {0: "batch_size", 1: "alphas_length"},
201
+ }
202
+
funasr/models/seaco_paraformer/model.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- encoding: utf-8 -*-
3
+ # Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights Reserved.
4
+ # MIT License (https://opensource.org/licenses/MIT)
5
+
6
+ import os
7
+ import re
8
+ import time
9
+ import copy
10
+ import torch
11
+ import codecs
12
+ import logging
13
+ import tempfile
14
+ import requests
15
+ import numpy as np
16
+ from typing import Dict, Tuple
17
+ from contextlib import contextmanager
18
+ from distutils.version import LooseVersion
19
+
20
+ from funasr.register import tables
21
+ from funasr.utils import postprocess_utils
22
+ from funasr.models.paraformer.model import Paraformer
23
+ from funasr.utils.datadir_writer import DatadirWriter
24
+ from funasr.models.paraformer.search import Hypothesis
25
+ from funasr.train_utils.device_funcs import force_gatherable
26
+ from funasr.models.bicif_paraformer.model import BiCifParaformer
27
+ from funasr.losses.label_smoothing_loss import LabelSmoothingLoss
28
+ from funasr.models.transformer.utils.add_sos_eos import add_sos_eos
29
+ from funasr.utils.timestamp_tools import ts_prediction_lfr6_standard
30
+ from funasr.models.transformer.utils.nets_utils import make_pad_mask, pad_list
31
+ from funasr.utils.load_utils import load_audio_text_image_video, extract_fbank
32
+
33
+ from funasr.utils.run_bmodel import EngineOV
34
+ from funasr.models.bicif_paraformer.cif_predictor import cif
35
+ from funasr.models.bicif_paraformer.cif_predictor import cif_wo_hidden
36
+
37
+ if LooseVersion(torch.__version__) >= LooseVersion("1.6.0"):
38
+ from torch.cuda.amp import autocast
39
+ else:
40
+ # Nothing to do if torch<1.6.0
41
+ @contextmanager
42
+ def autocast(enabled=True):
43
+ yield
44
+
45
+
46
+ @tables.register("model_classes", "SeacoParaformer")
47
+ class SeacoParaformer(BiCifParaformer, Paraformer):
48
+ """
49
+ Author: Speech Lab of DAMO Academy, Alibaba Group
50
+ SeACo-Paraformer: A Non-Autoregressive ASR System with Flexible and Effective Hotword Customization Ability
51
+ https://arxiv.org/abs/2308.03266
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ *args,
57
+ **kwargs,
58
+ ):
59
+ super().__init__(*args, **kwargs)
60
+
61
+ self.inner_dim = kwargs.get("inner_dim", 256)
62
+ self.bias_encoder_type = kwargs.get("bias_encoder_type", "lstm")
63
+ bias_encoder_dropout_rate = kwargs.get("bias_encoder_dropout_rate", 0.0)
64
+ bias_encoder_bid = kwargs.get("bias_encoder_bid", False)
65
+ seaco_lsm_weight = kwargs.get("seaco_lsm_weight", 0.0)
66
+ seaco_length_normalized_loss = kwargs.get("seaco_length_normalized_loss", True)
67
+
68
+ # bias encoder
69
+ if self.bias_encoder_type == "lstm":
70
+ self.bias_encoder = torch.nn.LSTM(
71
+ self.inner_dim,
72
+ self.inner_dim,
73
+ 2,
74
+ batch_first=True,
75
+ dropout=bias_encoder_dropout_rate,
76
+ bidirectional=bias_encoder_bid,
77
+ )
78
+ if bias_encoder_bid:
79
+ self.lstm_proj = torch.nn.Linear(self.inner_dim * 2, self.inner_dim)
80
+ else:
81
+ self.lstm_proj = None
82
+ # self.bias_embed = torch.nn.Embedding(self.vocab_size, self.inner_dim)
83
+ elif self.bias_encoder_type == "mean":
84
+ self.bias_embed = torch.nn.Embedding(self.vocab_size, self.inner_dim)
85
+ else:
86
+ logging.error("Unsupport bias encoder type: {}".format(self.bias_encoder_type))
87
+
88
+ # seaco decoder
89
+ seaco_decoder = kwargs.get("seaco_decoder", None)
90
+ if seaco_decoder is not None:
91
+ seaco_decoder_conf = kwargs.get("seaco_decoder_conf")
92
+ seaco_decoder_class = tables.decoder_classes.get(seaco_decoder)
93
+ self.seaco_decoder = seaco_decoder_class(
94
+ vocab_size=self.vocab_size,
95
+ encoder_output_size=self.inner_dim,
96
+ **seaco_decoder_conf,
97
+ )
98
+ self.hotword_output_layer = torch.nn.Linear(self.inner_dim, self.vocab_size)
99
+ self.criterion_seaco = LabelSmoothingLoss(
100
+ size=self.vocab_size,
101
+ padding_idx=self.ignore_id,
102
+ smoothing=seaco_lsm_weight,
103
+ normalize_length=seaco_length_normalized_loss,
104
+ )
105
+ self.train_decoder = kwargs.get("train_decoder", True)
106
+ self.seaco_weight = kwargs.get("seaco_weight", 0.01)
107
+ self.NO_BIAS = kwargs.get("NO_BIAS", 8377)
108
+ self.predictor_name = kwargs.get("predictor")
109
+
110
+ # self.encoder_bmodel = EngineOV(kwargs["model_path"]+"/encoder_fp32_10b.bmodel", device_id=kwargs['dev_id'])
111
+ self.encoder_bmodel = EngineOV(kwargs["model_path"]+"/encoder_fp16_10b.bmodel", device_id=kwargs['dev_id'])
112
+
113
+ # self.predictor_bmodel = EngineOV(kwargs["model_path"]+"/predictor_fp32_10b.bmodel", device_id=kwargs['dev_id'])
114
+ self.predictor_bmodel = EngineOV(kwargs["model_path"]+"/predictor_fp16_10b.bmodel", device_id=kwargs['dev_id'])
115
+
116
+ self.decoder_bmodel = EngineOV(kwargs["model_path"]+"/decoder_fp32_10b.bmodel", device_id=kwargs['dev_id'])
117
+
118
+ def forward(
119
+ self,
120
+ speech: torch.Tensor,
121
+ speech_lengths: torch.Tensor,
122
+ text: torch.Tensor,
123
+ text_lengths: torch.Tensor,
124
+ **kwargs,
125
+ ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
126
+ """Frontend + Encoder + Decoder + Calc loss
127
+
128
+ Args:
129
+ speech: (Batch, Length, ...)
130
+ speech_lengths: (Batch, )
131
+ text: (Batch, Length)
132
+ text_lengths: (Batch,)
133
+ """
134
+ if len(text_lengths.size()) > 1:
135
+ text_lengths = text_lengths[:, 0]
136
+ if len(speech_lengths.size()) > 1:
137
+ speech_lengths = speech_lengths[:, 0]
138
+ # Check that batch_size is unified
139
+ assert (
140
+ speech.shape[0] == speech_lengths.shape[0] == text.shape[0] == text_lengths.shape[0]
141
+ ), (speech.shape, speech_lengths.shape, text.shape, text_lengths.shape)
142
+
143
+ hotword_pad = kwargs.get("hotword_pad")
144
+ hotword_lengths = kwargs.get("hotword_lengths")
145
+ seaco_label_pad = kwargs.get("seaco_label_pad")
146
+ if len(hotword_lengths.size()) > 1:
147
+ hotword_lengths = hotword_lengths[:, 0]
148
+
149
+ batch_size = speech.shape[0]
150
+ # for data-parallel
151
+ text = text[:, : text_lengths.max()]
152
+ speech = speech[:, : speech_lengths.max()]
153
+
154
+ # 1. Encoder
155
+ encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
156
+ if self.predictor_bias == 1:
157
+ _, ys_pad = add_sos_eos(text, self.sos, self.eos, self.ignore_id)
158
+ ys_lengths = text_lengths + self.predictor_bias
159
+
160
+ stats = dict()
161
+ loss_seaco = self._calc_seaco_loss(
162
+ encoder_out,
163
+ encoder_out_lens,
164
+ ys_pad,
165
+ ys_lengths,
166
+ hotword_pad,
167
+ hotword_lengths,
168
+ seaco_label_pad,
169
+ )
170
+ if self.train_decoder:
171
+ loss_att, acc_att, _, _, _ = self._calc_att_loss(
172
+ encoder_out, encoder_out_lens, text, text_lengths
173
+ )
174
+ loss = loss_seaco + loss_att * self.seaco_weight
175
+ stats["loss_att"] = torch.clone(loss_att.detach())
176
+ stats["acc_att"] = acc_att
177
+ else:
178
+ loss = loss_seaco
179
+
180
+ stats["loss_seaco"] = torch.clone(loss_seaco.detach())
181
+ stats["loss"] = torch.clone(loss.detach())
182
+
183
+ # force_gatherable: to-device and to-tensor if scalar for DataParallel
184
+ if self.length_normalized_loss:
185
+ batch_size = (text_lengths + self.predictor_bias).sum()
186
+ loss, stats, weight = force_gatherable((loss, stats, batch_size), loss.device)
187
+ return loss, stats, weight
188
+
189
+ def _merge(self, cif_attended, dec_attended):
190
+ return cif_attended + dec_attended
191
+
192
+ def calc_predictor(self, encoder_out, encoder_out_lens):
193
+ encoder_out_mask = (
194
+ ~make_pad_mask(encoder_out_lens, maxlen=encoder_out.size(1))[:, None, :]
195
+ ).to(encoder_out.device)
196
+ predictor_outs = self.predictor(
197
+ encoder_out, None, encoder_out_mask, ignore_id=self.ignore_id
198
+ )
199
+ return predictor_outs[:4]
200
+
201
+ def _calc_seaco_loss(
202
+ self,
203
+ encoder_out: torch.Tensor,
204
+ encoder_out_lens: torch.Tensor,
205
+ ys_pad: torch.Tensor,
206
+ ys_lengths: torch.Tensor,
207
+ hotword_pad: torch.Tensor,
208
+ hotword_lengths: torch.Tensor,
209
+ seaco_label_pad: torch.Tensor,
210
+ ):
211
+ # predictor forward
212
+ encoder_out_mask = (
213
+ ~make_pad_mask(encoder_out_lens, maxlen=encoder_out.size(1))[:, None, :]
214
+ ).to(encoder_out.device)
215
+ pre_acoustic_embeds = self.predictor(
216
+ encoder_out, ys_pad, encoder_out_mask, ignore_id=self.ignore_id
217
+ )[0]
218
+ # decoder forward
219
+ decoder_out, _ = self.decoder(
220
+ encoder_out, encoder_out_lens, pre_acoustic_embeds, ys_lengths, return_hidden=True
221
+ )
222
+ selected = self._hotword_representation(hotword_pad, hotword_lengths)
223
+ contextual_info = (
224
+ selected.squeeze(0).repeat(encoder_out.shape[0], 1, 1).to(encoder_out.device)
225
+ )
226
+ num_hot_word = contextual_info.shape[1]
227
+ _contextual_length = (
228
+ torch.Tensor([num_hot_word]).int().repeat(encoder_out.shape[0]).to(encoder_out.device)
229
+ )
230
+ # dha core
231
+ cif_attended, _ = self.seaco_decoder(
232
+ contextual_info, _contextual_length, pre_acoustic_embeds, ys_lengths
233
+ )
234
+ dec_attended, _ = self.seaco_decoder(
235
+ contextual_info, _contextual_length, decoder_out, ys_lengths
236
+ )
237
+ merged = self._merge(cif_attended, dec_attended)
238
+ dha_output = self.hotword_output_layer(
239
+ merged[:, :-1]
240
+ ) # remove the last token in loss calculation
241
+ loss_att = self.criterion_seaco(dha_output, seaco_label_pad)
242
+ return loss_att
243
+
244
+ def _seaco_decode_with_ASF(
245
+ self,
246
+ encoder_out,
247
+ encoder_out_lens,
248
+ sematic_embeds,
249
+ ys_pad_lens,
250
+ hw_list,
251
+ nfilter=50,
252
+ seaco_weight=1.0,
253
+ ):
254
+ # decoder forward
255
+
256
+ """
257
+ decoder_out, decoder_hidden, _ = self.decoder(
258
+ encoder_out,
259
+ encoder_out_lens,
260
+ sematic_embeds,
261
+ ys_pad_lens,
262
+ return_hidden=True,
263
+ return_both=True,
264
+ )
265
+
266
+ decoder_pred = torch.log_softmax(decoder_out, dim=-1)
267
+ """
268
+ outputs = self.decoder_bmodel([encoder_out.detach().cpu().numpy(), encoder_out_lens.detach().cpu().numpy().astype(np.int32), sematic_embeds.detach().cpu().numpy(), ys_pad_lens.detach().cpu().numpy().astype(np.int32)])
269
+ decoder_pred, decoder_hidden = torch.from_numpy(outputs[0]), torch.from_numpy(outputs[1])
270
+ if hw_list is not None:
271
+ hw_lengths = [len(i) for i in hw_list]
272
+ hw_list_ = [torch.Tensor(i).long() for i in hw_list]
273
+ hw_list_pad = pad_list(hw_list_, 0).to(encoder_out.device)
274
+ selected = self._hotword_representation(
275
+ hw_list_pad, torch.Tensor(hw_lengths).int().to(encoder_out.device)
276
+ )
277
+
278
+ contextual_info = (
279
+ selected.squeeze(0).repeat(encoder_out.shape[0], 1, 1).to(encoder_out.device)
280
+ )
281
+ num_hot_word = contextual_info.shape[1]
282
+ _contextual_length = (
283
+ torch.Tensor([num_hot_word])
284
+ .int()
285
+ .repeat(encoder_out.shape[0])
286
+ .to(encoder_out.device)
287
+ )
288
+
289
+ # ASF Core
290
+ if nfilter > 0 and nfilter < num_hot_word:
291
+ hotword_scores = self.seaco_decoder.forward_asf6(
292
+ contextual_info, _contextual_length, decoder_hidden, ys_pad_lens
293
+ )
294
+ hotword_scores = hotword_scores[0].sum(0).sum(0)
295
+ # hotword_scores /= torch.sqrt(torch.tensor(hw_lengths)[:-1].float()).to(hotword_scores.device)
296
+ dec_filter = torch.topk(hotword_scores, min(nfilter, num_hot_word - 1))[1].tolist()
297
+ add_filter = dec_filter
298
+ add_filter.append(len(hw_list_pad) - 1)
299
+ # filter hotword embedding
300
+ selected = selected[add_filter]
301
+ # again
302
+ contextual_info = (
303
+ selected.squeeze(0).repeat(encoder_out.shape[0], 1, 1).to(encoder_out.device)
304
+ )
305
+ num_hot_word = contextual_info.shape[1]
306
+ _contextual_length = (
307
+ torch.Tensor([num_hot_word])
308
+ .int()
309
+ .repeat(encoder_out.shape[0])
310
+ .to(encoder_out.device)
311
+ )
312
+
313
+ # SeACo Core
314
+ cif_attended, _ = self.seaco_decoder(
315
+ contextual_info, _contextual_length, sematic_embeds, ys_pad_lens
316
+ )
317
+ dec_attended, _ = self.seaco_decoder(
318
+ contextual_info, _contextual_length, decoder_hidden, ys_pad_lens
319
+ )
320
+ merged = self._merge(cif_attended, dec_attended)
321
+
322
+ dha_output = self.hotword_output_layer(
323
+ merged
324
+ ) # remove the last token in loss calculation
325
+ dha_pred = torch.log_softmax(dha_output, dim=-1)
326
+
327
+ def _merge_res(dec_output, dha_output):
328
+ lmbd = torch.Tensor([seaco_weight] * dha_output.shape[0])
329
+ dha_ids = dha_output.max(-1)[-1] # [0]
330
+ dha_mask = (dha_ids == self.NO_BIAS).int().unsqueeze(-1)
331
+ a = (1 - lmbd) / lmbd
332
+ b = 1 / lmbd
333
+ a, b = a.to(dec_output.device), b.to(dec_output.device)
334
+ dha_mask = (dha_mask + a.reshape(-1, 1, 1)) / b.reshape(-1, 1, 1)
335
+ # logits = dec_output * dha_mask + dha_output[:,:,:-1] * (1-dha_mask)
336
+ logits = dec_output * dha_mask + dha_output[:, :, :] * (1 - dha_mask)
337
+ return logits
338
+
339
+ merged_pred = _merge_res(decoder_pred, dha_pred)
340
+ return merged_pred
341
+ else:
342
+ return decoder_pred
343
+
344
+ def _hotword_representation(self, hotword_pad, hotword_lengths):
345
+ if self.bias_encoder_type != "lstm":
346
+ logging.error("Unsupported bias encoder type")
347
+
348
+ """
349
+ hw_embed = self.decoder.embed(hotword_pad)
350
+ hw_embed, (_, _) = self.bias_encoder(hw_embed)
351
+ if self.lstm_proj is not None:
352
+ hw_embed = self.lstm_proj(hw_embed)
353
+ _ind = np.arange(0, hw_embed.shape[0]).tolist()
354
+ selected = hw_embed[_ind, [i-1 for i in hotword_lengths.detach().cpu().tolist()]]
355
+ return selected
356
+ """
357
+
358
+ # hw_embed = self.sac_embedding(hotword_pad)
359
+ hw_embed = self.decoder.embed(hotword_pad)
360
+ hw_embed = torch.nn.utils.rnn.pack_padded_sequence(
361
+ hw_embed,
362
+ hotword_lengths.cpu().type(torch.int64),
363
+ batch_first=True,
364
+ enforce_sorted=False,
365
+ )
366
+ packed_rnn_output, _ = self.bias_encoder(hw_embed)
367
+ rnn_output = torch.nn.utils.rnn.pad_packed_sequence(packed_rnn_output, batch_first=True)[0]
368
+ if self.lstm_proj is not None:
369
+ hw_hidden = self.lstm_proj(rnn_output)
370
+ else:
371
+ hw_hidden = rnn_output
372
+ _ind = np.arange(0, hw_hidden.shape[0]).tolist()
373
+ selected = hw_hidden[_ind, [i - 1 for i in hotword_lengths.detach().cpu().tolist()]]
374
+ return selected
375
+
376
+ def inference(
377
+ self,
378
+ data_in,
379
+ data_lengths=None,
380
+ key: list = None,
381
+ tokenizer=None,
382
+ frontend=None,
383
+ **kwargs,
384
+ ):
385
+
386
+ # init beamsearch
387
+ is_use_ctc = kwargs.get("decoding_ctc_weight", 0.0) > 0.00001 and self.ctc != None
388
+ is_use_lm = (
389
+ kwargs.get("lm_weight", 0.0) > 0.00001 and kwargs.get("lm_file", None) is not None
390
+ )
391
+ if self.beam_search is None and (is_use_lm or is_use_ctc):
392
+ logging.info("enable beam_search")
393
+ self.init_beam_search(**kwargs)
394
+ self.nbest = kwargs.get("nbest", 1)
395
+ meta_data = {}
396
+
397
+ # extract fbank feats
398
+ time1 = time.perf_counter()
399
+ audio_sample_list = load_audio_text_image_video(
400
+ data_in, fs=frontend.fs, audio_fs=kwargs.get("fs", 16000)
401
+ )
402
+ time2 = time.perf_counter()
403
+ meta_data["load_data"] = f"{time2 - time1:0.3f}"
404
+ speech, speech_lengths = extract_fbank(
405
+ audio_sample_list, data_type=kwargs.get("data_type", "sound"), frontend=frontend
406
+ )
407
+ time3 = time.perf_counter()
408
+ meta_data["extract_feat"] = f"{time3 - time2:0.3f}"
409
+ meta_data["batch_data_time"] = (
410
+ speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000
411
+ )
412
+
413
+ speech = speech.to(device=kwargs["device"])
414
+ speech_lengths = speech_lengths.to(device=kwargs["device"])
415
+
416
+ # hotword
417
+ """
418
+ self.hotword_list = self.generate_hotwords_list(
419
+ kwargs.get("hotword", None), tokenizer=tokenizer, frontend=frontend
420
+ )
421
+ """
422
+ self.hotword_list = None
423
+
424
+ # Encoder
425
+ #encoder_out, encoder_out_lens = self.encode(speech, speech_lengths)
426
+ outputs = self.encoder_bmodel([speech.detach().cpu().numpy(), speech_lengths.detach().cpu().numpy()])
427
+ encoder_out = torch.from_numpy(outputs[0])
428
+ encoder_out_lens = speech_lengths
429
+ if isinstance(encoder_out, tuple):
430
+ encoder_out = encoder_out[0]
431
+
432
+ """
433
+ # predictor
434
+ predictor_outs = self.calc_predictor(encoder_out, encoder_out_lens)
435
+ pre_acoustic_embeds, pre_token_length = predictor_outs[0], predictor_outs[1]
436
+ """
437
+ hidden, alphas, pre_token_length = torch.from_numpy(outputs[1]), torch.from_numpy(outputs[2]), torch.from_numpy(outputs[3])
438
+ pre_acoustic_embeds, pre_peak_index = cif(hidden, alphas, 1.0)
439
+ token_num_int = torch.max(pre_token_length).type(torch.int32).item()
440
+ pre_acoustic_embeds = pre_acoustic_embeds[:, :token_num_int, :]
441
+ pre_token_length = pre_token_length.round().long()
442
+ if torch.max(pre_token_length) < 1:
443
+ return ([],)
444
+
445
+ decoder_out = self._seaco_decode_with_ASF(
446
+ encoder_out,
447
+ encoder_out_lens,
448
+ pre_acoustic_embeds,
449
+ pre_token_length,
450
+ hw_list=self.hotword_list,
451
+ )
452
+
453
+ # decoder_out, _ = decoder_outs[0], decoder_outs[1]
454
+ if self.predictor_name == "CifPredictorV3":
455
+ outputs = self.predictor_bmodel([encoder_out.detach().cpu().numpy(), encoder_out_lens.detach().cpu().numpy().astype(np.int32)])
456
+ alphas2 = torch.from_numpy(outputs[0])
457
+ us_alphas = alphas2 * ((pre_token_length / torch.from_numpy(outputs[1]))[:, None].repeat(1, alphas2.size(1)))
458
+ us_peaks = cif_wo_hidden(us_alphas, 1.0 - 1e-4)
459
+ """
460
+ _, _, us_alphas, us_peaks = self.calc_predictor_timestamp(
461
+ encoder_out, encoder_out_lens, pre_token_length
462
+ )
463
+ """
464
+ else:
465
+ us_alphas = None
466
+
467
+ results = []
468
+ b, n, d = decoder_out.size()
469
+ for i in range(b):
470
+ x = encoder_out[i, : encoder_out_lens[i], :]
471
+ am_scores = decoder_out[i, : pre_token_length[i], :]
472
+ if self.beam_search is not None:
473
+ nbest_hyps = self.beam_search(
474
+ x=x,
475
+ am_scores=am_scores,
476
+ maxlenratio=kwargs.get("maxlenratio", 0.0),
477
+ minlenratio=kwargs.get("minlenratio", 0.0),
478
+ )
479
+
480
+ nbest_hyps = nbest_hyps[: self.nbest]
481
+ else:
482
+
483
+ yseq = am_scores.argmax(dim=-1)
484
+ score = am_scores.max(dim=-1)[0]
485
+ score = torch.sum(score, dim=-1)
486
+ # pad with mask tokens to ensure compatibility with sos/eos tokens
487
+ yseq = torch.tensor([self.sos] + yseq.tolist() + [self.eos], device=yseq.device)
488
+ nbest_hyps = [Hypothesis(yseq=yseq, score=score)]
489
+ for nbest_idx, hyp in enumerate(nbest_hyps):
490
+ ibest_writer = None
491
+ if kwargs.get("output_dir") is not None:
492
+ if not hasattr(self, "writer"):
493
+ self.writer = DatadirWriter(kwargs.get("output_dir"))
494
+ ibest_writer = self.writer[f"{nbest_idx + 1}best_recog"]
495
+
496
+ # remove sos/eos and get results
497
+ last_pos = -1
498
+ if isinstance(hyp.yseq, list):
499
+ token_int = hyp.yseq[1:last_pos]
500
+ else:
501
+ token_int = hyp.yseq[1:last_pos].tolist()
502
+
503
+ # remove blank symbol id, which is assumed to be 0
504
+ token_int = list(
505
+ filter(
506
+ lambda x: x != self.eos and x != self.sos and x != self.blank_id, token_int
507
+ )
508
+ )
509
+
510
+ if tokenizer is not None:
511
+ # Change integer-ids to tokens
512
+ token = tokenizer.ids2tokens(token_int)
513
+ text = tokenizer.tokens2text(token)
514
+ if us_alphas is not None:
515
+ _, timestamp = ts_prediction_lfr6_standard(
516
+ us_alphas[i][: encoder_out_lens[i] * 3],
517
+ us_peaks[i][: encoder_out_lens[i] * 3],
518
+ copy.copy(token),
519
+ vad_offset=kwargs.get("begin_time", 0),
520
+ )
521
+ text_postprocessed, time_stamp_postprocessed, _ = (
522
+ postprocess_utils.sentence_postprocess(token, timestamp)
523
+ )
524
+ result_i = {
525
+ "key": key[i],
526
+ "text": text_postprocessed,
527
+ "timestamp": time_stamp_postprocessed,
528
+ }
529
+ if ibest_writer is not None:
530
+ ibest_writer["token"][key[i]] = " ".join(token)
531
+ ibest_writer["timestamp"][key[i]] = time_stamp_postprocessed
532
+ ibest_writer["text"][key[i]] = text_postprocessed
533
+ else:
534
+ text_postprocessed, _ = postprocess_utils.sentence_postprocess(token)
535
+ result_i = {"key": key[i], "text": text_postprocessed}
536
+ if ibest_writer is not None:
537
+ ibest_writer["token"][key[i]] = " ".join(token)
538
+ ibest_writer["text"][key[i]] = text_postprocessed
539
+ else:
540
+ result_i = {"key": key[i], "token_int": token_int}
541
+ results.append(result_i)
542
+
543
+ return results, meta_data
544
+
545
+ def generate_hotwords_list(self, hotword_list_or_file, tokenizer=None, frontend=None):
546
+ def load_seg_dict(seg_dict_file):
547
+ seg_dict = {}
548
+ assert isinstance(seg_dict_file, str)
549
+ with open(seg_dict_file, "r", encoding="utf8") as f:
550
+ lines = f.readlines()
551
+ for line in lines:
552
+ s = line.strip().split()
553
+ key = s[0]
554
+ value = s[1:]
555
+ seg_dict[key] = " ".join(value)
556
+ return seg_dict
557
+
558
+ def seg_tokenize(txt, seg_dict):
559
+ pattern = re.compile(r"^[\u4E00-\u9FA50-9]+$")
560
+ out_txt = ""
561
+ for word in txt:
562
+ word = word.lower()
563
+ if word in seg_dict:
564
+ out_txt += seg_dict[word] + " "
565
+ else:
566
+ if pattern.match(word):
567
+ for char in word:
568
+ if char in seg_dict:
569
+ out_txt += seg_dict[char] + " "
570
+ else:
571
+ out_txt += "<unk>" + " "
572
+ else:
573
+ out_txt += "<unk>" + " "
574
+ return out_txt.strip().split()
575
+
576
+ seg_dict = None
577
+ if frontend.cmvn_file is not None:
578
+ model_dir = os.path.dirname(frontend.cmvn_file)
579
+ seg_dict_file = os.path.join(model_dir, "seg_dict")
580
+ if os.path.exists(seg_dict_file):
581
+ seg_dict = load_seg_dict(seg_dict_file)
582
+ else:
583
+ seg_dict = None
584
+ # for None
585
+ if hotword_list_or_file is None:
586
+ hotword_list = None
587
+ # for local txt inputs
588
+ elif os.path.exists(hotword_list_or_file) and hotword_list_or_file.endswith(".txt"):
589
+ logging.info("Attempting to parse hotwords from local txt...")
590
+ hotword_list = []
591
+ hotword_str_list = []
592
+ with codecs.open(hotword_list_or_file, "r") as fin:
593
+ for line in fin.readlines():
594
+ hw = line.strip()
595
+ hw_list = hw.split()
596
+ if seg_dict is not None:
597
+ hw_list = seg_tokenize(hw_list, seg_dict)
598
+ hotword_str_list.append(hw)
599
+ hotword_list.append(tokenizer.tokens2ids(hw_list))
600
+ hotword_list.append([self.sos])
601
+ hotword_str_list.append("<s>")
602
+ logging.info(
603
+ "Initialized hotword list from file: {}, hotword list: {}.".format(
604
+ hotword_list_or_file, hotword_str_list
605
+ )
606
+ )
607
+ # for url, download and generate txt
608
+ elif hotword_list_or_file.startswith("http"):
609
+ logging.info("Attempting to parse hotwords from url...")
610
+ work_dir = tempfile.TemporaryDirectory().name
611
+ if not os.path.exists(work_dir):
612
+ os.makedirs(work_dir)
613
+ text_file_path = os.path.join(work_dir, os.path.basename(hotword_list_or_file))
614
+ local_file = requests.get(hotword_list_or_file)
615
+ open(text_file_path, "wb").write(local_file.content)
616
+ hotword_list_or_file = text_file_path
617
+ hotword_list = []
618
+ hotword_str_list = []
619
+ with codecs.open(hotword_list_or_file, "r") as fin:
620
+ for line in fin.readlines():
621
+ hw = line.strip()
622
+ hw_list = hw.split()
623
+ if seg_dict is not None:
624
+ hw_list = seg_tokenize(hw_list, seg_dict)
625
+ hotword_str_list.append(hw)
626
+ hotword_list.append(tokenizer.tokens2ids(hw_list))
627
+ hotword_list.append([self.sos])
628
+ hotword_str_list.append("<s>")
629
+ logging.info(
630
+ "Initialized hotword list from file: {}, hotword list: {}.".format(
631
+ hotword_list_or_file, hotword_str_list
632
+ )
633
+ )
634
+ # for text str input
635
+ elif not hotword_list_or_file.endswith(".txt"):
636
+ logging.info("Attempting to parse hotwords as str...")
637
+ hotword_list = []
638
+ hotword_str_list = []
639
+ for hw in hotword_list_or_file.strip().split():
640
+ hotword_str_list.append(hw)
641
+ hw_list = hw.strip().split()
642
+ if seg_dict is not None:
643
+ hw_list = seg_tokenize(hw_list, seg_dict)
644
+ hotword_list.append(tokenizer.tokens2ids(hw_list))
645
+ hotword_list.append([self.sos])
646
+ hotword_str_list.append("<s>")
647
+ logging.info("Hotword list: {}.".format(hotword_str_list))
648
+ else:
649
+ hotword_list = None
650
+ return hotword_list
651
+
652
+ def export(
653
+ self,
654
+ **kwargs,
655
+ ):
656
+ if "max_seq_len" not in kwargs:
657
+ kwargs["max_seq_len"] = 512
658
+ from .export_meta import export_rebuild_model
659
+
660
+ models = export_rebuild_model(model=self, **kwargs)
661
+ return models
funasr/models/seaco_paraformer/template.yaml ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is an example that demonstrates how to configure a model file.
2
+ # You can modify the configuration according to your own requirements.
3
+
4
+ # to print the register_table:
5
+ # from funasr.register import tables
6
+ # tables.print()
7
+
8
+ # network architecture
9
+ model: SeacoParaformer
10
+ model_conf:
11
+ ctc_weight: 0.0
12
+ lsm_weight: 0.1
13
+ length_normalized_loss: true
14
+ predictor_weight: 1.0
15
+ predictor_bias: 1
16
+ sampling_ratio: 0.75
17
+ inner_dim: 512
18
+ bias_encoder_type: lstm
19
+ bias_encoder_bid: false
20
+ seaco_lsm_weight: 0.1
21
+ seaco_length_normal: true
22
+ train_decoder: false
23
+ NO_BIAS: 8377
24
+
25
+ # encoder
26
+ encoder: SANMEncoder
27
+ encoder_conf:
28
+ output_size: 512
29
+ attention_heads: 4
30
+ linear_units: 2048
31
+ num_blocks: 50
32
+ dropout_rate: 0.1
33
+ positional_dropout_rate: 0.1
34
+ attention_dropout_rate: 0.1
35
+ input_layer: pe
36
+ pos_enc_class: SinusoidalPositionEncoder
37
+ normalize_before: true
38
+ kernel_size: 11
39
+ sanm_shfit: 0
40
+ selfattention_layer_type: sanm
41
+
42
+ # decoder
43
+ decoder: ParaformerSANMDecoder
44
+ decoder_conf:
45
+ attention_heads: 4
46
+ linear_units: 2048
47
+ num_blocks: 16
48
+ dropout_rate: 0.1
49
+ positional_dropout_rate: 0.1
50
+ self_attention_dropout_rate: 0.1
51
+ src_attention_dropout_rate: 0.1
52
+ att_layer_num: 16
53
+ kernel_size: 11
54
+ sanm_shfit: 0
55
+
56
+ # seaco decoder
57
+ seaco_decoder: ParaformerSANMDecoder
58
+ seaco_decoder_conf:
59
+ attention_heads: 4
60
+ linear_units: 1024
61
+ num_blocks: 4
62
+ dropout_rate: 0.1
63
+ positional_dropout_rate: 0.1
64
+ self_attention_dropout_rate: 0.1
65
+ src_attention_dropout_rate: 0.1
66
+ kernel_size: 21
67
+ sanm_shfit: 0
68
+ use_output_layer: false
69
+ wo_input_layer: true
70
+
71
+ predictor: CifPredictorV3
72
+ predictor_conf:
73
+ idim: 512
74
+ threshold: 1.0
75
+ l_order: 1
76
+ r_order: 1
77
+ tail_threshold: 0.45
78
+ smooth_factor2: 0.25
79
+ noise_threshold2: 0.01
80
+ upsample_times: 3
81
+ use_cif1_cnn: false
82
+ upsample_type: cnn_blstm
83
+
84
+ # frontend related
85
+ frontend: WavFrontend
86
+ frontend_conf:
87
+ fs: 16000
88
+ window: hamming
89
+ n_mels: 80
90
+ frame_length: 25
91
+ frame_shift: 10
92
+ lfr_m: 7
93
+ lfr_n: 6
94
+ dither: 0.0
95
+
96
+ specaug: SpecAugLFR
97
+ specaug_conf:
98
+ apply_time_warp: false
99
+ time_warp_window: 5
100
+ time_warp_mode: bicubic
101
+ apply_freq_mask: true
102
+ freq_mask_width_range:
103
+ - 0
104
+ - 30
105
+ lfr_rate: 6
106
+ num_freq_mask: 1
107
+ apply_time_mask: true
108
+ time_mask_width_range:
109
+ - 0
110
+ - 12
111
+ num_time_mask: 1
112
+
113
+ train_conf:
114
+ accum_grad: 1
115
+ grad_clip: 5
116
+ max_epoch: 150
117
+ val_scheduler_criterion:
118
+ - valid
119
+ - acc
120
+ best_model_criterion:
121
+ - - valid
122
+ - acc
123
+ - max
124
+ keep_nbest_models: 10
125
+ log_interval: 50
126
+
127
+ optim: adam
128
+ optim_conf:
129
+ lr: 0.0005
130
+ scheduler: warmuplr
131
+ scheduler_conf:
132
+ warmup_steps: 30000
133
+
134
+ dataset: AudioDataset
135
+ dataset_conf:
136
+ index_ds: IndexDSJsonl
137
+ batch_sampler: BatchSampler
138
+ batch_type: example # example or length
139
+ batch_size: 1 # if batch_type is example, batch_size is the numbers of samples; if length, batch_size is source_token_len+target_token_len;
140
+ max_token_length: 2048 # filter samples if source_token_len+target_token_len > max_token_length,
141
+ buffer_size: 500
142
+ shuffle: True
143
+ num_workers: 0
144
+
145
+ tokenizer: CharTokenizer
146
+ tokenizer_conf:
147
+ unk_symbol: <unk>
148
+ split_with_space: true
149
+
150
+
151
+ ctc_conf:
152
+ dropout_rate: 0.0
153
+ ctc_type: builtin
154
+ reduce: true
155
+ ignore_nan_grad: true
156
+ normalize: null