# VoxCPM 微调指南 本指南介绍了如何使用全量微调(Full Fine-tuning)和 LoRA 微调两种方式对 VoxCPM 模型进行微调。 ### 🎓 SFT (监督微调) 全量微调会更新所有模型参数。适用于: - 📊 大型、专业的数据集 - 🔄 需要显著改变模型行为的场景 ### ⚡ LoRA 微调 LoRA (Low-Rank Adaptation) 是一种参数高效的微调方法,它: - 🎯 仅训练少量额外参数 - 💾 显著降低显存需求和训练时间 - 🔀 支持多个 LoRA 适配器热插拔 ## 目录 - [快速开始:WebUI](#快速开始webui) - [数据准备](#数据准备) - [全量微调](#全量微调) - [LoRA 微调](#lora-微调) - [推理](#推理) - [LoRA 热插拔](#lora-热插拔) - [常见问题](#常见问题) --- ## 快速开始:WebUI 对于喜欢图形界面的用户,我们提供了 `lora_ft_webui.py` —— 一个用于训练和推理的综合 WebUI: ### 启动 WebUI ```bash python lora_ft_webui.py ``` 然后在浏览器中打开 `http://localhost:7860`。 ### 功能特点 - **🚀 训练标签页**:通过直观的界面配置并启动 LoRA 训练 - 设置训练参数(学习率、Batch Size、LoRA Rank 等) - 实时监控训练进度 - 从现有断点恢复训练 - **🎵 推理标签页**:使用训练好的模型生成音频 - 从 LoRA 检查点配置自动加载基座模型 - 带自动 ASR(参考文本识别)的声音克隆 - 在多个 LoRA 模型间热切换 - 无参考音频的零样本 TTS ## 数据准备 训练数据应准备为 JSONL 清单文件,每行一个样本: ```jsonl {"audio": "path/to/audio1.wav", "text": "音频1的文本内容。"} {"audio": "path/to/audio2.wav", "text": "音频2的文本内容。"} {"audio": "path/to/audio3.wav", "text": "可选的时长字段。", "duration": 3.5} {"audio": "path/to/audio4.wav", "text": "多数据集训练可选的 dataset_id。", "dataset_id": 1} ``` ### 必填字段 | 字段 | 描述 | |-------|-------------| | `audio` | 音频文件路径(绝对或相对路径) | | `text` | 对应的文本内容 | ### 可选字段 | 字段 | 描述 | |-------|-------------| | `duration` | 音频时长(秒),用于加速样本过滤 | | `dataset_id` | 多数据集训练的数据集 ID(默认:0) | ### 要求 - 音频格式:WAV - 采样率:VoxCPM-0.5B 为 16kHz,VoxCPM1.5 为 44.1kHz - 文本:与音频内容匹配的文本 查看 `examples/train_data_example.jsonl` 获取完整示例。 --- ## 全量微调 全量微调更新所有模型参数。适用于大数据集或需要显著改变模型行为的情况。 ### 配置 创建 `conf/voxcpm_v1.5/voxcpm_finetune_all.yaml`: ```yaml pretrained_path: /path/to/VoxCPM1.5/ train_manifest: /path/to/train.jsonl val_manifest: "" sample_rate: 44100 batch_size: 16 grad_accum_steps: 1 num_workers: 2 num_iters: 2000 log_interval: 10 valid_interval: 1000 save_interval: 1000 learning_rate: 0.00001 # 全量微调使用较小的学习率 weight_decay: 0.01 warmup_steps: 100 max_steps: 2000 max_batch_tokens: 8192 save_path: /path/to/checkpoints/finetune_all tensorboard: /path/to/logs/finetune_all lambdas: loss/diff: 1.0 loss/stop: 1.0 ``` ### 训练 ```bash # 单 GPU python scripts/train_voxcpm_finetune.py --config_path conf/voxcpm_v1.5/voxcpm_finetune_all.yaml # 多 GPU CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --nproc_per_node=4 \ scripts/train_voxcpm_finetune.py --config_path conf/voxcpm_v1.5/voxcpm_finetune_all.yaml ``` ### 检查点结构 全量微调保存完整的模型目录,可以直接加载: ``` checkpoints/finetune_all/ └── step_0002000/ ├── model.safetensors # 模型权重 (不含 audio_vae) ├── config.json # 模型配置 ├── audiovae.pth # Audio VAE 权重 ├── tokenizer.json # Tokenizer ├── tokenizer_config.json ├── special_tokens_map.json ├── optimizer.pth └── scheduler.pth ``` --- ## LoRA 微调 LoRA (Low-Rank Adaptation) 是一种参数高效的微调方法,仅训练少量额外参数,显著降低显存需求。 ### 配置 创建 `conf/voxcpm_v1.5/voxcpm_finetune_lora.yaml`: ```yaml pretrained_path: /path/to/VoxCPM1.5/ train_manifest: /path/to/train.jsonl val_manifest: "" sample_rate: 44100 batch_size: 16 grad_accum_steps: 1 num_workers: 2 num_iters: 2000 log_interval: 10 valid_interval: 1000 save_interval: 1000 learning_rate: 0.0001 # LoRA 可以使用较大的学习率 weight_decay: 0.01 warmup_steps: 100 max_steps: 2000 max_batch_tokens: 8192 save_path: /path/to/checkpoints/finetune_lora tensorboard: /path/to/logs/finetune_lora lambdas: loss/diff: 1.0 loss/stop: 1.0 # LoRA 配置 lora: enable_lm: true # 对语言模型应用 LoRA enable_dit: true # 对 Diffusion Transformer 应用 LoRA enable_proj: false # 对投影层应用 LoRA (可选) r: 32 # LoRA rank (越高容量越大) alpha: 16 # LoRA alpha, scaling = alpha / r dropout: 0.0 # 目标模块 target_modules_lm: ["q_proj", "v_proj", "k_proj", "o_proj"] target_modules_dit: ["q_proj", "v_proj", "k_proj", "o_proj"] # 分发选项 (可选) # hf_model_id: "openbmb/VoxCPM1.5" # HuggingFace ID # distribute: true # 如果为 true,在 lora_config.json 中保存 hf_model_id ``` ### LoRA 参数 | 参数 | 描述 | 推荐值 | |-----------|-------------|-------------| | `enable_lm` | 对 LM (语言模型) 应用 LoRA | `true` | | `enable_dit` | 对 DiT (扩散模型) 应用 LoRA | `true` (声音克隆必须) | | `r` | LoRA rank (越高容量越大) | 16-64 | | `alpha` | 缩放因子, `scaling = alpha / r` | 通常 `r/2` 或 `r` | | `target_modules_*` | 添加 LoRA 的层名称 | attention layers | ### 分发选项 (可选) | 参数 | 描述 | 默认值 | |-----------|-------------|---------| | `hf_model_id` | HuggingFace 模型 ID (例如 `openbmb/VoxCPM1.5`) | `""` | | `distribute` | 如果为 `true`,将 `hf_model_id` 作为 `base_model` 保存到检查点;否则保存本地 `pretrained_path` | `false` | > **注意**:如果 `distribute: true`,则必须提供 `hf_model_id`。 ### 训练 ```bash # 单 GPU python scripts/train_voxcpm_finetune.py --config_path conf/voxcpm_v1.5/voxcpm_finetune_lora.yaml # 多 GPU CUDA_VISIBLE_DEVICES=0,1,2,3 torchrun --nproc_per_node=4 \ scripts/train_voxcpm_finetune.py --config_path conf/voxcpm_v1.5/voxcpm_finetune_lora.yaml ``` ### 检查点结构 LoRA 训练保存 LoRA 参数和配置: ``` checkpoints/finetune_lora/ └── step_0002000/ ├── lora_weights.safetensors # 仅含 lora_A, lora_B 参数 ├── lora_config.json # LoRA 配置 + 基座模型路径 ├── optimizer.pth └── scheduler.pth ``` `lora_config.json` 包含: ```json { "base_model": "/path/to/VoxCPM1.5/", "lora_config": { "enable_lm": true, "enable_dit": true, "r": 32, "alpha": 16, ... } } ``` `base_model` 字段包含: - 本地路径 (默认):当 `distribute: false` 或未设置时 - HuggingFace ID:当 `distribute: true` 时 (例如 `"openbmb/VoxCPM1.5"`) 这允许在没有原始训练配置文件的情况下加载 LoRA 检查点。 --- ## 推理 ### 全量微调推理 检查点目录是一个完整的模型,直接加载: ```bash python scripts/test_voxcpm_ft_infer.py \ --ckpt_dir /path/to/checkpoints/finetune_all/step_0002000 \ --text "你好,这是微调后的模型。" \ --output output.wav ``` 带声音克隆: ```bash python scripts/test_voxcpm_ft_infer.py \ --ckpt_dir /path/to/checkpoints/finetune_all/step_0002000 \ --text "这是声音克隆的结果。" \ --prompt_audio /path/to/reference.wav \ --prompt_text "参考音频的文本内容" \ --output cloned_output.wav ``` ### LoRA 推理 LoRA 推理只需要检查点目录(基座模型路径和 LoRA 配置从 `lora_config.json` 读取): ```bash python scripts/test_voxcpm_lora_infer.py \ --lora_ckpt /path/to/checkpoints/finetune_lora/step_0002000 \ --text "你好,这是 LoRA 微调的结果。" \ --output lora_output.wav ``` 带声音克隆: ```bash python scripts/test_voxcpm_lora_infer.py \ --lora_ckpt /path/to/checkpoints/finetune_lora/step_0002000 \ --text "这是带 LoRA 的声音克隆。" \ --prompt_audio /path/to/reference.wav \ --prompt_text "参考音频的文本内容" \ --output cloned_output.wav ``` 覆盖基座模型路径 (可选): ```bash python scripts/test_voxcpm_lora_infer.py \ --lora_ckpt /path/to/checkpoints/finetune_lora/step_0002000 \ --base_model /path/to/another/VoxCPM1.5 \ --text "使用不同的基座模型。" \ --output output.wav ``` --- ## LoRA 热插拔 LoRA 支持在推理时动态加载、卸载和切换,无需重新加载整个模型。 ### API 参考 ```python from voxcpm.core import VoxCPM from voxcpm.model.voxcpm import LoRAConfig # 1. 加载带 LoRA 结构和权重的模型 lora_cfg = LoRAConfig( enable_lm=True, enable_dit=True, r=32, alpha=16, target_modules_lm=["q_proj", "v_proj", "k_proj", "o_proj"], target_modules_dit=["q_proj", "v_proj", "k_proj", "o_proj"], ) model = VoxCPM.from_pretrained( hf_model_id="openbmb/VoxCPM1.5", # 或本地路径 load_denoiser=False, # 可选:禁用降噪器以加快加载 optimize=True, # 启用 torch.compile 加速 lora_config=lora_cfg, lora_weights_path="/path/to/lora_checkpoint", ) # 2. 生成音频 audio = model.generate( text="你好,这是 LoRA 微调的结果。", prompt_wav_path="/path/to/reference.wav", # 可选:用于声音克隆 prompt_text="参考音频的文本内容", # 可选:用于声音克隆 ) # 3. 禁用 LoRA (仅使用基座模型) model.set_lora_enabled(False) # 4. 重新启用 LoRA model.set_lora_enabled(True) # 5. 卸载 LoRA (重置权重为零) model.unload_lora() # 6. 热切换到另一个 LoRA loaded, skipped = model.load_lora("/path/to/another_lora_checkpoint") print(f"Loaded {len(loaded)} params, skipped {len(skipped)}") # 7. 获取当前 LoRA 权重 lora_state = model.get_lora_state_dict() ``` ### 简化用法 (从 lora_config.json 加载) 如果你的检查点包含 `lora_config.json`(由训练脚本保存),你可以自动加载所有内容: ```python import json from voxcpm.core import VoxCPM from voxcpm.model.voxcpm import LoRAConfig # 从检查点加载配置 lora_ckpt_dir = "/path/to/checkpoints/finetune_lora/step_0002000" with open(f"{lora_ckpt_dir}/lora_config.json") as f: lora_info = json.load(f) base_model = lora_info["base_model"] lora_cfg = LoRAConfig(**lora_info["lora_config"]) # 加载带 LoRA 的模型 model = VoxCPM.from_pretrained( hf_model_id=base_model, lora_config=lora_cfg, lora_weights_path=lora_ckpt_dir, ) ``` 或者直接使用测试脚本: ```bash python scripts/test_voxcpm_lora_infer.py \ --lora_ckpt /path/to/checkpoints/finetune_lora/step_0002000 \ --text "Hello world" ``` ### 方法参考 | 方法 | 描述 | torch.compile 兼容性 | |--------|-------------|--------------------------| | `load_lora(path)` | 从文件加载 LoRA 权重 | ✅ | | `set_lora_enabled(bool)` | 启用/禁用 LoRA | ✅ | | `unload_lora()` | 将 LoRA 权重重置为初始值 | ✅ | | `get_lora_state_dict()` | 获取当前 LoRA 权重 | ✅ | | `lora_enabled` | 属性:检查是否配置了 LoRA | ✅ | --- ## 常见问题 (FAQ) ### 1. 显存溢出 (OOM) - 增加 `grad_accum_steps` (梯度累积步数) - 减小 `batch_size` - 使用 LoRA 微调代替全量微调 - 减小 `max_batch_tokens` 以过滤长样本 ### 2. LoRA 效果不佳 - 增加 `r` (LoRA rank) - 调整 `alpha` (尝试 `alpha = r/2` 或 `alpha = r`) - 增加训练步数 - 添加更多目标模块 ### 3. 训练不收敛 - 减小 `learning_rate` (学习率) - 增加 `warmup_steps` - 检查数据质量 ### 4. LoRA 在推理时未生效 - 检查检查点目录下是否存在 `lora_config.json` - 检查 `load_lora()` 返回值 - `skipped_keys` 应该为空 - 确认调用了 `set_lora_enabled(True)` ### 5. 检查点加载错误 - 全量微调:检查点目录应包含 `model.safetensors` (或 `pytorch_model.bin`)、`config.json`、`audiovae.pth` - LoRA:检查点目录应包含: - `lora_weights.safetensors` (或 `lora_weights.ckpt`) - LoRA 权重 - `lora_config.json` - LoRA 配置和基座模型路径