メインコンテンツへスキップ
← ブログに戻る
チュートリアル2026年2月22日by K.hirano

RTX 4070 Ti 自宅サーバーに Qwen3-TTS を入れた実録 — API変更で3回詰まった話

自宅の RTX 4070 Ti に Qwen3-TTS をデプロイした記録。クラス名・メソッド名・戻り値の形式が全部変わっていて3回詰まった。同じ罠に落ちる人が減るよう全部書く。

#AI#TTS#Qwen3#Ubuntu#自宅サーバー#RTX4070Ti#FastAPI#音声合成#ローカルAI#qwen3-tts

自宅のサーバーラックに、RTX 4070 Ti が刺さっています。12GB VRAM のカードが、毎日 ComfyUI と ACE-Step のために待機している。

音声合成もそこにある GPU でやればいい、という話です。Qwen3-TTS は Alibaba が2026年にリリースした TTS モデルで、日本語を含む10言語に対応し、VRAM は 4GB で足ります。クラウド TTS API は文字数課金なので、利用頻度が上がると費用が積み上がります。一度ローカルにデプロイしてしまえば、その後は GPU の電力代だけです。

実際に入れてみたら、ドキュメントと実際の API が噛み合わない箇所で3回つまずきました。同じところで止まる人が減るよう、全部記録します。

Qwen3-TTS とはなにか

Alibaba(阿里巴巴)が開発した多言語 TTS モデルです。

項目詳細
モデルQwen3-TTS-12Hz-1.7B-CustomVoice
パラメータ数1.7B
対応言語10言語(日本語・英語・中国語・ロシア語・仏語・独語・西語・韓国語・ポルトガル語・伊語)
日本語話者Ono_Anna / Seina_Yuki / Koharu ほか(9名)
VRAM要件約4GB(bfloat16)
ライセンスApache 2.0

1.7B で VRAM 4GB というのは、RTX 4070 Ti(12GB)にとって余裕のある要件です。日本語話者が複数用意されていて、Ono_Anna は聴いてすぐ「使える」と判断できる品質でした。

META-MARK の AI ワークロードページに Qwen3-TTS の詳細スペック をまとめていますので、技術仕様だけ確認したい方はそちらへ。

動作環境

項目
CPUAMD Ryzen 9 5900(24コア)
RAM32GB
GPUNVIDIA GeForce RTX 4070 Ti(12GB VRAM)
OSUbuntu 24.04.4 LTS
CUDA13.0
Python3.12
PyTorch2.6.0+cu124
qwen-tts0.1.1

セットアップ手順

1. 仮想環境の準備

bash
mkdir -p ~/qwen3-tts/output
chmod 700 ~/qwen3-tts
cd ~/qwen3-tts
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip

2. PyTorch(CUDA版)のインストール

bash
pip install torch torchaudio \
    --index-url https://download.pytorch.org/whl/cu124

pip install torch だけではCPU版が入る可能性があります。--index-url の指定は省略しないこと。

3. qwen-tts と sox のインストール

bash
pip install qwen-tts
sudo apt install sox

sox はPythonライブラリとシステムバイナリが別物です。pip install qwen-tts でPython側の sox ライブラリは入りますが、システム側のバイナリは別途必要です。sudo apt install sox を忘れると起動時に警告が出続けます。

4. FastAPI サーバーの実装

次の「詰まったところ」セクションを先に読むことをすすめます。

API の変更点を知らずに書くと、3箇所で止まります。

python
# api_server.py
from contextlib import asynccontextmanager
import asyncio, io, os
import soundfile as sf
import torch
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
from pydantic import BaseModel, Field
from typing import Optional
from dotenv import load_dotenv

load_dotenv()

MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice")
_model = None
_gpu_lock = asyncio.Lock()

