やりたいこと

Macでローカルで文字起こしがしたい

M4Macを手に入れて、Whisperでローカルで文字起こしが出来るようになるかなと思ってやってみたが、PythonのWhisperライブラリではいまいち速度が出ない。 WhisperライブラリはPyTorchベースのため、MacのMetal GPUが活用できないことが原因らしい。

whisper.cppならいけた

whisper.cpp とは? OpenAIが開発した音声認識モデル「Whisper」を、軽量・高速に動作させるためにC/C++で再実装したオープンソースの音声文字起こしエンジン

項目 内容
開発者 Georgi Gerganov 氏(GitHub)
元になった技術 OpenAI Whisper
特徴 C/C++で書かれており、ローカルで軽量・高速に動作
対応プラットフォーム macOS(Apple Silicon対応)、Linux、Windows
利用用途 音声→文字のリアルタイム変換(例:議事録、字幕生成、音声コマンド)

手順(備忘録)

whisper.cpp をクローン

git clone https://github.com/ggml-org/whisper.cpp.git
#作成されたディレクトリに移動
cd whisper.cpp

サブモジュール(外部ライブラリ依存)を取得・初期化

git submodule update --init --recursive

プロジェクトのビルド設定を生成

cmake -DGGML_METAL=ON .

GGML_METAL=ON により、Apple Silicon(M1/M2/M3)のMetal GPU を使う高速化オプションが有効に。

cmake で準備されたMakefileに従ってビルド(コンパイル)

make

Whisperの音声認識モデル large-v3(最も高精度) をダウンロード

bash ./models/download-ggml-model.sh large-v3

もとのディレクトリに戻る

cd ..

これでコマンドからwhisper.cppが使えるように。subprocessモジュールを使ってPythonから実行。

import subprocess

def transcribe(audio_file):
    result = subprocess.run(
        [
            "./whisper.cpp/main",#実行ファイルのパス
            "-m", "whisper.cpp/models/ggml-large-v3.bin",
            "-f", audio_file,
            "-l", "ja",#日本語指定
            "--no-timestamps"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )
    return result.stdout

print(transcribe("example.wav"))

whisperAPIと遜色無い精度と速度で文字起こし出来るようになりました!! whisperAPIいいお値段だったのでありがたい!!

以下、streamlitでアプリケーション化。

import streamlit as st
import subprocess
import tempfile
import os

def transcribe_audio(file_path):
    """whisper-cli を使って音声ファイルを文字起こしする関数"""
    result = subprocess.run(
        [
            "whisper.cpp/bin/whisper-cli",
            "-m", "whisper.cpp/models/ggml-large-v3.bin",
            "-f", file_path,
            "-l", "ja",
            "--no-timestamps"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )
    return result.stdout

def main():
    st.title("Whisper 文字起こしアプリ (ローカル + Metal GPU)")
    st.write("音声ファイル (.wav) をアップロードして文字起こしを実行します。")

    uploaded_file = st.file_uploader("音声ファイルをアップロード", type=["wav", "mp3", "m4a"])

    if uploaded_file is not None:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
            tmp.write(uploaded_file.read())
            tmp_path = tmp.name

        st.info("音声を処理中...")
        try:
            result_text = transcribe_audio(tmp_path)
            st.success("文字起こし完了!")
            st.text_area("文字起こし結果", value=result_text, height=300)
        except Exception as e:
            st.error(f"エラーが発生しました: {e}")
        finally:
            os.remove(tmp_path)

if __name__ == "__main__":
    main()