Paraformer 本地微调通用手册(含 XTTS 语音数据自建)
这份手册以 FunASR/ModelScope 上的 Paraformer 为核心,结合 XTTS v2 进行数据增广与自建语音数据集,面向“本地数据、本地模型、本地显卡”场景,提供从数据到训练、评测、导出、推理的可复制模板。文中给出可直接运行的高通用性脚本,并解释每一个关键开关及替代方案。
0. 你将获得
-
可直接运行的 训练编排脚本(torchrun,多卡/单卡通用)。
-
通用 JSONL/TSV/Kaldi 清单 转换器,支持从“文件夹 + 文本”快速构建训练/验证集。
-
XTTS v2 数据集生成器:从一批文本和多位克隆说话人批量合成、自动切句、随机化语速/温度、16k/PCM16 统一、生成
metadata.tsv/jsonl。 -
质量控制:长度/SNR/VAD/重复检测、中文正则化、数字读法修正等。
-
训练配置的“安全默认值”与 小数据/大数据/低显存 三类配方。
-
评测(CER/WER)与 ONNX/TorchScript 导出、推理脚本。
适用范围:中文为主,兼顾多语/方言;音频 16 kHz/16-bit/单声道;GPU ≥ 12GB(低显存配方可 8GB 起步)。
1. 环境与依赖
1.1 系统与硬件
-
Ubuntu 20.04+/WSL2;macOS 可做数据预处理(训练较慢)。
-
NVIDIA GPU(建议 ≥ 24GB),驱动与 CUDA 对齐。
-
存储建议 ≥ 200GB。
1.2 Conda 与 Python 依赖
conda create -n paraformer python=3.10 -y
conda activate paraformer
# 安装 PyTorch(根据你机器的 CUDA 版本选择官方轮子)
pip install --index-url https://download.pytorch.org/whl/cu121 torch torchvision torchaudio
# 通用工具
pip install soundfile librosa pydub rich tqdm numpy pandas matplotlib tensorboard
# FunASR + ModelScope(Paraformer 官方生态)
pip install modelscope
pip install 'funasr[modelscope]'
# XTTS v2(本地 TTS 合成)
pip install TTS==0.22.0
# 质量控制(可选)
pip install webrtcvad auditok pyannote.audio==2.1.1 rapidfuzz jieba
sudo apt-get update && sudo apt-get install -y ffmpeg sox
提示:不同 CUDA/PyTorch 版本需严格匹配。Windows 建议使用 WSL2。
2. 数据格式与目录布局
project/
├─ data/
│ ├─ raw/ # 真实语音(原始长录音)
│ ├─ local/ # 切分后的真实语音片段
│ ├─ synthetic_xtts/ # XTTS 合成语音
│ ├─ manifests/ # JSONL/TSV/Kaldi 列表
│ └─ lm/ # 可选:语言模型/词表材料
├─ conf/
│ ├─ decoding.yaml # 推理/解码配置
│ └─ train_overrides.yaml # 训练超参覆盖(可选)
├─ scripts/
│ ├─ 00_prepare_jsonl.py
│ ├─ 01_qc_filter.py
│ ├─ 02_split_sets.py
│ ├─ 03_make_kaldi_lists.py
│ ├─ 10_xtts_dataset_builder.py
│ ├─ 20_train_torchrun.py
│ ├─ 30_eval_and_cer.py
│ └─ 40_export_and_infer.py
└─ exp/
└─ paraformer/ # 训练产物
2.1 通用 JSONL/TSV 规范
-
JSONL:每行一个样本,示例:
{"key":"utt001","source":"/abs/path/a.wav","target":"今天 天气 不错"}
-
TSV:
audio_path\ttext[\tspeaker]。 -
Kaldi(可选):
wav.scp、text、utt2spk、spk2utt、segments。
建议以 JSONL 为主,乐于和 FunASR 配套;Kaldi 列表用于评测和通用兼容。
3. 文本规范化与质量控制
3.1 中文文本规范化
-
全角/半角统一,异常符号清理。
-
标点用于切句,不计入 CER(训练时通常移除标点)。
-
数字/日期/单位读法统一(如“10%→百分之十”)。
-
英文缩写与序号读法(A→“诶”、MI→“M一”)等定制表。
3.2 音频与配对规则
-
采样率 16 kHz、16-bit、单声道。
-
过滤超短 (<0.2s) 与超长 (>30s) 片段。
-
可选:VAD 切分、SNR 下限(如 ≥ 15dB)、重复文本/音频去重。
全流程中保持
key一致(音频文件名或哈希),避免训练/验证集泄漏。
4. XTTS v2 数据集自建(多说话人合成)
4.1 基础思路
-
准备若干参考音色(
source_voices/*.wav)。 -
准备文本语料(每行一个句子)。
-
XTTS 对每句进行 切句 → 合成 → 统一采样率/位深 → 存盘,并生成
metadata.jsonl/tsv。 -
参数随机化(温度/语速/停顿),提升多样性与稳健性。
4.2 通用生成脚本(可直接运行)
scripts/10_xtts_dataset_builder.py
import os, re, json, random, numpy as np
import torch, librosa, soundfile as sf
from TTS.api import TTS
# ========= 可配区 =========
TEXTS_FILE = 'data/raw/xtts_texts.txt' # 每行一条文本
VOICES_DIR = 'data/raw/source_voices' # 放多位说话人的参考 wav
OUT_DIR = 'data/synthetic_xtts' # 输出语音与清单
LOCAL_XTTS = './models/XTTS-v2' # 本地 XTTS v2 路径
LANG = 'zh-cn'
TEMP_RANGE = (0.65, 1.10)
SPEED_RANGE = (0.85, 1.10)
TARGET_SR = 16000
SUBTYPE = 'PCM_16'
MAX_CHARS_PER_SENT = 80 # 过长切句
SEED = 2025
# =========================
random.seed(SEED)
os.makedirs(OUT_DIR, exist_ok=True)
voices = [os.path.join(VOICES_DIR, f) for f in os.listdir(VOICES_DIR) if f.endswith('.wav')]
assert voices, f"{VOICES_DIR} 为空!"
# 加载本地模型
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
tts = TTS(model_path=LOCAL_XTTS, config_path=f"{LOCAL_XTTS}/config.json").to(device)
# 读文本
def normalize_text(t):
t = re.sub(r"\s+", " ", t).strip()
# 示例:数字/百分号/字母读法定制,可扩展
t = t.replace('%', ' 百分之 ')
return t
with open(TEXTS_FILE, 'r', encoding='utf-8') as f:
texts = [normalize_text(x.strip()) for x in f if x.strip()]
records = []
for idx, base in enumerate(texts):
# 切句并限长
sents = [s.strip() for s in re.split(r'[。!?;;,.]', base) if s.strip()]
sents2 = []
for s in sents:
while len(s) > MAX_CHARS_PER_SENT:
sents2.append(s[:MAX_CHARS_PER_SENT])
s = s[MAX_CHARS_PER_SENT:]
if s: sents2.append(s)
spk_wav = random.choice(voices)
temp = random.uniform(*TEMP_RANGE)
speed = random.uniform(*SPEED_RANGE)
chunks = []
for s in sents2:
wav = tts.tts(text=s, speaker_wav=spk_wav, language=LANG, speed=speed, temperature=temp)
chunks.append(np.array(wav))
utt = f"xtts_{idx:07d}"
wav_out = os.path.join(OUT_DIR, f"{utt}.wav")
audio = np.concatenate(chunks) if len(chunks) else np.array([])
audio16k = librosa.resample(y=audio, orig_sr=tts.synthesizer.output_sample_rate, target_sr=TARGET_SR)
sf.write(wav_out, audio16k, TARGET_SR, subtype=SUBTYPE)
records.append({"key": utt, "source": os.path.abspath(wav_out), "target": base})
with open(os.path.join(OUT_DIR, 'metadata.jsonl'), 'w', encoding='utf-8') as f:
for r in records:
f.write(json.dumps(r, ensure_ascii=False) + '\n')
print(f"合成完成,共 {len(records)} 条,清单写入 metadata.jsonl")
实践建议:
参考音色建议每人 ≥ 2 段高质量干声(≥10s)。
控制文本来源的多域性(口语/书面/特定术语),防止风格过窄。
合成数据仅作增广,建议与真实录音按 2:1 或 1:1 混合,真实数据优先。
5. 真实 + 合成数据的清单与划分
5.1 从“文件夹 + 文本”生成 JSONL
scripts/00_prepare_jsonl.py
import os, json, random
AUDIO_DIR = 'data/local' # wav 文件夹
TRANS_TXT = 'data/local_text.txt' # 形如:file_id<space>文本
OUT_DIR = 'data/manifests'
VAL_SIZE = 400
SEED = 2025
random.seed(SEED)
os.makedirs(OUT_DIR, exist_ok=True)
with open(TRANS_TXT, 'r', encoding='utf-8') as f:
id2text = {}
for ln in f:
if ' ' not in ln: continue
k, txt = ln.strip().split(' ', 1)
id2text[k] = txt
id2wav = {os.path.splitext(x)[0]: os.path.abspath(os.path.join(AUDIO_DIR, x))
for x in os.listdir(AUDIO_DIR) if x.endswith('.wav')}
keys = sorted(list(id2text.keys() & id2wav.keys()))
random.shuffle(keys)
assert len(keys) > VAL_SIZE
train_ids, val_ids = keys[:-VAL_SIZE], keys[-VAL_SIZE:]
def dump_jsonl(keys, path):
with open(path, 'w', encoding='utf-8') as f:
for k in keys:
f.write(json.dumps({"key": k, "source": id2wav[k], "target": id2text[k]}, ensure_ascii=False)+"\n")
train_p = os.path.join(OUT_DIR, 'train.jsonl')
val_p = os.path.join(OUT_DIR, 'val.jsonl')
dump_jsonl(train_ids, train_p)
dump_jsonl(val_ids, val_p)
print('train.jsonl:', train_p, 'val.jsonl:', val_p)
5.2 质量过滤与统计
scripts/01_qc_filter.py(示例:长度阈值/空白/重复文本/异常能量)
# 可结合 librosa 统计时长、RMS;rapidfuzz 做文本去重;写回过滤后的 JSONL
5.3 训练/验证/测试划分策略
-
Speaker-disjoint(按说话人拆分)优于随机样本拆分,避免泄漏。
-
按域/话题做分层抽样更稳健。
6. 使用 FunASR 进行 Paraformer 微调
6.1 最小可运行训练器(torchrun,多卡/单卡通吃)
scripts/20_train_torchrun.py(与 FunASR 的train.py对齐)
import os, subprocess
CUDA_VISIBLE_DEVICES = os.environ.get('CUDA_VISIBLE_DEVICES', '0')
PRETRAINED_MODEL = '/abs/path/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online'
FUNASR_DIR = '/abs/path/FunASR'
TRAIN_JSONL = 'data/manifests/train.jsonl'
VAL_JSONL = 'data/manifests/val.jsonl'
OUTPUT_DIR = 'exp/paraformer'
BATCH_SIZE = 2 # 低显存友好;batch_type=sample
MAX_EPOCH = 80
LR = 1e-4
WORKERS = 4
os.makedirs(OUTPUT_DIR, exist_ok=True)
ngpu = len(CUDA_VISIBLE_DEVICES.split(','))
cmd = [
'torchrun','--nnodes','1','--nproc_per_node',str(ngpu),
f'{FUNASR_DIR}/funasr/bin/train.py',
f'++model={PRETRAINED_MODEL}',
f'++train_data_set_list={os.path.abspath(TRAIN_JSONL)}',
f'++valid_data_set_list={os.path.abspath(VAL_JSONL)}',
'++dataset_conf.batch_type=sample',
f'++dataset_conf.batch_size={BATCH_SIZE}',
f'++dataset_conf.num_workers={WORKERS}',
f'++train_conf.max_epoch={MAX_EPOCH}',
'++train_conf.log_interval=10',
'++train_conf.resume=true',
'++train_conf.validate_interval=2000',
'++train_conf.save_checkpoint_interval=2000',
f'++train_conf.keep_nbest_models={MAX_EPOCH}',
f'++optim_conf.lr={LR}',
f'++output_dir={os.path.abspath(OUTPUT_DIR)}'
]
print('CMD:', ' '.join(cmd))
subprocess.run(' '.join(cmd), shell=True, check=True, env={**os.environ,'CUDA_VISIBLE_DEVICES':CUDA_VISIBLE_DEVICES})
批次策略:
batch_type=sample按“样本数”计 batch,更稳妥;如想以“帧数/时长”自适应,切换为batch_type=length并设置batch_size为帧/秒上限。
6.2 小数据/低显存配方
-
小数据:降低 LR(e.g. 5e-5),增大
keep_nbest_models并做 checkpoint averaging(详见 8.3)。 -
低显存:
BATCH_SIZE=1,开启 AMP(若已内置);或梯度累积(在train_overrides.yaml中设置)。 -
冻结层:仅微调后若干编码层或 CTC/输出层,减少过拟合与显存占用(在 YAML 中的
freeze_mods/requires_grad=False)。
6.3 数据增强(可选)
-
SpecAugment、随机时间缩放(Speed Perturb)、添加环境噪声(RIRS/noise list)。
7. 评测:解码与 CER/WER
7.1 推理与解码
conf/decoding.yaml(示例)
beam_size: 10
ctc_weight: 0.3
lm_weight: 0.0
# 依据模型与任务微调
使用 FunASR pipeline 或
bin/asr_infer.py指定--config conf/decoding.yaml进行离线解码。
7.2 计算 CER/WER
scripts/30_eval_and_cer.py
import os, json
from modelscope.metrics.builder import build_metric
DECODE_DIR = 'exp/paraformer/decode_results'
REF_TEXT = 'data/kaldi/text' # 或从 JSONL 生成的 text
HYP_TEXT = os.path.join(DECODE_DIR, '1best_recog/text')
cer_metric = build_metric('cer') # P(字表)通常对汉字级可省略
cer_metric.add(hyp=HYP_TEXT, ref=REF_TEXT)
print(cer_metric.result)
评测前务必同样的文本正则化(去标点、大小写、空白)。
8. 导出与部署
8.1 ONNX/TorchScript 导出
-
FunASR 通常提供导出脚本(如
export_onnx.py或 CLI 子命令)。 -
导出时指定
--quantize(如 int8 动态量化)可减小体积与 CPU 推理延迟。
8.2 实时/流式(可选)
-
选择 online 预训练底座;设置
chunk_size、look_ahead、端点检测门限。
8.3 Checkpoint Averaging(推荐)
-
对若干最优/末期 epoch 的
*.pt做参数平均,常带来 0.2–0.5 CER 改善。
9. 端到端 QuickStart(最短路径)
-
准备真实语音与文本:
data/local/*.wav与data/local_text.txt。 -
(可选)准备
data/raw/source_voices/*.wav与data/raw/xtts_texts.txt,运行scripts/10_xtts_dataset_builder.py合成数据。 -
运行
scripts/00_prepare_jsonl.py生成data/manifests/train.jsonl、val.jsonl。 -
启动训练:
CUDA_VISIBLE_DEVICES=0,1 python scripts/20_train_torchrun.py -
解码与评测:运行
scripts/30_eval_and_cer.py,查看 CER。 -
导出 ONNX 并集成到你的离线/在线推理服务。
10. 常见问题(FAQ)
-
OOM:降
batch_size;改batch_type=sample;或开 AMP/梯度累积/冻结层。 -
收敛慢:检查文本清洗;增大训练量;合成数据占比不要过高;调低 LR;加 SpecAug。
-
发音问题:在 XTTS 阶段建立读法词典(规则替换);训练时使用同样的正则化 pipeline 保持一致性。
-
验证 CER 先降后升:过拟合;早停或做 checkpoint averaging;增加正则化。
-
跨域泛化差:扩展多域文本与多说话人;引入真实场景噪声;调解码超参(beam/ctc_weight)。
11. 进阶:多语/方言与词表
-
中文汉字级通常无需额外词表;若走 BPE/SentencePiece,请用同一语料训练
spm.model,并在数据/解码端同步处理。 -
多语:分语种标签(如
<zh>/<en>)或多任务;词表需覆盖全部字符集。
12. 许可与合规
-
XTTS 克隆音色需具备合法授权;合成语音不可冒充真人。
-
训练数据涉及隐私、医疗、金融等敏感领域时,请遵循相应合规要求。
附:需要完整工程代码可以私聊我(免费)
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)