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:
2026-09-07 17:21:04 +08:00
parent c063aed1f1
commit a8fe133aa4
10 changed files with 695 additions and 106 deletions
+95
View File
@@ -0,0 +1,95 @@
{
"id": "learn-translate",
"name": "学习资料转译+翻译字幕",
"description": "面向学习/教学类视频(人声讲解为主)的中文字幕工作流。采用本次修复的 decode_full 无 VAD 整段解码 + 日语幻觉清洗,优先保证'说了的话不漏'(弱语音/快速讲解/轻微 BGM 下的人声均能召回),再靠幻觉清洗移除无语音段套话。每个参数旁以 _note_ 前缀标注设定理由,_node_help 字段给出节点关键参数的解释与正反例。",
"version": 1,
"definition": {
"name": "学习资料转译+翻译字幕",
"version": 1,
"nodes": [
{
"id": "extract",
"node_type": "ffmpeg-extract",
"params": {
"sample_rate": 16000,
"channels": 1,
"_note_sample_rate": "16000Hz 是 faster-whisper 训练采用的采样率;ffmpeg 提取时直接对齐可避免节点内二次重采样造成的时间轴误差与音质损失。正例:speech_60s.wav 即 16k 单声道,转写时间轴与素材严格一致。反例:若用 44100Hzfaster-whisper 会自动降采样,极端情况下边界样本插值引入轻微漂移。",
"_note_channels": "单声道是 whisper 输入要求;立体声转单声道由 ffmpeg 平均合并,避免人声在左右声道相位抵消(成人视频双声道常有人声偏置,合并后更清晰)。正例:CJOD-255 全程 BGM 覆盖仍能稳定提取人声。反例:保留双声道直接喂 whisper 会告警且浪费显存。",
"_node_help": "ffmpeg-extract 节点:把上传视频提取为 16kHz 单声道 WAV。\n关键参数:\n- sample_rate=16000:对齐 whisper 训练采样率,避免重采样时间误差。\n- channels=1whisper 要求单声道。\n正例:16k 单声道 WAV -> 转写时间轴精确到 0.1s。\n反例:44.1k 立体声 -> 需内部重采样且可能引入边界误差。"
},
"inputs": {
"video_uri": "input.video_uri"
}
},
{
"id": "asr",
"node_type": "faster-whisper",
"params": {
"language": "ja",
"model_path": "faster-whisper-large-v3",
"decode_full": true,
"condition_on_previous_text": false,
"chunk_seconds": 60,
"vad_filter": false,
"beam_size": 1,
"_note_decode_full": "【本次修复核心】decode_full=true 强制无 VAD 整段解码,绕过 silero VAD 对呻吟/轻语/快速讲解/BGM 混叠人声的切段误杀。实测 savr-1054 全片:生产 VAD 仅召回 115 条,decode_full 召回 340 条(弱语音全找回)。正例:教师快速带过一句'つまりね'(弱语音),decode_full 能捕捉;反例(decode_full=false 默认):该句被 silero 概率<阈值当静音剔除,字幕整句消失。代价是无语音段会产生长时'おやすみなさい/ご視聴ありがとうございました'幻觉——处理方式:whisper 转录后立即**连带时间戳把整条 cue 删除**(不留下 '-' 占位污染下游,占位会渲染进 ASS 成减号),短时(≤15s)相同词可能是剧情真实道晚安则保留。实测 speech_60s:前 30s 无语音幻觉被整条剔除,字幕直接从 30s 真实内容开始、序号连续。",
"_note_vad_filter": "本工作流 decode_full=true 时 vad_filter 被强制置 false(两者互斥,decode_full 优先)。保留 vad_filter=false 仅为显式声明'不启用 VAD 切段'。正例:学习视频讲解者偶有停顿、翻页声,无 VAD 不误删。反例:vad_filter=true + 讲解者语速快/带气声,弱音节被整段吞掉(见 decode_full 反例)。",
"_note_chunk_seconds": "60s 分块:内存/显存有界、失败粒度小,块偏移按 WAV 实际时长累积无漂移。正例:2 小时学习视频切成 120 块逐块转写,每块独立,某块失败只重跑该块。反例:chunk_seconds=0(不分块)在长视频上显存吃紧,且一旦中途异常整段重来。decode_full 模式下分块同样生效,幻觉清洗在合并后的整条 SRT 上执行。",
"_note_condition_on_previous_text": "false(默认):每个 30s 窗口独立解码,避免长音频下 whisper 把前文错误延续成重复/漂移(官方建议长音频方案)。正例:讲解视频 1 小时,各窗口互不污染,无重复句。反例:设为 true 时,若某窗口误识别,会沿上文把错误放大成整段重复。",
"_note_beam_size": "1(贪心解码):速度最快且对清晰讲解足够;学习视频人声清晰,不需要 beam search 的多候选。正例:清晰人声 + beam=1 已能稳定出句。反例:若遇多说话人/嘈杂环境想提升,可调 beam_size=5,但速度明显下降(RTX3090 上约慢 3-5 倍),对本场景收益低。",
"_node_help": "faster-whisper 节点(asr):把 WAV 转写为日语 SRT。\n【关键参数与效果】\n- decode_full=true(默认 false):无 VAD 整段解码,救回被 silero 当非语音剔除的弱语音/快速讲解/呻吟/混叠人声。\n 正例:savr-1054 全片 115 条 -> 340 条,'说了的话不漏'。\n 反例(false):弱语音整句消失,字幕出现无端空洞。副作用=无语音段长套话幻觉,已由节点日语幻觉清洗自动移除(连带时间戳整条删除,非 '-' 占位)。\n- vad_filterdecode_full 下被强制 false;独立开启时靠 silero 切语音段,适合纯安静对话,但对 BGM/气声场景会误杀真话。\n- chunk_seconds=60:分块转写,显存有界、块偏移按实际时长累积不漂移、可断点。\n- condition_on_previous_text=false:长音频防重复/漂移(官方建议)。\n- beam_size=1:贪心最快,清晰讲解足够。\n正例:学习视频讲解者轻声带过关键词也能召回。\n反例:开启 VAD 且遇快速讲解/轻微 BGM,弱音节被吞 -> 字幕缺句。"
},
"inputs": {
"audio_uri": "extract.audio_uri"
}
},
{
"id": "translate",
"node_type": "llm-translate",
"params": {
"target_language": "zh-CN",
"_note_target_language": "zh-CN:输出简体中文字幕,与媒体库既有 .CN.srt 命名一致(批量侧车字幕约定)。正例:demo 工作流同参数,产物对齐 CN.srt。反例:若设 zh-TW 会得到繁体,下游命名/惯例不匹配。",
"_node_help": "llm-translate 节点:把日语 SRT 逐行翻译为中文(走 SiliconFlow LLM)。\n关键参数:\n- target_language=zh-CN:目标语言。\n效果:翻译按批(20 行/批)调用 LLM,行数与原文严格对齐(_repair_batch 防内容-时间错位),完成后做中文长时寒暄幻觉清洗(clean_srt_text30s'晚安'->'-'2s 真实晚安保留)。\n正例:decode_full 召回的长句被正确翻译。\n反例:LLM 返回行数错位时由 _repair_batch 兜底,宁缺勿错位。"
},
"inputs": {
"srt_uri": "asr.srt_uri"
}
},
{
"id": "ass",
"node_type": "srt-to-dual-eye-ass",
"params": {
"resolution": "3840x1920",
"margin_top": 700,
"_note_resolution": "3840x1920:VR 双眼视频的典型单眼分辨率(每眼 1920 宽),ASS 按此布局左右半幅。正例:3dsvr/savr 系列 8k 视频单眼即 1920x1920 左右并排,此分辨率匹配。反例:若设 1920x1080 会在 8k 视频上字幕缩在左上角。",
"_note_margin_top": "700:顶部安全边距(2026-09 起默认)。字幕定位在屏幕上部安全区、避开画面中央人脸/重点区,且处于视线自然可读高度。正例:VR 观看时字幕在视线自然位置,不遮挡中央内容。反例:margin_top=120 字幕贴最顶需抬头看;=0 则可能盖住画面中央人脸。",
"_node_help": "srt-to-dual-eye-ass 节点:把中文字幕生成 VR 双眼 ASS(左右眼各占半幅、A-1 零视差、an8 顶部居中)。\n关键参数:\n- resolution=3840x1920VR 单眼分辨率。\n- margin_top=700:顶部安全边距。\n效果:文字 70% 透明描边半透明黑,降低遮挡;历史字幕可用 scripts/unify_ass_style.py 统一。\n正例:3840x1920 + margin_top 700 -> 双眼字幕顶部自然可读、零视差不眩晕。\n反例:resolution 与视频不符 -> 字幕偏移;margin_top 过小 -> 遮挡画面中央。"
},
"inputs": {
"cn_srt_uri": "translate.cn_srt_uri"
}
}
],
"edges": [
{
"from": "extract",
"to": "asr"
},
{
"from": "asr",
"to": "translate"
},
{
"from": "translate",
"to": "ass"
}
],
"entry_inputs": {
"video_uri": "file"
},
"final_outputs": {
"cn_srt": "translate.cn_srt_uri",
"ass": "ass.ass_uri"
}
}
}