0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成するたび「録音場所」が変わるTTS — 素材の音響を揃えないとスタイルが飛ぶ

0
Posted at

📝 この記事は forge.workstyle.tech に掲載した記事の転載です。

音声モデルの学習コーパスを、別のTTSで生成している。同じ話者設定、同じモデル、同じサーバ。それでもクリップごとに音の質感が違う

具体的には、感情スタイルを切り替えて合成したときに気づいた。喜びスタイルから悲しみスタイルに切り替えると、声色だけでなく音質そのものが変わる。片方は近くで録ったように聞こえ、もう片方はやや遠い。同じ話者が別の部屋で喋っているような違和感がある。

原因は学習素材だった。コーパスのクリップが、生成条件ごとに違う周波数特性を持っていた。

なぜ質感が変わるのか

このコーパスは、感情ごとに違う参照音声(アンカー)を使って生成している(品質ゲートを厳しくするほど、棒読みが生き残る)。

joy の本文     → joy用アンカーを参照に生成
sadness の本文 → sadness用アンカーを参照に生成
anger の本文   → anger用アンカーを参照に生成

参照音声が違えば、TTSはその参照の声色だけでなく音響特性も真似る。アンカー自体が別々に生成されたものなので、それぞれ微妙に違うスペクトル形状を持っている。結果、感情グループごとに音の質感がずれる。

さらに極端演技(絶叫、爆笑など)の素材は、そもそも音圧のレンジが違う。並べて学習させると、モデルは「このスタイルのときは音が遠い」まで一緒に覚えてしまう。

人間の録音なら同じマイク・同じ部屋で録るので起きない問題が、生成音声では起きる。TTSは録音環境という概念を持たないので、条件が変わればチャンネル特性も変わる。

LTASを揃える

対処は、学習前に全クリップを共通の周波数特性へ寄せることだった。

LTAS(Long-Term Average Spectrum、長時間平均スペクトル)は、音声全体を平均した周波数分布のこと。話者性や部屋の特性を反映する。これを基準に合わせる。

手順はこうなる。

  1. コーパス全体、あるいは基準となるクリップ群からLTASを求めて基準スペクトルにする
  2. 各クリップのLTASを求め、基準との差分を計算する
  3. その差分を打ち消すEQを各クリップに適用する
  4. 最後にRMSを揃える
def match_ltas(wav, ref_ltas, max_gain_db=10.0):
    """クリップのLTASを基準へ寄せる(±max_gain_db でクリップ)"""
    spec = stft(wav)
    ltas = np.mean(np.abs(spec), axis=1)              # 時間方向に平均

    gain_db = 20 * np.log10((ref_ltas + eps) / (ltas + eps))
    gain_db = np.clip(gain_db, -max_gain_db, max_gain_db)   # 極端な補正を防ぐ
    gain    = 10 ** (gain_db / 20.0)

    spec = spec * gain[:, None]
    return istft(spec)

±10dBでクリップする理由

補正量に上限を置いた。無制限にすると、元のクリップに存在しない帯域を無理に持ち上げることになる。そこには信号がなくノイズしかないので、ノイズだけが増幅される。

特に高域は、生成音声だと8kHz以上がほとんど無いクリップがある。そこを基準に合わせようとすると、サーというノイズが乗る。±10dBに制限すれば、寄せきれなくても破綻はしない。

ゼロ位相で処理する

EQをかけるとき、位相を動かさないようにした。通常のフィルタは群遅延を持つので、帯域ごとに時間がずれる。人間の耳には分かりにくいが、学習素材としては望ましくない。

STFTの振幅だけを操作して位相をそのまま残すか、フィルタを前後両方向にかけて位相の回転を打ち消す(filtfilt相当)。今回は前者を採った。

最後にRMSを揃える

周波数特性を揃えたら、音量も揃える。

def normalize_rms(wav, target_dbfs=-20.0):
    rms = np.sqrt(np.mean(wav ** 2))
    gain = 10 ** (target_dbfs / 20.0) / (rms + 1e-9)
    return np.clip(wav * gain, -1.0, 1.0)

