Repository navigation
fix(runtime): trim batch padding from CIF peaks before funasr_onnx and funasr_torch Paraformer timestamps - #3871
Merged
LauraGPT merged 1 commit intoNov 12, 2026
Conversation
…estamps With a timestamp export (4 outputs) and batch_size > 1, the Paraformer wrappers in funasr_onnx (Paraformer, ContextualParaformer and the SeacoParaformer that inherits it) and funasr_torch (Paraformer) passed each row of us_peaks to time_stamp_lfr6_onnx with the batch padding still attached. time_stamp_lfr6_onnx takes the frame count from the array length, so the last token of every file shorter than the longest in its batch ended past the end of its own audio. With two 440 Hz tones of 1 s and 3 s and BiCif-shaped outputs whose peaks lie inside each row's own frames, batch_size=1 gives the 1 s file [[70, 470], [470, 945]], while batch_size=2 gives [[70, 470], [470, 1935]], 935 ms past its 1000 ms end. Pass each row's peaks cut to 3 * feats_len, as BiCifParaformer and SeacoParaformer in funasr/models and the C++ runtime (paraformer-torch.cpp) already do, so batch_size=2 gives the same timestamps as batch_size=1. Extend tests/test_runtime_eval_native.py: for the four wrappers, with the acoustic model session replaced by these outputs, a batched call must return the same results as one call per file.
4 of 9 tasks
LauraGPT
added a commit
that referenced
this pull request
Nov 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #3870
With a timestamp export (4 outputs) and
batch_size > 1, these wrappers pass each row ofus_peakstotime_stamp_lfr6_onnxwith the batch padding still attached:funasr_onnx:Paraformer.__call__andContextualParaformer.__call__(SeacoParaformerinherits it);funasr_torch:Paraformer.__call__.time_stamp_lfr6_onnxtakes the frame count from the array length, and the last token ends halfway between its peak and that end. So the last token of every file shorter than the longest file in its batch ends past the end of its own audio.The in-tree models already cut each row to its own length:
us_peaks[i][: encoder_out_lens[i] * 3]infunasr/models/bicif_paraformer/model.pyandfunasr/models/seaco_paraformer/model.py. The C++ libtorch runtime (paraformer-torch.cpp) does the same withparaformer_length[index]*3. #3554 made the same per-sample trim for the mainParaformer. This PR does the same in the three Python call sites: zipfeats_leninto the loop and passus_peaks_[: feat_len * 3](2 lines per call site).feats_lenis the LFR frame count fed to the model. As far as I can tell from the code, the in-treeencoder_out_lensequals it for these models, because a SANM encoder whoseinput_layerispeadds a position encoding without subsampling. I did not check this against a released model config.Two 440 Hz tones of 1 s (17 LFR frames) and 3 s (50), decoded by the real wrappers with only the model session replaced by BiCif-shaped outputs (tokens
你 好, CIF peaks inside each row's own frames, rows zero-padded like the exported graph):batch_size=1batch_size=2onmainbatch_size=2with this PRfunasr_onnxParaformer,ContextualParaformer,SeacoParaformer[[70, 470], [470, 945]][[70, 470], [470, 1935]][[70, 470], [470, 945]]funasr_torchParaformer[[70, 470], [470, 945]][[70, 470], [470, 1935]][[70, 470], [470, 945]]On
main,好ends at 1935 ms, 935 ms past the end of the 1000 ms file. The 3 s file gets[[70, 670], [1450, 2925]]in every case.funasr_torchContextualParaformerreturns no timestamps, so it is not changed. Infunasr_torch,feats_lenis an int32 tensor, and slicing with a 0-d tensor works.Type of change
Validation
I added
test_batched_timestamps_ignore_paddingtotests/test_runtime_eval_native.py, forfunasr_onnxParaformer,ContextualParaformerandSeacoParaformer, and forfunasr_torchParaformer. It loads each wrapper with its real constructor, using the synthetic hotword-fixture graphs withlfr_m=7, lfr_n=6. It replaces only the acoustic model session (ort_infer, orort_infer_bbfor the hotword classes) with the outputs above. Then it checks that each file decoded alone has the timestamps in the table, and that onebatch_size=2call returns the same results.test-runtime-eval.ymlruns this file for changes underruntime/python/onnxruntime/funasr_onnx/**andruntime/python/libtorch/funasr_torch/**, so no workflow change is needed. With the same pins it installs (Python 3.12.15, torch 2.8.0+cpu, numpy 1.26.4, onnx 1.19.0, onnxruntime 1.23.2), on Windows 11:mainsource: 4 failed, 55 passed. All four fail at the batched call:[[70, 470], [470, 1935]] != [[70, 470], [470, 945]].Also on this branch (Windows 11, Python 3.11.17, numpy 2.4.6, torch 2.10.0+cpu, onnxruntime 1.23.2):
tests/test_runtime_eval_cli.py tests/test_runtime_eval_native.py tests/test_paraformer_timestamp_padding.py tests/test_paraformer_timestamp_contract.py tests/test_timestamp_tools.py: 71 passed onmain(7651777), 75 passed on this branch.The
funasr_onnxpackage test scriptsoverlap,empty_finalandpunctuation_empty_inputpass from source.tests/test_funasr_torch_package.py: 13 passed, 1 failed, the same as onmain. The failure istest_wheel_contains_all_runtime_sources, which on Windows builds its expected file names with backslashes.black --line-length=100(24.4.0, as in pre-commit) leaves the test file unchanged.python -m compileall funasr examples testsDocs or links checked
Runtime/deployment command tested (the wrapper runs above, CPU, with the model session replaced)
No real model was run. The model outputs are synthetic, so I have not compared these timestamps with a real export.
User impact
Users of a timestamp export (BiCif Paraformer such as paraformer-large-vad-punc, or SeACo Paraformer) who decode several files per call with
batch_size > 1throughfunasr_onnxParaformer,ContextualParaformer,SeacoParaformerorfunasr_torchParaformerget the same timestamps as withbatch_size=1. Before this change, the last token of each shorter file was stretched toward the end of the longest file in its batch. Results withbatch_size=1(the default), results without timestamps, and the text are not changed.Notes for reviewers
paraformer_bin.pyfiles changes.timestamp_utils.pyandpostprocess_utils.pyare not touched.git merge-tree --write-treeof this branch with the heads of fix(postprocess): keep a trailing @@ piece instead of dropping it #3836, fix(runtime): honor use_itn in funasr_onnx and funasr_torch SenseVoiceSmall #3860, fix(runtime): honor SenseVoice ITN and preserve mixed batch query tags #3861 and fix(runtime): merge spelled-out acronyms into one word in funasr_onnx and funasr_torch sentence_postprocess #3869 gives no conflicts.