体系 › 音声系
·無料·論文·18分で読めます

音の表現 — メルスペクトログラムとトークン化

音声モデルが波形をそのまま食べない理由から出発し、短時間フーリエ変換 → メル目盛り → 対数 → 離散トークンという変換の連鎖を前提知識ゼロで解説。窓長のトレードオフ、MFCCがDCTを捨てた事情、音響トークンと意味トークンの違い、そして設定を1つ間違えるだけで音が壊れる現場の落とし穴まで。

要点

  • マイクは空気の圧力を一定間隔で測った数列を渡し、16 kHzなら1秒16,000個、10秒で16万個になる。
  • STFTは25ミリ秒ほどの窓を10ミリ秒ずつずらしてフーリエ変換し、時間ごとの周波数成分の表を作る。
  • 窓を長くすると周波数の分解能が上がり時間の分解能が落ちる。この関係は原理的な制約である。
  • メルフィルタバンクは80個ほどの三角窓で513本の周波数ビンを束ね、対数を取ってログメルスペクトログラムにする。
  • 音響トークンは波形の復元を目指し、意味トークンはHuBERTなどの中間層をk-meansで丸めて音韻に近いものを残す。
対象textタスクspeech

Robust Speech Recognition via Large-Scale Weak Supervision

一次資料 — この記事の根拠

論文の発表 2022-12-06→この解説の公開 2026-08-273年9か月後

HuBERT: Self-Supervised Speech Representation Learning by Masked Prediction of Hidden UnitsarXiv:2106.07447論文ページ·PDF
Robust Speech Recognition via Large-Scale Weak SupervisionAlec Radford, Jong Wook Kim, Tao Xu ほか · 2022-12-06 · v1arXiv:2212.04356論文ページ·PDF
原文の要旨(Abstract)を読む

We study the capabilities of speech processing systems trained simply to predict large amounts of transcripts of audio on the internet. When scaled to 680,000 hours of multilingual and multitask supervision, the resulting models generalize well to standard benchmarks and are often competitive with prior fully supervised results but in a zero-shot transfer setting without the need for any fine-tuning. When compared to humans, the models approach their accuracy and robustness. We are releasing models and inference code to serve as a foundation for further work on robust speech processing.


マイクが渡してくるのは1本の数列

マイクがやっていることは単純です。空気の圧力の上下を一定間隔で測り、数字として書き留める。それだけです。音声認識でよく使う16 kHzなら1秒あたり16,000回測るので、10秒しゃべった音声は16万個の数字が一列に並んだ帯になります。

この帯の中に、あなたの声も、隣の部屋の物音も、部屋の壁の反射も、すべてが1本の波として溶け合っています。人間の耳はここから言葉を取り出せますが、機械にこの16万個をそのまま渡すのは、本を「紙の上のインクの濃さの数値」として読ませるようなものです。読めないわけではないけれど、あまりに遠回りです。

だから音声を扱うシステムはほぼ例外なく、波形をいったん別の表現に変換してから本題に入ります。この記事は、その変換の連鎖 — フーリエ変換 → メル目盛り → 対数 → 離散トークン — を一本の道として辿ります。どれか1つを知っていても、なぜその順に並んでいるかが分かると景色が変わります。

比喩: 楽譜という圧縮

オーケストラの演奏を紙に残すことを考えます。空気の振動そのものを書き写すのは不可能ですが、私たちには楽譜があります。楽譜が残しているのは「いつ・どの高さの音が・どれくらいの強さで鳴るか」だけで、奏者の音色の細部も、ホールの反響も、波の位相も書かれていません。それでも別の楽団が読めば、同じ曲が鳴ります。

スペクトログラムは、機械のための楽譜です。音を「縦=高さ、横=時間」の表に直したもので、楽譜と同じく位相を捨てています。

メルスペクトログラムは、その五線譜の間隔を人間の耳の感度に合わせて引き直した楽譜です。低い音域は細かく、高い音域はざっくりと書く。

そしてトークン化は、楽譜の音符を「有限個の決まった記号」に丸める操作です。手書きの音符を活字にするようなもので、こうすると音が文字列になり、言語モデルという既存の強力な機械にそのまま食べさせられます。

なぜ波形をそのまま食べないのか

理由は3つあります。

1つ目は、冗長すぎること。 16 kHzで30秒の音声は48万点です。Transformerの自己注意は系列長の2乗の計算量なので、この長さは正面から扱う気になれません。

