feat: whisper 新增 decode_full 无VAD整段解码参数并改为整条删除式幻觉清洗
解决转写漏句(有人说话但没识别出来)问题:silero VAD 对呻吟/轻语/BGM 混叠声学切段能力天然不足,把真话当非语音剔除(实测 savr-1054 全片仅 召回 115 条)。新增 decode_full 参数(默认 false 保持 VAD 现状): - decode_full=true 时强制无 VAD 整段解码 + 跳过自动 VAD 分析,救回被 剔除的弱语音(savr-1054 全片 115 条 → 340 条) - 副作用是长时寒暄套话幻觉(おやすみなさい/ご視聴ありがとうございま した 等),whisper 转录后连带时间戳整条删除(clean_japanese_ha lllucinations),不留下 '-' 占位污染下游(占位会渲染进 ASS 成减号) - llm-translate 翻译后同样整条删除中文长时寒暄幻觉(clean_srt_text) - 短时(≤15s)相同词可能是剧情真实道晚安,保留(15s 阈值实测校准) - subtitle_cleanup 由 '-' 占位式改为整条删除式 + 剩余重编号,新增 JAPANESE_HALLUCINATION_TOKENS 词表 新增工作流 learn-translate(学习资料转译+翻译字幕)示范 decode_full 用法,并确立参数标注约定:params._note_<参数名> 存放设定理由与正反例、 _node_help 放节点参数手册(_ 前缀说明键,节点执行时忽略,零运行影响)。 调研记录见 docs/调研-whisper漏句与decode_full验证.md(A/B 实验、结论 修正与 5 个待决问题)。
This commit is contained in:
+23
-7
@@ -228,6 +228,10 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
# language 默认日语;vad_filter 默认开启(用户 2026-08 决定):过滤静音
|
||||
# 段以提速并减少无语音处幻觉;长静音时 VAD 压缩时间轴可能轻微错位,
|
||||
# 如需极致对齐可在工作流参数中显式关闭。
|
||||
# decode_full=true(默认 false):VAD 对呻吟/轻语/BGM 混叠声学切段会
|
||||
# 把真话当非语音剔除(实测 savr-1054 全片仅召回 115 条),开启后强制
|
||||
# 无 VAD 整段解码(vad_filter=False 且跳过自动 VAD 分析)以召回弱语音,
|
||||
# 代价是无语音段会产生长时套话幻觉,由下方日语幻觉清洗兜底移除。
|
||||
# task 默认 transcribe,中文直出模型可传 translate 直接翻译为目标语言。
|
||||
# condition_on_previous_text 默认 False:长音频下开启会导致重复/漂移,
|
||||
# 关闭后每个 30s 窗口独立解码,是 faster-whisper 官方建议的长音频方案。
|
||||
@@ -236,12 +240,12 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
# 分块转写:默认每 1 分钟一块(chunk_seconds=60),切块失败自动回退整段。
|
||||
chunk_seconds = int(request.params.get("chunk_seconds", 60))
|
||||
chunks = _split_audio(audio_path, output_dir, chunk_seconds, _ffmpeg_bin())
|
||||
# 每视频自适应 VAD:若开启 vad_filter 且未显式传 vad_parameters,则根据本音频
|
||||
# 信号分析自动确定 VAD 参数(BGM 覆盖/静音比例/长停顿),改善碎片化与漏识别。
|
||||
# 门控 WOV_AUTO_VAD=0 可关闭;显式传入 vad_parameters 时跳过。
|
||||
# decode_full 时强制无 VAD:不传 vad_filter/vad_parameters,也不跑自动 VAD
|
||||
# 分析(分析结果对呻吟/轻语类音频无效,只会把整块切碎/剔除真话)。
|
||||
decode_full = bool(request.params.get("decode_full", False))
|
||||
vad_parameters = request.params.get("vad_parameters")
|
||||
vad_filter = bool(request.params.get("vad_filter", True))
|
||||
if vad_filter and not vad_parameters and os.getenv("WOV_AUTO_VAD", "1") == "1":
|
||||
if not decode_full and vad_filter and not vad_parameters and os.getenv("WOV_AUTO_VAD", "1") == "1":
|
||||
try:
|
||||
from nodes.vad_profiler import vad_parameters_for_audio
|
||||
|
||||
@@ -272,13 +276,14 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
if (Path(request.output_dir).parent.parent / PAUSE_FLAG).exists():
|
||||
raise RuntimeError(f"whisper 被暂停(run {request.run_id})")
|
||||
chunk_started = time.monotonic()
|
||||
# decode_full=true 时 vad_filter 传 False:跳过 VAD 剔除弱语音段。
|
||||
segments, _info = model.transcribe(
|
||||
str(chunk),
|
||||
language=str(request.params.get("language", "ja")),
|
||||
task=str(request.params.get("task", "transcribe")),
|
||||
beam_size=int(request.params.get("beam_size", 1)),
|
||||
vad_filter=vad_filter,
|
||||
vad_parameters=vad_parameters,
|
||||
vad_filter=False if decode_full else vad_filter,
|
||||
vad_parameters=None if decode_full else vad_parameters,
|
||||
condition_on_previous_text=bool(
|
||||
request.params.get("condition_on_previous_text", False)
|
||||
),
|
||||
@@ -295,8 +300,19 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
|
||||
chunk_seconds / chunk_elapsed if chunk_elapsed > 0 else 0.0,
|
||||
time.monotonic() - transcribe_started,
|
||||
)
|
||||
# decode_full(无 VAD)副作用:无语音/音乐/呻吟段会产生长时寒暄套话幻觉
|
||||
# (おやすみなさい/ご視聴ありがとうございました 等),在此**连带时间戳整条
|
||||
# 剔除**(序号/时间轴/文本全删、剩余重编号),不留下 '-' 占位污染下游
|
||||
# (占位会渲染进 ASS 成减号、翻译/过滤都要额外处理);短时(≤15s)相同词
|
||||
# 可能是剧情真实道晚安,保留。见 nodes/subtitle_cleanup.py。
|
||||
if decode_full:
|
||||
from nodes.subtitle_cleanup import clean_japanese_hallucinations
|
||||
|
||||
body = clean_japanese_hallucinations("\n".join(lines))
|
||||
else:
|
||||
body = "\n".join(lines)
|
||||
output_path = output_dir / "transcript.srt"
|
||||
output_path.write_text("\n".join(lines), encoding="utf-8")
|
||||
output_path.write_text(body, encoding="utf-8")
|
||||
return InvokeResponse(status="completed", outputs={"srt_uri": str(output_path)})
|
||||
except Exception as exc: # noqa: BLE001
|
||||
# 模型加载或转写异常统一转换为 failed 响应,不让调度线程崩溃。
|
||||
|
||||
Reference in New Issue
Block a user