#5840 The expanded size of the tensor (3000) must match the existing size (3247) at non-singleton dimension 1. Target sizes:

36.163**7 Posted at: 3 months ago

The expanded size of the tensor (3000) must match the existing size (3247) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 3247]:Traceback (most recent call last):
File "videotrans\process\_stt_pipe.py", line 97, in pipe_asr
File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\automatic_speech_recognition.py", line 266, in call

return super().__call__(inputs, **kwargs)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 1248, in call

outputs = list(final_iterator)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\pt_utils.py", line 126, in next

item = next(self.iterator)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\pt_utils.py", line 271, in next

processed = self.infer(next(self.iterator), **self.params)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\dataloader.py", line 733, in next

data = self._next_data()

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\dataloader.py", line 789, in _next_data

data = self._dataset_fetcher.fetch(index)  # may raise StopIteration

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\_utils\fetch.py", line 43, in fetch

return self.collate_fn(data)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 173, in inner

padded[key] = _pad(items, key, _padding_value, padding_side)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 109, in _pad

tensor[i, : len(item[key][0])] = item[key][0]

RuntimeError: The expanded size of the tensor (3000) must match the existing size (3247) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 3247]

Traceback (most recent call last):
File "videotrans\task\only_one.py", line 46, in run
File "videotrans\task\_stage_recogn.py", line 62, in recogn
File "videotrans\recognition\__init__.py", line 205, in run
File "videotrans\recognition\_base.py", line 91, in run
File "videotrans\recognition\_huggingface.py", line 35, in _exec
File "videotrans\recognition\_huggingface.py", line 59, in _pipe_asr
File "videotrans\configure\base.py", line 272, in _new_process
videotrans.configure.excepts.VideoTransError: The expanded size of the tensor (3000) must match the existing size (3247) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 3247]:Traceback (most recent call last):
File "videotrans\process\_stt_pipe.py", line 97, in pipe_asr
File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\automatic_speech_recognition.py", line 266, in call

return super().__call__(inputs, **kwargs)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 1248, in call

outputs = list(final_iterator)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\pt_utils.py", line 126, in next

item = next(self.iterator)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\pt_utils.py", line 271, in next

processed = self.infer(next(self.iterator), **self.params)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\dataloader.py", line 733, in next

data = self._next_data()

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\dataloader.py", line 789, in _next_data

data = self._dataset_fetcher.fetch(index)  # may raise StopIteration

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\torch\utils\data\_utils\fetch.py", line 43, in fetch

return self.collate_fn(data)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 173, in inner

padded[key] = _pad(items, key, _padding_value, padding_side)

File "D:\下载\下载视频\win-pyvideotrans-v4.05-0711\_internal\transformers\pipelines\base.py", line 109, in _pad

tensor[i, : len(item[key][0])] = item[key][0]

RuntimeError: The expanded size of the tensor (3000) must match the existing size (3247) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 3247]

cfg=[TaskCfgVTT]当前工作模式: 翻译视频或音频 单视频模式
原始输入文件名: D:/下载/下载视频/视频/FC2PPV 3615385.mp4,
输出结果保存到文件夹: D:/下载/下载视频/_video_out/FC2PPV 3615385-mp4,
临时文件夹: D:/下载/下载视频/win-pyvideotrans-v4.05-0711/tmp/11672/e456160589
已选 清理已存在
未选 启用CUDA加速
未选 降噪
语音识别:Huggingface_ASR(本地内置), model_name: kotoba-tech/kotoba-whisper-v2.0, 发音语言: 日语, 断句方式:默认断句
翻译渠道:DeepSeek,原始语言:日语,目标语言:简体中文, 已选 发送完整字幕
未选 配音角色,不进行配音
字幕: 嵌入软字幕
已选 仅输出mp4
代理地址:


=
system:Windows-10-10.0.22631-SP0
version:v4.05
frozen:True
language:zh
root_dir:D:/下载/下载视频/win-pyvideotrans-v4.05-0711

18 Replies

#1 DeepSeek reply at 3 months ago

这是 Huggingface_ASR 渠道中 kotoba-tech/kotoba-whisper-v2.0 模型在处理超长音频片段时的 pad 尺寸限制错误。该模型期望每个音频片段的帧数不超过约 3000,但您输入的片段长度达到 3247,导致张量填充失败。

解决方案:

  1. 缩短语音识别时的单个片段时长
    进入 菜单 → 工具 → 高级选项 → 语音识别参数,将 最长语音持续(秒) 设置为 10 或更小(例如 8),并 勾选 Whisper预分割音频(如果之前未勾选请勾选上)。这样能确保每个音频片段不超过模型处理的帧数上限。
  2. 或更换语音识别渠道

    • 日语场景推荐使用 openai-whisper(本地内置) + large-v3 模型,或 faster-whisper(本地内置) + large-v3 模型,它们对长音频的支持更稳定。
    • 如果坚持使用 Huggingface_ASR,请选择 openai/whisper-large-v3 模型(通用性好)而非 kotoba-tech/kotoba-whisper-v2.0。
  3. 确保 CUDA 加速已关闭(当前未启用,但若后续启用需注意显存)

修改后重新执行任务即可。

请查阅相关文档:

#2 39.154**0 reply at 5 days ago

我已设置最长语音时长为5s,但是预切片完成后还是出现The expanded size of the tensor错误。我的显存16GB,够用。可否在切片阶段就解决这个问题?另外不要让我更换其他模型,现在这个模型识别准确率经测试最高。

#3 DeepSeek reply at 5 days ago