2つ目は、耳に同じ音が、数列としてはまったく違うこと。 同じ「あ」を1ミリ秒だけずらして録音すると、数字の並びは総取り替えになります。振幅を少し変えても、位相が反転しても、人間には同じ音に聞こえるのに数列は別物です。波形を直接扱うネットワークは、この「どう変わっても同じ音」という不変性を、まずゼロから学ばなければなりません。

3つ目は、意味に効く量が時間領域では見えないこと。 「あ」と「い」を区別しているのは、声道の共鳴によってできる周波数の山(フォルマント)の位置です。ところが波形をいくら睨んでも、その山は見えません。周波数に直した瞬間に、初めて目に見える特徴になります。

周波数の表現に移すと、3つとも同時に軽くなります。系列は10ミリ秒刻みまで短くなり、位相を捨てることで不変性が最初から手に入り、フォルマントは画像の縞模様として現れます。

仕組み①: 短時間フーリエ変換(STFT)

「混ざった波を、高さごとの成分にほどく」道具がフーリエ変換です(この変換自体の仕組みと、なぜ高速に計算できるのかはFFTを1から理解するで扱っています)。

ただし10秒の音声にフーリエ変換を1回かけると、10秒ぶんの周波数成分が1枚のグラフに潰れてしまい、「その音がいつ鳴ったか」が完全に失われます。曲全体の楽譜が、和音1つに要約されてしまうようなものです。

そこで、音を短く切ってから変換します。25ミリ秒くらいの窓で切り出し、その窓の中だけフーリエ変換する。窓を10ミリ秒ずつずらしながら繰り返す。これが短時間フーリエ変換(STFT)です。

X[t,k]=∑n=0N−1w[n] x[n+tH] e−i2πkn/NX[t, k] = \sum_{n=0}^{N-1} w[n]\, x[n + tH]\, e^{-i 2\pi k n / N}
(1)

記号の意味を1つずつ。xx が波形の数列、NN が窓の長さ(サンプル数)、HH が窓をずらす幅(ホップ長)、ww が窓関数、tt が何番目の切り出しか、kk が周波数の番号です。式(1)が言っているのは、「tt 番目に切り出した短い音の中に、kk 番の高さの波がどれだけ含まれているか」を、すべての tt と kk について並べた表を作る、それだけです。

この XX は複素数で、大きさ(成分の強さ)と角度(位相)を持っています。ここから位相を捨て、大きさの2乗 ∣X∣2|X|^2(パワー)だけを残したものがパワースペクトログラムです。

最後の e−i2πkn/Ne^{-i 2\pi k n / N} は、要するに「周波数 kk の正弦波」を表しています。それを信号に掛けて全部足す、というのは内積そのものです。信号がその正弦波と同じ向きに揃っているほど、和が大きくなる。周波数分析の正体は、無数の正弦波との内積を一斉に取る作業です。

FIG 1周波数の分析は、突き詰めれば内積1回。信号を1本のベクトル、「ある高さの正弦波」をもう1本のベクトルと見て、向きが揃っているほどその高さの成分が強いと判定される。角度が90度に近づくと値が0に沈む様子まで、その場で確かめられます

窓の長さが、見える世界を決めてしまう

STFTで最初に決めるのは窓の長さです。そしてこれは、後から取り返せないトレードオフを含んでいます。

窓を長くすると、その中に入る波の周期が増えるので周波数の分解能が上がり、近い高さの音を細かく見分けられます。代わりに、その長さの中で起きた変化はすべて1枚に潰れるので、時間の分解能が落ちます。破裂音の立ち上がりのような一瞬の出来事がぼやけます。窓を短くすると、まったく逆のことが起きます。

Δt⋅Δf\Delta t \cdot \Delta f がおおよそ一定、という関係で、片方を良くすればもう片方が悪くなる。これは実装の都合ではなく、時間と周波数という2つの見方の間にある原理的な制約です。

音声で25ミリ秒窓・10ミリ秒ホップが定番なのは、「この程度の時間なら口や舌の形はほとんど変わらない」という経験則に合うからです。音楽の解析ではもっと長い窓(2048点など)が好まれます。和音の構成音を見分けたいので、周波数側に分解能を寄せるわけです。

