这份手册以 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":"今天 天气 不错"}
  • TSVaudio_path\ttext[\tspeaker]

  • Kaldi(可选):wav.scptextutt2spkspk2uttsegments

建议以 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_sizelook_ahead、端点检测门限。

8.3 Checkpoint Averaging(推荐)

  • 对若干最优/末期 epoch 的 *.pt 做参数平均,常带来 0.2–0.5 CER 改善。


9. 端到端 QuickStart(最短路径)

  1. 准备真实语音与文本:data/local/*.wavdata/local_text.txt

  2. (可选)准备 data/raw/source_voices/*.wavdata/raw/xtts_texts.txt,运行 scripts/10_xtts_dataset_builder.py 合成数据。

  3. 运行 scripts/00_prepare_jsonl.py 生成 data/manifests/train.jsonlval.jsonl

  4. 启动训练:

    CUDA_VISIBLE_DEVICES=0,1 python scripts/20_train_torchrun.py
    
  5. 解码与评测:运行 scripts/30_eval_and_cer.py,查看 CER。

  6. 导出 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 克隆音色需具备合法授权;合成语音不可冒充真人。

  • 训练数据涉及隐私、医疗、金融等敏感领域时,请遵循相应合规要求。


附:需要完整工程代码可以私聊我(免费)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