Files
audio2text/app/services/asr_service.py
audio2text dev a6b5c7231c perf: ASR GPU 利用率优化 — batch_size 16→32 + beam_size 5→2
faster-whisper 的 GPU 利用率呈尖刺波(峰=批量解码满载,谷=CPU 提取 Mel
特征 + 处理结果时 GPU 空闲),平均利用率低。瓶颈不在算力而在 CPU/GPU
未重叠。

- batch_size 16→32:拉长单次 GPU 解码时间,相对掩盖 CPU 特征提取间隙,
  尖刺变宽变平,平均利用率上升。turbo FP16 仅 ~1.6GB,3090 24G 充裕。
- beam_size 5→2:turbo 模型鲁棒,候选数 5→2 大幅减少解码步数,让 GPU
  峰更密、间隙更短。保留 1 个候选做歧义发音保险,质量损失小。
- beam_size 从硬编码提到 config 可调,CPU/CPU 模板/GPU/示例 四份配置对齐
- /health 增加 asr_beam_size,模型加载日志同步输出 batch+beam

word_timestamps 保留 True:segmenter 强依赖词级时间戳做精确断句,
关闭会触发匀速估算退化路径,得不偿失。
2026-07-06 22:28:37 +08:00

69 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""语音识别服务faster-whisper输出带词级时间戳的 segments。
CPU dev: tiny.en + int8GPU prod: large-v3-turbo + float16。同一份代码。
"""
from __future__ import annotations
import logging
from pathlib import Path
from ..config import get_settings
from .model_manager import get_model_manager
from .types import Segment, Word
logger = logging.getLogger("audio2text.asr")
def transcribe(wav_path: Path) -> list[Segment]:
"""转写 wav返回 segments含词级时间戳
Args:
wav_path: 16kHz mono PCM wav
Returns:
list[Segment],每个 Segment 带词级 words若 word_timestamps 启用)。
"""
s = get_settings().asr
if not wav_path.is_file():
raise FileNotFoundError(f"音频不存在:{wav_path}")
model = get_model_manager().get_asr()
logger.debug("开始转写 %smodel=%s language=%s batch_size=%d",
wav_path.name, s.model, s.language, s.batch_size)
segments_gen, info = model.transcribe(
str(wav_path),
language=s.language,
word_timestamps=s.word_timestamps,
vad_filter=s.vad_filter,
beam_size=s.beam_size, # beam search 宽度config 可调GPU turbo 建议降到 2 加速
batch_size=s.batch_size, # 批量解码:多音频块一次性送 GPU
without_timestamps=False, # BatchedInferencePipeline 默认 True需显式关闭以生成段级时间戳
)
logger.debug(
"音频时长 %.1fs检测语言=%s(置信度 %.2f",
info.duration, info.language, info.language_probability,
)
segments: list[Segment] = []
for seg in segments_gen:
words: list[Word] = []
if s.word_timestamps and getattr(seg, "words", None):
for w in seg.words:
words.append(Word(
text=w.word.strip(),
start=float(w.start),
end=float(w.end),
probability=float(getattr(w, "probability", 1.0)),
))
segments.append(Segment(
text=seg.text.strip(),
start=float(seg.start),
end=float(seg.end),
words=words,
))
logger.debug("转写完成:%d 段,%d 词。",
len(segments), sum(len(s.words) for s in segments))
return segments