窓関数(Hann窓など)を掛ける理由も押さえておきます。信号を四角く切り取ると、切り口で値が急に0に落ちます。この段差は、フーリエ変換の目には「あらゆる高さの成分が薄く混じっている」と映ります。実際には鳴っていない周波数に、偽の成分がばらまかれる。これをスペクトル漏れと呼びます。両端を滑らかに絞る窓関数は、その段差をなくすための細工です。

仕組み②: メル — 目盛りを耳の物差しに張り替える

STFTが返す周波数の目盛りは、物理的に等間隔です。0 Hzから8,000 Hzまで、たとえば513本の目盛りが均等に並びます。ところが、この等間隔は耳と合っていません。

100 Hzと200 Hzの差は、はっきり別の高さに聞こえます(1オクターブ違います)。一方で8,000 Hzと8,100 Hzは、ほとんど同じ音に聞こえます。物理的な差は同じ100 Hzなのに、耳にとっての意味がまるで違う。

そこで、目盛りのほうを耳に合わせて張り替えます。

m=2595log⁡10 ⁣(1+f700)m = 2595 \log_{10}\!\left(1 + \frac{f}{700}\right)
(2)

ff が物理的な周波数(Hz)、mm が耳にとっての高さ(メル)です。式(2)は結局のところ、低い音域は引き伸ばし、高い音域は圧縮すると言っているだけです。対数が入っているので、周波数が2倍になるごとにメルの増分がだいたい一定になります。

この新しい目盛りの上に、等間隔で三角形の窓を80個ほど並べます。これがメルフィルタバンクです。各三角形の下にあるパワーを重み付きで足し合わせると、513本の周波数ビンが80本に束ねられます。低域では細い三角形が密に並び、高域では太い三角形がまばらに並ぶので、結果として低域が細かく、高域が粗く記録されます。

行列で書けば、(80×513)(80 \times 513) の疎な行列を掛けるだけです。この行列は学習しません。人間の聴覚の性質を測った結果を、そのまま定数として焼き込んだものです。

同じ「耳の物差し」の考え方は音声圧縮でも使われますが、目的が正反対なのが面白いところです。MP3は臨界帯域を「どの成分なら捨てても気づかれないか」を決めるために使い、メルスペクトログラムは「どの成分を残せば十分か」を決めるために使います。捨てる側から見るか残す側から見るかの違いで、下敷きにしている耳の測定結果は同じものです。

仕組み③: 対数 — 桁を畳む

メルフィルタを通した値は、まだ扱いにくい状態です。パワーの値は、静かな部分と大きな音の部分で桁が10桁近く違うことがあります。このまま損失関数に入れると、大音量のフレームだけが学習を支配し、小さな音の情報が無視されます。

人間の音量の感じ方も、そもそも対数的です。パワーを10倍にしても「10倍うるさい」とは感じません。だから対数を取ります。

logmel = np.log(mel + 1e-6)   # あるいは np.log(np.maximum(mel, 1e-10))

この 1e-6 のような小さな下駄が地味に重要です。完全な無音区間ではパワーが0になり、対数が −∞-\infty に発散するからです。下駄の値は「無音をどれだけ低い値として表現するか」を決めており、ここを変えると特徴量全体のダイナミックレンジが変わります。

ここまでで出来上がるのがログメルスペクトログラムです。現代の音声認識・音声合成の入り口は、ほぼこれで統一されています。Whisperの場合、16 kHzの音声を25ミリ秒窓・10ミリ秒ホップでSTFTし、80本のログメルにして食わせます(large-v3ではメルの本数が128に増えました)。数字は違っても、この記事で辿った工程そのままです。

MFCC: かつての標準と、いまも残る場所

ログメルにもう1段、離散コサイン変換(DCT)をかけて、先頭の13本くらいを取り出したものが MFCC(メル周波数ケプストラム係数)です。長らく音声認識の標準特徴量でした。

なぜDCTをかけたのか。当時の主流だったGMM-HMMという手法は、特徴量の各次元が互いに独立であってほしいという事情を抱えていました。ところがメルフィルタの出力は隣同士が強く相関します(隣り合う三角形は重なっているので当然です)。DCTはこの相関をほどき、少ない次元に情報を集める役目を果たしていました。

深層学習の時代になって、DCTは外されました。ニューラルネットは次元間の相関を自分で扱えますし、13本に切り詰めるのは単なる情報の損失です。いまTTSや音声認識のモデルに渡すのは、ほぼログメルそのものです。

