Mirror wlc952/FunASR-bmodel from ModelScope (part 2)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- funasr/models/paraformer_streaming/export_meta.py +136 -0
- funasr/models/paraformer_streaming/model.py +702 -0
- funasr/models/paraformer_streaming/template.yaml +143 -0
- funasr/models/qwen_audio/__init__.py +0 -0
- funasr/models/qwen_audio/audio.py +442 -0
- funasr/models/qwen_audio/model.py +151 -0
- funasr/models/qwen_audio/template.yaml +46 -0
- funasr/models/rwkv_bat/__init__.py +0 -0
- funasr/models/rwkv_bat/cuda_decoder/wkv_cuda.cu +135 -0
- funasr/models/rwkv_bat/cuda_decoder/wkv_op.cpp +37 -0
- funasr/models/rwkv_bat/cuda_encoder/wkv_cuda.cu +135 -0
- funasr/models/rwkv_bat/cuda_encoder/wkv_op.cpp +37 -0
- funasr/models/rwkv_bat/rwkv.py +145 -0
- funasr/models/rwkv_bat/rwkv_attention.py +608 -0
- funasr/models/rwkv_bat/rwkv_encoder.py +158 -0
- funasr/models/rwkv_bat/rwkv_feed_forward.py +89 -0
- funasr/models/rwkv_bat/rwkv_subsampling.py +250 -0
- funasr/models/rwkv_bat/template.yaml +64 -0
- funasr/models/sa_asr/__init__.py +0 -0
- funasr/models/sa_asr/attention.py +50 -0
- funasr/models/sa_asr/beam_search_sa_asr.py +540 -0
- funasr/models/sa_asr/e2e_sa_asr.py +508 -0
- funasr/models/sa_asr/transformer_decoder.py +731 -0
- funasr/models/sanm/__init__.py +0 -0
- funasr/models/sanm/attention.py +903 -0
- funasr/models/sanm/decoder.py +498 -0
- funasr/models/sanm/encoder.py +612 -0
- funasr/models/sanm/model.py +28 -0
- funasr/models/sanm/multihead_att.py +241 -0
- funasr/models/sanm/positionwise_feed_forward.py +33 -0
- funasr/models/sanm/template.yaml +121 -0
- funasr/models/sanm_kws/__init__.py +0 -0
- funasr/models/sanm_kws/export_meta.py +98 -0
- funasr/models/sanm_kws/model.py +266 -0
- funasr/models/sanm_kws_streaming/__init__.py +0 -0
- funasr/models/sanm_kws_streaming/export_meta.py +98 -0
- funasr/models/sanm_kws_streaming/model.py +442 -0
- funasr/models/scama/__init__.py +0 -0
- funasr/models/scama/beam_search.py +978 -0
- funasr/models/scama/chunk_utilis.py +493 -0
- funasr/models/scama/decoder.py +498 -0
- funasr/models/scama/encoder.py +499 -0
- funasr/models/scama/model.py +739 -0
- funasr/models/scama/template.yaml +127 -0
- funasr/models/scama/utils.py +89 -0
- funasr/models/seaco_paraformer/__init__.py +0 -0
- funasr/models/seaco_paraformer/export_meta.py +202 -0
- funasr/models/seaco_paraformer/model.py +661 -0
- 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
|