Repository navigation
Description
ContextualParaformer.proc_hotword in runtime/python/onnxruntime/funasr_onnx/paraformer_bin.py (:409-433) and runtime/python/libtorch/funasr_torch/paraformer_bin.py (:367-389) builds the hotword input of the embedding model. SeacoParaformer inherits it in both packages:
hotwords = hotwords.split(" ")
hotwords_length = [len(i) - 1 for i in hotwords]
...
hotwords = pad_list(hotword_int, pad_value=0, max_len=10)- A hotword longer than 10 characters does not fit in its row of 10 ids, so the whole call fails with
ValueError: could not broadcast input array from shape (12,) into shape (10,). split(" ")turns repeated, leading or trailing spaces, and an empty hotword string, into empty hotwords. Each one becomes a row of padding (ten 0 ids), and its embedding is read at positionlen("") - 1 = -1, the last of the 10. It goes to the model as an extra hotword.
The exported embedding model takes exactly 10 ids per hotword. ContextualEmbedderExport in funasr/models/seaco_paraformer/export_meta.py (the Contextual Paraformer export uses its subclass ContextualEmbedderExport2) exports a [6, 10] dummy input and makes only num_hotwords dynamic, so model_eb.onnx declares its input as ['num_hotwords', 10]. The C++ runtime keeps the first max_hotword_len = 10 tokens of each hotword and skips hotwords that have no tokens (CompileHotwordEmbedding in runtime/onnxruntime/src/paraformer.cpp:565-607 and paraformer-torch.cpp:530-572). generate_hotwords_list in funasr/models splits with strip().split(), so it has no empty hotwords either.
Reproduction on main (0e52c3c), no model files: proc_hotword only needs the vocabulary, and the call fails in it before it reads the audio. Save as repro.py, then run from an empty directory:
PYTHONPATH=<FunASR>/runtime/python/onnxruntime python repro.py onnx
PYTHONPATH=<FunASR>/runtime/python/libtorch python repro.py torchimport importlib
import sys
backend = sys.argv[1] # "onnx" or "torch"
module = importlib.import_module("funasr_" + backend + ".paraformer_bin")
tokens = ["<blank>", "<s>", "</s>"] + list("阿里巴达摩院语音实验室魔搭") + ["<unk>"]
for name in ("ContextualParaformer", "SeacoParaformer"):
cls = getattr(module, name)
model = cls.__new__(cls) # skip loading model files: proc_hotword only needs the vocabulary
model.vocab = {token: index for index, token in enumerate(tokens)}
print(f"funasr_{backend}.{name}")
for hotwords in ["魔搭 达摩院", "魔搭 达摩院", "", "魔搭 阿里巴巴达摩院语音实验室"]:
try:
matrix, lengths = model.proc_hotword(hotwords)
except ValueError as error:
print(f" {hotwords!r}: ValueError: {error}")
continue
rows = [[int(i) for i in row if i] for row in matrix.tolist()]
print(f" {hotwords!r}: ids {rows}, embedding positions {lengths.tolist()}")
try:
model("audio.wav", "魔搭 阿里巴巴达摩院语音实验室")
except ValueError as error:
print(f" model('audio.wav', '魔搭 阿里巴巴达摩院语音实验室'): ValueError: {error}")Output (SeacoParaformer and both funasr_torch classes print the same lines):
funasr_onnx.ContextualParaformer
'魔搭 达摩院': ids [[14, 15], [6, 7, 8], [1]], embedding positions [1, 2, 0]
'魔搭 达摩院': ids [[14, 15], [], [6, 7, 8], [1]], embedding positions [1, -1, 2, 0]
'': ids [[], [1]], embedding positions [-1, 0]
'魔搭 阿里巴巴达摩院语音实验室': ValueError: could not broadcast input array from shape (12,) into shape (10,)
model('audio.wav', '魔搭 阿里巴巴达摩院语音实验室'): ValueError: could not broadcast input array from shape (12,) into shape (10,)
The ids are each hotword's row without its padding zeros, and <s> (id 1) is the row that proc_hotword appends. The embedding positions are where __call__ reads each hotword's embedding.
Expected, as in the C++ runtime: a longer hotword keeps its first 10 characters, and empty hotwords are skipped, so '魔搭 达摩院' gives the same input as '魔搭 达摩院', and '' gives only <s>.
Environment: Windows 11, Python 3.11.17, numpy 2.4.6, torch 2.10.0+cpu, sources from main.
Activity