ただしMFCCは死んでいません。計算が軽く次元が小さいので、常時動くキーワード検出や、話者の識別、音声区間の検出(VAD)といった軽量な用途では現役です。そしてHuBERTの学習では、1回目のクラスタリングの入力として使われました。次に見る「意味トークン」の出発点が、実は古い特徴量だったわけです。

仕組み④: 連続から離散へ — 音のトークン化

ログメルは連続値の行列です。ここからもう一歩、有限個の記号の列に変えるのが音のトークン化です。

動機ははっきりしています。言語モデルは「有限個の記号が並んだ列」を扱う装置です。音を記号にできれば、テキストのために作られた機械とインフラをそのまま流用できます。音声合成が「トークンを予測する問題」に化けるのは、この変換のおかげです。

音のトークンには、目的の違う2つの系統があります。

音響トークン(acoustic tokens) は、「元の波形に戻せること」を目標に学習した符号です。エンコーダが波形を短いベクトル列に畳み、各ベクトルをコードブックの中の最も近い符号に丸め、デコーダが符号列から波形を組み立て直す。1段の丸めでは粗すぎるので、残差を何段も重ねて量子化する残差量子化(RVQ)が使われます。EnCodecやSoundStreamがこの系統で、詳しくはニューラル音声コーデックで扱っています。声色も環境音も残響も全部入る代わりに、1フレームあたり複数の符号が必要になります。

意味トークン(semantic tokens) は、自己教師あり学習したモデル(HuBERTなど)の中間層の出力を、k-meansで数百〜数千個の重心に丸めたものです。こちらは音を戻すことを目標にしていないので、話者の声色や録音環境の情報はかなり落ち、音韻に近いものが残ります。1フレーム1個で済むので系列が短く、長い文脈を扱いやすい。代わりに、これだけでは音に戻せません。

AudioLMのような音声生成の枠組みが両方を使うのは、この非対称性のためです。意味トークンで「何を言うか」を長い時間スケールで決め、音響トークンで「どう聞こえるか」を埋める。役割分担がそのままトークンの種類に対応しています。

どちらの系統でも、離散化の中身は同じ操作です。手元のベクトルに一番近いものを、決まった辞書の中から探す。ただそれだけです。

FIG 2量子化の核心は最近傍探索です。クエリを動かすと選ばれるベクトルの顔ぶれが入れ替わり、尺度をL2・コサイン・内積で切り替えると結果も変わる。コードブックから符号を1つ選ぶ操作は、これの「上位1件だけ取る」版だと思ってください

コードで書く: 波形からトークンまで

まず波形をログメルにするところ。実務ではライブラリの1行で済みますが、引数がそのままこれまでの説明と対応しています。

import torch, torchaudio

wav, sr = torchaudio.load("hello.wav")                    # (1, サンプル数)
wav = torchaudio.functional.resample(wav, sr, 16_000)     # 16 kHz に統一

melspec = torchaudio.transforms.MelSpectrogram(
    sample_rate=16_000,
    n_fft=400,          # 25 ms の窓(16000 × 0.025)
    hop_length=160,     # 10 ms ずつずらす
    n_mels=80,          # メルの帯を80本
    f_min=0.0, f_max=8000.0,   # 上限はナイキスト周波数
    power=2.0,          # 振幅ではなくパワー
)
mel = melspec(wav)                    # (1, 80, フレーム数)
logmel = torch.log(mel + 1e-6)        # 桁を畳む

次に、そのフレームをコードブックの符号に丸めるところ。量子化と呼ばれると身構えますが、中身は距離の最小値を取るだけです。

def quantize(frames, codebook):        # frames: (T, D), codebook: (K, D)
    dist = torch.cdist(frames, codebook)   # (T, K) 全フレーム×全符号の距離
    idx = dist.argmin(dim=1)               # (T,) 各フレームが選んだ符号番号
    return idx, codebook[idx]              # トークン列と、その復元ベクトル

idx が音のトークン列です。ここまで来れば、あとはテキストのトークン列と同じ扱いができます。

逆向き: 表現から音へ戻す

行きの道で位相を捨てたので、帰り道では位相を作り直さなければなりません。ここが音の表現のいちばん厄介な部分です。

古典的な手段がGriffin-Limです。位相を適当に置いてSTFTと逆STFTを往復させ、辻褄が合う位相へ少しずつ寄せていく反復法。動きますが、独特の金属的な響きが残ります。

