序文:日本語が消える? 戯言を申すな!
Fish Speech 1.5 は、10万時間を超える日本語データで鍛え上げられた素晴らしいモデルでござる。
しかし、そのまま導入して「こんにちは」と入力しても、城内(コード内部)に潜む「検閲官」どもが日本語をノイズとみなして勝手に消去し、虚無(無音)を生成する事態が多発しておる。
本記事では、最新の Mac M4 Pro において、不届きな役人(バグ・仕様)を「御免状パッチ」で根こそぎ成敗し、勝利の音声を響かせるまでの手順を認め(したため)た。
第零章:土台(Conda)の築城
まずは馬(Python実行環境)を飼わねばならぬ。Mac M4 Pro(Apple Silicon)に最適化された Miniconda を導入せよ。
-
インストーラーの奪取:
Miniconda公式サイトより、M1/M2/M3/M4 (Apple Silicon) 用のpkgまたはshファイルを拾いなされ。 -
儀式の実行:
ターミナルで以下を叩き、道筋(パス)を通すのじゃ。
# インストール(例:shファイルの場合)
bash Miniconda3-latest-MacOSX-arm64.sh
# 初期化
source ~/miniconda3/bin/activate
conda init zsh
一度ターミナルを閉じ、再び開いて左端に (base) と出れば成功でござる!
第一章:城(環境)の構築と「時の固定」
最新の main ブランチは日々仕様が変わる魔境。まずは時を戻し、安定版に固定するのが兵法の定石でござる。
1. 陣の設営
# プロジェクトのクローン
git clone https://github.com/fishaudio/fish-speech.git
cd fish-speech
# 【最重要】v1.5.0 のタグにチェックアウトせよ!
# これを忘れると、最新の仕様変更(引数エラーなど)に翻弄されることになる。
git checkout v1.5.0
# 仮想環境の作成(conda推奨)
conda create -n fish-speech python=3.10
conda activate fish-speech
# 依存関係のインストール
pip install -e .
pip install torch torchaudio torchvision
2. 武器(モデル)の調達と「大八車」の用意
モデルを Hugging Face から引き出すには、専用の道具(CLI)が必要じゃ。これを入れ忘れると、空の棚を眺めることになりますぞ。
# 大八車(huggingface_hub)のインストール
pip install huggingface_hub
# ログイン(必要に応じて)
# huggingface-cli login
# モデルの一括ダウンロード
huggingface-cli download fishaudio/fish-speech-1.5 --local-dir checkpoints/fish-speech-1.5
第二章:検閲官の粛清(御免状パッチ)
日本語を「不審な文字」として切り捨てる仕様を、以下の 4つの修正(パッチ) で物理的に書き換える。
1. clean.py の無害化
日本語を消去する主犯。機能そのものをザルにする。
fish_speech/text/clean.py の clean_text 関数を以下のように書き換えるのじゃ。
def clean_text(text):
# 検閲を無効化し、ありのままの日本語を通す
return str(text)
2. 正規化の強制解除
tools/inference_engine/__init__.py にて、中国語の物差し(ChnNormedText)を無視させる。
# 150行目付近
text=(
req.text
if not req.normalize
else req.text, # ← ここを強制的に req.text にする!
),
3. オーディオバックエンドの修正
Mac での自爆を防ぐため、以下の2ファイルの backends = ... を backends = [] に書き換える。
tools/inference_engine/reference_loader.pytools/vqgan/extract_vq.py
第三章:真・実行スクリプト(Master's Script)
WebUI 経由でも動くが、真の侍なら CLI で直接生成したいはず。
数多のエラー(MPS行列サイズ不一致、KV Cache未初期化、タプル戻り値問題)をすべて克服した 「v7:解脱版」 のスクリプトをここに授ける。
これを run_custom_tts_v1.5.0.py として保存し、実行せよ!
import os
import sys
import torch
import torch.nn.functional as F
import torchaudio
import soundfile as sf
import numpy as np
from pathlib import Path
# --- 【勝利の鍵1】検閲官の暗殺(メモリ上でも念押し) ---
try:
import fish_speech.text.clean as clean_module
clean_module.clean_text = lambda x: str(x)
print("⚔️ clean_text を無力化しました。")
except ImportError:
pass
# --- 【勝利の鍵2】MPS用・行列サイズ強制補正パッチ ---
original_sdpa = F.scaled_dot_product_attention
def patched_sdpa(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
if attn_mask is not None and attn_mask.shape[-1] > key.shape[-2]:
target_len = key.shape[-2]
attn_mask = attn_mask[..., :target_len, :target_len]
return original_sdpa(query, key, value, attn_mask=attn_mask, dropout_p=dropout_p, is_causal=is_causal, scale=scale)
torch.nn.functional.scaled_dot_product_attention = patched_sdpa
print("🔧 MPS用 Attention Mask パッチを適用しました。")
from tools.llama.generate import load_model as load_llama_model, generate_long
from tools.vqgan.inference import load_model as load_vqgan_model
# ================= 設定エリア =================
DEVICE = "mps"
CHECKPOINT_DIR = "checkpoints/fish-speech-1.5"
DECODER_CKPT = "checkpoints/fish-speech-1.5/firefly-gan-vq-fsq-8x1024-21hz-generator.pth"
DECODER_CONFIG = "firefly_gan_vq"
TARGET_TEXT = "こんにちは。御免状式修正により、Mac M4 Proで日本語生成に成功しました。"
REFERENCE_AUDIO_PATH = "reference.wav" # 貴殿のWAVファイル
REFERENCE_TEXT = "参照音声が喋っている内容"
# ============================================
def main():
PRECISION = torch.float32
print("📥 Loading Models...")
decoder_model = load_vqgan_model(DECODER_CONFIG, DECODER_CKPT, DEVICE)
llama_model, decode_one_token = load_llama_model(Path(CHECKPOINT_DIR), DEVICE, PRECISION, compile=False)
# 【重要】KV Cache の初期化
print("🧠 Setting up Caches...")
with torch.device(DEVICE):
llama_model.setup_caches(max_batch_size=1, max_seq_len=llama_model.config.max_seq_len, dtype=PRECISION)
# リファレンス処理
audio, sr = torchaudio.load(REFERENCE_AUDIO_PATH)
if sr != decoder_model.spec_transform.sample_rate:
audio = torchaudio.functional.resample(audio, sr, decoder_model.spec_transform.sample_rate)
audio = audio.mean(dim=0, keepdim=True).to(DEVICE)
audio_length = torch.tensor([audio.shape[1]], device=DEVICE, dtype=torch.long)
with torch.no_grad():
prompt_tokens = decoder_model.encode(audio[None], audio_length)[0][0]
# 生成
print(f"📝 Generating: {TARGET_TEXT}")
generator = generate_long(
model=llama_model, device=DEVICE, decode_one_token=decode_one_token,
text=TARGET_TEXT, num_samples=1, max_new_tokens=0,
top_p=0.7, repetition_penalty=1.2, temperature=0.7,
prompt_text=[REFERENCE_TEXT], prompt_tokens=[prompt_tokens],
iterative_prompt=True, chunk_length=150
)
all_codes = []
for response in generator:
if response.action == "sample": all_codes.append(response.codes)
elif response.action == "next": break
# デコード(v1.5仕様:feature_lengths必須 & タプル戻り値)
print("🔊 Decoding...")
full_codes = torch.cat(all_codes, dim=1).unsqueeze(0)
feature_lengths = torch.tensor([full_codes.shape[2]], device=DEVICE, dtype=torch.long)
with torch.no_grad():
decoded_output = decoder_model.decode(full_codes, feature_lengths)
decoded_audio = decoded_output[0] if isinstance(decoded_output, tuple) else decoded_output
sf.write("output.wav", decoded_audio.squeeze().cpu().float().numpy(), decoder_model.spec_transform.sample_rate)
print("🎉 勝利の凱旋! output.wav が生成されました。")
if __name__ == "__main__":
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
main()
実行結果:
(fish-speech) kouairchair@tanakagoujunoMac-mini fish-speech % python run_custom_tts_v1.5.0.py
⚔️ clean_text を無力化しました。
🔧 MPS用 Attention Mask パッチ(v7) を適用しました。
🚀 Device: mps で起動中...
📥 Loading VQGAN (Decoder)...
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/vector_quantize_pytorch/vector_quantize_pytorch.py:445: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
@autocast(enabled = False)
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/vector_quantize_pytorch/vector_quantize_pytorch.py:630: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
@autocast(enabled = False)
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/vector_quantize_pytorch/finite_scalar_quantization.py:147: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
@autocast(enabled = False)
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/vector_quantize_pytorch/lookup_free_quantization.py:209: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
@autocast(enabled = False)
2026-03-09 20:36:41.967 | INFO | tools.vqgan.inference:load_model:43 - Loaded model: <All keys matched successfully>
📥 Loading Llama (Encoder)...
2026-03-09 20:36:48.367 | INFO | tools.llama.generate:load_model:682 - Restored model from checkpoint
2026-03-09 20:36:48.368 | INFO | tools.llama.generate:load_model:688 - Using DualARTransformer
🔧 Setting up KV Caches...
🎤 Processing Reference Audio: sample_voice.m4a
objc[10346]: Class AVFFrameReceiver is implemented in both /Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/av/.dylibs/libavdevice.62.1.100.dylib (0x16d9103a8) and /opt/homebrew/Cellar/ffmpeg/8.0.1_2/lib/libavdevice.62.1.100.dylib (0x17c8b4328). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
objc[10346]: Class AVFAudioReceiver is implemented in both /Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/av/.dylibs/libavdevice.62.1.100.dylib (0x16d9103f8) and /opt/homebrew/Cellar/ffmpeg/8.0.1_2/lib/libavdevice.62.1.100.dylib (0x17c8b4378). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/torch/functional.py:681: UserWarning: An output with one or more elements was resized since it had shape [], which does not match the required output shape [1, 663, 1025]. This behavior is deprecated, and in a future PyTorch release outputs will not be resized unless they have zero elements. You can explicitly reuse an out tensor t by resizing it, inplace, to zero elements with t.resize_(0). (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/aten/src/ATen/native/Resize.cpp:38.)
return _VF.stft( # type: ignore[attr-defined]
/Users/kouairchair/miniconda3/envs/fish-speech/lib/python3.10/site-packages/vector_quantize_pytorch/residual_fsq.py:170: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.
with autocast(enabled = False):
✅ Reference Encoded.
📝 Generating Semantic Tokens for text: こんにちは。御免状式修正により、Mac M4 Proで日本語生成に成功しました。
2026-03-09 20:36:48.680 | INFO | tools.llama.generate:generate_long:789 - Encoded text: こんにちは。御免状式修正により、Mac M4 Proで日本語生成に成功しました。
2026-03-09 20:36:48.681 | INFO | tools.llama.generate:generate_long:807 - Generating sentence 1/1 of sample 1/1
2%|▊ | 152/7954 [00:13<11:29, 11.32it/s]
2026-03-09 20:37:02.493 | INFO | tools.llama.generate:generate_long:861 - Generated 154 tokens in 13.81 seconds, 11.15 tokens/sec
2026-03-09 20:37:02.493 | INFO | tools.llama.generate:generate_long:864 - Bandwidth achieved: 7.11 GB/s
🔊 Decoding to Audio...
🎉 Success! Audio saved to: output_cli.wav
(fish-speech) kouairchair@tanakagoujunoMac-mini fish-speech %
結び:ならぬものはならぬ、通らぬものは通す
Fish Speech は、正しく扱えば最強の武器となる。
もし貴殿の環境で「日本語が喋らぬ」と悩んでいるのなら、この御免状パッチと時を戻す術を試すがよい。
以上。これにて一件落着!