Files
wangjia 25acf9db6e feat(eval): ASR 模型评估框架
横向对比云端 gummy 与本地开源模型(faster-whisper/SenseVoice/Paraformer),
重点覆盖中英混说,产出准确率(CER/WER/MER)/速度(延迟/RTF)/资源(cpu/mem/模型大小)
对比报告。公共集(ASCEND/AISHELL/LibriSpeech)统一走 HF 适配器 + 自定义 JSONL manifest。
gummy 引擎对照 server/internal/asr/gummy.go 协议移植。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:25:04 +08:00

90 lines
3.0 KiB
Python

"""通用 HuggingFace 数据集适配器(流式)。
config 条目示例:
- name: ascend
type: hf
hf_id: CAiRE/ASCEND
split: test
text_field: transcription
audio_field: audio # 默认 audio
lang: zh-en
domain: general
trust_remote_code: false
流式(streaming=True)避免为跑小样而下载整个数据集;每条把音频物料化为 16k wav 缓存。
"""
from __future__ import annotations
import io
import os
from collections.abc import Iterator
import soundfile as sf
from ..manifest import Sample
from .base import Dataset, write_wav_16k
class HFDataset(Dataset):
def __init__(self, cfg: dict, audio_cache: str):
self.name = cfg["name"]
self.hf_id = cfg["hf_id"]
self.hf_config = cfg.get("hf_config")
self.split = cfg.get("split", "test")
self.text_field = cfg["text_field"]
self.audio_field = cfg.get("audio_field", "audio")
self.lang = cfg.get("lang", "zh")
self.domain = cfg.get("domain", "general")
self.trust_remote_code = cfg.get("trust_remote_code", False)
self.cache_dir = os.path.join(audio_cache, self.name)
def samples(self, limit: int | None = None) -> Iterator[Sample]:
from datasets import Audio, load_dataset
ds = load_dataset(
self.hf_id,
self.hf_config,
split=self.split,
streaming=True,
trust_remote_code=self.trust_remote_code,
)
# 关闭自动解码(新版 datasets 需 torchcodec):拿原始 bytes/path 自己用 soundfile 读,避开重依赖。
ds = ds.cast_column(self.audio_field, Audio(decode=False))
n = 0
for i, row in enumerate(ds):
if limit is not None and n >= limit:
break
text = (row.get(self.text_field) or "").strip()
if not text:
continue
audio = row.get(self.audio_field)
if not audio:
continue
wav_path = os.path.join(self.cache_dir, f"{i:06d}.wav")
if not os.path.exists(wav_path):
array, sr = self._decode(audio)
if array is None:
continue
write_wav_16k(array, sr, wav_path)
n += 1
yield Sample(
id=f"{self.name}-{i:06d}",
audio_path=wav_path,
ref_text=text,
lang=self.lang,
domain=self.domain,
dataset=self.name,
)
@staticmethod
def _decode(audio: dict):
"""从未解码的 HF 音频列读出 (array, sr):优先 bytes,其次本地 path。"""
if audio.get("bytes"):
data, sr = sf.read(io.BytesIO(audio["bytes"]), dtype="float32", always_2d=False)
return data, sr
if audio.get("path") and os.path.exists(audio["path"]):
data, sr = sf.read(audio["path"], dtype="float32", always_2d=False)
return data, sr
return None, None