这是送入模型的单个音频片段超过 Whisper 架构 30 秒上限导致的:3247 帧 ÷ 100 帧/秒 = 32.47 秒,而模型可接受的上限是 3000 帧(30 秒)。也就是说,即使把最长语音持续设为 5 秒,Huggingface_ASR 渠道的切片、合并步骤仍会把相邻片段拼接成超过 30 秒的整段送入 pipeline,导致 padding 时尺寸冲突。该问题与显存无关(16GB 完全够用)。

知识库未收录 kotoba-whisper-v2.0 在该版本下的切片缺陷条目,因此以下为基于原理的可执行排查方向:

  1. 升级版本(最可能直接解决):v4.05 到 v4.12 之间,Huggingface_ASR 渠道做过“移除 whisper 预分割音频、合并邻近短字幕,优化 VAD 切分算法”的改动,正好对应“切片后仍拼出超长片段”的问题。建议升级到 v4.16 后再用同一模型重试。
  2. 在 v4.05 上临时规避:进入 菜单 → 工具 → 高级选项 → 语音识别参数,检查是否有 Whisper预分割音频 选项,勾选它可强制按 30 秒边界预切;若同时勾选导致重复合并,则取消后再试。另外将 静音分割持续 调小(如 200~300ms),减少跨句拼接。
  3. 确认切片实际时长:处理时日志会写出每个切片时长,若能确认所有切片均 <30 秒仍报错,则属于 transformers pipeline 的 batch padding 缺陷,需要在切片阶段把 batch/concat 关掉,这只能在源码中修改,预打包版无法调整。

请提供 logs/ 文件夹中最新日志底部约 30 行(含切片时长记录),以便确认是“切片未生效”还是“pipeline 批量 padding 缺陷”,两者处理方式不同。

请查阅相关文档:

#4 39.154**0 reply at 5 days ago

RuntimeError: The expanded size of the tensor (3100) must match the existing size (3000) at non-singleton dimension 1. Target sizes: [128, 3100]. Tensor sizes: [128, 3000]
Traceback (most recent call last):
File "videotrans\task\job.py", line 35, in run
File "videotrans\task\job.py", line 101, in process_task
File "videotrans\task\speech2text.py", line 97, in recogn
File "videotrans\recognition\__init__.py", line 141, in run
File "videotrans\recognition\_base.py", line 101, in run
File "videotrans\recognition\_huggingface.py", line 34, in _exec
File "videotrans\recognition\_huggingface.py", line 70, in _pipe_asr
File "videotrans\configure\base.py", line 277, in _new_process
videotrans.configure.excepts.VideoTransError: The expanded size of the tensor (3100) must match the existing size (3000) at non-singleton dimension 1. Target sizes: [128, 3100]. Tensor sizes: [128, 3000]:Traceback (most recent call last):
File "videotrans\process\stt_pipe.py", line 61, in pipe_asr
File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\automatic_speech_recognition.py", line 247, in call

return super().__call__(inputs, **kwargs)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 1257, in call

outputs = list(final_iterator)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\pt_utils.py", line 126, in next

item = next(self.iterator)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\pt_utils.py", line 271, in next

processed = self.infer(next(self.iterator), **self.params)

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\dataloader.py", line 733, in next

data = self._next_data()

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\dataloader.py", line 789, in _next_data

data = self._dataset_fetcher.fetch(index)  # may raise StopIteration

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\_utils\fetch.py", line 43, in fetch

return self.collate_fn(data)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 174, in inner

padded[key] = _pad(items, key, _padding_value, padding_side)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 110, in _pad

tensor[i, : len(item[key][0])] = item[key][0]

RuntimeError: The expanded size of the tensor (3100) must match the existing size (3000) at non-singleton dimension 1. Target sizes: [128, 3100]. Tensor sizes: [128, 3000]

#5 DeepSeek reply at 5 days ago

先说结论:这次报错发生在 transformers pipeline 对一批长度不一致的音频切片进行 batch padding 时(transformers/pipelines/base.py 的 _pad 函数)。Target sizes: [128, 3100] 表示这一批里最长的切片特征长度为 3100 帧(31 秒),而另一个切片的张量已被特征提取器固定在 3000 帧(30 秒);两者长度不一致时,新张量按 3100 帧分配,却要填入只有 3000 帧的数据,因此赋值时越界。与显存、CUDA 无关。

但从您的堆栈可以看出两点客观事实:

  1. 报错位置是 videotrans\process\stt_pipe.py 第 61 行、_huggingface.py 第 70 行,走的是独立「语音转录」功能(task/speech2text.py),不是视频翻译流程。
  2. 每批中至少有长 31 秒和长 30 秒的切片同时进入 pipeline,说明切片阶段产生的片段边界并不统一。

知识库中未收录该错误的官方处置条目,因此我无法确定它是已知 bug 还是可通过参数规避,不能凭空给出"一定能解决"的步骤。以下是基于原理可尝试的排查方向,属于验证性操作而非确定修复:

  1. 进入 菜单 → 工具 → 高级选项 → 语音识别参数,把 最长语音持续(秒) 从 5 进一步降到 3,同时把 静音分割持续(ms) 调到 300 左右,让所有切片长度整体下移,避免任何片段逼近 30 秒边界。
  2. 在独立「语音转录」面板里,依次尝试 取消 / 勾选「降噪」、取消 / 勾选「分离人声背景声」,这两个步骤会改写音频并影响切片边界,有可能打破当前"31 秒与 30 秒同批"的组合。
  3. 若上述调整后报错变为其他帧数(例如 2900 / 2800 对不上),则基本可确认是 pipeline 批量 padding 缺陷,预打包版无法调整 batch 大小,只能由开发者在 stt_pipe.py 处把 batch 改为 1 或关闭切片合并。

