微调优化 #
微调概述 #
微调(Fine-tuning)是在预训练模型基础上,使用特定数据集进行进一步训练,以适应特定场景或声音。
text
┌─────────────────────────────────────────────────────────────┐
│ 微调 vs 从头训练 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 从头训练: │
│ ├── 需要大量数据(10+ 小时) │
│ ├── 训练时间长(数天到数周) │
│ ├── 需要大量计算资源 │
│ └── 适合:大规模生产项目 │
│ │
│ 微调: │
│ ├── 需要较少数据(30分钟 - 2小时) │
│ ├── 训练时间短(数小时) │
│ ├── 计算资源需求低 │
│ └── 适合:个人项目、特定声音 │
│ │
└─────────────────────────────────────────────────────────────┘
微调准备 #
选择基础模型 #
python
from TTS.api import TTS
# 查看可用模型
models = TTS.list_models()
# 推荐用于微调的模型
recommended_models = [
"tts_models/en/ljspeech/vits",
"tts_models/en/vctk/vits",
"tts_models/multilingual/multi-dataset/xtts_v2",
]
for model in recommended_models:
print(model)
数据要求 #
text
┌─────────────────────────────────────────────────────────────┐
│ 微调数据要求 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 最低要求: │
│ ├── 音频时长:30 分钟 │
│ ├── 音频质量:清晰、无噪音 │
│ └── 单一说话人 │
│ │
│ 推荐配置: │
│ ├── 音频时长:1-2 小时 │
│ ├── 采样率:22050 Hz │
│ ├── 格式:WAV │
│ └── 录音环境:安静、一致 │
│ │
│ 最佳实践: │
│ ├── 音频时长:5+ 小时 │
│ ├── 专业录音 │
│ ├── 多样化内容 │
│ └── 一致的音量和语速 │
│ │
└─────────────────────────────────────────────────────────────┘
微调 VITS 模型 #
准备配置文件 #
json
{
"model": "vits",
"run_name": "finetuned_vits",
"run_description": "Fine-tuned VITS model",
"audio": {
"sample_rate": 22050,
"fft_size": 1024,
"win_length": 1024,
"hop_length": 256,
"num_mels": 80
},
"datasets": [
{
"name": "my_voice",
"path": "my_dataset/",
"meta_file_train": "metadata.csv",
"language": "en"
}
],
"training": {
"batch_size": 16,
"epochs": 100,
"learning_rate": 0.0001,
"save_step": 500,
"output_path": "finetune_output/"
}
}
执行微调 #
bash
# 微调命令
tts --config_path finetune_config.json \
--restore_path ~/.local/share/tts/tts_models--en--ljspeech--vits/model_file.pth \
--coq_dataset_path my_dataset/
Python 微调脚本 #
python
import subprocess
from pathlib import Path
def finetune_vits(
base_model="tts_models/en/ljspeech/vits",
dataset_path="my_dataset",
config_path="finetune_config.json",
output_dir="finetune_output",
epochs=100
):
# 下载基础模型
from TTS.api import TTS
tts = TTS(base_model)
# 获取模型路径
model_path = Path.home() / ".local/share/tts"
model_path = model_path / base_model.replace("/", "--")
# 执行微调
cmd = [
"tts",
"--config_path", config_path,
"--restore_path", str(model_path / "model_file.pth"),
"--coq_dataset_path", dataset_path,
"--output_path", output_dir,
]
subprocess.run(cmd)
print(f"微调完成,模型保存在: {output_dir}")
# 使用
finetune_vits(
base_model="tts_models/en/ljspeech/vits",
dataset_path="my_dataset",
config_path="finetune_config.json"
)
微调 XTTS 模型 #
XTTS 微调配置 #
json
{
"model": "xtts_v2",
"run_name": "finetuned_xtts",
"audio": {
"sample_rate": 22050,
"output_path": "output"
},
"datasets": [
{
"name": "custom_voice",
"path": "my_dataset/",
"meta_file_train": "metadata.csv",
"language": "en"
}
],
"training": {
"batch_size": 4,
"epochs": 10,
"learning_rate": 0.00001,
"save_step": 100,
"output_path": "xtts_finetune/"
}
}
XTTS 微调脚本 #
python
import torch
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
from trainer import Trainer, TrainerArgs
def finetune_xtts(
dataset_path,
output_path,
base_model_path=None,
epochs=10,
batch_size=4,
learning_rate=1e-5
):
# 加载配置
config = XttsConfig()
config.audio.sample_rate = 22050
config.batch_size = batch_size
config.epochs = epochs
config.learning_rate = learning_rate
config.output_path = output_path
# 加载模型
model = Xtts.init_from_config(config)
if base_model_path:
model.load_checkpoint(config, base_model_path)
# 初始化训练器
trainer = Trainer(
TrainerArgs(),
config,
output_path,
model=model,
train_dataset=dataset_path
)
# 开始训练
trainer.fit()
# 使用
finetune_xtts(
dataset_path="my_dataset/",
output_path="xtts_finetune/",
epochs=10
)
微调策略 #
学习率策略 #
python
# 微调学习率建议
learning_rate_strategies = {
"保守": 1e-5, # 最小改动,保持原模型特性
"适中": 1e-4, # 平衡改动和保持
"激进": 1e-3, # 较大改动,需要更多数据
}
# 推荐使用较小的学习率
recommended_lr = 1e-4
冻结层策略 #
python
import torch
def freeze_encoder(model, freeze=True):
"""冻结编码器层"""
for param in model.encoder.parameters():
param.requires_grad = not freeze
print(f"编码器 {'已冻结' if freeze else '已解冻'}")
def freeze_decoder(model, freeze=True):
"""冻结解码器层"""
for param in model.decoder.parameters():
param.requires_grad = not freeze
print(f"解码器 {'已冻结' if freeze else '已解冻'}")
# 使用:只微调解码器
from TTS.api import TTS
tts = TTS("tts_models/en/ljspeech/vits")
model = tts.synthesizer.tts_model
freeze_encoder(model, freeze=True)
freeze_decoder(model, freeze=False)
渐进式微调 #
python
def progressive_finetune(model, epochs_per_stage=20):
"""渐进式微调策略"""
# 阶段 1:只训练最后几层
print("阶段 1:训练输出层")
freeze_encoder(model, freeze=True)
freeze_decoder(model, freeze=False)
# train(epochs=epochs_per_stage)
# 阶段 2:解冻更多层
print("阶段 2:训练更多层")
freeze_encoder(model, freeze=False)
# train(epochs=epochs_per_stage)
# 阶段 3:全模型微调
print("阶段 3:全模型微调")
# 使用更小的学习率
# train(epochs=epochs_per_stage, lr=1e-5)
性能优化 #
数据优化 #
python
import librosa
import soundfile as sf
import numpy as np
from pathlib import Path
def optimize_dataset(input_dir, output_dir):
"""优化数据集以提高训练效果"""
input_dir = Path(input_dir)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
for audio_file in input_dir.glob("*.wav"):
audio, sr = librosa.load(str(audio_file), sr=None)
# 1. 归一化音量
audio = audio / np.max(np.abs(audio)) * 0.95
# 2. 去除静音
audio, _ = librosa.effects.trim(audio, top_db=25)
# 3. 确保最小长度
min_length = sr * 1 # 至少 1 秒
if len(audio) < min_length:
continue
# 4. 重采样
if sr != 22050:
audio = librosa.resample(audio, sr, 22050)
sr = 22050
# 保存
output_path = output_dir / audio_file.name
sf.write(str(output_path), audio, sr)
print(f"优化: {audio_file.name}")
# 使用
optimize_dataset("raw_audio", "optimized_dataset/wavs")
训练优化 #
json
{
"training": {
"batch_size": 16,
"gradient_accumulation_steps": 2,
"mixed_precision": true,
"num_loader_workers": 8,
"pin_memory": true,
"optimizer": "AdamW",
"optimizer_params": {
"betas": [0.9, 0.999],
"weight_decay": 0.01
},
"lr_scheduler": "OneCycleLR",
"lr_scheduler_params": {
"max_lr": 0.001,
"pct_start": 0.1
}
}
}
评估微调效果 #
对比测试 #
python
from TTS.api import TTS
import soundfile as sf
import numpy as np
def compare_models(
original_model,
finetuned_model_path,
config_path,
test_texts,
output_dir="comparison"
):
from pathlib import Path
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
# 加载原始模型
tts_original = TTS(original_model)
# 加载微调模型
tts_finetuned = TTS(
model_path=finetuned_model_path,
config_path=config_path
)
for i, text in enumerate(test_texts):
# 原始模型
original_path = output_dir / f"original_{i}.wav"
tts_original.tts_to_file(text=text, file_path=str(original_path))
# 微调模型
finetuned_path = output_dir / f"finetuned_{i}.wav"
tts_finetuned.tts_to_file(text=text, file_path=str(finetuned_path))
print(f"对比 {i}:")
print(f" 原始: {original_path}")
print(f" 微调: {finetuned_path}")
# 使用
compare_models(
original_model="tts_models/en/ljspeech/vits",
finetuned_model_path="finetune_output/best_model.pth",
config_path="finetune_config.json",
test_texts=[
"Hello, this is a test.",
"The quick brown fox jumps over the lazy dog."
]
)
自动评估 #
python
import numpy as np
import soundfile as sf
from scipy import signal
def calculate_spectral_distance(audio1_path, audio2_path):
"""计算频谱距离"""
audio1, sr1 = sf.read(audio1_path)
audio2, sr2 = sf.read(audio2_path)
# 确保长度一致
min_len = min(len(audio1), len(audio2))
audio1 = audio1[:min_len]
audio2 = audio2[:min_len]
# 计算频谱
f1, P1 = signal.welch(audio1, sr1)
f2, P2 = signal.welch(audio2, sr2)
# 计算距离
distance = np.sqrt(np.mean((P1 - P2) ** 2))
return distance
def evaluate_finetuning(original_dir, finetuned_dir):
"""评估微调效果"""
from pathlib import Path
original_dir = Path(original_dir)
finetuned_dir = Path(finetuned_dir)
distances = []
for orig_file in original_dir.glob("*.wav"):
finetuned_file = finetuned_dir / orig_file.name
if finetuned_file.exists():
dist = calculate_spectral_distance(str(orig_file), str(finetuned_file))
distances.append(dist)
print(f"{orig_file.name}: 距离 = {dist:.4f}")
avg_distance = np.mean(distances)
print(f"\n平均频谱距离: {avg_distance:.4f}")
return avg_distance
微调最佳实践 #
数据准备 #
text
┌─────────────────────────────────────────────────────────────┐
│ 微调最佳实践 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 数据方面: │
│ ├── 使用高质量录音 │
│ ├── 保持一致的录音环境 │
│ ├── 覆盖多样化的内容 │
│ └── 确保文本转录准确 │
│ │
│ 训练方面: │
│ ├── 使用较小的学习率(1e-4 到 1e-5) │
│ ├── 监控验证损失,避免过拟合 │
│ ├── 定期保存检查点 │
│ └── 使用早停策略 │
│ │
│ 评估方面: │
│ ├── 主观听感测试 │
│ ├── 与原模型对比 │
│ ├── 多人评估 │
│ └── 记录问题和改进 │
│ │
└─────────────────────────────────────────────────────────────┘
避免过拟合 #
json
{
"training": {
"epochs": 50,
"early_stopping": true,
"early_stopping_patience": 5,
"early_stopping_metric": "loss",
"early_stopping_threshold": 0.001,
"weight_decay": 0.01,
"dropout": 0.1
}
}
下一步 #
掌握了微调优化后,继续学习 高级配置,了解分布式训练和自定义模型!
最后更新:2026-04-05