🤗

LLMに音声を聴かせる🎧 → 🧠 →📝 〜 LLM Based ASR〜

に公開

LLMに画像を見せることができるんだから音声も聴かせたいですね。

直近だと日本語の音声モーダルを扱えるLLMであるPhi-4-multimodal-instructのリリースがありました。

https://huggingface.co/microsoft/Phi-4-multimodal-instruct

Phi-4に音声を入力できるモデルで、音声認識(Automatic Speech Recognition: ASR)の性能だとエキスパートモデルであるWhisperを超えています[1]。加えて音声要約とかもできます(日本語だと微妙性能でしたが)

そこで、今回はマルチモーダルLLMを理解するために、音声モーダルを扱う上で最もシンプルだと思われる、SLAM-ASRの手法を利用してLLMにASRをさせる実験をしてみました。

提案手法

SLAM-ASRは”An Embarrassingly Simple Approach for LLM with Strong ASR Capacity”[2]で提案された手法です。
音声をAudio Encoderでベクトル化、ダウンサンプリングして、LLMの潜在空間へ変換するprojectorに通して変換されたAudioの潜在表現をLLMに入力、LLMを通してテキストを出力する構成になっています。


An Embarrassingly Simple Approach for LLM with Strong ASR Capacity, fig1

特にprojectorはシンプルな線形層を使い、projector以外はFreezeしてパラメータを更新しない。というシンプルな構成でASRを行えることを提案したことがポイントになっています。

コードイメージ

inputs_embedsでAudioのEncoder outputとLLMのtoken embeddingをconcatする。

def forward(
    self,
    input_features: torch.FloatTensor,
    input_ids: torch.LongTensor,
) -> CausalLMOutputWithPast:
    text_decoder = self.text_decoder.model
    audio_embed = self.audio_encoder(input_features=input_features)
    prj_out = self.projector(audio_embed.last_hidden_state)
    llm_embed = text_decoder.model.embed_tokens(input_ids)
    concat_embed = torch.cat((prj_out, llm_embed), dim=1)
    outputs = text_decoder.model(inputs_embeds=concat_embed)

実験

データセット

ASRには音声とそれに対応したテキスト化したデータが必要になります。

Librispeech[3]はパブリックドメインのテキストを読み上げた英語音声データセットで、ノイズ多めなサブセットも合わせると960時間くらいになります。英語においてはこのデータセットがよく使われている印象でSLAM-ASRもこれで実験しています。

実際にこのデータセットを使った予備実験で、この程度の音声時間があれば実験には十分という感触が得られました。ということで、どうせなら日本語で実験したいので1k時間を超えるくらいの日本語データセットを探します。

ReasonSpeechコーパス

この規模の音声時間かつ、日本語で気軽に使える(Webでホストされている)データセットの選択肢として、ReasonSpeechコーパス[4]が挙げられます。このデータセットはワンセグ放送の音声とその字幕を集めた大規模音声データセットで全体で35k時間あります。

大規模ではありますが、ワンセグの字幕を使っているために発話とテキストのアライメントがあまり良くないという課題があります。(例えば先頭や後半の発話と字幕が一致してないとか)

比較的小さいサブセットを使う場合には、データの品質がより重要になると思うので、そのまま使うのではなく、コーパスのクリーニングから実施しました。

具体的な方法としては、Whisper-v3-turboを使って、文字起こしを行いその結果の文字誤り率(CER)を測り、一定の閾値を超えるサンプルを破棄しました。0.35以下とすることで、概ね90%ほどが残りました。

他にも音声時間に対してテキストの文字数が多すぎる(人が発話できる文字数は限られている)などのフィルタも利用して、約4k時間の音声を確保ました。

モデル

DecoderとなるLLMには、Qwen2.5-1.5bsarashina2.2-1b/3b(すべてinstructionモデル)を利用しました。特に後者は日本語のスクラッチLLMなのでその性能をQwenと比較検討したいところです。

