学習後に話速は変えられない — パラメータは受け取るのに、無視されていた
音声モデルを役割別に作っている。ナレーター、コールセンター、営業、司会。それぞれ喋り方が違ってほしい。
最初は「学習は1回で、話速や語尾は合成時に調整すればいい」と考えていた。合成APIには話速らしきパラメータがあった。
実測したら、効いていなかった。
パラメータが無視されている
同じモデル・同じテキストで、話速パラメータだけ変えて測った。
for kw in [{}, {"length": 1.15}, {"length": 1.35}, {"length": 0.85}]:
wav = synth(model_id=MID, text=T, style="Neutral", **kw)
sec, lvl, f0, rng = acoustics(wav)
print(f"{kw} 発話{sec:.1f}s 話速{mora(T)/sec:.1f}")
{} 発話3.9s 話速5.3
{'length': 1.15} 発話4.0s 話速5.2
{'length': 1.35} 発話4.1s 話速5.1
{'length': 0.85} 発話4.0s 話速5.2
1.35(遅くする)でも0.85(速くする)でも、話速は5.1〜5.3。合成のたびの揺らぎの範囲で、パラメータの効果ではない。
APIはパラメータを受け取り、エラーも返さず、正常に音声を返す。受け取っているが使っていない。 サーバ側の実装が、そのパラメータを合成器に渡していなかった。
「渡せる」と「効く」は別だった。ドキュメントにパラメータが載っていても、実測しないと分からない。
何が「焼き付く」のか
パラメータで変えられないなら、学習時に決まっているということだ。実際、学習コーパスの内容で決まる要素はいくつもある。
話速。コーパスがゆっくり喋っていれば、モデルもゆっくり喋る。
語尾の癖。コーパスに語尾の伸びたクリップが混ざっていると、台本に書いていなくても伸ばすようになる。実際に「こんにちは」を「こんにちわぁ」と読むモデルができた。
保有する感情スタイル。コーパスに絶叫や爆笑の素材が入っていれば、そのスタイルが作られる。入っていなければ存在しない。
文型の癖。コーパスの台本が「〜です」「〜ます」中心なら敬体寄りに、「〜だよ」「〜じゃん」中心ならカジュアル寄りになる。
つまり用途は製造時に決めきるしかない。この前提で全体を組み直した。
話体プロファイルを定義する
用途ごとに「話体(conv_style)」を定義し、コーパスの台本セットと品質ゲートの厳しさを切り替えるようにした。14種ある。
_CONV_STYLE_PATHS = {
"news": _SAMPLES / "conversational_news_texts.txt",
"narration": _SAMPLES / "conversational_narration_texts.txt",
"support": _SAMPLES / "conversational_support_texts.txt",
"presentation": _SAMPLES / "conversational_presentation_texts.txt",
"sales": _SAMPLES / "conversational_sales_texts.txt",
"counseling": _SAMPLES / "conversational_counseling_texts.txt",
"guidance": _SAMPLES / "conversational_guidance_texts.txt", # IVR・館内
"compliance": _SAMPLES / "conversational_compliance_texts.txt", # 重要事項説明
"guide": _SAMPLES / "conversational_guide_texts.txt", # 観光・展示
"mc": _SAMPLES / "conversational_mc_texts.txt",
"secretary": _SAMPLES / "conversational_secretary_texts.txt",
}
# 上記に polite(受付) / casual(VTuber・配信) / mixed(汎用・既定) を加えて14種
台本の中身は用途そのものだ。コールセンターなら「お電話ありがとうございます。サポートセンターでございます。」、ナレーターなら「創業以来、私たちは品質にこだわり続けてきました。」といった実務の文型を並べる。
業務系では極端素材を外す
もう1つの分岐が、極端演技の素材を入れるかどうかだった。
# ビジネス系話体: 極端演技(絶叫/爆笑)素材を含めない
BUSINESS_CONV_STYLES = {"polite", "news", "narration", "support",
"presentation", "sales", "counseling",
"guidance", "compliance", "guide", "secretary"}
# 注: mc(イベントMC/実況)は高揚系のため極端素材あり=ビジネス集合に含めない
def build_corpus_plan(conv_style=None):
per_cat = int(os.getenv("EXTREME_PER_CAT", "8"))
if (conv_style or "").lower() in BUSINESS_CONV_STYLES:
per_cat = 0 # 絶叫・爆笑などを1本も入れない
...
結果、モデルの持つスタイル数が変わる。
業務系 = 12スタイル
Neutral, Joy, Excitement, Pride, Relief, Surprise,
Fear, Sadness, Shame, Anger, Contempt, Disgust
casual/mixed/mc = 17スタイル
上記12 + Scream, Laugh, Cry, JoyBurst, Shock
これは音質の話ではなく事故防止だった。ランタイム側に「盛り上がったら全力でリアクションする」機能があり、その発火条件が "Scream" in styles になっている。つまりScreamスタイルを持っている声だけが全力リアクションする。
コールセンターの声が突然「あっはっはっはっは!」と笑い出したら困る。構成の段階でスタイルを持たせないことで、実行時のロジックに関係なく事故を防ぐ設計にした。
⚠️ 業務っぽいのに極端素材が入る話体
上のコードで mc だけ BUSINESS_CONV_STYLES から外してある。イベント司会は盛り上げる役なので、絶叫や爆笑があったほうがいい。
ところがMC声は業務用途に見える。「イベント司会者」は接客側の役割なので、うっかり問い合わせ窓口に割り当てられる可能性がある。そして17スタイル=Screamを持つので、全力リアクションが発火する。
実測で確認した。
| 話体 | スタイル数 | Scream保有 |
|---|---|---|
| counseling / sales / support / presentation / narration / news | 12 | なし |
| mc | 17 | あり |
この例外はドキュメントに明記し、声を自動で割り当てる仕組みには「mc声を接客窓口に解決してはいけない」という制約として入れた。「業務っぽさ」と「極端素材の有無」は一致しないので、名前から推測できない。
同じ声で用途違いを焼き足す
話体が変えられないなら、用途ごとに焼くしかない。ただしこれは思ったより軽い運用だった。
声のアイデンティティは (caption, seed) で決まり、話体はそれと独立している(声をガチャで引く)。したがって同じ声のまま話体だけ変えてもう1本焼ける。
INSERT INTO voice_design_jobs (name, caption, seed, progress) VALUES
('シオリ(ナレーション版)', '落ち着いた知的な大人の女性の声。…', 1042,
'{"params":{"conv_style":"narration"}}'),
('シオリ(コールセンター版)', '落ち着いた知的な大人の女性の声。…', 1042,
'{"params":{"conv_style":"support"}}');
キャプションとseedは完全に同じ。話体だけ違う。1本1〜2時間で焼けるので、必要になったときに増やせる。
実際、あるキャラクターは「配信用(カジュアル・17スタイル)」と「業務用(丁寧・12スタイル)」の2バリアントを持っている。
「変えられない」を受け入れると、「増やせる」という運用に切り替わる。 ランタイムで調整できると思い込んでいた間は、この設計に辿り着けなかった。
波及: 声を選ぶ仕組みにも影響する
話体がコーパスに焼き付くということは、声を選ぶ側も話体を知る必要がある。
「コールセンター用の声をください」というリクエストに対して、カタログから conv_style = 'support' の声を返す。話体が記録されていない古いモデルは、この解決の対象にならない。
実際にカタログを棚卸ししたら、76件のうち話体が記録されているのは19件だけだった(登録経路が4つ、管理画面が0)。残りは話体プロファイル導入前のモデルや、別経路で登録された参照音声で、conv_style が NULL になっている。
ここで「遡って話体を付けるか」という話が出たが、付けないことにした。conv_style は「そのモデルがどう振る舞うか」のラベルではなく、どの台本コーパスで学習したかの記録だからだ。旧レシピのモデルに後から narration と書いても、実際にナレーション用のコーパスで学習したわけではない。ラベルと実体が乖離した状態をDBに固定することになる。
「話体不明」として明示し、自動解決の対象外にする。UIには「用途未記録」と出す。欠損ではなく事実として扱う方針にした。
まとめ
- パラメータが「受け取られる」ことと「効く」ことは別。話速のように重要なものは実測する
- コーパスに焼き付くものを洗い出す。話速・語尾の癖・保有スタイル・文型
- 実行時のロジックに頼らず、構成で事故を防ぐ。Screamスタイルを持たせなければ全力リアクションは発火しない
- 例外は名前から推測できない。MCは業務っぽいが極端素材を持つ。明記して機械可読な制約にする
- 「変えられない」を受け入れると「増やせる」に切り替わる。同じ声で話体違いを焼き足す運用が成立した
- 後から付けられないラベルは、付けない。実体と乖離した記録は、自動化の判断材料として害になる
シリーズ: 拡散TTSから実用ボイスを量産する
キャプション1行から声を設計し、学習コーパスを製造して、役割別の実用ボイスを量産するまでの記録です。この記事は第2部 製造にあたります。
← 前: 品質ゲートを厳しくするほど、棒読みが生き残る → 次: 生成するたび「録音場所」が変わるTTS
シリーズ全18本
- 音質で選んだTTSが、会話には遅すぎた
- 声をガチャで引く
- 「ナレーターっぽい声」を24候補から機械に選ばせる
- 品質ゲートを厳しくするほど、棒読みが生き残る 5. 学習後に話速は変えられない ← いまここ
- 生成するたび「録音場所」が変わるTTS
- クリップ1本の粗さが、スタイル全体を嗄れさせる
- AIが「こんにちわー」と伸ばす癖は、どこで身についたのか
- 「少々」が「しょも」になる — 許可文字リストが日本語を削っていた
- ハルシネーション対策のコードが、ハルシネーションの時だけ動かなかった
- 品質ゲートが許した「3文字」が、モデルの口癖になった
- 治せる欠陥で候補を落としていた
- 文字起こしでは見つからない欠陥がある
- 70分かけた学習素材が、ネットワークの一瞬の瞬きで消えた
- “ja” を “JP” と書いて喃語モデルができるまで
- 登録経路が4つ、管理画面が0
- デプロイのたびに互いの成果を消していた
- 測れていないものを閾値で追い込むと、必ず失敗する
知見の元になったノートは 拡散TTSから実用ボイスを量産する製造パイプライン にまとめてあります。