いま主流なのはニューラルボコーダです。HiFi-GANのようなモデルが、ログメルから波形を直接生成します。位相を推定するのではなく、正しく聞こえる波形を作るように学習してしまう、という割り切りです。トークンから始める場合は、ニューラルコーデックのデコーダがそのまま復元役になります。

ここから、実務でいちばん事故になる帰結が出てきます。特徴量を作る側と、音に戻す側の設定は、1つでも食い違ってはいけない。ボコーダは「自分が訓練で見たログメル」しか正しく解釈できません。メルの本数もホップ長も対数の下駄も、すべてが暗黙の契約です。

現場ではこう使う

この変換に触るのは、音声認識やTTSを扱うMLエンジニア、アプリに音声機能を載せる開発者、そして音声データの前処理パイプラインを組むデータエンジニアです。モデルを1行も書かなくても、データを整える側に回った瞬間にここが仕事になります。

実際に触るパラメータとツール。 sample_rate / n_fft / win_length / hop_length / n_mels / f_min・f_max / power / center / norm / mel_scale。実装は librosa.feature.melspectrogram か torchaudio.transforms.MelSpectrogram が二大勢力で、その手前のフォーマット統一には ffmpeg -ar 16000 -ac 1 や sox を使います。

知らないと事故になる落とし穴を6つ。

  1. サンプリングレートの不一致。 16 kHz前提のモデルに44.1 kHzの音声を渡しても、多くの実装はエラーを出しません。ただ全部の周波数がずれた特徴量が静かに流れ込み、精度がじわっと落ちるだけです。前処理の先頭で必ずリサンプルとモノラル化をやる、を機械的な規則にしてください。

  2. メルフィルタの流儀違い。 librosaの既定はSlaney流(htk=False、フィルタの面積で正規化)で、HTK流とは出てくる値が変わります。学習側とボコーダ側でライブラリが違うと、ここが黙ってずれます。聴いて分かるほど音が変わるのに、コードのどこにもバグがない、という探しにくい不具合になります。

  3. 対数の床の違い。 log(x + 1e-6) と log(max(x, 1e-10)) では、無音区間の値がまったく別物になります。特徴量の平均・分散正規化まで含めて、統計はデータセットとレシピごとに取り直すこと。他所の統計値を持ち込まない。

  4. center=True の暗黙パディング。 既定では信号の前後にパディングが入るので、フレーム数が サンプル数 // hop_length + 1 になります。CTCのアラインメントやラベルの時刻対応でフレーム数を計算している箇所と、1フレームずれます。境界の1フレームなので、短い音声ほど相対的な被害が大きくなります。

  5. パワーか振幅か。 power=2.0 と power=1.0 は、対数を取ると定数倍の差になります。正規化で吸収されるように見えますが、床の位置との相対関係が変わるので、無音付近の表現が別物になります。

  6. トークン化器の使い回し。 k-meansの重心もRVQのコードブックも、訓練したコーパスの音響条件に張り付いています。電話帯域(8 kHz、帯域制限あり)で録った音を、16 kHzのスタジオ音声で作ったコードブックに通すと、符号が一部に集中して表現が潰れます。コードブックの使用率を数えるのは、この崩れを見つける安価な検査です。

面接や設計レビューで問われる形。 「窓を長くすると何が良くなって何が悪くなるか」「なぜ位相を捨てても音声認識が成立するのか」「MFCCとログメルはどう使い分けるか」。どれも答えの筋道はこの記事の中にあります。1つ目は時間と周波数のトレードオフ、2つ目は音韻の情報が振幅スペクトルの形にほぼ乗っているから、3つ目は「深層学習ならログメル、軽量な古典手法ならMFCC」です。

まとめ

この表現の上に音声認識がどう建つのかは、音声認識を1からへ続きます。

参考文献

  1. HuBERT: Self-Supervised Speech Representation Learning by Masked Prediction of Hidden Units. arXiv:2106.07447論文ページ·PDF
  2. Alec Radford, Jong Wook Kim, Tao Xu, Greg Brockman et al.. (2022-12-06) Robust Speech Recognition via Large-Scale Weak Supervision. arXiv:2212.04356論文ページ·PDF

本記事は上記論文の本文にもとづいて執筆しています。数値・主張は原典を優先してください。

運営: TechAthletes · 編集方針 · 出典の付け方 · 訂正の記録

コメント

コメントにはログインが必要です