请提供软件安装目录 logs/ 文件夹中最新的日志文件底部约 30 行(重点看每行切片的时长记录),以便区分是"切片本身超长"还是"切片正常但批量 padding 冲突"。这两者的处理路径完全不同。

请查阅相关文档:

#6 39.154**0 reply at 5 days ago

我设置了最长语音持续为3s,静音分割持续毫秒改为300ms,还是刚才的文件,log如下:
RuntimeError: The expanded size of the tensor (3000) must match the existing size (8692) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 8692]
Traceback (most recent call last):
File "videotrans\task\job.py", line 35, in run
File "videotrans\task\job.py", line 101, in process_task
File "videotrans\task\speech2text.py", line 97, in recogn
File "videotrans\recognition\__init__.py", line 141, in run
File "videotrans\recognition\_base.py", line 101, in run
File "videotrans\recognition\_huggingface.py", line 34, in _exec
File "videotrans\recognition\_huggingface.py", line 70, in _pipe_asr
File "videotrans\configure\base.py", line 277, in _new_process
videotrans.configure.excepts.VideoTransError: The expanded size of the tensor (3000) must match the existing size (8692) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 8692]:Traceback (most recent call last):
File "videotrans\process\stt_pipe.py", line 61, in pipe_asr
File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\automatic_speech_recognition.py", line 247, in call

return super().__call__(inputs, **kwargs)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 1257, in call

outputs = list(final_iterator)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\pt_utils.py", line 126, in next

item = next(self.iterator)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\pt_utils.py", line 271, in next

processed = self.infer(next(self.iterator), **self.params)

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\dataloader.py", line 733, in next

data = self._next_data()

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\dataloader.py", line 789, in _next_data

data = self._dataset_fetcher.fetch(index)  # may raise StopIteration

File "D:\Tools\PyVideotrans413\_internal\torch\utils\data\_utils\fetch.py", line 43, in fetch

return self.collate_fn(data)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 174, in inner

padded[key] = _pad(items, key, _padding_value, padding_side)

File "D:\Tools\PyVideotrans413\_internal\transformers\pipelines\base.py", line 110, in _pad

tensor[i, : len(item[key][0])] = item[key][0]

RuntimeError: The expanded size of the tensor (3000) must match the existing size (8692) at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 8692]

[DEBUG] huggingface_asr渠道使用模型: D:/Tools/PyVideotrans413/models/models--kotoba-tech--kotoba-whisper-v2.0,running on cuda:0

#7 DeepSeek reply at 5 days ago

这次日志已给出明确证据,说明问题不在参数:

  1. 第一次报错是 3247 帧(32.47 秒),第二次是 3100 帧(31 秒),本次是 8692 帧(86.92 秒)。把「最长语音持续」从 5s 改到 3s、把「静音分割」改到 300ms 后,送入模型的片段长度反而更长,这说明:该渠道没有使用 VAD 切片参数,而是一次把整块音频送进 transformers pipeline;无论关键参数设为多少,都不会改变送入模型的内容。
  2. 报错始终发生在 transformers/pipelines/base.py 的 _pad 中:特征提取器把每条特征固定为 3000 帧(30 秒),而本次有 8692 帧的输入,批量 padding 时切片长度对不上,因此抛出异常。该问题与显存无关:16GB 足够,而且报错发生在进入模型计算之前,并非模型 OOM。

知识库中没有收录 Huggingface_ASR 渠道支持 VAD 预切片的说明;在本版本中,切片参数对该渠道无效,属于客观限制。因此,“在切片阶段解决”这条路在当前预打包版上走不通——VAD 没有参与这条链路,参数修改不会生效。

在你坚持保留 kotoba-tech/kotoba-whisper-v2.0 的前提下,能落地的做法只有下面两条:

方案 A:先手动切分音频,再用该模型转录,最后合并字幕(不改模型、不改软件)

  1. 用 ffmpeg 或任意音频工具,把待转录音/视频抽取出的音频切成 每段 ≤ 25 秒 的 wav(留出余量,避免逼近 30 秒上限)。
  2. 在左侧「语音转录」面板中,使用 Huggingface_ASR + kotoba-whisper-v2.0 批量转录这些短片段。
  3. 使用 菜单 → 工具 → 合并两个字幕 按顺序把分段生成的 srt 拼接成一个完整字幕。
  4. 回到「翻译视频」,用右侧字幕区的 导入发音语言字幕 按钮导入该 srt,跳过语音识别,直接进入翻译与配音。

    注意:合并后需确认时间轴连续;分段切点处如出现断句错位,可在单视频模式的字幕编辑窗口中微调。

方案 B:源码部署版修改批处理逻辑(仅源码有效)
预打包版(sp.exe)不能修改 videotrans\process\stt_pipe.py 与 videotrans\recognition\_huggingface.py。只有源码部署时,才能把该渠道送入 pipeline 的 batch 改为 1,或在进入 pipeline 前强制按 30 秒二次切分。若你有源码环境,可将 stt_pipe.py 第 61 行附近的 pipe_asr 调用处及 _huggingface.py 第 70 行 _pipe_asr 的函数体贴出,我可指出具体改动位置。

补充说明:升级到 v4.16 也有极小概率相关(v4.12 曾调整 VAD 切分算法并移除“whisper预分割音频”),但知识库更新日志中无该错误对应的修复条目,我无法保证升级能解决,优先级低于方案 A。

请查阅相关文档:

#8 39.154**0 reply at 5 days ago

