ここ10年の音声認識のアーキテクチャの変化を雑に整理する（前編）

<img width="1280" alt="asr.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66417/91889900-03be-0e75-0088-6e4bd6e0b251.png">

この記事は [bosyu Advent Calendar 2019](https://qiita.com/advent-calendar/2019/bosyu) の2日目の記事。会社と一切関係ないけど音声認識の話がしたくなった。書き始めたらボリュームがありすぎることが分かったので、前中後編に分ける予定。

 * [前編](https://qiita.com/corocn/items/81c255e5f742f767144f)（本記事）
 * [中編](https://qiita.com/corocn/items/7eb846ff83c0f67bca53)
 * 後編（執筆中）

## はじめに

機械学習の盛り上がりと共に、音声認識技術に興味を持つ人も増えていると思われる。私もかなり昔に音声認識を触っていたが、ここ10年ぐらいはWeb技術ばかり触っており、特にDNNを用いたモダンな機械学習に対して浦島太郎状態。

最近また音声認識器でも書いてみたいな〜と思ったが、まずは10年前から現在のトレンドの差分を埋めたいなと思い、ここに昔の知識 + 調べた結果を残しておく。これから音声認識やる人も、歴史を知っておくのは良いことだと思いたい。

いきなり現在に飛ぶと死んでしまうので、歴史を追いかけながら整理していこう。
前編では **DNNが流行りだす前の音声認識** について。

## そもそも音声認識って

**話し言葉を書き起こすこと**

"音声認識" と聞くと、多くの人は Siri や Google Assistant のような、自然言語の書き起こしができるものを想像してしまう。しかし「○○ して ください」のように、文章ではあるが、決まった文脈しか認識できないものや、単語しか認識できないものも立派な音声認識である。もちろん後者のほうが性能は低いし、それはそのまま音声認識の歴史となる。

似たような技術で話者認識・話者識別があるが、そっちは喋っている"内容"ではなく"誰"にフォーカスしたものとなるので、若干アプローチが変わる。

音声認識は **Speech Recognition** と呼ばれることが多いが、文献を見ていくと **ASR** : Automatic Speech Recognition、STT : Speech-to-Textなど様々な呼ばれ方をしている。特に数万語以上の語彙を扱う文章の認識になると **LVCSR** : Large Vocabulary Continous Speech Recognitionという呼ばれ方もする。

STTを逆にすると、TTS : Text-to-Speechとなる。これは音声合成のこと。

## 音声認識の階層

複数の階層から成り立ち、処理順的には **音声 > （前処理） > 特徴量 > 音素 > 単語 > 文章** となる。ちなみに変換ではなく、もっともらしい文章の推定である。

ちなみに、この推定を行うのが **音声認識エンジン = 音声認識デコーダー**と呼ばれるソフトウェアである。

このエンジンは大きく3つのモデルを使う。

 * 音響モデル（特徴量 -> 音素）
 * 単語辞書（音素 -> 単語）
 * 言語モデル（単語 -> 文章）

上記のパイプラインを見てしまうと、各ステップの処理が全て終わり次第、次のステップを駆動させるように見えるが、実際は1フレーム（だいたい25msec）の入力ごとに、音響モデルから言語モデルが全部関係して仮説の探索を進めるのでエンジンの中身はカオスってる場合が多い。

### 音声 から 前処理

発話した音声はそのまま使えるわけではない。ノイズがあったり、そもそも何も喋ってなかったりする。認識率を上げるためには前処理が必要。

 * 音声区間検出 （VAD : Voice Activity Detection）
 * ノイズ除去 （Noise Reduction）
 
あたりがキーワード。雑音を除去したり、音声の中から話者が喋っている区間を切り抜いたりする。雑なVADだと、ある一定の振幅を見て切るとか。

### 前処理 から 特徴量

前処理でキレイキレイした音声だが、そのまま認識エンジンに突っ込めるわけではない。
特徴量と呼ばれる、声を特徴づけるデータに変換・圧縮してから使用する。

音声の波形は、複数の波形を重ね合わせたもので形成され、その色々な波の中には「あ」「い」「う」のような音素の情報もあれば、声が高かったり低かったり個々人を特定できるような情報も含まれている。

 * 声道 = 声を特徴づける = 音素が決まる
 * 声帯 = 声の高さを決める = 声の高さが決まる

音声認識が必要としているのは前者の **声道**。話者ごとの特徴は（万人の言葉を認識したいのであれば）むしろ排除した状態で使いたい。そこで、MFCCと呼ばれる特徴量がよく使われている。

 * ケプストラム
 * メル周波数ケプストラム係数（**MFCC**: Mel Frequency Cepstrum Coefficient）

ケプストラムを使うこともできるが、MFCCはもっと人間の聴覚特性を反映した特徴量になっていて、界隈でめっちゃ使われている。MFCCに変換する過程で、みんな大好きフーリエ変換を使う。もうだいぶ忘れた。

### 特徴量 から 音素

まず音素って何？から。
音素は音の最小単位で、例えば "ありがとう" を分割すると /a/ /r/ /i/ /g/ /a/ /t/ /o:/ となる。

この1個1個の音素を、特徴量と紐付けてモデル化していく。
/a/という音素単体でモデル化することもあれば、前後の音素を考慮してモデル化することもある。

例えば、"ありがとう" には2つの /a/ が登場するが、先頭と途中の/a/を明確に区別してモデル化するということである。こちらのほうが認識精度が上がるが、モデル数も増えるし、学習データも必要になる。前者はmono-phone、後者は（前後の文脈を考慮する場合）tri-phoneと呼ばれてる。

これらの音素と特徴量の関係を統計的にモデル化したものを**音響モデル（AM: Acourstic Model）**と呼ぶ。音響モデルには**隠れマルコフモデル（HMM: Hidden Markov Model）** がよく使われている。

隠れマルコフモデルはマルコフ過程に基づくモデルで、音声のような時系列データのモデル化が得意。

 * マルコフ過程 = 未来の状態は現在の状態で決まる
 * 隠れ = 状態遷移を観測することができない

の合せ技。「何が隠れているんですか？」という質問で有識者からマウントされることがあるので注意されたい。

![hmm.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66417/d21dbd30-cdd0-5f6a-1874-2e0d0075af67.jpeg)

よくみかける3状態のHMMの例。各音素ごとにHMMが作られる。遷移部分と状態部分に確率を持っていて、
状態（出力確率）の部分は **ガウス混合モデル（GMM: Gaussian Mixture Model)** が使われることが多い。

1つの状態をぐーるぐるできるので、「そーですね」「そーーですね」「そーーーーーですね」のように、同じ音素なんだけど発話区間が短かったり長かったりを吸収しやすくなる。そーですねの例は極端だけども、25msecでフレームを区切ってるので結構話者がごとにブレる。


### 音素 から 単語

音素がモデル化できたら次は単語。音素を組み合わせると単語になる。
単語は音響モデルというより、その先の言語モデルに含まれて語られることが多いかも。

ありがとう
a r i g a t o:

今日は雨です
ky o: h a a m e d e s u

という感じで単語と音素列のデータがずらーっと並ぶ。まさに辞書である。

![phone-word.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66417/d9664495-fa34-60aa-fba4-74d1d82c1401.jpeg)

HMMをつないで表すとこういうイメージ。

### 単語 から 文章

単語を組み合わせると文章になる。

例えば話者が「きょうはあめです」と発話したとする。
人間の脳であれば「今日」「は」「雨」「です」と瞬時に判断できるが、計算機では

 * 今日は雨です
 * 今日は飴です
 * 今日は亀です
 * 京は雨です
 * 京都は雨です
 * （以下略）

のような、たくさんの仮説から絞り込むことができない。

似たような音もあるし、特に「雨」「飴」のように同音異義語の場合は、音の情報だけでは判定のしようがない。そこで、単語の前後のつながりを学習した**言語モデル（LM: Language Model）** を使用することで**文章としてもっともらしい組み合わせを推定する**。

言語モデルは昔は単純なルールベースのものもあれば、統計的なアプローチのものがある。

#### ルールベースの場合
 * <場所> は <天気> です
 * <日時> は <天気> です

のようなルールを作っておけば、東京は晴れです / 名古屋は雨です / 大阪は大雨です のような文章や、今日は晴れです / 明日は雨です のような文章を認識する。

一昔のカーナビなんかは、かなり限られた文脈や単語しか認識できなかったけど、ユースケースが限られれば場合によってはそっちのほうが精度も出るし、使いやすい場合もある。しかし、だいたいユーザーは開発が想定した通りに発話してくれない。世の中あまくない。

#### 統計的手法の場合
N-gramが使われることが多い。単語の共起関係をモデル化したものである。自然言語処理かじってる人は知ってるはず。

 * 2-gram / bi-gram
    * 今日-は
    * は-天気
    * 天気-です
 * 3-gram / tri-gram
    * 今日-は-天気
    * は-天気-です

のような感じで分割する。この共起確率を使って、「今日」って単語が出てきたら次は「は」の可能性が高いじゃろ〜って推定したい。このモデルは大量の言語データセット（コーパス）から学習して生成する。

認識結果は、このN-gram生成元のコーパスの質に大きく依存する。
ユースケースに対して、どんなコーパス・言語モデルを使用するかをちゃんと考えなきゃいけない。例えば、話し言葉の認識なのに、新聞のお固めな文章セットから言語モデルを作っても認識精度は落ちるので注意。

## ここまでのまとめ

ここまで説明したものをまとめると、こんな感じ。

![Speech Recognition.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66417/68e83929-e678-7f27-c75e-19cd11bdc557.jpeg)

いわゆる「統計的手法」を用いた音声認識である。DNNが流行るまでの音声認識の大枠がこれ。
デジタル信号から自然言語処理まで扱うので面白い。

各種モデルはエンジンの中でいい感じに扱って頑張っている。マジカオス。
次回は、まさにその音声認識エンジンの役割というか仮説探索みたいな話を書く予定。

反響がありそうならもっと頑張る！お楽しみに！