Audio EncoderにはWhisperのsmallとmediumを利用しました。

実験設定

SLAM-ASRでは、更新するパラメータはprojectorのみにしていますが、今回の実験ではAudio EncoderはFreezeして、LLMはLoRAを使った微調整を行いました。

後の研究結果によるとLoRAでLLMを学習するとprojectorによって変換されたベクトルが、発話に対応するテキストトークンに近くなった(=意味のある潜在表現に近づいた)と報告しています。[5]実際に英語での予備実験でも、LoRAを用いた方が最終的な性能は良かったです。

学習率などのパラメータはすべての実験で以下で固定しています。

warmup_steps: 1000
learning_rate: 1e-4
weight_decay: 0.01
train_batch_size: 4
gradient_accumulation_steps: 4

結果

評価指標には音声認識では一般的なCERを用います。CERは文字の誤り率なので記号の正規化の影響をもろに受けます。

今回は、テキストでおそらく発声がないと思われる記号(括弧とか)を削除してますが、句読点などは保持するようにクリーニングしました。[6]訓練と評価時も同様の正規化を実施します。

また、ReasonSpeechコーパスにはテストセットがないので適当にfoldoutを切ってテストセットにしました。

50kステップで実験

最初にAudio Encoderをwhisper-smallで固定してLLMを変えた実験をします。
設定したバッチサイズだと50kステップで2000時間の音声時間になります。(ちなみに、RTX3090 x1で大体8h-10hくらいで終わります)

Decoder CER
Qwen2.5-1.5B 0.149
sarashina2.2-1b 0.159
sarashina2.2-3b 0.135

sarashina2.2-3bが最も良い性能になりました。
次いでQwen2.5-1.5Bです。sarashina2.2-1bは日本語特化なのでQwen2.5との0.5b差を埋められるかな、と思いましたが残念ながらそう甘くはなかったようです。とはいってもQwen2.5は日本語性能も高いので妥当そうな順番じゃないでしょうか。

学習曲線は以下のようになります。ほぼ必ずこのようなカーブを描くのが面白いです。

AudioからLLMの潜在空間へのアライメントが取れると一気にlossが下がる感じでしょうか。VLMも似たようなアーキテクチャを取ると思うのでそちらの学習曲線も気になりますね。

Audio Encoderのサイズを大きくする

続いて、Audio EncoderをWhisper Mediumにしてみます。LLMはsarashina2.2の1b/3bで比較します。

Encoder Decoder CER
small sarashina2.2-1b 0.159
sarashina2.2-3b 0.135
medium sarashina2.2-1b 0.137
sarashina2.2-3b 0.120

Audio Encoderを大きくするとスコアが大きく改善しました。sarashina2.2-1bで見ると、Whisper SmallとMediumのパラメータサイズ差は525Mなので、LLMのサイズを1b -> 3bで大きくしたときより改善幅が大きい印象ですね。

似た事例として、Whisper-v3-turboは、Whisper-v3の蒸留バージョンですが、エンコーダーはそのままに、デコーダーのパラメータのみを削減してモデルサイズを小さくしてます。
直感的には音声をちゃんと聞き取れていればデコーディングは比較的簡単そうなので、エンコーダーの性能の方が重要なのかなあと思ってます。

サンプル数(ステップ数)を増やす

Whisper Medium、sarashina2.2-1bでステップ数を倍に増やします。音声時間は4k時間ほどになります。

Steps CER
50k 0.137
100k 0.119

これまでで一番性能が良くなりました。やはりデータ数は正義ですね。
4k時間でも全体(35K時間)の11%ほどなので全部で学習すればもっと良くなりそうです。GPU時間が無限にあればもっと実験してみたいですね。

文字起こし結果

最後に文字起こし結果を見てみます。
Whisper-medium/sarashina2.2-1bに関しては最も良かった100kステップのモデルを使ってます。

