TTS DiT を 4-bit 量子化したまま推論する、軽量ランタイムです。
オリジナルの FP32 チェックポイント(1.88 GB)を 279 MB のディスク容量 と、DiT 単体で 552 MB のピーク GPU メモリ で動かせます。音質はほぼ劣化しません。
さらにオプションの --codec-int4 を付ければ DACVAE コーデックも 4-bit で動かせて、エンドツーエンド(DiT + コーデック + トークナイザ)でピーク 約 1 GB(実測 988.7 MB)になります。
| Mode | Safetensors |
|---|---|
| FP32 | 1888 MB |
| BF16 | 944 MB |
| INT4 (本リポジトリ) | 279 MB |
| Mode | max_memory_allocated |
max_memory_reserved |
|---|---|---|
| FP32 | 1916.8 MB | 1992.0 MB |
| FP16 | 978.7 MB | 1004.0 MB |
| INT4 | 552.3 MB | 568.0 MB |
| Mode | --codec-device cpu |
--codec-device cuda |
--codec-int4 (cuda) |
|---|---|---|---|
| FP32 | (未計測) | 2867.7 MB | — |
| BF16 | (未計測) | 1990.6 MB | — |
| INT4 | 988.7 MB | 1512.6 MB | 988.7 MB |
DACVAE コーデックも 4-bit にする
--codec-int4モードを追加しました。 重みを uint8-nibble pack のまま保持して forward の中で 1 レイヤずつ on-the-fly dequant するので、 Conv 重み 377 → 59 MB(▲ 84%)、エンドツーエンドのピークが 1513 → 989 MB(▲ 525 MB)になります。 DiT と DACVAE が同時に走るわけではないので、両者を 4-bit にしても合計は単なる加算にはならず、 VRAM 1 GB 弱でフルパイプラインが回ります。DACVAE コーデックを GPU に置いて codec も int4 にすれば decode_latent は ~330 ms、 コーデック fp16 のままなら ~170 ms、CPU に逃がすと ~3.3 s。 レイテンシ要件が緩い環境では
--codec-device cpuで更に 500 MB 節約できます。再現は
tools/measure_peak_memory.pyで。
| Mode | sample_rf | decode_latent | total |
|---|---|---|---|
| FP32 (codec=cuda) | 270 ms | 116 ms | 392 ms |
| BF16 (codec=cuda) | 367 ms | 124 ms | 499 ms |
| INT4 (codec=cuda, fp16) | 296 ms | 172 ms | 473 ms |
| INT4 (codec=cuda, int4) | 292 ms | 333 ms | 629 ms |
| INT4 (codec=cpu) | 334 ms | 3316 ms | 3.66 s |
Aratako/Irodori-TTS-500M-v3 も同じパイプラインで int4 化できます。量子化済みの重みは kizuna-intelligence/Irodori-TTS-500M-v3-int4 で公開しています。
| 段階 | サイズ | OpenVoice-v2 sim |
|---|---|---|
| DiT block のみ(GPTQ 実キャリブレーション) | 561 MB | 0.8718 |
| + エンコーダ attention Linear(GPTQ 実 Hessian) | 511 MB | 0.8743 |
| + text_embedding(RTN 4-bit + PackedEmbedding) | 444 MB | 0.8668(held-out 0.9075) |
FP32 baseline ≈ sim 0.886 / CER 8.3%。音質は実質劣化なし。
| 指標 | 実測 |
|---|---|
| median latency(3s 合成, 6 RF step) | ~336 ms |
| ピーク VRAM(DiT + DACVAE コーデック GPU 同居, after 3 synth) | ~1.14 GB |
VRAM は DACVAE コーデックを GPU に同居させた状態の実測です。
text_embedding(97 MB fp16)は入力 ID の行だけを forward 内で on-the-fly dequant するので、テーブル全体を VRAM に展開しません。
- 実アクティベーションでの GPTQ キャリブレーションが品質の最大レバー。ランダム Gaussian calib だと CER ~33%、実 calib で 0% まで落ちます。
- エンコーダ MLP(1996 次元)/ AdaLN / duration_predictor / cond_module は fp16 のまま。1996 次元が AutoGPTQ packing を壊し、blind RTN はエンコーダを破壊します。
--actorderは使いません(.permテンソルを Lite のローダが拒否するため)。
v2/moespeech のように duration predictor を持たない(use_duration_predictor=False)モデルには、configure(duration_donor="<v3 ckpt path>") で v3 の学習済み predictor を移植できます(v2/v3 でエンコーダ次元が一致するため)。固定 seconds を強制すると発話が崩れて CER ~25% になりますが、移植した predictor に長さを決めさせると CER 0% になります。
import irodori_tts_lite
irodori_tts_lite.configure(
use_fused=True, force_fp16=True,
pack_rtn_extras=True, # int4 packed extras を on-the-fly dequant
duration_donor="path/to/v3/model.safetensors",
)
irodori_tts_lite.patch()- 自己完結: OneCompression を実行時依存に持ちません(量子化済みウェイト + ランタイムだけで動きます)。
- Triton 高速カーネル: DiT block の Linear は
FusedInt4Linear(GPTQ-v1 packed × cuBLAS 級カーネル)で実行。 - 賢い eager-dequant: AdaLN(ホットパス、起動オーバヘッドが支配的)と エンコーダ系(コールドパス、1 推論あたり 1 回呼び)はロード時に fp16 化して
nn.Linearに差し替え。 - 既存パイプラインに 1 行で割り込み:
irodori_tts_lite.patch()を呼ぶだけで、irodori_tts.inference_runtime.InferenceRuntime.from_keyが 4-bit セーフテンソルを直接読めるようになります。
pip install git+https://github.com/kizuna-intelligence/Irodori-TTS-Lite.git
pip install pyopenjtalk # run_tts.py でテキスト → 秒数の自動推定に使用
# 上流 TTS パイプラインは別途インストールしてください(patch() で実行時にフックします)。重みは初回実行時に kizuna-intelligence/Irodori-TTS-Lite-int4 から自動ダウンロードされます(HF キャッシュに保存)。
# --checkpoint を省略すると HF から自動ダウンロード
python example/run_tts.py \
--text "こんにちは、メラだよ。テスト中なの!" \
--output-wav /tmp/sample.wav \
--no-refローカルの safetensors を使いたい場合は --checkpoint <path>、別 HF リポを指定したい場合は --checkpoint hf://<org>/<repo>/<file> で渡せます。--no-ref は話者性を重みに焼き込んだチェックポイント用フラグ。
# フルパイプラインのピーク (DACVAE 含む)
python tools/measure_peak_memory.py --mode int4 --no-ref --json
# DACVAE を CPU に逃がして VRAM を最小化
python tools/measure_peak_memory.py --mode int4 --no-ref --codec-device cpu --json
# DACVAE もそのまま GPU で int4 化(VRAM 約 1 GB でフル推論)
python tools/measure_peak_memory.py --mode int4 --no-ref --codec-int4 --json未量子化との比較は --mode bf16 / --mode fp32 に切り替え + 対応する未量子化 checkpoint を --checkpoint で渡してください。
patch() で既存の irodori_tts.inference_runtime にフックを差し込みます。あとは普段通り infer.main() を呼べば 4-bit ファイルがそのまま読み込まれます。
import irodori_tts_lite
irodori_tts_lite.configure(use_fused=True, force_fp16=True)
irodori_tts_lite.patch()
import infer
infer.main()configure() で調整できる主なオプション:
| 引数 | デフォルト | 説明 |
|---|---|---|
use_fused |
True |
DiT block の Linear を Triton カーネルで実行する |
force_fp16 |
False |
モデル全体を fp16 に強制(カーネルのネイティブ dtype) |
disable_eager |
False |
AdaLN の eager-dequant を無効化(デバッグ用) |
pack_rtn_extras |
False |
RTN 4-bit の extras(エンコーダ / text_embedding 等)を fp16 展開せず packed のまま on-the-fly dequant(VRAM 節約) |
duration_donor |
None |
duration predictor を持たないモデルに、指定した checkpoint の学習済み predictor を移植する |
codec_int4 |
False |
DACVAE コーデックの NormConv1d / NormConvTranspose1d を int4 packed に置き換える(VRAM ピーク ~525 MB 削減、decode は ~170→330 ms) |
codec_int4_groupsize |
32 |
コーデック int4 量子化のグループサイズ |
⚠️ 現状、FusedInt4Linearの fp32 入力フォールバックパスに既知の shape-check 制限があります。推論時はforce_fp16=True(またはexample/run_tts.pyのデフォルト)を推奨します。
量子化ウェイトは安全テンソルのメタデータ内に二系統で記録されています:
| スコープ | 形式 | ロード時の置換先 |
|---|---|---|
| DiT block の Linear(q/k/v/o、SwiGLU MLP の w1/w2/w3) | GPTQ uniform 4-bit, groupsize=32 | FusedInt4Linear(Triton カーネル) |
| AdaLN projection | GPTQ 4-bit | eager-dequant → fp16 nn.Linear |
| エンコーダ / cond_module / text_embedding / RMSNorm 拡張 | RTN 4-bit, uint8-nibble pack | eager-dequant → fp16 module(型はターゲットに合わせる) |
AdaLN を eager-dequant している理由は、12 個の小さな projection × 12 block × N RF step → 数千回の極小 Triton kernel launch になり、起動オーバヘッドが int4 速度メリットを食い潰してしまうため。エンコーダ系は逆に「呼び出し回数が少なく、サイズも小さい」ためカーネルを使う旨味がほぼ無いので fp16 にしてしまった方が単純です。
詳細な設計判断は docs/architecture.md を参照してください。
.
├── README.md # 本ファイル
├── LICENSE # MIT
├── pyproject.toml # パッケージ定義
├── irodori_tts_lite/ # ランタイム本体(自己完結、外部依存は torch/triton/safetensors のみ)
│ ├── __init__.py
│ ├── checkpoint_loader.py # inference_runtime へのモンキーパッチ
│ ├── fused_int4_linear.py # Triton カーネル(OneCompression からベンダリング)
│ ├── quant_utils.py # GPTQ-v1 unpack + RTN-extras dequant
│ ├── packed_conv.py # DACVAE 用 int4 packed Conv1d / ConvTranspose1d
│ └── weights.py # HF からの自動ダウンロード
├── example/
│ └── run_tts.py # 単発合成サンプル
├── tools/
│ ├── measure_peak_memory.py # フルパイプラインのピーク VRAM 計測
│ └── quantize_dacvae_poc.py # DACVAE int4 PoC(ベースラインと SNR 比較)
└── docs/
└── architecture.md # 内部設計
- CUDA 対応 GPU(compute capability 8.0 以上を推奨。Triton カーネルが Ampere 系チューニング)
- VRAM:
- DiT 単体なら 1 GB あれば十分(実測ピーク 552 MB + CUDA コンテキスト分)
- DACVAE コーデックを GPU に置く場合は 2 GB 推奨(実測ピーク 1513 MB)
CPU 推論は未対応です(DACVAE のみ CPU offload 可)。
pip install -e .[infer]ベンチや追加の動作確認用スクリプトは tools/ 配下に追加していく方針です。
- TTS パイプライン本体: Aratako/Irodori-TTS
- DACVAE コーデック: Aratako/Semantic-DACVAE-Japanese-32dim
どちらも Aratako 氏の成果物を 4-bit 量子化して使わせていただいています。
irodori_tts_lite/fused_int4_linear.py は OneCompression プロジェクト(Fujitsu Ltd. 著作)からのベンダリングです。元のライセンス表記を保持しています。
Copyright © 2025-2026 Kizuna Intelligence contributors.
(irodori_tts_lite/fused_int4_linear.py のみ Copyright © 2025-2026 Fujitsu Ltd.)