音声合成を1から — テキストから声まで
数文字のテキストから、その数万倍の長さの波形をでっちあげる——それが音声合成です。素直な回帰がなぜ破綻するのか(一対多と位相)、なぜテキスト→メルスペクトログラム→波形の二段構えが定番になったのか、そして数秒の参照音声で声色が移る仕組みまでを前提知識ゼロから解説します。
要点
- 音声合成(TTS)の出力は数字の列で、24,000 Hz なら1秒に2万個以上の数値を並べる必要がある。
- 素直な回帰が破綻する理由は、長さの情報がないこと、正解が1つでないこと、位相の3つである。
- 仕事は二段構えに分割され、前半が言語の問題(メルスペクトログラム)、後半が信号の問題(ボコーダ)である。
- メルスペクトログラムは周波数軸を耳に合わせた画像で、滑らかで位相を含まない音の設計図である。
- 音をトークンにすればTTSは言語モデルの問題になり、数秒の参照音声で声色を移せる。
WaveNet: A Generative Model for Raw Audio
一次資料 — この記事の根拠
この解説の公開 2026-08-26
WaveNet: A Generative Model for Raw AudioarXiv:1609.03499論文ページ·PDFNatural TTS Synthesis by Conditioning WaveNet on Mel Spectrogram PredictionsarXiv:1712.05884論文ページ·PDF
FastSpeech 2: Fast and High-Quality End-to-End Text to SpeecharXiv:2006.04558論文ページ·PDF
HiFi-GAN: Generative Adversarial Networks for Efficient and High Fidelity Speech SynthesisarXiv:2010.05646論文ページ·PDF
Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-SpeecharXiv:2106.06103論文ページ·PDF
Neural Codec Language Models are Zero-Shot Text to Speech SynthesizersarXiv:2301.02111論文ページ·PDF
「声を作る」とは何を作ることか
音声合成(TTS: Text-To-Speech)の出力は、突き詰めれば数字の列です。スピーカーの振動板が前後に動く量を、一定間隔で測った値を並べたもの。それだけです。
音声合成でよく使われるサンプリング周波数は 22,050 Hz や 24,000 Hz。つまり1秒しゃべらせるには2万個以上の数値を、正しい順番で、正しい大きさで並べる必要があります。「こんにちは」と1.5秒かけて言わせるなら、入力は5文字、出力は3万6千個の数値です。入力の7千倍の長さのものを、無から作る——これが音声合成という仕事の正体です。
音声認識を1からで扱ったのは、ちょうど逆向きの仕事でした。数万個の数字を数文字に畳む。畳むのは情報を捨てる作業なので、細部が多少ずれても答えは同じ文字に落ちます。膨らませる方はそうはいきません。捨てるものがない代わりに、足りない情報を全部でっちあげる必要がある。
素直な回帰が破綻する3つの理由
1つめは、長さを誰も教えてくれないこと。 文字は「どれくらいの時間をかけて発音するか」の情報を持っていません。「あ」は0.05秒にも1秒にもなります。テキスト側に手がかりがないこの長さを、モデルが自分で決めなければならない。
2つめは、正解が1つではないこと。 「明日は晴れです」の読み方は無数にあります。速さ、間の取り方、どこを高く言うか。ところが訓練データにはそのうちの1通りしか入っていません。ここで「正解の音声との二乗誤差を最小にせよ」と素直に命じると、モデルはありうる全部の読み方の平均を出すのが最も損をしない、と学習します。平均された音声は輪郭がぼやけ、こもった声になります。生成モデル一般で over-smoothing と呼ばれる現象です。
対策は、1つの正解を当てにいくのをやめて、分布を作ってそこから引くことです。そして分布の尖り具合が、そのまま読み上げの表情になります。
3つめは、位相です。 人間の耳は、波の「ずれ」にほとんど鈍感です。ある音を1000分の1秒ぶんずらして鳴らしても、同じ音に聞こえる。ところが数値の列としては、ずらした波形は元の波形とまったく別物です。つまりサンプルごとの二乗誤差は、聞こえ方とほとんど対応しません。耳に同じでも損失は巨大、耳に別物でも損失は小さい、ということが平気で起きる。波形をそのまま学習の的にしてはいけない、いちばん深い理由がこれです。
分割統治: 二段構えという発明
この3つを一度に相手にするのは無理がある、というのが現代のTTSが出した答えです。仕事を2つに割ります。
テキスト ──[音響モデル]──▶ メルスペクトログラム ──[ボコーダ]──▶ 波形
前半は「何を、どんな抑揚で、どれくらいの長さで読むか」という言語の問題。後半は「その音色を実際の空気の震えにする」という信号の問題です。分けるとご利益が2つあります。後半は言語にも話者にも依存しないので、テキストのない大量の音声だけで訓練でき、いろいろな前半と組み合わせて使い回せます。そして前半は、位相という厄介者を扱わずに済みます。
メルスペクトログラム: 音の設計図
前半の出力であるメルスペクトログラムは、音を「縦=周波数、横=時間」の画像に直したものです。
作り方は素朴です。波形を20〜50ミリ秒くらいの窓で切り出し、窓ごとにフーリエ変換して周波数成分の強さを測る。窓を10ミリ秒ほどずつずらしながらこれを繰り返す(短時間フーリエ変換、STFT)。すると時間の刻みごとに「どの高さの音がどれだけ鳴っているか」の縦一列が得られ、並べると画像になります。
ここで周波数の目盛りを、物理ではなく耳に合わせて張り替えます。
は物理的な周波数(Hz)、 が耳にとっての「高さ」の目盛りです。式(1)が言っているのは要するに、低い音域ほど細かい違いが聞き分けられ、高い音域はざっくりでよい、ということだけです。100 Hz と 200 Hz の差ははっきり分かるのに、8,000 Hz と 8,100 Hz はほぼ同じに聞こえる。その耳の性質を、目盛りの側に焼き込んでいます。
この目盛りで周波数軸を80本前後の帯(メルフィルタバンク)に束ね、強さの対数を取ったものがメルスペクトログラムです。24 kHz の波形が毎秒24,000個の数値なのに対し、メル80本を10ミリ秒ごとに並べれば毎秒8,000個。数はそこまで減りませんが、決定的なのは滑らかで、位相を含まないことです。理由3が消え、理由2の被害も小さくなります。
裏を返せば、メルスペクトログラムは音の設計図であって音そのものではありません。捨てた位相を作り直して音に戻す担当が、別途どうしても要ります。
前半: テキストから設計図へ
読みを決める
意外にも、最初の関門は機械学習より前にあります。テキスト正規化——「2026年」を「にせんにじゅうろくねん」に、「3/4」を文脈次第で「よんぶんのさん」か「さんがつよっか」に開く工程です。続いて書記素から音素へ(G2P)。日本語なら「今日」が「きょう」か「こんにち」かを決め、さらにアクセント核(雨と飴、橋と箸を分けている音の高低)まで付ける必要があります。英語なら "read" の現在形と過去形、"live" の形容詞と動詞。この辺りは辞書と規則が今も強い領域で、pyopenjtalk や espeak-ng のような形態素解析・G2P器が使われます。
長さを決める
理由1の「長さ」をどう解くかで、前半は2つの流派に分かれます。
自己回帰型(Tacotron 2 系) は、メルの縦一列(フレーム)を1枚ずつ順番に出します。注意機構で「いまテキストのどこを読んでいるか」を追いながら進むので、長さは明示的に決めず結果として決まる。自然な抑揚が出やすい一方、注意が外れると単語を飛ばす・同じ語を繰り返す・いつまでも終わらないという壊れ方をします。
非自己回帰型(FastSpeech 系) は逆に、各音素が何フレーム続くかを duration predictor に先に予測させ、その回数だけコピーして引き伸ばします(length regulator)。全フレームを並列に出せるので速く、暴走もしません。
import numpy as np
def length_regulate(phoneme_hidden, duration):
# phoneme_hidden: (音素数, 次元) duration: 例 [3, 7, 2, 5, ...]
return np.repeat(phoneme_hidden, duration, axis=0) # (フレーム数, 次元)
正味1行です。「あ」を7フレーム続けたいなら、「あ」のベクトルを7回コピーして並べるだけ。厄介だった長さの問題を、予測1つとコピーに落としたのが FastSpeech の発明でした。FastSpeech 2 はさらに、長さに加えてピッチとエネルギーも別々に予測させます(variance adaptor)。おかげで話速や抑揚を、推論時に外からスケールで触れるようになりました。
後半: ボコーダ — 設計図を空気の震えに
捨てた位相を作り直す担当がボコーダです。ここは世代がはっきり分かれます。
第1世代・信号処理(Griffin-Lim)。位相を適当に置いてSTFTと逆STFTを何十回も往復させ、辻褄の合う位相へ収束させます。学習不要で軽い代わりに、金属質な「ロボ声」感が残ります。
第2世代・自己回帰(WaveNet)。波形サンプルを1個ずつ、過去のサンプルを条件にして予測します。品質は一段跳ね上がりましたが、1秒の音声を作るのに2万回以上の順番待ちが必要でした。リアルタイム合成には致命的で、この一点を潰すために蒸留による並列化やフローベースの手法が続きます。
第3世代・敵対的学習(HiFi-GAN)。生成器はメルを転置畳み込みで一気に波形の長さまで引き伸ばし、全サンプルを並列に出します。順番待ちがない。品質を担保しているのは識別器の設計の方で、HiFi-GAN は波形を周期ごとに折り返して見る multi-period discriminator と、解像度を変えて見る multi-scale discriminator を並べました。声には声帯の振動という強い周期性があるので、周期を明示的に見る目を持たせたのが効いています。この「生成器と識別器で殴り合う」枠組み自体はGANの興亡で扱っています。
その後、拡散モデルを使うボコーダ(DiffWave、WaveGrad)も現れました。反復回数と品質を引き換えにできるのが特徴です。
なお VITS は、二段構えという分割そのものを畳んだモデルです。VAEと正規化フローと敵対的学習を組み合わせ、テキストから波形までを1つのモデルとして訓練します。継ぎ目がなくなるので、後述する「設定不一致」系の事故が原理的に起きません。
現代: 音をトークンにして言語モデルに解かせる
ニューラル音声コーデックは、音を有限個の整数トークンに変える装置でした。音がトークンなら、TTSは「テキストというトークン列を、音というトークン列に翻訳する」問題になり、そのまま言語モデルの土俵に乗ります。
AudioLM や VALL-E がこの路線です。VALL-E はTTSを条件付き言語モデリングと呼び直し、コーデックのトークンを予測させます。残差量子化(RVQ)は「骨格→細部」の階層になっているので、1段目は自己回帰で丁寧に、残りの段は非自己回帰でまとめて、と役割を分けています。
この方式の効き所は2つ。メルの設計や位相復元を人間が設計せずに済むこと。そして言語モデルの道具——サンプリング温度、top-p、プロンプト——がそのまま使えることです。さきほどの温度の図は、ここでそのまま実務のつまみになります。温度が低すぎれば平板で単調な読み上げに、高すぎれば言い間違い・繰り返し・途中で別人になる、といった崩れが出ます。
ゼロショット声色: 数秒で誰かの声になる
かつては話者ごとに数時間の録音とファインチューニングが必要でした。いま数秒の参照音声で足りるようになった仕組みは、大きく2通りです。
(a) 話者埋め込み方式。 話者照合(この2本の音声は同一人物か、を当てるタスク)用に訓練したエンコーダで、参照音声を1本のベクトル(d-vector、x-vector などと呼ばれます)に潰し、合成モデルの条件として差し込みます。話者照合の訓練は「同じ人の声は近く、別人は遠く」を目指すので、出来上がったベクトル空間では向きが声色になります。似ているかどうかはコサイン類似度で測ります。
(b) 音声プロンプト方式。 VALL-E 系のやり方です。参照音声をコーデックトークンに変換し、これから生成するトークン列の先頭に置く。あとはモデルが「この続きを書く」だけで、続きである以上、声色も話し方も引き継がれます。言語モデルのfew-shotとまったく同じ理屈で、専用の話者エンコーダが要りません。
ただし(b)は、録音の癖——部屋の残響、マイクの色、背景雑音——まで一緒に引き継ぎます。参照音声の環境ごと再現できる長所であり、汚い参照を渡すと汚い声が出る短所でもあります。
評価: 何をもって「良い」とするか
3つの軸を混ぜないことが肝心です。
- 自然さ: MOS(複数人に5段階で採点してもらう主観評価)。2案の優劣を直接比べるならCMOS。最終的には人の耳でしか決まりません。
- 明瞭さ: 合成音声を音声認識にかけてWER/CERを測ります。読み間違い・飛ばし・繰り返しはここに数字として出ます。
- 話者類似度: 参照音声と合成音声の話者ベクトルのコサイン類似度。
速度は RTF(real-time factor: 1秒ぶんの音声を作るのにかかった秒数)で見ます。1.0未満ならリアルタイムに間に合う計算です。
数字の扱いには注意が要ります。MOSは評価者の集団・提示順・再生機材で動くので、別の論文や別の実験のMOSと直接比べても意味がありません。同じ実験の中での相対比較だけが根拠になります。
現場ではこう使う
誰が、いつ触るか。 音声アシスタント、読み上げアプリ、ゲームや動画のナレーション自動化、スクリーンリーダーなどのアクセシビリティ機能を作るチームです。多くの現場はモデルを自作せず、ESPnet・Coqui TTS・NVIDIA NeMo のレシピか、クラウドのTTS APIから入ります。自作に降りていくのは、対応していない言語・方言が要るとき、社内音声を外に出せないとき、特定話者の声を長期運用したいときです。
実際に触るパラメータ。
n_fft/hop_length/n_mels/fmin/fmaxとメルの正規化方式 — 音響モデルとボコーダで完全に一致させるdurationのスケール(話速)、pitch/energyのスケール(FastSpeech 2 系の抑揚調整)temperature/top_p(トークン言語モデル系の崩れ具合)- ボコーダの
sampling_rate、出力段のラウドネス正規化
知らないと事故になる落とし穴。
- メル設定の不一致。 音響モデルは hop=256、ボコーダは hop=300 で訓練——合成は最後まで走るのにエラーも出ず、出てくる音だけがキンキンしたり、早口・遅口になったりします。二段構えを自前で組むときの定番の事故です。
- 訓練メルと予測メルのギャップ。 ボコーダはふつう「本物の音声から作ったメル」で訓練されますが、本番で流れ込むのは音響モデルが予測したメルで、こちらは少しぼやけています。この差でボコーダが雑音を吐きます。対策は、音響モデルの予測メルでボコーダをファインチューニングすること。VITSのような一体型が支持される理由でもあります。
- テキスト正規化の穴。 数字・単位・記号・英字混じり・固有名詞。モデルの品質と無関係に、ここで「2026年」が「にーぜろにーろく」になります。ログに入力テキストと音素列の両方を残していないと、モデルの問題かG2Pの問題か切り分けられません。
- 長文で自己回帰モデルが壊れる。 Tacotron系は入力が長いと注意が外れ、繰り返しや無限生成に入ります。文単位に切って合成し、短い無音でつなぐのが実務の定番回避策です。
- 音量とDCオフセット。 ボコーダの出力をそのまま16bit整数に丸めるとクリップして歪みます。ラウドネス正規化を必ず通す。
- 声の権利。 ゼロショット声色は数秒で他人の声を複製できます。本人の同意と用途の合意なしに実在人物の声を作らない。合成音声である旨の開示や電子透かしを求める運用も増えています。技術的にできることと、やってよいことは別だという前提で設計してください。
面接や設計レビューでよく問われる問い。 「なぜ波形を直接回帰しないのか」。答えの筋道は本文の理由3です——位相をずらしただけの、耳には同じ音が、サンプル単位ではまったく別の数値列になる。だからサンプルごとの損失は聞こえ方と対応しない。いったん位相を捨てたメルを経由し、位相の復元だけを専門の生成モデル(ボコーダ)に任せる、という分業になっている。ここまで言えれば、二段構えの必然性を理解していると伝わります。
まとめ
- 音声合成は「短いテキストから、数万倍の長さの数値列をでっちあげる」仕事
- 素直な回帰が効かない理由は、正解が一つでないこと(平均するとこもる)と位相(耳に無関係なのに数値は激変すること)
- 定番の解は二段構え。テキスト→メルスペクトログラム(言語の問題)→波形(信号の問題)
- 長さは duration predictor、波形化はGANや拡散のボコーダ、というのが並列化された現代形
- 音をトークンにすればTTSは言語モデルの問題になり、数秒の参照音声で声色が移る
- 事故の大半はモデルの中ではなく、部品と部品の間の設定の不一致で起きる
コメント
コメントにはログインが必要です