−20dBFSに揃えた。ピークではなくRMSで揃えるのがポイントで、ピーク正規化だと1発の大きな音(絶叫の頭など)に引っ張られて、全体の音量がばらつく。

効果

感情スタイルを切り替えたときの音質の飛びが消えた。これが第一の目的だった。

そして予想外の副作用があった。極端演技の素材が学習可能になった。

正規化前は、絶叫のクリップを学習させると全体が荒れる傾向があった。音圧が飛び抜けていて、スペクトルも他と違うので、モデルがそれを「別の何か」として扱いにくかったのだと思う。

正規化後は、絶叫の時間構造(ピッチが急激に上下する掃引のような動き)が学習に乗るようになった。音圧とスペクトルの違いが取り除かれて、残った「動き」の情報がモデルに届いた、という理解をしている。

つまり正規化は「揃えて情報を捨てる」処理に見えて、捨てるべき情報(チャンネル特性)を捨てることで、残すべき情報(韻律の動き)を際立たせる働きをしていた。

どこまで揃えるか

やりすぎると別の問題が出る。

話者性まで消える危険がある。 LTASには話者の声道特性も含まれるので、強く揃えると声の個性が薄まる。今回は同一話者のコーパス内で揃えているので問題にならなかったが、複数話者を混ぜる場合は注意が要る。

感情の音響的特徴も一部は周波数に出る。 怒りは高域が強く、悲しみは弱い、といった傾向がある。完全に揃えるとこれが消える。±10dBの制限は、この意味でも効いていた。強い補正がかからないので、感情由来の差はある程度残る。

基準をどう決めるかも判断が要る。今回はコーパス全体の平均を使ったが、「一番良い状態のクリップ群」を基準にする選び方もある。平均だと、悪い素材が多いときに基準自体が引きずられる。

実装上の注意

処理は学習の直前に一括でやる。 生成時に1本ずつ正規化すると、基準スペクトルが決まる前に処理することになる。全部揃ってから、まとめて通す。

元のクリップは残す。 正規化はやり直しが効くようにしておく。パラメータ(上限dB、目標RMS)を変えて再学習したくなることがある。

正規化後の音を聴く。 数値上は揃っていても、聴くとノイズが乗っていたり不自然だったりすることがある。特に補正量が上限に張り付いたクリップは要確認だ。

# 補正量が上限に達したクリップを記録しておく
clipped = np.sum(np.abs(gain_db_raw) >= max_gain_db) / len(gain_db_raw)
if clipped > 0.3:
    logger.warning(f"{clip_id}: 補正量の30%以上が上限に張り付いた(元素材が基準から遠い)")

これが多いクリップは、そもそも他と違いすぎる素材なので、揃えるより除外したほうがいいこともある。

まとめ

  • 生成音声は「録音環境」を持たないので、条件が変わればチャンネル特性も変わる。人間の録音では起きない問題
  • 感情ごとに参照を変えると、感情ごとに音質が変わる。切り替え時の音質の飛びとして現れる
  • LTASマッチング+RMS正規化で揃える。補正は±10dB程度に制限し、位相は動かさない
  • 揃えることで、残すべき情報が際立つ。極端演技の時間構造が学習に乗るようになった
  • 揃えすぎない。話者性や感情由来の周波数差まで消える。上限を置くことが品質を守る

シリーズ: 拡散TTSから実用ボイスを量産する

キャプション1行から声を設計し、学習コーパスを製造して、役割別の実用ボイスを量産するまでの記録です。この記事は第2部 製造にあたります。

← 前: 学習後に話速は変えられない
→ 次: クリップ1本の粗さが、スタイル全体を嗄れさせる

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

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


元記事: https://forge.workstyle.tech/blog/tts-changes-recording-room-every-time/?utm_source=qiita&utm_medium=crosspost&utm_campaign=tts-changes-recording-room-every-time

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?