@asynccontextmanager
async def lifespan(app: FastAPI):
    global _model
    from qwen_tts import Qwen3TTSModel  # ★ QwenTTS ではない
    _model = Qwen3TTSModel.from_pretrained(
        MODEL_NAME,
        device_map="cuda:0",
        dtype=torch.bfloat16,       # ★ torch_dtype= ではなく dtype=
        attn_implementation="sdpa", # FlashAttention2 未インストール時はこれ
    )
    yield

app = FastAPI(lifespan=lifespan)

class SynthesizeRequest(BaseModel):
    text: str = Field(..., min_length=1, max_length=2000)
    speaker: str = Field(default="Ono_Anna")
    language: str = Field(default="Japanese")
    instruct: Optional[str] = None

@app.get("/health")
async def health():
    return {"status": "ok", "model_loaded": _model is not None}

@app.post("/v1/tts/synthesize")
async def synthesize(body: SynthesizeRequest):
    if _model is None:
        raise HTTPException(503, "モデルロード中")
    async with _gpu_lock:
        loop = asyncio.get_event_loop()
        wavs, sr = await loop.run_in_executor(
            None,
            lambda: _model.generate_custom_voice( # ★ synthesize() ではない
                text=body.text,
                speaker=body.speaker,              # ★ speaker_name= ではなく speaker=
                language=body.language,
                instruct=body.instruct,
            )
        )
    buf = io.BytesIO()
    sf.write(buf, wavs[0], sr, format="WAV")  # ★ 戻り値は (List[ndarray], int) → wavs[0]
    return Response(content=buf.getvalue(), media_type="audio/wav")

5. systemd でサービス化

ini
# /etc/systemd/system/qwen3tts.service
[Unit]
Description=Qwen3-TTS Speech Synthesis Server
After=network-online.target
Conflicts=comfyui.service acestep.service

[Service]
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER/qwen3-tts
ExecStart=/home/YOUR_USER/qwen3-tts/.venv/bin/uvicorn \
    api_server:app --host 0.0.0.0 --port 9001 --workers 1
Restart=on-failure
EnvironmentFile=/home/YOUR_USER/qwen3-tts/.env
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/home/YOUR_USER/qwen3-tts /home/YOUR_USER/.cache/huggingface
MemoryMax=16G
TimeoutStartSec=300

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl start qwen3tts
journalctl -u qwen3tts -f

6. ファイアウォール設定と動作確認

bash
sudo ufw allow from 192.168.x.x to any port 9001 proto tcp

curl -X POST http://localhost:9001/v1/tts/synthesize \
  -H "Content-Type: application/json" \
  -d '{"text":"こんにちは、テスト音声です。"}' \
  -o test.wav && file test.wav

詰まったところ全部書く

詰まり① ImportError: cannot import name 'QwenTTS'

最初に必ず引っかかる罠です。

terminal
ImportError: cannot import name 'QwenTTS' from 'qwen_tts'

検索で出てくるサンプルコードのほぼ全部が古い API 名を使っています。qwen-tts 0.1.1 の実際のクラス名は Qwen3TTSModel です。クラス名だけでなく、メソッド名・引数名・戻り値の形式も全部変わっています。

旧API(誤り)新API(qwen-tts 0.1.1)
QwenTTSQwen3TTSModel
.synthesize().generate_custom_voice()
speaker_name=speaker=
戻り値: ndarray戻り値: (List[ndarray], int)

戻り値がタプルになっていることに気づかず、最初は wavs を直接 sf.write に渡してエラーになりました。正しくは wavs[0] です。

現在の公開インターフェースを確認するには:

python
import qwen_tts; print(dir(qwen_tts))
# → ['Qwen3TTSModel', 'Qwen3TTSTokenizer', 'VoiceClonePromptItem', ...]

バージョンアップのたびに変わる可能性があるので、インストール後は一度 dir() で確認する習慣をつけておくと時間を節約できます。

詰まり② torch_dtype is deprecated!

python
# ❌
Qwen3TTSModel.from_pretrained(MODEL, torch_dtype=torch.bfloat16)

# ✅
Qwen3TTSModel.from_pretrained(MODEL, dtype=torch.bfloat16)