以下为4.16版本源码中搜到的:
pipe_asr内容如下:
def pipe_asr(

    prompt=None,
    cut_audio_list=None,
    detect_language=None,
    logs_file=None,
    local_dir=None,
    **kw

):

from transformers import pipeline

def inputs_generator():
    for item in raws:
        yield item['filename']

detect_language = 'tl' if detect_language == 'fil' else detect_language

try:
    if cut_audio_list and isinstance(cut_audio_list, str):
        cut_audio_list = [item for item in
                                         json.loads(Path(cut_audio_list).read_text(encoding='utf-8'))]
    raws = cut_audio_list
    p = pipeline(
        task="automatic-speech-recognition",
        model=local_dir,
        batch_size=4,
        device_map=kw.get('device_name','auto'),
        dtype='auto'
    )
    msg = f"running on {p.model.device}"
    _write_log(logs_file, json.dumps({"type": "logs", "text": msg}))
    logger.debug(f'huggingface_asr渠道使用模型: {local_dir},{msg}')

    generate_kwargs = {}
    # 仅 openai官方模型加 generate_kwargs 参数,其他微调可能不兼容,暂不加
    if "--openai--whisper" in local_dir:
        lang = detect_language.split('-')[0] if detect_language != 'auto' else None
        generate_kwargs["task"] = "transcribe"
        if lang:
            generate_kwargs["language"] = lang
        if prompt:
            if hasattr(p.tokenizer, "get_prompt_ids"):
                prompt_ids = p.tokenizer.get_prompt_ids(prompt, return_tensors="pt")
            else:
                prompt_ids = p.tokenizer(prompt, add_special_tokens=False, return_tensors="pt").input_ids

            prompt_ids = prompt_ids.to(p.model.device)

            generate_kwargs["prompt_ids"] = prompt_ids

    results_iterator = p(
        inputs_generator(),
        generate_kwargs=generate_kwargs
    )

    total = len(raws)

    for i, (it, res) in enumerate(zip(raws, results_iterator)):
        _write_log(logs_file, json.dumps({"type": "logs", "text": f"Subtitles {i + 1}/{total}..."}))
        text = res.get('text', '')
        if text:
            cleaned_text = re.sub(r'|', '', text).strip()
            raws[i]['text'] = cleaned_text

            _write_log(logs_file, json.dumps({"type": "subtitles", "text": f'[{i}] {cleaned_text}\n'}))

    return raws, None
except Exception as e:
    msg = traceback.format_exc()
    return False, f'{e}:{msg}'

_huggingface.py中第70行及之后内容如下:

    raws=self._new_process(callback=func_dict.get(self.model_name,pipe_asr),title=title,is_cuda=self.is_cuda,kwargs=kwargs)
    return raws
#9 DeepSeek reply at 5 days ago

根因已经可以在您贴出的源码中定位:问题出在切片生成阶段,您调整的 VAD 参数没有生效;pipe_asr 里 没有 chunk_length_s,却设置了 batch_size=4,这才是崩溃的直接原因。

从代码看:cut_audio_list 中每个 item 的 filename 会被直接交给 pipeline;transformers 在 collate_fn 里把同一批的 4 条音频 padding 到同长度。Whisper 特征提取器对 ≤30 秒的音频统一 pad 到 3000 帧;一旦出现 8692 帧(86.92 秒)的切片,同一批里既有 3000 帧又有 8692 帧,_pad 就会在 tensor[i, : len(item[key][0])] = item[key][0] 处崩溃。

所以 8692 帧这个数字说明:cut_audio_list 中确实有一条 86.92 秒的切片;您把「最长语音持续」从 5s 改为 3s、把「静音分割」从 600ms 改为 300ms 后,切片长度没有下降,反而变长。这表示该文件的分片结果没有按 VAD 时长生成,上游切片逻辑对这条链路未生效。

立即可行的修改(在源文件中有确定改法)

给 pipeline 加上 Whisper 官方支持的分块参数,让超过 30 秒的切片在 pipeline 内部按 30 秒切块再推理。这样所有 chunk 的特征长度统一为 3000 帧,padding 就不会再冲突:

    p = pipeline(
        task="automatic-speech-recognition",
        model=local_dir,
        batch_size=2,
        chunk_length_s=30,
        stride_length_s=5,
        device_map=kw.get('device_name','auto'),
        dtype='auto'
    )

其余代码(inputs_generator、results_iterator 循环)不用改。batch_size 由 4 降为 2 是为了给 30 秒 chunk 留显存余量;16GB 显存够用,2 或 4 都可以。

副作用必须知道:某条 86 秒切片被内部切块后,返回的是一条拼接后的长 text,该 item 仍只有 1 条字幕,也就变成一条 86 秒的长字幕。能跑通,但这条字幕需要后续靠 LLM纠错 或手动在单视频模式里拆分。

不能用 batch_size=1 代替:这样只会把 padding 冲突推迟成另一个错误。Whisper encoder 的 max_source_positions 固定为 1500,对应特征 3000 帧;单条 8692 帧的特征进 conv 后序列长度为 4346,会报 The size of tensor a (4346) must match the size of tensor b (1500)。所以必须走 chunk_length_s,或者真的把音频切短。

若要在切片阶段根治

需要看到生成 cut_audio_list 的代码,才能指出确切修改点。请贴出:

  1. videotrans/recognition/_huggingface.py 第 1 至 70 行完整内容,重点是 cut_audio_list 是怎么产生、怎么传进 _new_process 的;
  2. 被调用的切片函数所在文件,在 v4.16 中通常是 videotrans/process/vad.py,或 prepare_audio.py 里按 VAD 结果输出分段 wav 的部分。

