Repository navigation
funasr_onnx streaming Paraformer repeats the last characters when chunk_size[0] is 0 #3789
Description
Description
funasr_onnx.paraformer_online_bin.Paraformer.add_overlap_chunk keeps the left context for the next chunk with
cache["feats"] = overlap_feats[:, -self.chunk_size[0] :, :]When chunk_size[0] == 0, -0: is 0:, so the slice returns every frame instead of none. When the final call is split into two chunks, the second chunk is prefixed with all of the first chunk's features, so the tail is decoded twice and the last characters are repeated.
chunk_size=[0, 10, 5] is the streaming setting used in README.md, README_zh.md and docs/python_api*.md, so users following the docs with the ONNX runtime hit this. The package's own demo uses [5, 10, 5] and is not affected. The C++ runtime does not use this code path.
The non-final branch has the same pattern (-(chunk_size[0] + chunk_size[2]):), which only matters when both values are 0.
Reproduction
Environment: funasr and funasr_onnx from main (fbbfe01), onnxruntime 1.20.1, numpy 1.26.4, Python 3.12, CPU.
- Download and export the model (writes
model.onnxanddecoder.onnxnext to the checkpoint):
from modelscope import snapshot_download
from funasr import AutoModel
model_dir = snapshot_download(
"iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online", revision="v2.0.5"
)
AutoModel(model=model_dir, device="cpu", disable_update=True).export(type="onnx", quantize=True)-
Optional, to get deterministic output: add
dither: 0.0underfrontend_conf:inmodel_dir/config.yaml. The config does not set it, so the frontend uses its defaultdither=1.0. The numbers below use 0.0; the duplication also appears with the default. -
Decode in 9600-sample (600 ms) pieces with
chunk_size=[0, 10, 5]:
import soundfile as sf
from funasr_onnx.paraformer_online_bin import Paraformer
model = Paraformer(model_dir, batch_size=1, chunk_size=[0, 10, 5], quantize=False)
speech, _ = sf.read("BAC009S0764W0129.wav", dtype="float32") # AISHELL-1 test
cache, out, step = {}, [], 9600
n = (len(speech) - 1) // step + 1
for i in range(n):
res = model(speech[i * step : (i + 1) * step], param_dict={"cache": cache, "is_final": i == n - 1})
out += [r["preds"][0] for r in res if r]
print("".join(out))AISHELL-1 test examples:
| utt | reference | current main | with fix | PyTorch AutoModel (same chunk_size, look-back 0) |
|---|---|---|---|---|
| BAC009S0764W0129 | 也助推了土地市场的火爆 | 也助推了土地市场的火爆爆爆 | 也助推了土地市场的火爆 | 也助推了土地市场的火爆 |
| BAC009S0764W0126 | 因此土地储备至关重要 | 因此土地储备至关重要要要 | 因此土地储备至关重要要 | 因此土地储备至关重要要 |
| BAC009S0764W0139 | 国家统计局的数据显示 | 国家统计局的数据显示是 | 国家统计局的数据显示 | 国家统计局的数据显示 |
The single repeat left in W0126 also appears in the PyTorch reference with the same settings, so the fix does not claim to remove every trailing repeat; it only removes the extra decoding of the previous chunk.
On the full AISHELL-1 test set (7176 utterances), 2170 outputs (30.2%) change after the fix, and CER (punctuation removed) goes from 17.23% to 15.18% with the FP32 export and from 17.15% to 15.08% with the quantized export. The PyTorch reference with the same settings is 14.81%.
Proposed fix
Compute the start index explicitly, feats[:, max(feats.shape[1] - n, 0):, :], for both cache updates. PR to follow.
Activity