この寒さでスマホが息していないといった報告が相次ぎます
Whisper-small/Qwen2.5-1.5B この寒さでスマホが意気してないといった報道が相次いでいます。
Whisper-small/sarashina2.2-1b この寒さでスマホが意識してないといった報告が相次いでいます。
Whisper-small/sarashina2.2-3b この寒さでスマホが維持してないといった報告が相次いでいます
Whisper-medium/sarashina2.2-1b この寒さでスマホが息してないといった報告が届いています。

「息してない」に関してはWhisper-medium/sarashina2.2-1bが合ってますが、後半が合ってないですね。Whisper-small/Qwen2.5-1.5Bは音韻的には合ってますが表記が合ってません。

プリンを丸々1個のせ生クリームを添えて
Whisper-small/Qwen2.5-1.5B クリームを丸々入れるのに生クリームを添えます。
Whisper-small/sarashina2.2-1b プリンを丸々1個の線生クリームを添える。
Whisper-small/sarashina2.2-3b プリンをまるまる1個分のせ結構生クリームを添える。
Whisper-medium/sarashina2.2-1b プリンを丸々と1個載せ生クリームを添える

「載せ」は誤用ですがWhisper-medium/sarashina2.2-1bが良さそうですね。

北海道によりますとこの養鶏場では昨日およそ70羽の鶏が死んでいるのが見つかり簡易検査で陽性が確認されましたその後の検査で高病原性鳥インフルエンザウイルスが今朝検出されたということですこれを受け北海道はこの養鶏場の鶏及び
Whisper-small/Qwen2.5-1.5B 北海道によりますとこの養鶏場ではきのうおよそ70羽のニワトリが死んでいるのが見つかり、関与検査で陽性が確認されました。
Whisper-small/sarashina2.2-1b 北海道によりますと、この養鶏場では、きのうおよそ70羽のニワトリが死んでいるのが見つかり、簡易検査で陽性が確認されました。その後の検査で、高病原性鳥インフルエンザウイルスが今朝発見されたということです。
Whisper-small/sarashina2.2-3b 北海道によりますとこの養鶏場ではきのうおよそ70羽のニワトリが死んでいるのが見つかり簡易検査で陽性が確認されました。
Whisper-medium/sarashina2.2-1b 北海道によりますとこの養鶏場では昨日およそ70羽のニワトリが死んでいるのが見つかり簡易検査で陽性が確認されましたその後検査で高病原性鳥インフルエンザウイルスが今朝検出されたということです

どれもいい感じですが、sarashina2.2-1b以外は後半の「その後検査で高病原性鳥インフルエンザウイルスが今朝検出されたということです」を無視しているようです。何起因なのかはっきりしないところです

数個だけですが全体的にみてもWhisper-medium/sarashina2.2-1bが最も良いイメージです。やっぱりデータ数が大事そうな印象を受けました。

コード

そのまま動くかはわかりませんが、LLMに音声を聴かせたい方は参考にしてみてください。
https://github.com/ktrw1011/llm-based-asr-ja/tree/main

おわり

最近はなんでもLLMを使う流れなのと、ASRの教師データである音声とテキストのアライメントが取れたデータを大規模に用意するのは大変なので、こういった感じで別々に学習して後で融合するモデルがこれからもどんどん出てくるんじゃないかなあと勝手に思ってます。

今回はASRに限って実験してみましたが、同様の考え方で、in/outを共に音声にするSLMも可能だったりするようなのでそっちも時間とお金があったら実験してみたいですね。おわり。

脚注
  1. https://huggingface.co/spaces/hf-audio/open_asr_leaderboard ↩︎

  2. https://arxiv.org/abs/2402.08846 ↩︎

  3. https://www.openslr.org/12 ↩︎

  4. https://research.reazon.jp/projects/ReazonSpeech/index.html ↩︎

  5. https://arxiv.org/abs/2411.03866 ↩︎

  6. 数学記号「+」とかは「プラス」で発話している可能性もあるので過度にクリーニングするのは良くない可能性もあります。この辺りは目検で取捨選択しました ↩︎

Discussion