Whisperを使ったリアルタイム音声認識を試みた(Macbook)|Python

3 アクセス

はじめに

これまで、Ollamaを使ったローカルLLMを扱ってきた
ローカルでSpeech to Textが実現できれば、完全にローカルで動く対話システムに大きく近づくと考えた

そこで、本記事ではwhisperによる文字起こしを行うpythonプログラムを紹介する
https://github.com/openai/whisper
このコードはClaude3.5 sonnetに書いてもらったものに手直しを加えたものである

結論、今回はMacbookのCPUでモデルサイズ”small”のwhisperを動かすことができた
それ以上のサイズのモデルについては未検証
また、MacのGPUであるMPSを利用することができないか試したが失敗した

実行環境

  • Macbook Air
  • チップ:Apple M3
  • メモリ:16GB
  • macOS:Sonoma 14.6.1
  • Python 3.12.7

Whisper導入の手順

pyenv+venvを用いた仮想環境で行なっている(必ず仮想環境でなければいけないというわけではなさそうなのでお好みで)

ffmpegのインストールが必要らしいが、もともと入っていたので気にしなかった
もし必要だと言われたらbrew installでインストール可能

ライブラリのインストール

# Whisperのインストール
pip3 install openai-whisper

# 音声入力用ライブラリ
pip3 install pyaudio numpy torch

ソースコード

import whisper
import pyaudio
import numpy as np
import threading
import queue
import time
import torch

class WhisperTranscriber:
    def __init__(self, model_size='base', language='japanese'):
        """
        Whisper音声認識クラス
        """
        self.model = whisper.load_model(model_size)

        for param in self.model.parameters():
            print(f"モデルのデバイス: {param.device}")
            break  # 最初のパラメータだけ確認すれば十分
        
        # 音声入力設定
        self.pyaudio = pyaudio.PyAudio()
        self.sample_rate = 16000
        self.chunk_duration = 3
        self.chunks_per_inference = int(self.sample_rate * self.chunk_duration)
        
        # スレッディング用キュー
        self.audio_queue = queue.Queue()
        self.stop_event = threading.Event()
        
        self.language = language

    def transcribe_audio(self):
        """
        リアルタイム音声文字起こし
        """
        if self.model is None:
            print("モデルの初期化に失敗しました")
            return

        audio_data = []
        while not self.stop_event.is_set():
            try:
                chunk = self.audio_queue.get(timeout=1)
                audio_data.extend(chunk)

                if len(audio_data) >= self.chunks_per_inference:
                    # テンソル変換
                    audio_np = np.array(audio_data[:self.chunks_per_inference])
                    
                    # 推論を実行
                    result = self.model.transcribe(
                        audio_np, 
                        language=self.language,
                        fp16=False
                    )
                    
                    print(f"文字起こし結果: {result['text']}")
                    audio_data = audio_data[self.chunks_per_inference:]

            except queue.Empty:
                continue
            except Exception as e:
                print(f"推論中にエラー: {e}")

    def audio_callback(self, in_data, frame_count, time_info, status):
        """
        音声データ処理用コールバック
        """
        audio_chunk = np.frombuffer(in_data, dtype=np.float32)
        self.audio_queue.put(audio_chunk)
        return (None, pyaudio.paContinue)

    def start_recording(self):
        """
        音声録音開始
        """
        self.stream = self.pyaudio.open(
            format=pyaudio.paFloat32,
            channels=1,
            rate=self.sample_rate,
            input=True,
            frames_per_buffer=self.chunks_per_inference,
            stream_callback=self.audio_callback
        )
        print("音声録音を開始")

    def run(self):
        """
        音声認識プロセス実行
        """
        self.start_recording()
        transcribe_thread = threading.Thread(target=self.transcribe_audio)
        transcribe_thread.start()

        try:
            transcribe_thread.join()
        except KeyboardInterrupt:
            print("\n音声認識を終了")
            self.stop_event.set()
            self.stream.stop_stream()
            self.stream.close()
            self.pyaudio.terminate()

def main():
    transcriber = WhisperTranscriber(
        model_size='base',  
        language='japanese'
    )
    transcriber.run()

if __name__ == "__main__":
    main()

初回実行時はモデルのダウンロードが行われる

実行結果例

色々と文章が表示されるかもしれないが、とりあえず動く

3秒ごとに認識結果を返してくれる

ノイズを拾って”ご視聴ありがとうございました”という結果を返すことがある
暫定的な措置ではあるが、マイクの入力閾値を上げてやればマシになる

モデルについて

公式ドキュメントに書いてある→https://github.com/openai/whisper
表によると、tiny~turboまで種類があるそうだ

今回の環境ではbaseとsmallが動くことを確認できた
推論速度の違いはあまり感じなかった(数値で測ったわけではなく、あくまでも体感)
音声データがもっと長ければ差は大きくなるだろう

精度はもちろんsmallのほうがいい気がする
ただし、smallでもはっきりと発音しないとすぐに間違うので、実用にはかなり微妙(個人の感想です)

MPSを使おうとした

MacのGPU(MPS)を使用することができるか試してみた
というか、はじめにClaudeが書いたコードではMPSを使うように指定した結果、エラーが出て動かなかった

mpsが使用可能かどうかは以下のプログラムで確認可能

import torch
print(torch.mps.is_available())

Trueと表示されたらmpsが使用できるという意味

whisperでmpsを使用するにはwhisper.load_model()メソッドの引数でdevice=”mps”と指定する

実行結果(の一部)

少ししか調べていないが、whisperの問題というよりもpytorchの問題ぽい
macOSやライブラリのバージョン等を調べたり、同様の問題が報告されていないかを調べたりする必要があるのだろうが、今回はここまでで断念

おわりに

今後やってみたいこと

  • small以上のモデルでもこのノートパソコンで動くかどうか
  • 今回のソースコードの中身について解読
  • MPSを使用するための方法の調査
    • 報告されているissueを読む
    • whisperから派生したモデルのほうを試す

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です