④ 部署 (HF · S3 · 本地)
训练完成后 <dataset>/exports/<model_name>/ 下会集中最终模型文件。
将此文件夹上传到 HuggingFace Hub / S3 / 本地文件夹 中的某一个,然后在其他机器上用 TTS().load("我的名字") 一行代码即可重新下载使用 — 这就是 cli publish 的作用。
它将手动操作 HF CLI·S3 CLI、记忆仓库结构、上传验证等过程整合为一个交互式流程。
运行
uv run poe cli在主菜单中选择 发布说话人 后会按以下顺序询问。
- 要部署的数据集(或外部文件夹)
- 后端 — CPU / GPU / CPU + GPU
- 检查点
- 说话人名称
- 目标位置 + 凭证
- 摘要面板 → 确认
- 自动上传 → 实际合成验证
各步骤在下方说明。
1. 选择部署对象
有两种对象。
- 训练 dataset —
data/dataset/<name>/exports/<model>/中有最终文件的数据集会自动列出。 - 从其他文件夹直接选择 — 训练在其他地方完成,只有 HayaKoe 格式文件夹时,直接输入路径。
从外部文件夹导入时需要的文件
<my-folder>/
├── config.json # 必需
├── style_vectors.npy # 必需
├── *.safetensors # 必需 (至少一个)
├── synthesizer.onnx # 可选 (有的话复用)
└── duration_predictor.onnx # 可选 (有的话复用)2. 选择后端
CPU (ONNX) — 无 GPU 的服务器/本地用
GPU (PyTorch) — 最低延迟
CPU + GPU (推荐) — 同时部署到两种环境选择 CPU + GPU 时两种后端的文件会 一起 上传到同一仓库。运行时用 TTS(device="cpu") 创建时只拉取 ONNX 侧,用 TTS(device="cuda") 创建时只拉取 PyTorch 侧。
只需上传一次即可在两种环境下以相同名称复用,所以没有特殊原因的话请选这个选项。
两种后端的差异在 后端选择 中详细说明。
3. 检查点与说话人名称
- 检查点只有 1 个时自动选择,多个时手动选择(通常是在 ③ 质量报告 中选定的)。
- 说话人名称 是运行时用
TTS().load("我的名字")时使用的标识符。建议简洁的小写加连字符风格(例如:tsukuyomi)。
4. 选择目标位置
有三个选项。只需首次输入一次凭证,之后会以 chmod 600 保存到 dev-tools/.env,下次会跳过提示。
HuggingFace Hub
输入仓库路径(org/repo 或 hf://org/repo)和 write 权限 token。可以用 @<revision> 指定分支/标签。
支持的 URL 格式 & 保存的环境变量
允许的 URL 格式:
lemondouble/hayakoe-voiceshf://lemondouble/hayakoe-voiceshf://lemondouble/hayakoe-voices@mainhttps://huggingface.co/lemondouble/hayakoe-voiceshttps://huggingface.co/lemondouble/hayakoe-voices/tree/dev
保存的 .env 示例:
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # write 权限 HuggingFace 访问 token
HAYAKOE_HF_REPO=lemondouble/hayakoe-voices # 说话人文件上传的 HF 仓库 (org/repo 格式)AWS S3
输入桶名称(+ 可选 prefix)和 AWS 凭证(AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY)。端点 URL 留空即可。
S3 兼容存储 (R2, MinIO 等)
使用 Cloudflare R2、MinIO、Wasabi 等 S3 兼容存储时需要 同时输入端点 URL。
- Cloudflare R2 —
https://<account>.r2.cloudflarestorage.com - MinIO —
http://<host>:9000
桶·凭证输入与 AWS S3 相同。
保存的环境变量示例
AWS S3
HAYAKOE_S3_BUCKET=my-tts-bucket # 说话人文件上传的 S3 桶名称
HAYAKOE_S3_PREFIX=hayakoe-voices # 桶内路径 prefix (留空则为桶根目录)
AWS_ACCESS_KEY_ID=<your_access_key_here> # AWS 访问密钥 ID
AWS_SECRET_ACCESS_KEY=<your_secret_key_here> # AWS 密钥
AWS_REGION=ap-northeast-2 # S3 区域 (示例为首尔)
# AWS_ENDPOINT_URL_S3 留空 (AWS S3 自动确定)S3 兼容 (Cloudflare R2)
HAYAKOE_S3_BUCKET=my-tts-bucket # 上传的 R2 桶名称
HAYAKOE_S3_PREFIX=hayakoe-voices # 桶内路径 prefix (留空则为桶根目录)
AWS_ACCESS_KEY_ID=<your_access_key_here> # R2 仪表盘中颁发的 Access Key ID
AWS_SECRET_ACCESS_KEY=<your_secret_key_here> # R2 Secret Access Key
AWS_REGION=auto # R2 始终为 auto
AWS_ENDPOINT_URL_S3=https://abc123def.r2.cloudflarestorage.com # R2 端点 (每个账户唯一)本地目录
不通过网络上传,仅复制到本地路径。适用于将文件放在 NFS 共享卷或内部网络驱动器上供团队共用的场景。运行时通过 file:///... URI 访问。
保存的环境变量示例
HAYAKOE_LOCAL_PATH=/srv/hayakoe-voices # 说话人文件复制到的本地目录路径5. 仓库结构
以 CPU + GPU 部署时仓库中会同时包含 ONNX 文件夹和 PyTorch 文件夹。可以在同一仓库中放置多个说话人一起管理(speakers/voice-a/、speakers/voice-b/、...)。
内部结构
<repo-root>/
├── pytorch/
│ └── speakers/
│ └── <speaker-name>/
│ ├── config.json
│ ├── style_vectors.npy
│ └── *.safetensors
└── onnx/
└── speakers/
└── <speaker-name>/
├── config.json
├── style_vectors.npy
├── synthesizer.onnx
└── duration_predictor.onnxBERT 模型也会一起上传到 pytorch/bert/ 和 onnx/bert/ 下的共享位置。运行时使用相同的缓存规则下载说话人文件和公共 BERT。
6. ONNX export (自动)
选择 CPU 后端(CPU (ONNX) · CPU + GPU)时,在上传前会自动将 PyTorch 检查点转换为 ONNX。无需单独的 cli export 命令。
转换结果缓存在 <dataset>/onnx/,同一检查点再次 publish 时会复用。想强制重新转换的话删除此文件夹后重新 publish。
内部机制 — 转换的模型和方式
通过 dev-tools/cli/export/exporter.py 以 opset 17 导出说话人专属的两个模型。
转换对象 — 说话人专属的两个模型
Synthesizer (VITS 解码器)
接收音素序列 + BERT 嵌入 + 风格向量作为输入,生成实际波形(waveform)的核心模型。由于每个说话人训练不同,部署对象的大部分都是这个模型。
- 函数:
export_synthesizer - 输出:
synthesizer.onnx(+ 可能的synthesizer.onnx.data)
Duration Predictor
预测每个音素应该发音多长时间。如果预测不准确,句子边界的 pause·节奏处理会不自然。
- 函数:
export_duration_predictor - 输出:
duration_predictor.onnx
synthesizer.onnx.data 是什么?
ONNX 内部使用 Protobuf 序列化,Protobuf 有 单条消息 2GB 限制。当 Synthesizer 的权重超过此阈值时,图结构保留在 .onnx 中而 大型张量外置到旁边的 .data 文件。
- 两个文件 必须始终在同一文件夹中(禁止分离移动)
- 根据模型大小可能完全不生成
.data - 运行时只指定
.onnx加载也会自动读取同文件夹的.data
BERT 不按说话人制作而是公用
BERT (DeBERTa) 是与说话人无关的日语语言模型。所有说话人共用的 Q8 量化 ONNX (bert_q8.onnx) 从 HuggingFace 的公用位置下载使用,publish 步骤不会为每个说话人重新转换。
- 得益于 Q8 量化,CPU 上也能以接近实时的延迟提取嵌入
- 所有说话人共享同一个 BERT,无需在每个仓库中重复存储
也就是说,此步骤实际转换的对象 仅有说话人专属的 Synthesizer + Duration Predictor 两个。
追踪耗时的原因
ONNX export 是"实际让模型跑一遍,同时记录计算图"的 追踪 方式。Synthesizer 结构复杂,可能需要数十秒到数分钟。
由于同一检查点可能会以不同名称·不同目标多次 publish,转换结果会缓存在 <dataset>/onnx/ 中复用。
用脚本直接 export
两个 export 函数是公开的,也可以用脚本直接调用。但 publish 流程会自动完成同样的事,除非有特殊原因否则建议使用 publish。直接调用路径将来可能会变更。
7. 覆盖确认
如果目标位置已存在同名的 speakers/<speaker-name>/,会 先询问是否覆盖。确认后仅清理该说话人目录并重新上传 — 同仓库中的其他说话人不受影响。
README 也遵循同样原则。如果仓库根目录没有 README 则自动生成四语(ko/en/ja/zh)模板一起上传,已存在则显示 diff 后询问是否覆盖。
8. 上传后自动验证
上传完成后会自动确认 用上传的文件是否真的能合成。
如果选择了 CPU + GPU 则分别验证两种后端,结果 wav 保存在 dev-tools/.verify_audio/<name>_<cpu|cuda>.wav 中可以直接播放确认。
验证成功意味着
"用仓库中上传的文件真的能合成了"。
此验证通过后可以保证在其他机器上用 TTS().load(<speaker>, source="hf://...") 等方式直接取出使用。
内部机制 — 验证流程
- 用选定的后端创建
TTS(device=...)实例 - 用刚上传的名称
load(<speaker>)→prepare() - 合成固定文本
"テスト音声です。" - 将结果 wav 保存到
dev-tools/.verify_audio/<name>_<cpu|cuda>.wav
GPU 验证前会重置全局 BERT / dynamo / CUDA 缓存以避免相互影响。
在运行时下载使用
上传完成的说话人可以在其他机器·容器上这样加载。
from hayakoe import TTS
# 从 HF
tts = TTS(device="cpu").load("tsukuyomi", source="hf://me/my-voices").prepare()
# 从 S3
tts = TTS(device="cuda").load("tsukuyomi", source="s3://my-bucket/hayakoe-voices").prepare()
# 从本地
tts = TTS(device="cpu").load("tsukuyomi", source="file:///srv/voices").prepare()
# 合成
audio = tts.speakers["tsukuyomi"].generate("こんにちは。")只需更改 device 即可自动使用 CPU(ONNX) / GPU(PyTorch) 后端 — 这是因为 publish 步骤选择了 CPU + GPU,两侧文件都在仓库中。
但运行时侧也需要安装对应后端的依赖。使用 device="cuda" 时实际运行的机器上需要安装 PyTorch CUDA 构建,device="cpu" 仅需基本安装。详情请参考 安装 — CPU vs GPU。
