Batch CTC greedy decoding for fast inference. Uses CTC output with greedy decoding (argmax + collapse repeats + remove blanks). Much faster than autoregressive beam search, with comparable accuracy.
(
self,
data_in,
data_lengths=None,
key: list = None,
tokenizer=None,
frontend=None,
**kwargs,
)
| 388 | return loss_ctc, cer_ctc |
| 389 | |
| 390 | def inference_batch_ctc( |
| 391 | self, |
| 392 | data_in, |
| 393 | data_lengths=None, |
| 394 | key: list = None, |
| 395 | tokenizer=None, |
| 396 | frontend=None, |
| 397 | **kwargs, |
| 398 | ): |
| 399 | """Batch CTC greedy decoding for fast inference. |
| 400 | |
| 401 | Uses CTC output with greedy decoding (argmax + collapse repeats + remove blanks). |
| 402 | Much faster than autoregressive beam search, with comparable accuracy. |
| 403 | """ |
| 404 | meta_data = {} |
| 405 | |
| 406 | # extract fbank feats |
| 407 | time1 = time.perf_counter() |
| 408 | audio_sample_list = load_audio_text_image_video( |
| 409 | data_in, |
| 410 | fs=frontend.fs, |
| 411 | audio_fs=kwargs.get("fs", 16000), |
| 412 | data_type=kwargs.get("data_type", "sound"), |
| 413 | tokenizer=tokenizer, |
| 414 | ) |
| 415 | time2 = time.perf_counter() |
| 416 | meta_data["load_data"] = f"{time2 - time1:0.3f}" |
| 417 | speech, speech_lengths = extract_fbank( |
| 418 | audio_sample_list, data_type=kwargs.get("data_type", "sound"), frontend=frontend |
| 419 | ) |
| 420 | time3 = time.perf_counter() |
| 421 | meta_data["extract_feat"] = f"{time3 - time2:0.3f}" |
| 422 | meta_data["batch_data_time"] = ( |
| 423 | speech_lengths.sum().item() * frontend.frame_shift * frontend.lfr_n / 1000 |
| 424 | ) |
| 425 | |
| 426 | speech = speech.to(device=kwargs["device"]) |
| 427 | speech_lengths = speech_lengths.to(device=kwargs["device"]) |
| 428 | |
| 429 | # Encoder |
| 430 | encoder_out, encoder_out_lens = self.encode(speech, speech_lengths) |
| 431 | if isinstance(encoder_out, tuple): |
| 432 | encoder_out = encoder_out[0] |
| 433 | |
| 434 | # CTC log probs |
| 435 | ctc_logprobs = self.ctc.log_softmax(encoder_out) |
| 436 | |
| 437 | results = [] |
| 438 | b = encoder_out.size(0) |
| 439 | if key is None: |
| 440 | key = [f"utt_{i}" for i in range(b)] |
| 441 | |
| 442 | for i in range(b): |
| 443 | x = ctc_logprobs[i, :encoder_out_lens[i].item(), :] |
| 444 | yseq = x.argmax(dim=-1) |
| 445 | yseq = torch.unique_consecutive(yseq, dim=-1) |
| 446 | mask = yseq != self.blank_id |
| 447 | token_int = yseq[mask].tolist() |
no test coverage detected