Skip to content

Commit 2c9b5c5

Browse files
authored
Add support for fun-asr model to accuracy_checker (#4063)
* Prepared draft of HfFunAsrPipeline * Added fix funasr for input format conversion. * Updated ASROptimumPipeline to add support for fun-asr model. * Added FunAsrOptimumPipeline * Applied changes to finetune fun asr evaluator pipeline * Removed data normalization in ARS pipeline and return_timestamps * Removed redundant code * Updated language handling in GenAIASRPipeline * Updated readme * Updated ASR classes naming * Updated readme * Updated imports in asr_pipeline_evaluator * Added test_hf_fun_asr_pipeline --------- Co-authored-by: Pawel Kowalczyk <pawel.kowalczyk>
1 parent 464159f commit 2c9b5c5

3 files changed

Lines changed: 115 additions & 2 deletions

File tree

‎tools/accuracy_checker/accuracy_checker/evaluators/custom_evaluators/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ Optionally you can provide `module_config` section which contains config for cus
6565
* **Whisper Evaluator** demonstrates how to evaluate Whisper family models with various pipeline classes, including GenAIWhisperPipeline, HFWhisperPipeline, and OptimumWhisperPipeline.
6666
Evaluator code: `<omz_dir>/tools/accuracy_checker/accuracy_checker/evaluators/custom_evaluators/whisper_evaluator.py`.
6767

68-
* **ASR Pipeline Evaluator** demonstrates how to evaluate automatic speech recognition models with various pipeline classes, including GenAIASRPipeline, Qwen3ASROptimumPipeline
68+
* **ASR Pipeline Evaluator** demonstrates how to evaluate automatic speech recognition models with various ASR pipeline classes, including GenAIASRPipeline,
69+
Qwen3ASROptimumPipeline and FunASROptimumPipeline
6970
Evaluator code: `<omz_dir>/tools/accuracy_checker/accuracy_checker/evaluators/custom_evaluators/asr_pipeline_evaluator.py`.

‎tools/accuracy_checker/accuracy_checker/evaluators/custom_evaluators/asr_pipeline_evaluator.py‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import importlib
1818
import re
1919

20+
import numpy as np
21+
2022
from ...representation import CharacterRecognitionPrediction
2123
from ...utils import UnsupportedPackage, extract_image_representations
2224
from .base_custom_evaluator import BaseCustomEvaluator
@@ -32,7 +34,9 @@ class ASRPipelineEvaluator(BaseCustomEvaluator):
3234
VALID_PIPELINE_CLASSES = [
3335
"GenAIASRPipeline",
3436
"HFASRPipeline",
37+
"HFFunASRPipeline",
3538
"Qwen3ASROptimumPipeline",
39+
"FunASROptimumPipeline",
3640
]
3741

3842
def __init__(self, dataset_config, pipe, orig_config):
@@ -123,6 +127,10 @@ def predict(self, identifiers, input_data, input_meta, encoder_callback=None):
123127

124128

125129
class GenAIASRPipeline(ASRPipeline):
130+
def __init__(self, config):
131+
self.language = config.get("language")
132+
super().__init__(config)
133+
126134
def _initialize_pipeline(self, config):
127135
try:
128136
import openvino_genai as ov_genai # pylint: disable=C0415
@@ -136,9 +144,11 @@ def _initialize_pipeline(self, config):
136144
return ov_genai.ASRPipeline(str(model_dir), device=device)
137145

138146
def _get_predictions(self, data, identifier, input_meta):
147+
kwargs = {"language": self.language} if self.language else {}
139148
return self.pipeline.generate(
140149
data[0],
141150
return_timestamps=True,
151+
**kwargs,
142152
).texts[0]
143153

144154

@@ -228,6 +238,60 @@ def parse_asr_output(raw_text):
228238
}
229239

230240