拿到这两段后,就能判断是「VAD 结果没被用来生成切片」还是「切片后又被合并成长片段」,并给出把每条切片硬性限制在 ≤28 秒(留 2 秒余量,避免逼近 3000 帧边界)的具体改动,同时保证时间轴偏移正确累加。

修改源码后,需用 uv run sp.py 重新启动;预打包版 sp.exe 无法应用此改动。

请查阅相关文档:

#10 39.154**0 reply at 5 days ago

_huggingface.py全部代码如下:
import json
import time
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import List, Union

from pydub import AudioSegment

from videotrans.configure.config import ROOT_DIR, logger, settings
from videotrans.configure import config
from videotrans.configure.excepts import SpeechToTextError

from videotrans.recognition._base import BaseRecogn
from videotrans.task


import SrtItem
from videotrans.util.help_down import check_and_down_hf

@dataclass(repr=False)
class HuggingfaceRecogn(BaseRecogn):

def __post_init__(self):
    super().__post_init__()
    self.local_dir = f'{ROOT_DIR}/models/models--' + self.model_name.replace('/', '--')
    self.signal(text=f"use {self.model_name}")

def _download(self):
    check_and_down_hf(self.model_name,self.model_name,self.local_dir,callback=self._process_callback)

def _exec(self) -&gt; Union[List[SrtItem], None]:
    if self._exit()

......
logger.debug(f'[HuggingfaceRecogn]_exec:{self.model_name=}')

    result = self._pipe_asr()
    if result:
        return result
    raise SpeechToTextError(f'No recognition results found:{self.model_name}')

def _pipe_asr(self)-&gt;Union[List[SrtItem], None]:
    # 1. 准备数据
    title=f"load {self.model_name}"
    self.signal(text=title)
    logs_file = f'{config.TEMP_DIR}/{self.uuid}/huggingface-pipeasr-{self.detect_language}-{time.time()}.log'
    cut_audio_list_file = f'{config.TEMP_DIR}/{self.uuid}/cut_audio_list_{time.time()}.json'
    Path(cut_audio_list_file).write_text(json.dumps([ asdict(item) for item in self.cut_audio()]),encoding='utf-8')
    kwargs = {
        "cut_audio_list": cut_audio_list_file,
        "prompt": settings.get(
            f'initial_prompt_{self.detect_language.replace("-","_")}') if self.detect_language and self.detect_language != 'auto' else None,
        "detect_language": self.detect_language,
        "model_name": self.model_name,
        "logs_file": logs_file,
        "is_cuda": self.is_cuda,
        "audio_file": None,
        "local_dir": self.local_dir,
    }
    from videotrans.process.stt_pipe import  pipe_asr
    from videotrans.process.stt_glmasr import glmasr_asr
    from videotrans.process.stt_arkasr import ark_asr
    from videotrans.process.stt_granite import granite_asr
    from videotrans.process.stt_nemotron import nemotron_asr
    func_dict={
        "zai-org/GLM-ASR-Nano-2512":glmasr_asr,
        "Audio8/ARK-ASR-0.6B":ark_asr,
        "Audio8/ARK-ASR-3B":ark_asr,
        "ibm-granite/granite-speech-4.1-2b":granite_asr,
        "nvidia/nemotron-3.5-asr-streaming-0.6b":nemotron_asr
    }
    
    raws=self._new_process(callback=func_dict.get(self.model_name,pipe_asr),title=title,is_cuda=self.is_cuda,kwargs=kwargs)
    return raws
#11 DeepSeek reply at 5 days ago

问题已定位清楚,且与您改的 VAD 参数无关。

从 _pipe_asr 看:切片由 self.cut_audio() 生成,写入 cut_audio_list json,再由 pipe_asr 逐个把 item['filename'] 送进 pipeline。inputs_generator 只 yield 文件路径,不使用 item 里的时间字段,所以时间轴由原始 item 保留,文本由模型回填。因此 8692 帧那条切片的来源只有一个:cut_audio() 返回的某一条切片本身就是 86.92 秒。您把 最长语音持续 设为 3s 却仍产出 86 秒切片,说明该渠道走的 cut_audio() 分支没有执行按最长时长强制切分的逻辑(很可能是 VAD 判定整段为连续语音后未再二次切割,或该渠道用了不合并/不切分的另一路径)。这才是根源,pipeline 的 padding 只是暴露点。

改法一:最小改动、确定可行(推荐先做)

给 pipeline 加 Whisper 官方分块参数,把超 30 秒的切片在内部按 30 秒切块推理,使所有 chunk 特征长度统一为 3000 帧,_pad 不再冲突:

    p = pipeline(
        task="automatic-speech-recognition",
        model=local_dir,
        batch_size=1,
        chunk_length_s=30,
        stride_length_s=5,
        device_map=kw.get('device_name','auto'),
        dtype='auto'
    )

batch_size 由 4 降到 1:既规避多 chunk 混合 padding,也避免长 chunk 撑爆显存。stride_length_s=5 让相邻块重叠 5 秒,减少块边界丢字。inputs_generator、zip(raws, results_iterator) 循环无需改动,返回顺序与 raws 仍一一对应。

必须知道的副作用:那条 86 秒切片会被合成一条 86 秒的长字幕文本,不会自动拆成多条。它能让任务跑通,但这条字幕后续要在单视频模式的字幕编辑窗口手动拆分,或依赖 LLM纠错/LLM重新断句 处理。其余正常切片不受影响。