動きはしますが、毎回 deprecated 警告が出ます。dtype= に変えれば消えます。些細なことですが、ログが汚いと本当のエラーを見落とします。

詰まり③ FlashAttention2 のインストール失敗

FlashAttention2 を入れようとするとコンパイルが走ります。数十分かけた結果、失敗しました。

PyTorch 組み込みの sdpa(Scaled Dot-Product Attention)でフォールバックすれば動きます。RTX 4070 Ti なら速度も実用的です。FlashAttention2 に拘る必要はありません。

python
try:
    model = Qwen3TTSModel.from_pretrained(MODEL, attn_implementation="flash_attention_2", ...)
except Exception:
    model = Qwen3TTSModel.from_pretrained(MODEL, attn_implementation="sdpa", ...)

詰まり④ systemd の Conflicts 設定で sudo grep が動かない

インストールスクリプトで Conflicts= ディレクティブを自動追記しようとしたとき、NOPASSWD リストに grep が入っていないために条件分岐が壊れました。

sudo cat で読み込んでから通常の grep に渡す組み合わせで解決できます。

bash
if ! sudo cat /etc/systemd/system/foo.service | grep -q "bar"; then
    if sudo cat /etc/systemd/system/foo.service | grep -q "^Conflicts="; then
        sudo sed -i '/^Conflicts=/ s/$/ bar.service/' /etc/systemd/system/foo.service
    else
        sudo sed -i '/^\[Unit\]/a Conflicts=bar.service' /etc/systemd/system/foo.service
    fi
fi

実測パフォーマンス(RTX 4070 Ti)

項目
VRAM使用量約4GB(3,985 MiB)
モデルロード時間(キャッシュ後)約30秒
日本語音声生成(約20文字)約7秒
生成音声の長さ5.68秒
出力フォーマットWAV 24000Hz 16bit mono

20文字で7秒は「速い」とは言えません。リアルタイム会話には使えないです。ただ、目的がバッチ処理——テキストを渡して音声ファイルをまとめて生成しておく——なら十分実用的です。クラウドAPIと違って並列数に料金が発生しないので、大量に流すような使い方には向いています。

GPU排他制御(複数AIサービスの共存)

自宅サーバーで ComfyUI や ACE-Step を既に動かしている場合、VRAM の競合が起きます。systemd の Conflicts= ディレクティブを使うと、Qwen3-TTS 起動時に他のサービスが自動停止します。

ini
[Unit]
Conflicts=comfyui.service acestep.service

停止したサービスは Qwen3-TTS を止めたあとに手動で起動し直す必要があります。自動復帰の仕組みは入れていませんが、用途的にはこれで十分です。

ローカル AI を複数同居させる構成については、ローカルAI vs クラウドAI、2026年の今どちらが本命なのかを正直に語るBlogローカルAI vs クラウドAI、2026年の今どちらが本命なのかを正直に語る2026年のAI環境の中で、ローカルAIとクラウドAIのメリット・デメリットを徹底比較。コスト、プライバシー、性能、手軽さを管理人の視点で解説します。にも整理しています。

チェックリスト

  • クラス名は Qwen3TTSModelQwenTTS ではない)
  • dtype=torch_dtype= は非推奨)
  • .generate_custom_voice() を使う(.synthesize() ではない)
  • speaker=speaker_name= ではない)
  • 戻り値は (wavs, sr) のタプル → wavs[0] が音声データ
  • sudo apt install sox でシステム側 sox もインストール
  • FlashAttention2 が入らないなら sdpa で十分

API はバージョンアップで変わる可能性があります。import qwen_tts; print(dir(qwen_tts)) で現在の公開クラスを都度確認するのが確実です。

ローカルでまともに動く TTS が、VRAM 4GB で動く。3年前はこんなことになるとは思っていませんでした。モデルが小さくなって、ハードルが下がって、自宅のラックに立てるのが「普通」になりつつある。

