#音声合成#TTS#機械学習#設計

学習後に話速は変えられない — パラメータは受け取るのに、無視されていた


音声モデルを役割別に作っている。ナレーター、コールセンター、営業、司会。それぞれ喋り方が違ってほしい。

最初は「学習は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本
  1. 音質で選んだTTSが、会話には遅すぎた
  2. 声をガチャで引く
  3. 「ナレーターっぽい声」を24候補から機械に選ばせる
  4. 品質ゲートを厳しくするほど、棒読みが生き残る 5. 学習後に話速は変えられない ← いまここ
  5. 生成するたび「録音場所」が変わるTTS
  6. クリップ1本の粗さが、スタイル全体を嗄れさせる
  7. AIが「こんにちわー」と伸ばす癖は、どこで身についたのか
  8. 「少々」が「しょも」になる — 許可文字リストが日本語を削っていた
  9. ハルシネーション対策のコードが、ハルシネーションの時だけ動かなかった
  10. 品質ゲートが許した「3文字」が、モデルの口癖になった
  11. 治せる欠陥で候補を落としていた
  12. 文字起こしでは見つからない欠陥がある
  13. 70分かけた学習素材が、ネットワークの一瞬の瞬きで消えた
  14. “ja” を “JP” と書いて喃語モデルができるまで
  15. 登録経路が4つ、管理画面が0
  16. デプロイのたびに互いの成果を消していた
  17. 測れていないものを閾値で追い込むと、必ず失敗する

知見の元になったノートは 拡散TTSから実用ボイスを量産する製造パイプライン にまとめてあります。

← 記事一覧へ