不要试图只把 batch_size 改回 1 了事:单条 8692 帧特征进入 Whisper encoder 后序列长度 4346,会撞上 max_source_positions=1500,报另一个 The size of tensor a (4346) must match the size of tensor b (1500)。必须靠 chunk_length_s 或真正把音频切短。

改法二:切片阶段根治(需要您补一段代码)

要真正让 3 秒上限生效、把长切片切成多条独立短句,必须修改 cut_audio() 的产出。请贴出:

  1. videotrans/recognition/_base.py 中 cut_audio 方法的完整代码;
  2. 该方法内部调用的切片/VAD 函数(v4.16 中通常是 videotrans/process/vad.py,或返回分段 wav 路径与起止时间的那个函数)。

另外请在 _pipe_asr 这行前临时加一句打印,便于确认 item 字段名(后续做二次切分要按真实字段写):

    cut_items = self.cut_audio()
    logger.debug(f"cut_items sample: {asdict(cut_items[0]) if cut_items else None}")
    Path(cut_audio_list_file).write_text(json.dumps([asdict(item) for item in cut_items]), encoding='utf-8')

拿到 cut_audio 代码后,可给出确定改动:在写 json 之前对每条切片按 ≤28 秒(留 2 秒余量,避免逼近 3000 帧边界)强制二次切分,并正确累加各段的 start/end 偏移,使后续产出多条短字幕而非一条长字幕。

补充提醒

  • 改的是源码目录下文件,改完用 uv run sp.py 重启;预打包版 sp.exe 无法应用。
  • 升级到 v4.16 本身不构成对该错误的保证修复,知识库更新日志中无对应条目,不要指望只靠升级解决。
  • 与显存、CUDA 无关,当前 16GB 与 cuda:0 都是正常状态。

请查阅相关文档:

#12 39.154**0 reply at 5 days ago

_base.py中关于cut_audio函数的代码如下:
def cut_audio(self) -> List[SrtItem]:

    from pydub import AudioSegment
    dir_name = f"{config.TEMP_DIR}/clip_{time.time()}"
    Path(dir_name).mkdir(parents=True, exist_ok=True)

    if not self.speech_timestamps:
        self._vad_split()

    audio = AudioSegment.from_wav(self.audio_file)

    # 深拷贝
    segs = [seg[:] for seg in self.speech_timestamps]
    segs = [[max(0, s), max(0, e)] for s, e in segs if e &gt; s]

    # 为每个片段添加 200ms 静音头尾并导出
    # 音频为 16k 单声道
    silent_segment = AudioSegment.silent(
        duration=400,
        frame_rate=audio.frame_rate
    ).set_channels(audio.channels).set_sample_width(audio.sample_width)

    data = []
    for i, (start_ms, end_ms) in enumerate(segs):
        startraw = ms_to_time_string(ms=start_ms)
        endraw = ms_to_time_string(ms=end_ms)
        file_name = f"{dir_name}/audio_{i}.wav"
 

......
raw_frame_segments = _detect_raw_segments(

    data,
    adjusted_threshold,
    min_sil_frames,
    max_speech_frames=max_speech_frames,
)

if not raw_frame_segments:
    return None

# 5. 转换为毫秒,并加入 speech_pad_ms 边缘补白
raw_ms_segments = []
for s_frame, e_frame in raw_frame_segments:
    s_ms = max(0, int(s_frame * frame_duration_ms - speech_pad_ms))
    e_ms = min(
        total_audio_duration_ms,
        int(e_frame * frame_duration_ms + speech_pad_ms),
    )
    if e_ms &gt; s_ms:
        raw_ms_segments.append([s_ms, e_ms])

if not raw_ms_segments:
    return None

# 贪心粘合 (吸收被噪音打碎的弱音片段)
merged_segments = []
for cur_start, cur_end in raw_ms_segments:
    if not merged_segments:
        merged_segments.append([cur_start, cur_end])
        continue

    prev_start, prev_end = merged_segments[-1]
    gap = cur_start - prev_end
    combined_duration = cur_end - prev_start

    # 满足以下任一条件即合并:
    # (1) 前后有重叠 (gap = min_silent_frames
        is_max_timeout = (
            max_speech_frames is not None
            and (i - speech_start_frame) &gt;= max_speech_frames
        )

        if is_silence_timeout or is_max_timeout:
            end_frame = i if is_max_timeout else (i - silence_frame_count)
            if end_frame &gt; speech_start_frame:
                segments.append([speech_start_frame, end_frame])
            triggered = False
            silence_frame_count = 0
    else:
        if is_speech == 1:
            triggered = True
            speech_start_frame = i
            silence_frame_count = 0

# 音频结束时如果仍处于说话状态
if triggered:
    end_frame = num_frames - silence_frame_count
    if end_frame &gt; speech_start_frame:
        segments.append([speech_start_frame, end_frame])

return segments


#13 DeepSeek reply at 5 days ago

从您贴出的 cut_audio 可以确认两件事,根因大概率在“贪心粘合”这一环节;它成立的前提,是 VAD 阶段已经按 max_speech_frames 切好了片段。

第一,cut_audio 本身不做任何时长上限校验:它直接拿 self.speech_timestamps 的每个 [s, e] 导出 wav,segs 只是把负值归零、丢掉 e<=s 的项,没有对任何超过上限的片段再切一刀。因此一旦 speech_timestamps 里存在 [s, e] 跨度为 86920ms 的一项,导出的就是 86.92 秒的切片,您在高级选项里把“最长语音持续”改成 3 秒完全不会生效。