ローカルで音楽生成を試したいなら、ローカルで音楽を作る。ACEStep 1.5を本気で試したら詰まって、直して、驚いたBlogローカルで音楽を作る。ACEStep 1.5を本気で試したら詰まって、直して、驚いたACEStep 1.5をRTX 4070 Ti 12GBで実際に動かした記録。CUDA graphバグの修正からJ-Pop・Lo-fi・Rock・Ambient 4ジャンルの生成実験まで、数字ありで正直に書く。も参考になると思います。あちらも同じくローカル環境で動かせます。

何が来ても驚かなくなってきたのが、正直なところです。それがいいことなのかはまだわかりません。

関連記事

関連リンク

  • 実際の構成を探すなら、GPU 比較ページやローカルLLM向け構成記事もあわせて見ると判断しやすいです。
  • ハードウェア候補は用途別の AI 構成ガイドからたどると、単体製品より違和感なく検討できます。

この記事を書いた人

HW系エンジニアとして20年以上、10,000件を超える顧客訪問と2,000件を超える単独ソリューション実績。AIツールを使った個人開発やIoT農園など、Raspberry Piを使ったオートメーション化なども実践中です!エンジニア専門結婚相談所も運営中、ClaudeCodeで解決できない心の課題も解決いたします!

AIに発言だけ見せて「この2体は誰だ」と聞いたらGeminiを当てた。でも根拠はこちらのバグだった
2026年8月13日
事例紹介

AIに発言だけ見せて「この2体は誰だ」と聞いたらGeminiを当てた。でも根拠はこちらのバグだった

AI同士に正体を伏せたまま20回会話させ、その発言ログだけを第三のAIに渡して「同じモデルか、どちらが上か」を推理させました。正体は当てましたが、根拠にはこちらの設定ミスが混ざっていました。同じ勝負を2回撮って分かったことの全記録です。

#AIコロシアム#AIアリーナ#AI#LLM比較#ベンチマーク
HeartMuLa 3B vs ACE-Step 1.5 徹底比較:ローカルAI音楽生成、歌詞はどこまで再現できるか
2026年7月18日
事例紹介

HeartMuLa 3B vs ACE-Step 1.5 徹底比較:ローカルAI音楽生成、歌詞はどこまで再現できるか

自宅サーバー(RTX 4070 Ti 12GB)でHeartMuLa 3BとACE-Step 1.5を実機比較。歌詞再現度をWhisperXで定量評価し、12GB VRAM環境で踏んだ導入の罠も記録しました。

#heartmula#Ace-Step#ローカルAI音楽生成#RTX4070Ti#whisperx
GEMMA4-12B-CODERは何に使うべきか:コード55/60、総合B評価の実測結論
2026年7月1日
開発ログ

GEMMA4-12B-CODERは何に使うべきか:コード55/60、総合B評価の実測結論

GEMMA4-12B-CODERをRTX 4070 Ti 12GBで実測。54.6 tok/s、24問160/240、コード55/60。Gemma 4 12B/E2B/E4Bとの使い分けとMTP可否を整理します。

#gemma4#ローカルLLM#llama.cpp#RTX4070Ti#ベンチマーク
スマホだけで英語YouTubeを同時通訳:Gemini 3.5 Live Translateを耳で使う実践メモ
2026年6月12日
チュートリアル

スマホだけで英語YouTubeを同時通訳:Gemini 3.5 Live Translateを耳で使う実践メモ

2026年6月公開のGemini 3.5 Live Translateを、スマホとBluetoothイヤホンだけで実機検証。スピーカーだと待たされるのにイヤホンだとほぼ完璧な同時通訳になる——その分かれ目と、英語YouTubeを耳で理解する一番実用的な使い方を共有します。

#Gemini#翻訳#AI#スマホ活用#tutorial

関連AIツール

META-MARK × AI

ローカルAIを動かすGPU、ちゃんと選べていますか?

VRAM・性能・コスパをMetaScoreで数値化。AIアプリ別の推奨ハードウェア要件も確認できます。