mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
Pass CIF alpha weights and peak indices to the timestamp helper in its declared order and add boundary/content regressions. Fixes #3723.
145 lines
5.4 KiB
Python
145 lines
5.4 KiB
Python
"""Regression tests for Paraformer timestamp flags and predictor arguments."""
|
|
|
|
import importlib
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import torch
|
|
|
|
|
|
class _Tokenizer:
|
|
def ids2tokens(self, token_ids):
|
|
return ["你" for _ in token_ids]
|
|
|
|
def tokens2text(self, tokens):
|
|
return "".join(tokens)
|
|
|
|
|
|
class TestParaformerTimestampContract(unittest.TestCase):
|
|
def _make_paraformer(self):
|
|
from funasr.models.paraformer.model import Paraformer
|
|
|
|
model = Paraformer.__new__(Paraformer)
|
|
torch.nn.Module.__init__(model)
|
|
model.beam_search = None
|
|
model.sos = 1
|
|
model.eos = 2
|
|
model.blank_id = 0
|
|
model.encode = MagicMock(
|
|
return_value=(torch.zeros((1, 2, 2)), torch.tensor([2]))
|
|
)
|
|
model.calc_predictor = MagicMock(
|
|
return_value=(
|
|
torch.zeros((1, 1, 2)),
|
|
torch.tensor([1.0]),
|
|
torch.ones((1, 2)),
|
|
torch.ones((1, 2)),
|
|
)
|
|
)
|
|
model.cal_decoder_with_predictor = MagicMock(
|
|
return_value=(
|
|
torch.tensor([[[0.0, 0.0, 0.0, 4.0]]]),
|
|
torch.tensor([1]),
|
|
)
|
|
)
|
|
return model
|
|
|
|
@staticmethod
|
|
def _sentence_postprocess(tokens, timestamp=None):
|
|
text = "".join(tokens)
|
|
if timestamp is None:
|
|
return text, None
|
|
return text, [[0, 100]], None
|
|
|
|
def test_pred_timestamp_precedence_and_output_timestamp_fallback(self):
|
|
paraformer_module = importlib.import_module("funasr.models.paraformer.model")
|
|
cases = (
|
|
({"output_timestamp": False}, False),
|
|
({"output_timestamp": True}, True),
|
|
({"pred_timestamp": True, "output_timestamp": False}, True),
|
|
({"pred_timestamp": False, "output_timestamp": True}, False),
|
|
)
|
|
|
|
with patch.object(
|
|
paraformer_module,
|
|
"ts_prediction_lfr6_standard",
|
|
return_value=("", [[0, 100]]),
|
|
), patch.object(
|
|
paraformer_module.postprocess_utils,
|
|
"sentence_postprocess",
|
|
side_effect=self._sentence_postprocess,
|
|
):
|
|
for timestamp_kwargs, expected_timestamp in cases:
|
|
with self.subTest(timestamp_kwargs=timestamp_kwargs):
|
|
results, _ = self._make_paraformer().inference(
|
|
torch.zeros((1, 2, 2)),
|
|
data_lengths=torch.tensor([[2]]),
|
|
key=["utt"],
|
|
tokenizer=_Tokenizer(),
|
|
frontend=None,
|
|
device="cpu",
|
|
data_type="fbank",
|
|
**timestamp_kwargs,
|
|
)
|
|
self.assertEqual("timestamp" in results[0], expected_timestamp)
|
|
|
|
def test_inference_timestamp_boundaries_with_real_helper(self):
|
|
from funasr.models.paraformer.cif_predictor import cif
|
|
from funasr.utils import timestamp_tools
|
|
|
|
cases = (
|
|
# Three fires already provide the two tokens' boundary positions.
|
|
([0.0] + [0.25] * 12, [[150, 390], [390, 780]], False),
|
|
# Two fires require the helper to normalize alphas to three boundaries.
|
|
(
|
|
[0.1, 0.1, 0.4, 0.6] + [0.0] * 4 + [0.1] * 10,
|
|
[[90, 510], [510, 1080]],
|
|
True,
|
|
),
|
|
)
|
|
for weights, expected, needs_fallback in cases:
|
|
for offset in (0, 1000):
|
|
with self.subTest(needs_fallback=needs_fallback, offset=offset):
|
|
model = self._make_paraformer()
|
|
alphas = torch.tensor([weights])
|
|
width = alphas.shape[1]
|
|
hidden = torch.zeros(1, width, 2)
|
|
_, peaks = cif(hidden, alphas, threshold=1.0)
|
|
model.encode.return_value = (hidden, torch.tensor([width]))
|
|
model.calc_predictor.return_value = (
|
|
torch.zeros(1, 2, 2),
|
|
torch.tensor([2.0]),
|
|
alphas,
|
|
peaks,
|
|
)
|
|
model.cal_decoder_with_predictor.return_value = (
|
|
torch.tensor([[[0.0, 0.0, 0.0, 4.0]] * 2]),
|
|
torch.tensor([2]),
|
|
)
|
|
with patch.object(
|
|
timestamp_tools,
|
|
"cif_wo_hidden",
|
|
wraps=timestamp_tools.cif_wo_hidden,
|
|
) as fallback:
|
|
results, _ = model.inference(
|
|
hidden,
|
|
data_lengths=torch.tensor([[width]]),
|
|
key=["utt"],
|
|
tokenizer=_Tokenizer(),
|
|
frontend=None,
|
|
device="cpu",
|
|
data_type="fbank",
|
|
pred_timestamp=True,
|
|
begin_time=offset,
|
|
)
|
|
self.assertEqual(results[0]["text"], "你 你")
|
|
self.assertEqual(
|
|
results[0]["timestamp"],
|
|
[[start + offset, end + offset] for start, end in expected],
|
|
)
|
|
self.assertEqual(fallback.call_count, int(needs_fallback))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|