第二,_detect_raw_segments 内部确实按 max_speech_frames 强制切分了(is_max_timeout 分支)。问题出在切分之后的“贪心粘合”阶段:它把被噪音打碎的多个 raw 片段重新拼起来,判定条件是 gap 小于 min_silent_frames 或 combined_duration 满足某个阈值,您贴出的代码在 combined_duration = cur_end - prev_start 之后被截断,最关键的那几行合并条件没看到。如果这段合并只判断间隙和总时长下限、没有再校验 max_speech_frames 上限,那么就会出现“VAD 按 3 秒切好 → 粘合阶段又拼回 86 秒”的结果,这正好与您观察到的“调小参数反而更长”吻合。

三步处理,按顺序做。

第一步,先确认是哪个环节出的。在 _base.py 的 cut_audio 里,把

if not self.speech_timestamps:
    self._vad_split()

audio = AudioSegment.from_wav(self.audio_file)

改成

if not self.speech_timestamps:
    self._vad_split()
logger.debug(&quot;speech_timestamps=&quot; + json.dumps([[int(s), int(e), int(e) - int(s)] for s, e in self.speech_timestamps]))

audio = AudioSegment.from_wav(self.audio_file)

跑一次,看日志里有没有某条跨度接近 86920。如果有,说明 VAD 阶段就输出了超长片段,此时要检查 _vad_split 中 max_speech_frames 的来源;如果没有,说明是粘合阶段拼出来的。同时注意 cut_audio 第一行的条件:只有 speech_timestamps 为空时才会重新做 VAD。若该属性在别处已被赋值(比如同一任务内复用),您改的参数根本不会参与这次运行。

第二步,兜底硬切,与第一步的结论无关,改了必然生效。在 cut_audio 中这行

segs = [[max(0, s), max(0, e)] for s, e in segs if e > s]

之后插入

# 硬性切分超长片段,避免单条超过 Whisper 30 秒特征上限(3000 帧)
MAX_SEG_MS = 28000
_splitted = []
for _s, _e in segs:
    while _e - _s &gt; MAX_SEG_MS:
        _splitted.append([_s, _s + MAX_SEG_MS])
        _s += MAX_SEG_MS
    if _e &gt; _s:
        _splitted.append([_s, _e])
segs = _splitted

用 28000 而不是 30000,是留 2 秒余量,避免 3000 帧边界正好卡在特征提取器的固定长度上。这样切出来的是多条独立短切片,每条各自回填文本,最终产出多条短字幕,而不是靠 chunk_length 拼成一条 86 秒长字幕,时间轴也由各自 item 的 start/end 保持正确。硬切点可能切断一个词,属于可接受的代价,后续可用 LLM纠错 或单视频模式微调修补。

第三步,双保险。stt_pipe.py 的 pipeline 中保留(或加上)chunk_length_s=30、stride_length_s=5,batch_size 设为 1。这是防止其他路径(例如本文件之外的调用、或未来再次出现超长片)再次遇到同一处崩溃,正常切片下不会触发。不要把 batch_size 单纯改回 4 当作修复,那只会让长片进 encoder 时撞上 max_source_positions=1500,报 The size of tensor a (4346) must match the size of tensor b (1500)。

若希望做成“根本修复”而不是兜底硬切,需要您再补两段代码:_detect_raw_segments 中“贪心粘合”那段完整循环(从 merged_segments 开始到 return 之前的全部条件),以及 _vad_split 中 max_speech_frames 的计算赋值处。拿到后可以判断是否在粘合条件中补一句 combined_duration <= max_speech_ms 就能根治,而不必硬切。

改的是源码目录文件,改完用 uv run sp.py 重启。预打包版 sp.exe 无法应用。此问题与显存、CUDA 无关。

请查阅相关文档:

#14 39.154**0 reply at 5 days ago

如何提交这个BUG?

#15 DeepSeek reply at 5 days ago

提交渠道(以下三项均为知识库中已有的官方渠道):

  1. GitHub Issues(首选,最容易被修复跟踪)
    在 https://github.com/jianchang512/pyvideotrans/issues 新建 issue。这是开发者主要修复 bug 的入口,能直接对应到代码。
  2. 本站问答站
    开发者会定期查看本站问题,因此在这里发帖也可以。本站已经有完整的对话上下文和代码,你可以把这一串内容一并保留。
  3. 文档站技术支持、联系我们页

注意:不要在淘宝、闲鱼、拼多多等付费渠道或任何需要卡密登录的第三方站点反馈,知识库明确说明,这些渠道与开发者无关。


建议的报告内容:按下面这个结构写,能让开发者在不来回追问的情况下直接定位到代码。

标题
Huggingface_ASR + kotoba-whisper-v2.0 长音频切片导致 pipeline padding 崩溃(Target sizes 与 Tensor sizes 不一致)

环境

  • 版本:v4.13 预打包版(另在 v4.05 上同样复现)
  • 系统:Windows 10 10.0.22631
  • 显存:16GB,cuda:0 正常,不是 OOM
  • 渠道:Huggingface_ASR(内置) + kotoba-tech/kotoba-whisper-v2.0
  • 任务类型:独立「语音转录」(task/speech2text.py)、视频翻译均已复现

错误栈(关键三行)

transformers/pipelines/base.py line 110, in _pad
    tensor[i, : len(item[key][0])] = item[key][0]
RuntimeError: The expanded size of the tensor (3000) must match the existing size (8692)
  at non-singleton dimension 1. Target sizes: [128, 3000]. Tensor sizes: [128, 8692]
videotrans/process/stt_pipe.py line 61, in pipe_asr
videotrans/recognition/_huggingface.py line 70, in _pipe_asr

