LoRA 微调和全量微调:VibeVoice 仓库只给了哪一种
想把一个 ASR 模型调到自己那堆行话上,通常的第一个动作是去仓库里翻训练脚本,然后在「上 LoRA 还是全量」之间做个决定。VibeVoice 这边,这个决定其实不需要你做——仓库把它替你做完了,你需要判断的是它划下的这条边界会不会咬到你。
仓库把边界划在哪
docs/vibevoice-asr.md 的 Finetuning 一节只有一句话,写明支持 LoRA(Low-Rank Adaptation)微调,并把读者指向 finetuning-asr/README.md。仓库根 README 的 VibeVoice-ASR 板块里,Finetuning 那条链接指的也是同一个文件。
再看目录:整个仓库里带训练性质的 Python 脚本只有 finetuning-asr/lora_finetune.py 一个,同目录下另有 inference_lora.py、README.md 和一个 toy_dataset/。全量微调的脚本、配置或说明,我们在仓库里没有找到——不是「不推荐」,是压根没有对应文件可读。
所以这篇不会去比「LoRA 和全量微调谁更划算」:一方有白纸黑字的代码,另一方在这个仓库里连一句描述都没有,没有依据的维度不比。能比的只有一件事——仓库给的这条路线,在数据、可训练范围、产物这三处各自要求你付出什么。
另外把家族分清楚:finetuning-asr/ 下的脚本从头到尾针对的是 ASR 这一支,模型参数默认值是 microsoft/VibeVoice-ASR。TTS、Realtime 那几个模型的微调说明,我们在仓库里没有找到。
第一处:依赖不在 pyproject 里
finetuning-asr/README.md 的 Requirements 节写的是两步:
# Install vibevoice first
pip install -e .
pip install peft
第二行值得单独说一句。pyproject.toml 的 dependencies 列表里有 torch、transformers、accelerate、librosa 这些,peft 不在里面,optional-dependencies 里也只有一个 streamingtts 分组。也就是说装完主包并不会带上 peft,而 lora_finetune.py 顶部直接 from peft import LoraConfig, get_peft_model, ...。漏了第二行,脚本在 import 阶段就走不下去。
第二处:数据长什么样
这条路线对数据形态的要求写得相当具体。README 的 Data Format 节说明音频文件与同名 JSON 标注放在同一个目录里,0.mp3 配 0.json,依次类推。
JSON 的结构在 README 与 VibeVoiceASRDataset 的 docstring 里各写了一份,字段一致:
| 字段 | 说明 |
|---|---|
audio_path | 音频文件名,_load_samples 用它拼出实际路径 |
audio_duration | 音频时长(秒) |
segments | 分段列表,每段含 speaker、text、start、end |
customized_context | 可选,领域术语或上下文句子 |
真正决定行为的是 _load_samples():它 glob("*.json") 遍历目录,以 JSON 为驱动,JSON 里没有 audio_path、或者按这个名字找不到音频文件,都只是打一条 warning 然后 continue。这意味着你少放几个音频不会报错中断,只会安静地少训几条——__init__ 末尾那句 Loaded {len(self.samples)} samples 是你唯一能对上数的地方。--max_audio_length 传了值的话,超过这个秒数的样本也会被跳过(audio_duration 缺失时按无穷大处理,也就是照样跳过)。
customized_context 这个字段和推理时的 hotwords 是同一条通路:__getitem__ 把列表用换行拼成一个字符串,作为 context_info 传给 processor;--use_customized_context 可以关掉它。README 表格里这一项写的是 True,这是仓库当前代码里的默认值,随版本可能变动。
还有一处别踩:README 明确写了 toy_dataset/ 里是 VibeVoice TTS 合成的音频,只作演示用,「不是一份完整的微调数据集」,并提示你换成真实录音与准确转写、按数据规模与领域调整学习率、epoch 与 LoRA rank。拿它当基线去判断效果是自己骗自己。
第三处:训练目标不是纯文本
这一点是 VibeVoice-ASR 微调和「随便找个 ASR 微调教程照做」差别最大的地方。_format_transcription() 把你 JSON 里的 segments 重新拼成模型要学的输出串,键名是首字母大写的 Start、End、Speaker、Content,时间戳 round(..., 2),最后 json.dumps(..., ensure_ascii=False, separators=(',', ':')) 序列化成不带空格的紧凑 JSON。
也就是说,你的标注要负责的不只是「说了什么」,还有「谁在什么时候说的」,而且模型学的是一段结构化 JSON 文本。__getitem__ 里把这段目标文本用 tokenizer.apply_chat_template 套上 assistant 角色再拼到输入后面,labels 前半段全填 -100,损失只落在这段回答上。
VibeVoiceASRDataCollator.__call__ 里那行注释也值得留意:processor 在推理/生成时用左 padding,训练这边用的是右 padding。它按三种长度基准把张量补齐——input_ids、attention_mask、labels、acoustic_input_mask 按 max_seq_len,speech_tensors 按原始波形的 max_speech_len,speech_masks 按 max_vae_len。你自己改 collator 时,这三条基准与各张量的对应关系是硬约束。
顺带一个读代码时会绊一下的地方:_format_transcription(self, segments, audio_duration) 收了 audio_duration 这个形参,但函数体里没有用到它;__getitem__ 调用时还为它准备了 data.get("audio_duration", len(speech) / 24000) 这样一个兜底。两处放在一起看就是:这个兜底目前不会影响生成的目标文本。仅此而已,不替作者解释为什么留着。
第四处:可训练的范围被钉死在语言模型侧
setup_model_for_training() 做了两件事。先按名字冻结:遍历 named_parameters(),凡名字里含 acoustic_tokenizer 或 semantic_tokenizer 的一律 requires_grad = False,注释写的是「只想微调语言模型」。然后 get_peft_model(model, lora_config) 套上 LoRA,再调 print_trainable_parameters()。
get_lora_config() 里,target_modules 形参默认 None,落到函数体里就是一份写死的列表:q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj、down_proj,注释标明这是 Qwen2 的注意力与 MLP 层。task_type 是 TaskType.CAUSAL_LM,bias="none"。
这里有个容易吃亏的点:README 说「脚本用的是 HuggingFace 的 TrainingArguments,所有标准选项都可用」,容易让人以为什么都能从命令行调。但 LoRA 侧只有 LoraArguments 这个 dataclass 暴露出来的 --lora_r、--lora_alpha、--lora_dropout 三个;train() 调 get_lora_config() 时没有透传 target_modules,命令行里也没有对应参数。想换一组注入层,只能改 lora_finetune.py 的源码。
同一类「命令行说了不算」的还有一处:README 的参数表把 --gradient_checkpointing 记为 False,但 main() 调 train(...) 时并没有把这个开关传下去,train() 签名里 gradient_checkpointing: bool = True,setup_model_for_training() 据此调用 enable_input_require_grads() 与 gradient_checkpointing_enable()。两处摆在一起就是不一致,我们只指出这一点,不推断它会带来什么后果。这两个值都是仓库当前文档与代码里的写法,随版本可能变动。
再补一条会咬 Windows 用户的:lora_finetune.py 与 inference_lora.py 加载模型时都把 attn_implementation="flash_attention_2" 写死在 from_pretrained 调用里,没有留命令行开关。环境里如果没有这个实现,改法只有一个——改源码。仓库里我们没有找到关于替换注意力实现的说明。
第五处:训完你手里拿到的是什么
README 的训练命令原样是这样(单卡那条):
torchrun --nproc_per_node=1 lora_finetune.py \
--model_path microsoft/VibeVoice-ASR \
--data_dir ./toy_dataset \
--output_dir ./output \
--num_train_epochs 3 \
--per_device_train_batch_size 1 \
--learning_rate 1e-4 \
--bf16 \
--report_to none
其中的 3、1、1e-4 是仓库示例里给的值,不是推荐配置,README 自己也让你按数据集调。以上命令原样抄自 finetuning-asr/README.md,未经实测,以仓库最新内容与 --help 的实际输出为准。
--output_dir 指向的目录,训完会被写进两样东西:trainer.save_model(training_args.output_dir) 存的是被 PEFT 包装过的模型(也就是 adapter 权重),processor.save_pretrained(training_args.output_dir) 把 processor 配置一并存进去。基座权重不在这里。
推理侧的用法与之对应,inference_lora.py 的 load_lora_model() 先 from_pretrained 把基座 microsoft/VibeVoice-ASR load 起来(processor 那边还带了 language_model_pretrained_name="Qwen/Qwen2.5-7B"),再 PeftModel.from_pretrained(model, lora_path) 贴上 adapter。基座和 adapter 是两份东西,分发时得一起给。 脚本里 model.merge_and_unload() 那行是注释掉的;README 的 Merging LoRA Weights 节把它列为可选步骤,合并后 save_pretrained("./merged_model") 得到一份独立权重。
那么,怎么决定要不要走这条路
按上面几处倒推,判断顺序大致是这样:
- 你的标注里有没有 speaker 和时间戳? 没有的话,
_format_transcription需要的start/end/speaker就得先补出来,这往往比训练本身更费事。 - 你要动的是语言模型这一侧吗? 脚本冻结了两个 tokenizer、LoRA 只注入 Qwen2 的注意力与 MLP 层。如果你的问题出在音频前端,这条路线覆盖不到。
- 能不能接受改源码?
target_modules与attn_implementation都只能改代码。不能改的话,先想清楚。 - 产物形态能不能接受? 训练脚本没有传
eval_dataset,Trainer只拿到train_dataset;评估、早停这类东西要你自己接。产物默认是 adapter,要独立权重得自己走一次 merge。
至于「全量微调会不会更合适」——这一点我们在 VibeVoice 仓库里没有找到任何依据,不比,也不推荐。
Windows 侧一句补充(通用做法,非该项目官方内容):README 里多卡那条命令写成 CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun ...,这种把环境变量前置在命令前的写法是 POSIX shell 的语法,在 PowerShell 与 cmd 里不成立,需要按各自 shell 的方式先设好环境变量再执行。仓库里没有给 Windows 的对应说明,请结合自身环境评估。
本文依据 github.com/microsoft/VibeVoice 仓库与 Hugging Face 模型卡于 2026-08-18 的公开内容整理,
事实来自仓库内的文档与源码。我们没有下载权重、没有跑过推理、也没有做过训练,
因此不涉及显存占用、推理速度、识别准确率与音质的任何描述,也不与其它模型做比较或排名。
该项目持续更新,文中涉及的模块路径、配置字段与接口写法随版本变动,请以仓库最新内容为准。
需要说明的是:仓库 README 记载,2025-09-05 微软因发现有与既定意图不符的使用方式,
基于负责任 AI 原则从该仓库移除了 VibeVoice-TTS 代码;
当前 vibevoice/modular/modeling_vibevoice.py 首行注释标明其来自社区 fork,
且该模块未被 vibevoice/modular/__init__.py 的 __all__ 导出。
本文只讲代码与架构,不构成 TTS 推理的可用性保证。
仓库 README 的风险与限制一节写明:该模型仅供研究与开发用途, 未经进一步测试与开发不建议用于商业或真实场景,并特别提示了合成语音被用于伪造与虚假信息的风险。 使用合成语音时应遵守所在司法辖区的法律法规,并在分享 AI 生成内容时主动披露。
本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。