241+
class FunASROptimumPipeline(ASRPipeline):
242+
SAMPLE_RATE = 16000
243+
EOS_TOKEN_IDS = [151643, 151645]
244+
245+
def __init__(self, config):
246+
self.max_new_tokens = config.get("max_new_tokens", 1000)
247+
# FunASR prompt language name, e.g. "英文" (English), "中文" (Chinese)
248+
self.language = config.get("language", "英文")
249+
self.itn = config.get("itn", True)
250+
super().__init__(config)
251+
252+
def _initialize_pipeline(self, config):
253+
try:
254+
from optimum.intel.openvino import OVModelForSpeechSeq2Seq # pylint: disable=C0415
255+
except ImportError as import_error:
256+
UnsupportedPackage("optimum.intel.openvino", import_error.msg).raise_error(
257+
self.__class__.__name__
258+
)
259+
260+
try:
261+
from transformers import AutoTokenizer # pylint: disable=C0415
262+
except ImportError as import_error:
263+
UnsupportedPackage("transformers", import_error.msg).raise_error(
264+
self.__class__.__name__
265+
)
266+
267+
model_dir = get_model_dir(config)
268+
device = config.get("_device", "CPU")
269+
ov_model = OVModelForSpeechSeq2Seq.from_pretrained(str(model_dir)).to(device)
270+
tokenizer = AutoTokenizer.from_pretrained(str(model_dir))
271+
return ov_model, tokenizer
272+
273+
def _get_predictions(self, data, identifier, input_meta):
274+
ov_model, tokenizer = self.pipeline
275+
sampling_rate = input_meta[0].get("sample_rate", self.SAMPLE_RATE)
276+
audio = np.ascontiguousarray(data[0], dtype=np.float32).flatten()
277+
inputs = ov_model.preprocess_input(
278+
audio, sampling_rate=sampling_rate, language=self.language, itn=self.itn
279+
)
280+
281+
output_ids = ov_model.generate(
282+
input_features=inputs["input_features"],
283+
decoder_input_ids=inputs["decoder_input_ids"],
284+
decoder_attention_mask=inputs["decoder_attention_mask"],
285+
eos_token_id=self.EOS_TOKEN_IDS,
286+
max_new_tokens=self.max_new_tokens,
287+
)
288+
289+
prompt_len = inputs["decoder_input_ids"].shape[1]
290+
return tokenizer.batch_decode(
291+
output_ids[:, prompt_len:], skip_special_tokens=True
292+
)[0].strip()
293+
294+
231295
class HFASRPipeline(ASRPipeline):
232296
def __init__(self, config):
233297
self.language = config.get("language")
@@ -305,3 +369,42 @@ def _get_predictions(self, data, identifier, input_meta):
305369
"num_beams": 1,
306370
},
307371
)["text"]
372+
373+
class HFFunASRPipeline(ASRPipeline):
374+
def __init__(self, config):
375+
self.device = config.get("_device", "cpu").lower()
376+
# FunASR prompt language name, e.g. "英文" (English), "中文" (Chinese)
377+
self.language = config.get("language", "英文")
378+
self.itn = config.get("itn", True)
379+
super().__init__(config)
380+
381+
def _initialize_pipeline(self, config):
382+
try:
383+
from funasr import AutoModel # pylint: disable=C0415
384+
except ImportError as ie:
385+
UnsupportedPackage("funasr", ie.msg).raise_error(self.__class__.__name__)
386+
387+
model_id = config.get("model_id")
388+
model = AutoModel(
389+
model=model_id,
390+
hub="hf",
391+
trust_remote_code=True,
392+
device=self.device
393+
)
394+
return model
395+
396+
def _get_predictions(self, data, identifier, input_meta):
397+
try:
398+
import torch # pylint: disable=C0415
399+
except ImportError as ie:
400+
UnsupportedPackage("torch", ie.msg).raise_error(self.__class__.__name__)
401+
402+
audio = torch.from_numpy(np.ascontiguousarray(data[0], dtype=np.float32)).flatten()
403+
res = self.pipeline.generate(
404+
input=[audio],
405+
cache={},
406+
hotwords=[],
407+
language=self.language,
408+
itn=self.itn,
409+
)
410+
return res[0]["text"]

‎tools/accuracy_checker/tests/test_asr_pipeline_evaluator.py‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
from accuracy_checker.evaluators.custom_evaluators.asr_pipeline_evaluator import (
2121
ASRPipelineEvaluator,
2222
GenAIASRPipeline,
23-
HFASRPipeline
23+
HFASRPipeline,
24+
HFFunASRPipeline,
2425
)
2526
from datasets import load_dataset
2627

@@ -107,3 +108,11 @@ def test_hf_asr_pipeline_whisper_tiny(self):
107108

108109
result = evaluator.pipe._get_predictions(input_data, identifiers[0], input_meta)
109110
assert isinstance(result, str)
111+
112+
def test_hf_fun_asr_pipeline(self):
113+
config = {"_device": "CPU", "model_id": "FunAudioLLM/Fun-ASR-Nano-2512", "language": "英文"}
114+
pipeline = HFFunASRPipeline(config)
115+
evaluator = ASRPipelineEvaluator(None, pipeline, None)
116+
117+
result = evaluator.pipe._get_predictions(input_data, identifiers[0], input_meta)
118+
assert isinstance(result, str)

0 commit comments

Comments
 (0)