Retrieval-based-Voice-Conversion-WebUI(RVC)模型训练实战指南:从数据预处理、音高/特征提取到训练参数详解
【免费下载链接】Retrieval-based-Voice-Conversion-WebUIEasily train a good VC model with voice data <= 10 mins!项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Conversion-WebUI
本文以官方文档 docs/en/training_tips_en.md 为主体,结合当前仓库中 GUI(infer-web.py)、预处理与特征提取脚本、训练脚本等源码逐环节展开。读完本文,你将完整掌握 RVC 训练 Tab 中 step1/step2a/step2b/step3 每一步的底层原理、目录产物与参数含义,能依据自己的机器配置(显存、CPU 核数、是否唱歌场景)自主设定训练参数,并理解预训练模型、Faiss 特征索引与一键训练的工作机制。
一、RVC 训练流程总览:一次训练的完整链路
RVC(Retrieval-based Voice Conversion)用不超过 10 分钟甚至更短的干净人声即可训练一个可用的音色转换模型。其训练 Tab 把整条流水线拆成几个可独立点击的步骤,官方文档按 GUI 顺序依次讲解。将文档步骤与仓库源码对应,可以得到如下链路:
| 步骤 | GUI 上的作用 | 核心脚本 | 产物目录(logs/<实验名>/下) |
|---|---|---|---|
| step1 | 填写实验配置 | GUI 直接处理 | 实验文件夹本身 |
| step2a | 数据处理 | infer/modules/train/preprocess.py | 0_gt_wavs、1_16k_wavs |
| step2b | 提取音高 + HuBERT 特征 | infer/modules/train/extract/extract_f0_print.py、infer/modules/train/extract_feature_print.py | 2a_f0、2b-f0nsf、3_feature256/3_feature768 |
| step3a | 训练生成器 G 与判别器 D | infer/modules/train/train.py | G_*.pth、D_*.pth、train.log等 |
| step3b | 训练 Faiss 特征索引 | GUI 内train_index函数 | added_IVF*.index等 |
GUI 中三行 Markdown 注释对这三个阶段做了精炼概括(见 infer-web.py#L1206-L1291):
- step2a:自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化,在实验目录下生成 2 个 wav 文件夹(暂时只支持单人训练);
- step2b:使用 CPU 提取音高(如果模型带音高),使用 GPU 提取特征(选择卡号);
- step3:填写训练设置,开始训练模型和索引。
二、step1:实验命名、采样率与是否带音高(pitch)指导
step1 只做两件事:
- 设置实验名(exp_name)。每次实验的数据、日志与模型都存放在
logs/<你的实验名>/下,GUI 默认值为mi-test(infer-web.py#L1178)。因此实验名既是文件夹名也是模型的"身份标识"。 - 决定模型是否考虑音高。GUI 的说明非常直接:"模型是否带音高指导(唱歌一定要,语音可以不要)"(infer-web.py#L1185-L1189)。
关于是否带音高,官方文档给出的取舍是:不考虑音高时模型更轻量,但不适合唱歌。原因是:
- 带音高的模型把基频 f0 作为额外输入(源码对应
SynthesizerTrnMs256NSFsid/SynthesizerTrnMs768NSFsid),转换时能保留源音频的旋律线; - 不带音高的模型(
SynthesizerTrnMs256NSFsid_nono/SynthesizerTrnMs768NSFsid_nono,见 infer/modules/train/train.py#L57-L68)更适合偏说话风格的配音转换,模型更小、更省资源。
除上述两个文档明确提到的选项外,GUI 在 step1 还提供两个与模型架构强相关的开关(infer-web.py#L1179-L1197):
- 目标采样率:
40k/48k(v1 仅这两档;切到 v2 后还多出32k档,相关联动逻辑见 infer-web.py#L433-L452)。它决定实验音频被切到多少采样率保存,以及最终加载哪份 json 配置; - 版本 v1 / v2:默认 v2。两者的核心差异之一是 HuBERT 特征维度不同——v1 使用 256 维特征(
3_feature256),v2 使用 768 维特征(3_feature768)。
源码补充:GUI 的"是/否考虑音高"在底层会同时决定两件事:训练命令中的
-f0 1/0参数,以及加载哪一套预训练模型(带 f0 前缀 vs 不带,见下文 step3 预训练小节)。
三、step2a:数据预处理(加载 → 去噪 → 切片 → 归一化)
step2a 的输入是一个训练音频文件夹(GUI 中还可指定说话人 id,见 infer-web.py#L1212-L1232)。核心实现在 infer/modules/train/preprocess.py,其命令行参数依次为:输入目录、采样率、CPU 进程数、实验目录、是否并行、单段秒数。
3.1 加载音频(load audio)
文档要点:
- 只需指定文件夹,其中的音频文件会被自动读取;
- 不会递归读取子目录。例如指定
C:\Users\hoge\voices时,C:\Users\hoge\voices\voice.mp3会被读入,但C:\Users\hoge\voices\dir\voice.mp3不会。源码中确实是os.listdir平铺扫描顶层文件(preprocess.py#L111-L116),所以请把所有素材直接平铺放在同一层目录; - 内部用 ffmpeg 解码,凡是 ffmpeg 支持的扩展名都能读(mp3/wav/m4a/flac 等)。
需要留意一个与文档描述的差异:官方文档写道"先用 ffmpeg 转成 int16,再转 float32 并归一化到 -1~1",而当前仓库的实现是让 ffmpeg 直接输出 32 位浮点 PCM、单声道、重采样到目标采样率(见 infer/lib/audio.py#L32-L51),float32 采样值天然落在 [-1,1],不再有 int16 中间步骤。这一实现与 Whisper 的音频读取思路一致(源码注释中亦有引用)。
3.2 去噪(denoising)
文档称"音频会经过 scipy 的 filtfilt 平滑"。结合源码看,这里的"去噪"实为高通滤波:使用 scipysignal.butter构造 5 阶、截止频率 48 Hz 的高通滤波器,滤除 48 Hz 以下的低频隆隆声/直流分量(preprocess.py#L46)。
另一个与文档的细节差异:当前代码中实际调用的是signal.lfilter而非filtfilt。源码注释解释了原因:零相位滤波(filtfilt)会引入前振铃(pre-ringing)噪声,因此改用普通因果滤波(preprocess.py#L84-L86)。如果你在旧版本文档或讨论中看到 filtfilt 的描述,需要注意当前实现已调整。
3.3 音频切片与响度归一化
文档对切片的描述是:先按"超过一定时长的静音段"(文档猜测max_sil_kept=5 秒)切分音频,然后再按"每 4 秒一段、0.3 秒重叠"继续切,4 秒内的音频做响度归一化后写入两个目录。
源码中的真实参数(preprocess.py#L36-L52)为:
- 静音检测切片器
Slicer:threshold=-42 dB(低于 -42 dB 视为静音)、min_length=1500、min_interval=400、hop_size=15、max_sil_kept=500(单位毫秒,即约 0.5 秒)——这是对文档中"5 秒"猜测的修正; - 单段长度
per:默认取配置项config.preprocess_per,即3.7 秒(GUI 完整命令行见 infer-web.py#L218-L232)。在低显存(≤4 GB)或无法使用 fp16 的机器上会被下调到 3.0 秒(见 configs/config.py#L128-L177); - 重叠
overlap=0.3秒:切分循环以per - overlap为步长滑动,避免断句被生硬切断(preprocess.py#L88-L102)。
响度归一化并非简单的峰值归一,而是一个带软限制的混合公式(norm_write,preprocess.py#L59-L79):
tmp_audio = (tmp_audio / tmp_max * (max * alpha)) + (1 - alpha) * tmp_audio其中max=0.9、alpha=0.75,本质是"75% 的峰值归一化结果 + 25% 原始信号",把响度拉齐的同时保留原始动态;若单段信号峰值异常(|tmp|>2.5,通常意味着爆音/破音)该段会被直接过滤丢弃。
最终每个切片生成两份文件:
- 原始采样率(如 40k/48k)的 wav →
logs/<实验名>/0_gt_wavs/(Ground Truth,供训练比对真实波形); - 重采样到16 kHz的 wav →
logs/<实验名>/1_16k_wavs/(供后续 HuBERT 提取特征,因为 HuBERT 的输入采样率就是 16k)。
预处理全程日志实时写入实验目录下的preprocess.log,GUI 的"处理数据"按钮会边跑边回传这些日志(infer-web.py#L245-L254)。
四、step2b:音高提取与 HuBERT 特征提取
step2b 的 GUI 说明是:"使用 CPU 提取音高(如果模型带音高),使用 GPU 提取特征(选择卡号)"(infer-web.py#L1233-L1237)。GUI 提供 CPU 进程数滑杆(范围 0 到机器 CPU 核数)、GPU 卡号输入(形如0-1-2)以及 f0 算法选择(infer-web.py#L1198-L1268)。
4.1 提取音高 f0
官方文档:从 wav 中提取音高信息,使用 parselmouth(pm 算法)或 pyworld(harvest/dio)内置方法完成,原始 f0 与量化后的 f0 分别保存;随后将音高做对数/梅尔化映射并量化为 1~255 的整数。源码实现见 infer/modules/train/extract/extract_f0_print.py:
- 统一从
1_16k_wavs读取音频,hop 长度 160(即 16k 下每 10 ms 一个音高帧); - 可选算法(GUI 的
f0method):pm:parselmouth 的to_pitch_ac自相关法,f0 范围 50~1100 Hz;harvest/dio:pyworld 算法,其中 harvest 精度更高但更慢;dio 更快,后续还用stonemask精修(extract_f0_print.py#L66-L83);rmvpe:基于 infer/lib/rmvpe.py 的深度学习音高估计,加载assets/rmvpe/rmvpe.pt,效果最好;rmvpe_gpu:把 rmvpe 放到 GPU 上多进程跑(对应 infer/modules/train/extract/extract_f0_rmvpe.py)。
- 量化:不是"对数"而是梅尔尺度。源码
coarse_f0用1127 * ln(1 + f/700)把频率转到 mel 域,再线性映射到 1~255 的整数 bin(f0_bin=256,extract_f0_print.py#L95-L109)。
目录对应关系需要按当前源码厘清:量化后的整数 f0 写入
2a_f0,连续值 f0 写入2b-f0nsf(extract_f0_print.py#L149-L160)。官方文档中"2a_f0 存原始 f0、2b-f0nsf 存量化值"的描述与当前实现相反,阅读旧资料时需注意。训练时两者都会被用到(前者是模型 token 化的 coarse pitch,后者作为 nsf 细粒度 f0 输入)。
4.2 HuBERT 特征提取(feature_print)
文档:用 HuBERT 把1_16k_wavs里的 wav 预计算为 embedding,v1 为 256 维特征,以 npy 存入3_feature256。脚本为 infer/modules/train/extract_feature_print.py:
- 加载
assets/hubert/hubert_base.pt(HuBERT Base,脚本第 55 行),在 GPU/MPS/DirectML/CPU 上逐段推理; - 输出目录随版本切换:v1 →
3_feature256(256 维),v2 →3_feature768(768 维,对应官方说明和 infer-web.py#L485-L531 中的fea_dim逻辑); - 每条切片保存为一个
.npy,文件名与 wav 同名,供训练与建索引使用。
这一步之所以要"预先算好",是因为训练时每步都要取特征,若实时过 HuBERT 会非常慢;预计算后训练只做查表。GUI 会把 f0 与特征任务按多个 GPU 卡拆成多进程并行(多卡编号用-分隔,如0-1-2)。
4.3 特征提取期间的自检逻辑
无论是否带 f0,真正启动训练前,GUI 会取0_gt_wavs、特征目录(以及带 f0 时的2a_f0、2b-f0nsf)四个集合的文件名交集,只有三个/四个环节都成功产出的切片才会进入训练名单,并逐行写入filelist.txt(infer-web.py#L481-L546)。此外还会向名单追加 2 条静音样本(mute),用于稳定训练。
五、step3:训练模型
5.1 新手词表:step / batch / epoch 与训练时间估算
官方文档在 step3 开头专门为新手解释基本概念,其要点值得保留并扩展:
- 一次模型更新称为一个 step(步),每个 step 取
batch_size条数据做前向预测与误差回传修正; - 把整个数据集完整过一遍称为一个 epoch;
- 因此总训练时间 ≈单步耗时 ×(数据集条数 ÷ batch_size)× epoch 数。
由此可以推出文档的核心调参建议:batch_size 越大,每个 epoch 内的 step 数越少(分摊到每条样本的耗时越小),训练更稳定,但单步显存占用更高。显存可通过nvidia-smi观察,应结合自己机器的显存尽量调大 batch_size 以缩短总时长。GUI 中 batch_size 是"每张显卡"的批大小,滑杆范围 1~40(infer-web.py#L1309-L1315),多卡时总 batch = 每卡值 × 卡数(源码见 train.py#L140-L148 中batch_size * n_gpus的 BucketSampler 设置)。
训练时长之外的另一个常见经验是"少样本足够":RVC 之所以能用极小数据量训练,除了下文预训练权重外,还因为特征被 HuBERT 预计算好了,每个 epoch 内的正向/反向都在做特征层面的"微调"而非从零学语言表征。
5.2 预训练模型与断点续训
文档说明 RVC不是从零开始训练,而是从预训练权重起步,所以小数据也能训出可用模型。文档所述默认加载路径为pretrained/f0G40k.pth与f0D40k.pth。结合当前源码,规则更完整:
- 命名规则为
{f0}{G|D}{采样率}.pth:带音高时前缀f0(如f0G40k.pth),不带音高时无前缀(如G40k.pth);版本 v1 放在 assets/pretrained,v2 放在 assets/pretrained_v2(自动回填逻辑见 infer-web.py#L398-L461); - GUI 默认值为
assets/pretrained_v2/f0G40k.pth与assets/pretrained_v2/f0D40k.pth(infer-web.py#L1340-L1348);切换版本/采样率/是否带 f0 时,路径会自动联动刷新; - 模型训练中按
save_every_epoch定期把参数存成G_<step>.pth与D_<step>.pth(train.py#L564-L594),通过把任意一份这样的 pth 填进"预训练 G/D 路径"输入框,即可从该断点继续训练,甚至可加载另一个实验训出的权重做迁移/续训。这正是官方文档所说"可从中断处重启,或从不同实验的权重继续训练"的机制。
启动训练时 GUI 会拼出形如下面的命令交给 infer/modules/train/train.py(infer-web.py#L570-L610):
python infer/modules/train/train.py -e mi-test -sr 40k -f0 1 -bs 8 -g 0 \ -te 200 -se 10 -pg assets/pretrained_v2/f0G40k.pth \ -pd assets/pretrained_v2/f0D40k.pth -l 0 -c 0 -sw 0 -v v2命令行参数与含义(参数解析见 infer/lib/train/utils.py#L291-L391):
| 参数 | 含义 |
|---|---|
-e / --experiment_dir | 实验名,对应logs/<名> |
-sr / --sample_rate | 32k / 40k / 48k,决定加载哪份 json 配置 |
-f0 / --if_f0 | 1 带音高,0 不带 |
-bs / --batch_size | 每卡批大小 |
-g / --gpus | 用-分隔的卡号,如0-1,多卡自动 DDP |
-te / --total_epoch | 总训练轮数 |
-se / --save_every_epoch | 每 N 轮保存一次 checkpoint |
-pg / -pd | 预训练生成器/判别器路径(也用于续训) |
-l / --if_latest | 是否只保留最新 ckpt(为省硬盘,固定覆盖*_2333333.pth) |
-c / --if_cache_data_in_gpu | 是否把整个数据集缓存进显存 |
-sw / --save_every_weights | 保存节点是否把可推理的小模型导出到 weights 目录 |
-v / --version | v1 / v2 |
训练时模型的架构与超参来自实验目录中的config.json(从 configs/v2/48k.json 或 configs/v1/40k.json 等模板复制而来,模板清单见 configs/config.py#L24-L30)。以 v2/48k 为例,关键字段包括:
train.learning_rate=1e-4、betas=[0.8,0.99]、lr_decay=0.999875(每轮指数衰减);train.fp16_run=true(混合精度;低端 GPU 或非 CUDA 环境会自动切 fp32,见 configs/config.py#L139-L177);train.c_mel=45、c_kl=1.0——mel 重建损失与 KL 损失的权重;data.sampling_rate=48000、hop_length=480、filter_length=2048、n_mel_channels=128;model.spk_embed_dim=109(说话人嵌入维度,与 GUI 说话人 id 滑杆配套)。
训练目标由五部分构成(train.py#L474-L499):判别器损失discriminator_loss、生成器对抗损失generator_loss、特征匹配损失feature_loss、mel 频谱 L1 损失(权重 c_mel)与 KL 损失(权重 c_kl)。训练进程会通过 TensorBoard 把各损失、学习率、梯度范数与真实/生成 mel 频谱写进实验目录(SummaryWriter),控制台与train.log中每个log_interval(默认 200 步)输出一次损失摘要。
5.3 GUI 训练参数速查(文档 + 源码默认值)
| 参数 | 范围 | 默认 | 说明 |
|---|---|---|---|
| 保存频率 save_every_epoch | 1~50 | 5 | 每隔多少 epoch 存一次G/D_*.pth |
| 总轮数 total_epoch | 2~1000 | 20 | 官方建议语音类先跑 200 轮上下;唱歌可酌情增加 |
| batch_size | 1~40 | 随显卡 | 越大越省总时长、越吃显存 |
| 是否仅保存最新 ckpt | 是/否 | 否 | 选"是"大幅省硬盘 |
| 是否缓存数据到显存 | 是/否 | 否 | ≤10 min 小数据建议"是"以加速;大数据会爆显存(GUI 注释原文如此) |
| 保存节点导出小模型 | 是/否 | 否 | 选"是"则每个保存节点把可推理模型放进 assets/weights |
对应源码见 infer-web.py#L1293-L1338。
六、step3b:训练 Faiss 特征索引(learning index)
官方文档解释了 RVC 名称中"Retrieval-based(基于检索)"的由来:训练期间 RVC 保存所用到的 HuBERT 特征值;推理时,需要在特征库里检索与输入最相似的特征用于转换。为了让这个检索足够快,需要预先对特征集合建立索引。RVC 使用近似最近邻库faiss:读取3_feature256(v2 为3_feature768)中的特征来学习索引并保存。
GUI 中点击"训练特征索引"调用的是 infer-web.py#L616-L711 的train_index,其实现细节是理解"检索"的关键:
- 把所有
.npy特征纵向拼接并打乱; - 若特征行数超过 20 万(
2e5),先用MiniBatchKMeans聚类到10000 个簇中心再做索引,控制索引体积(infer-web.py#L639-L658); - 使用
faiss.index_factory(256 或 768, "IVF{n},Flat")构造倒排文件索引,其中聚类数n_ivf = min(int(16 * sqrt(特征数)), 特征数 // 39),并把检索探针数nprobe设为 1(infer-web.py#L661-L669); - 先把索引在特征上
train,保存trained_IVF*_Flat_nprobe_*.index,再以 8192 条为一批add入库,保存added_IVF*_Flat_nprobe_*.index,并尝试软链到推理侧索引目录,供后续"转换"环节直接检索使用。
关于官方文档末尾的历史注记——"自 20230428 版本起,推理时自动从(实验目录中的)索引读取,不再需要手动保存/指定索引路径"——在 GUI 上体现为:训练侧不再要求用户为索引命名,索引文件名由代码按IVF{n}_Flat_nprobe_1_<实验名>_<v1|v2>自动生成,训练/推理两侧都能据此约定自动找到它。
七、三个按钮的职责与"一键训练"
step3 区域共有三个主按钮(infer-web.py#L1372-L1410):
- 训练模型(Train model):要求先完成 step2b,然后按当前参数训练 G/D 网络;
- 训练特征索引(Train feature index):训练完模型后再执行 Faiss 索引学习;
- 一键训练(One-click training):把 step2b 数据处理、音高/特征提取、模型训练与索引训练全部串起来一次跑完。
一键训练对应的后端编排函数train1key(infer-web.py#L714-L778)严格按preprocess_dataset → extract_f0_feature → click_train → train_index的顺序执行,每完成一段就向界面回传一次进度信息,全程串行、无人工干预,适合素材与超参都已确定后的批量跑实验。若想分步排查问题(例如先看预处理切片质量、再看 f0 是否提取成功),则建议手动分步执行。
八、实验目录产物总览
一次完整训练结束后,logs/<实验名>/下的结构与用途如下(可与文档 step1~step3 对照核查):
| 目录/文件 | 内容 |
|---|---|
0_gt_wavs/ | 归一化后的原始采样率切片(训练的真实波形目标) |
1_16k_wavs/ | 同一批切片的 16 kHz 重采样版本(特征提取输入) |
2a_f0/ | 量化到 1~255 的梅尔域整数 f0 |
2b-f0nsf/ | 连续值 f0(nsf 用细粒度音高) |
3_feature256/或3_feature768/ | HuBERT 预计算特征(v1 256 维 / v2 768 维) |
filelist.txt | 四类产物的逐条配对清单(带说话人 id 与 mute 样本) |
config.json | 该实验冻结的模型/训练超参(训练开始时从 configs 模板拷贝) |
G_*.pth/D_*.pth | 生成器与判别器 checkpoint(按 save_every_epoch 保存) |
trained_IVF*.index/added_IVF*.index | Faiss 索引(训练与入库两阶段产物) |
train.log/preprocess.log/extract_f0_feature.log | 各阶段日志 |
| TensorBoard event 文件 | 损失/学习率/频谱可视化 |
九、调参与避坑要点(综合文档与源码)
- 显存是 first-class 约束:优先观察
nvidia-smi,在显存允许范围内调大 batch_size;显存小(≤4 GB)或无独立显卡时,项目会自动降级为 fp32、缩短切片长度(3.0 s)并调低检索相关窗口,无需手动干预(configs/config.py)。 - 唱歌必须带 f0,纯语音可去掉 f0:去掉 f0 后模型更轻,但会丢失旋律跟随能力。
- 素材尽量平铺、干净:step2a 不递归子目录;切片前 48 Hz 高通只滤低频底噪,明显的口水声、喷麦与混响最好在进入训练前人工清理,因为切出来的每段 3.7 s 都会被当作 ground truth。
- f0 算法按需选择:CPU 差选
pm/dio提速,追求质量选harvest,rmvpe/rmvpe_gpu在噪声鲁棒性上最稳。 - 断点续训 = 填旧 ckpt 路径:中途中断无需重来,把最新的
G_*.pth/D_*.pth填进预训练路径输入框再点训练即可。 - 小数据可缓存进显存:≤10 min 的素材建议打开"缓存训练集至显存"提速;大数据量时该选项反而会爆显存且收益有限(GUI 提示与 train.py#L315-L394 的 cache 逻辑一致)。
- 产物命名以当前仓库为准:文档中
add_XXX.index、f0 目录的存法在历史版本间有变动,本文已按当前源码逐一标注,遇到老教程请交叉核对。
如果你希望在完全不动 GUI 的前提下复现流水线,可参考 tools/infer_batch_rvc.py 等批处理入口了解推理侧约定;训练侧的最终权威入口始终是 infer-web.py 中的 Training Tab 与 infer/modules/train/ 下的各脚本。
【免费下载链接】Retrieval-based-Voice-Conversion-WebUIEasily train a good VC model with voice data <= 10 mins!项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-Voice-Conversion-WebUI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考