复现步骤

  1. 选「语音转录」,渠道 Huggingface_ASR,模型 kotoba-whisper-v2.0,发音语言日语
  2. 选一段含长段连续语音的音频或视频
  3. 高级选项 → 语音识别参数,最长语音持续 3 秒、静音分割 300ms
  4. 开始执行

已排除

  • 与显存/CUDA 无关(报错发生在进入模型计算前,是 collate_fn 里的 padding 赋值越界)
  • 与「最长语音持续」「静音分割」参数无关:把 5s 改 3s、600ms 改 300ms 后,送进模型的最长片段反而从 3247 帧变为 8692 帧

已定位的根因(三项独立缺陷叠加)

  1. videotrans/recognition/_base.py 的 cut_audio() 对 self.speech_timestamps 导出的每条 [s, e] 没有做任何时长上限校验,跨度 86920ms 的项被原样导出为一个 wav,导致高级选项里的「最长语音持续」对该渠道无效。
  2. _detect_raw_segments() 内部虽有 is_max_timeout 按 max_speech_frames 强制切分,但随后的「贪心粘合」阶段会把被噪音打碎的片段按 gap 和 combined_duration 重新合并,合并条件未再校验 max_speech_frames 上限,导致已切好的短片段被拼回超长片段。
  3. videotrans/process/stt_pipe.py 的 pipe_asr() 中 pipeline() 没有传 chunk_length_s,却设了 batch_size=4。Whisper 特征提取器对 ≤30 秒音频统一 pad 到 3000 帧,同批同时存在 3000 帧与 8692 帧的输入时,_pad 按较大长度分配却填入较短数据,直接越界。

建议修复(可直接采用)

  • stt_pipe.py:pipeline() 增加 chunk_length_s=30, stride_length_s=5,batch_size 建议 1(注意:仅把 batch_size 改回 1 无效,单条 8692 帧特征进 encoder 会撞 max_source_positions=1500,报 The size of tensor a (4346) must match the size of tensor b (1500))
  • _base.py 的 cut_audio():导出前对 segs 做硬性切分,单条不超过 28000ms(留 2 秒余量,不逼近 3000 帧边界),并正确累加各子段 start/end 偏移
  • _detect_raw_segments():贪心粘合条件中补 combined_duration <= max_speech_ms 上限校验,从源头避免切好又被拼回

附带说明
pipe_asr() 中 generate_kwargs 仅在 --openai--whisper 模型下生效,kotoba-whisper 等微调模型拿不到 language 参数。若该模型需要显式指定语种,可能影响识别准确性,请一并确认是否为预期行为。


报告发送后,建议在软件安装目录 logs/ 下保留该次任务的日志文件(按日期命名的 .log),开发者可能需要你提供。

请查阅相关文档:

#16 Admin reply at 5 days ago

源码部署重新拉取最新代码git pull
预打包先升级到 4.16 再看看问题是否仍存在

#17 104.28**4 reply at 4 days ago

预打包版本升级到4.16依然复现此错误。

#18 DeepSeek reply at 4 days ago

按开发者给出的路径走下一步:开发者说的两条是并列的两个动作,而你只完成了第二条(预打包升级到 4.16 仍复现),尚未执行第一条(源码部署 + git pull 最新代码)。修复很可能只存在于较新的源码提交中,未打进 4.16 预打包包,因此两种版本表现不同是正常的。

一、执行源码部署 + 拉取最新代码

  1. 需要安装:Python 3.10、uv、ffmpeg>=6(Windows 需把 ffmpeg.exe、ffprobe.exe 放到 代码目录/ffmpeg/ 内),且必须能连 github.com。
  2. 命令:

    git clone https://github.com/jianchang512/pyvideotrans
    cd pyvideotrans
    uv sync
    uv run sp.py
  3. 若你已有旧源码目录,直接在目录内执行 git pull,再 uv sync,然后 uv run sp.py。
  4. 启动后先确认代码是新的:查看 videotrans/__init__.py 里的 VERSION,并在目录内执行 git log -1 记录提交号。

二、在源码版上原样重跑同一任务

保持同样的渠道设置(Huggingface_ASR + kotoba-tech/kotoba-whisper-v2.0、日语、同一文件),不要改动“最长语音持续/静音分割”,先看是否仍报同一处 _pad 越界:

  • 若不再报错:说明修复已在最新源码中生效,等后续补丁包/完整包即可,无需再改代码。
  • 若仍报错:说明该缺陷尚未修复,回到 GitHub issue 补充说明,这比继续在本站追问更有效。

三、回帖给开发者时补上这三条信息(缺一不可)

  1. 4.16 预打包版仍复现,附同一段错误栈三行(base.py 的 _pad、stt_pipe.py、_huggingface.py)。
  2. 源码版 git log -1 的提交号与 videotrans/__init__.py 的 VERSION,以及源码版是否仍复现。
  3. logs/ 下对应日期的日志文件,重点是那条被送入模型的最长切片时长(此前记录为 8692 帧 ≈ 86.92 秒)。

四、源码版仍复现时的临时可用做法(不改模型、不改软件逻辑)

用 ffmpeg 把待识别音频切成每段 ≤25 秒的 wav,批量用 Huggingface_ASR + kotoba-whisper-v2.0 转录,再用 菜单 → 工具 → 合并两个字幕 拼接,最后在「翻译视频」中用右侧「导入发音语言字幕」跳过语音识别。这条路径不受该缺陷影响。

请查阅相关文档:

Thinking...
Post Your Reply
If you have more than 50 messages, you will no longer receive an automatic reply. Please ask a new question!
Open source and free maintenance is not easy. If this project is helpful to you, please consider making a small donation to help the project continue to maintain and update.

Dondate via ko-fi.com

Related resource