首页
/ RVC 的 faiss 索引调优指南:IVF 倒排索引、距离度量与 nprobe 参数选择

RVC 的 faiss 索引调优指南:IVF 倒排索引、距离度量与 nprobe 参数选择

2026-09-05 11:04:26作者:卓炯娓

本文围绕 Retrieval-based-Voice-Conversion-WebUI(RVC)仓库中的 faiss 调优文档 展开,系统讲解 faiss 在检索式变声中的角色、从 HuBERT 特征到 .index 文件的完整构建流程,以及 index factory 字符串、距离度量、IVF 聚类数、nprobe、FastScan 与 RFlat 等关键参数的含义与推荐取值。读完本文,你可以结合 infer-web.pytools/infer/train-index-v2.py 中的真实实现,为不同规模的训练集选择合理的索引参数,并理解推理时 index_rate 混合权重的底层调用链。

什么是 faiss

faiss 是 Facebook Research 开发的稠密向量近邻搜索(neighborhood search)库,高效实现了多种近似最近邻搜索(Approximate Neighbor Search, ANNS)算法。ANNS 的思路是牺牲少量检索精度换取大幅加速:不做全量暴力比对,而是通过量化、倒排索引、产品量化等结构在毫秒级时间内找出“足够相似”的向量。这正是 RVC 这类实时性要求较高的推理场景所需要的能力。

faiss 在 RVC 中的作用

RVC 的核心卖点之一就是用不到 10 分钟的语音数据训练一个可用的变声模型。在此之上,faiss 索引提供了“检索式”增强:

  • 推理时,输入音频先经 HuBERT 提取出逐帧特征(embedding);
  • 再用 faiss 在训练集全部帧特征构成的向量库中检索与该帧最相似的若干条训练特征;
  • 将这些相似特征按距离加权混合,替换(或按比例混合)当前帧特征,使最终输出音色更贴近训练集说话人。

从源码看,这一过程在 infer/modules/vc/pipeline.pyvc() 方法中实现:

score, ix = index.search(npy, k=8)          # 每帧检索 k=8 个最近邻
weight = np.square(1 / score)               # 距离越小权重越大(平方倒数)
weight /= weight.sum(axis=1, keepdims=True) # 权重归一化
npy = np.sum(big_npy[ix] * np.expand_dims(weight, axis=2), axis=1)  # 加权求和
feats = torch.from_numpy(npy).unsqueeze(0).to(self.device) * index_rate \
      + (1 - self.index_rate) * feats   # 与原特征按 index_rate 线性混合

两点值得注意:

  1. 混合权重 index_rate:最终特征 = 检索混合特征 × index_rate + 原始特征 × (1 − index_rate)。index_rate = 0 时完全禁用检索,1 时完全使用检索结果。
  2. 必须使用 added_*.index:实时推理入口 tools/rvc_for_realtime.py 在搜索结果为无效索引时会明确报错:Invalid index. You MUST use added_xxxx.index but not trained_xxxx.index!。原因是 trained_*.index 只完成了聚类中心训练,尚未写入任何向量,index.ntotal == 0,检索必然失败。

实现总览:从特征 npy 到 .index 文件

文档给出的流程是:模型训练目录下 /logs/your-experiment/3_feature256(v1)中存放着 HuBERT 从每段语音中提取的特征 npy 文件;按文件名排序读取、拼接成 big_npy(形状 [N, 256],v2 为 [N, 768]),保存为 total_fea.npy 后再用 faiss 训练。

仓库中这一流程的完整实现是 infer-web.pytrain_index() 函数,其实际步骤比文档描述更完整:

feature_dir = "%s/3_feature256" % exp_dir if version19 == "v1" else "%s/3_feature768" % exp_dir
# 1. 按文件名排序读取所有 npy 并拼接
for name in sorted(listdir_res):
    phone = np.load("%s/%s" % (feature_dir, name))
    npys.append(phone)
big_npy = np.concatenate(npys, 0)
# 2. 随机打乱顺序,避免 faiss 内部顺序偏差
big_npy_idx = np.arange(big_npy.shape[0])
np.random.shuffle(big_npy_idx)
big_npy = big_npy[big_npy_idx]
# 3. 数据量超过 20 万帧时,先用 MiniBatchKMeans 压缩到 1 万个聚类中心
if big_npy.shape[0] > 2e5:
    big_npy = MiniBatchKMeans(n_clusters=10000, batch_size=256 * config.n_cpu,
                             compute_labels=False, init="random").fit(big_npy).cluster_centers_
# 4. 持久化合并后的特征
np.save("%s/total_fea.npy" % exp_dir, big_npy)
# 5. 计算 IVF 聚类数并构建索引
n_ivf = min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39)
index = faiss.index_factory(256 if version19 == "v1" else 768, "IVF%s,Flat" % n_ivf)
index_ivf = faiss.extract_index_ivf(index)
index_ivf.nprobe = 1
index.train(big_npy)
faiss.write_index(index, "%s/trained_IVF%s_Flat_nprobe_%s_%s_%s.index" % ...)
# 6. 分批 add 数据(batch 8192),生成可直接使用的 added_ 索引
for i in range(0, big_npy.shape[0], batch_size_add):
    index.add(big_npy[i : i + batch_size_add])
faiss.write_index(index, "%s/added_IVF%s_Flat_nprobe_%s_%s_%s.index" % ...)

由此可以归纳出几个实操要点:

  • 维度随版本变化:v1 用 256 维特征,v2 用 768 维,index_factory 的第一个参数必须与之匹配;
  • 大特征库先 kmeans 降量:超过 20 万帧时先用 MiniBatchKMeans 压缩到 1 万个中心再建索引,可显著降低索引体积与训练耗时(从源码结构看这是一种经验性的规模控制手段);
  • trained_added_ 两份产物:前者是“只训练了 IVF 量化器”的索引,后者才包含全部向量,推理端(pipeline.py)通过 index.reconstruct_n(0, index.ntotal)added_ 索引中反解出 big_npy,因此只需要一个 .index 文件;
  • 独立脚本版本tools/infer/train-index.py(v1,固定 IVF512,Flatnprobe = 9)与 tools/infer/train-index-v2.py(v2,768 维,nprobe = 1,IVF 数由公式动态计算)是同一流程的命令行脚本形式,输入目录 3_feature768 / 2-co256 等需要按自身实验路径修改。

方法详解

index factory:用字符串描述检索流水线

index factory 是 faiss 的一种独特表示法:用一条字符串把多个近似搜索方法串联成一条流水线,只需改字符串就能切换整套检索方案。RVC 中的用法:

index = faiss.index_factory(256, "IVF%s,Flat" % n_ivf)

index_factory 的参数依次为:向量维度、index factory 字符串、距离类型(第三个参数可省略,默认 L2)。faiss 官方维基中有关于该表示法的完整说明(可查阅 faiss 项目的 "The index factory" 文档页),例如 IVF1024,PQ128x4fs,RFlat 就是一条“IVF 粗量化 → 4bit 产品量化快速扫描 → 精确重排”的三级流水线。

距离度量:L2 还是内积

作为 embedding 相似度度量,faiss 中最常用的是两种索引距离:

  • 欧氏距离(METRIC_L2):对每一维求差的平方、全部维度求和后再开方,即日常二维、三维空间中的距离;
  • 内积(METRIC_INNER_PRODUCT):内积本身不直接作相似度索引,一般配合 L2 归一化使用,此时内积等价于余弦相似度

哪种更好取决于具体数据。对于 word2vec 类词向量、ArcFace 类图像检索模型产出的 embedding,业界常用余弦相似度。用 numpy 做 L2 归一化时,需要 eps 防止除零:

X_normed = X / np.maximum(eps, np.linalg.norm(X, ord=2, axis=-1, keepdims=True))

对 index factory 而言,通过第三个参数即可切换计算所用的距离度量:

index = faiss.index_factory(dimension, text, faiss.METRIC_INNER_PRODUCT)

IVF:倒排文件索引

IVF(Inverted File Index)的思想类似于全文检索中的倒排索引:

  • 训练阶段:用 kmeans 对全部数据聚类,以聚类中心做 Voronoi 划分,每个数据点归属一个簇,于是得到“簇 → 该簇内数据点”的字典;
  • 检索阶段:先在聚类中心中挑选最近的 n_probe 个簇,再只在这些簇内部计算精确距离。

例如各数据点被分配到簇如下:

index Cluster
1 A
2 B
3 A
4 C
5 B

则形成的倒排索引为:

cluster index
A 1, 3
B 2, 5
C 4

搜索时先定位候选簇,再对簇内数据点算距离,从而把“全库扫描”变成“局部扫描”。

推荐参数与选择依据

faiss 官方有专门的选择索引指南(可查阅 faiss 项目维基的 "Guidelines to choose an index" 文档页),本文按其思路结合 RVC 的实际情况展开。

小于 100 万条数据:PQ 4bit + 精确重排

对 100 万条以下的数据集,截至 2023 年 4 月 faiss 中最高效的方案是 4bit 产品量化(4bit-PQ)。将 IVF 与 4bit-PQ 组合——先用 IVF 缩小候选范围,再用 4bit-PQ 粗筛,最后用精确索引重算距离——可以写成:

index = faiss.index_factory(256, "IVF1024,PQ128x4fs,RFlat")

这条流水线正是 RVC 源码中预留的备选方案:infer-web.pyIVF%s,Flat 的下一行就是被注释掉的 "IVF%s,PQ128x4fs,RFlat" 版本,说明作者将其作为大特征库场景下的升级路径保留了下来。

IVF 聚类数 n_ivf 的推荐范围

聚类数过多时(例如等于数据条数),粗量化退化为逐条暴力搜索,反而低效。文档给出的经验规则是:对 N 条数据,IVF 聚类数取 4×√N ~ 16×√N 之间

RVC 源码实际采用的取值恰好落在这个区间的上界,并加了一个上限约束:

n_ivf = min(int(16 * np.sqrt(big_npy.shape[0])), big_npy.shape[0] // 39)

即取 16×√NN/39 的较小值——保证每个簇平均至少约 39 个向量,防止簇过小导致 IVF 失去意义。而 tools/infer/train-index.py 的旧版 v1 脚本则直接固定了 IVF512(对应约 1 万~100 万量级数据的常见取值)。

nprobe:精度与速度的权衡

检索耗时与 nprobe 成正比:probe 的簇越多,需要扫描的数据点越多,召回越高但越慢。文档的结论是:RVC 场景对检索精度要求并不高,nprobe = 1 即可,这也是 WebUI(infer-web.py)与 v2 独立脚本(tools/infer/train-index-v2.py)的默认设置;旧版 v1 脚本使用了更保守的 nprobe = 9,可作为精度不足时的调高起点。

FastScan:寄存器级加速的产品量化

FastScan 是一种让 PQ(Product Quantization,乘积量化)距离近似计算在 CPU 寄存器内完成的高速方法。其原理:

  • PQ 在训练时对每个子维度独立聚类(通常将向量切成 d 段,每段用 2 个聚类中心,即 4bit 编码),并预先计算好查询向量与各簇中心的距离,形成查表;
  • 检索时对每个子维度只需 O(1) 查表累加,无需逐维乘加。

这也是 index factory 字符串里 PQ 后面的数字通常是向量维度的一半(如 256 维写作 PQ128)的原因——每个字节编码 2 个维度的 4bit 码字。关于 FastScan 的更多细节可查阅 faiss 官方文档中的 "Fast accumulation of PQ and AQ codes (FastScan)" 页面。

RFlat:用精确距离重排粗筛结果

RFlat 是 index factory 流水线中的一个指令:让 faiss 用精确索引(Flat,即暴力精确距离)重算 FastScan/PQ 给出的粗略距离。取 k 个近邻时,实际会重排 k × k_factor 个候选点,用少量额外计算换回接近精确搜索的排序质量,是“快筛 + 精排”两级结构中的精排环节。

关键参数速查

参数 含义 RVC 仓库中的取值 说明
dim 向量维度 v1 为 256,v2 为 768 须与特征目录 3_feature256 / 3_feature768 一致
IVF 聚类数 n_ivf 粗量化簇数 min(16×√N, N//39),旧脚本固定 512 官方推荐区间 4×√N ~ 16×√N
nprobe 检索时扫描的簇数 默认 1(旧 v1 脚本为 9) 与检索耗时成正比,精度足够时不必调大
流水线 索引类型组合 IVF%s,Flat;备选 IVF%s,PQ128x4fs,RFlat infer-web.py
距离度量 默认 METRIC_L2 L2 切换余弦相似度需传 faiss.METRIC_INNER_PRODUCT 并归一化
index_rate 检索特征与原始特征的混合比 推理参数,0 表示禁用 pipeline.py

小结

  • faiss 索引让 RVC 以近似检索替代暴力比对,推理端按 index_rate 把 HuBERT 特征向训练集最相似特征“拉拢”,从而更贴近目标音色;
  • 构建流程固定为:排序拼接特征 → (超 20 万帧时 kmeans 降量)→ 计算 n_ivfindex_factory 建 IVF → train → 分批 add,产出 trained_added_ 两个索引,推理必须使用 added_
  • 参数选择上,n_ivf 取 4×√N ~ 16×√N(源码取 16×√N 并受 N/39 封顶),nprobe = 1 即可满足 RVC 的精度需求;数据量较大时可升级为 IVF1024,PQ128x4fs,RFlat 这类“IVF + 4bit-PQ + 精确重排”流水线,兼顾速度、内存与召回。

以上所有结论均可在当前仓库中对照验证:索引构建逻辑见 infer-web.pytools/infer/train-index-v2.py,检索与混合逻辑见 infer/modules/vc/pipeline.pytools/rvc_for_realtime.py

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384