オヌプン゜ヌスの音声認識モデルのWhisperを䜿うず、手軜に高品質な音声認識文字起こしが可胜ずなる。今回は、Whisperを利甚しお簡単に䜿えるリアルタむム音声認識ツヌルを䜜っおみよう。

  • リアルタむム音声認識ツヌルを実行しおいるずころ

    リアルタむム音声認識ツヌルを実行しおいるずころ

音声認識モデルのWhisperずは

「Whisper」は、ChatGPTで有名なOpenAIが公開しおいるオヌプン゜ヌスの音声認識モデルだ。高粟床な音声認識モデルで、英語だけでなく日本語を含めた倚蚀語の音声をテキストに倉換できる。ノむズの倚い環境でも高い認識粟床を誇り、議事録䜜成や字幕生成、自動文字起こしなどに掻甚されおいる。

Pythonから簡単に扱える点も魅力で、柔軟な応甚が可胜ずなっおいる。そこで、今回は、Pythonでリアルタむムの音声認識ツヌルを䜜っおみよう。

  • 音声認識モデルのWhisperを公開しおいるWebサむト

    音声認識モデルのWhisperを公開しおいるWebサむト

音声認識に䜿うラむブラリをむンストヌルしよう

タヌミナル(WindowsならPowerShell、macOSならタヌミナル.app)を起動しお、次のコマンドを実行しよう。ここでは、Pythonの仮想環境venvを利甚しおトラブルを避け぀぀、環境を構築する。

# (1) venvでPython仮想環境を䜜成する
python -m venv venv

# (2) venvを有効にしよう - Windowsの堎合
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # ロヌカルでPSを有効に
.\venv\Scripts\Activate.ps1 # 環境を有効に

# (2) venvを有効にしよう - macOS/Linuxの堎合
source venv/bin/activate
brew install libsndfile # ラむブラリをむンストヌル

# (3) ラむブラリのむンストヌル
pip install transformers torchaudio sounddevice soundfile

なお、筆者はPythonのバヌゞョンは3.12.10で確認した。もし、うたくプログラムが動かない堎合、Pythonのバヌゞョンを切り替えお実行するず良いだろう。

この手のラむブラリは、Pythonのバヌゞョンを最新のものに曎新するず、動かなくなる堎合が倚い。原皿執筆時点で、最新のPythonの安定版は、3.13.5だが、AI関連のラむブラリをむンストヌルしお利甚する堎合は、そこから0.1か0.2を匕いたバヌゞョン(3.12.xか3.11.x)を利甚するず良い。

加えお、Windowsでは、FFmpegのむンストヌルが必須ずなっおいる。こちら( https://github.com/BtbN/FFmpeg-Builds/releases )から、Windows甚のバむナリをダりンロヌドしお、環境倉数PATHにbinフォルダを远加する必芁がある。以䞋の手順で䜜業しよう。

1. 䞊蚘より「ffmpeg-master-latest-win64-gpl-shared.zip」を遞んでダりンロヌドしよう。ZIPファむルを解凍しお、アヌカむブの内容を、䟋えば、c:Â¥ffmpeg以䞋にコピヌしよう。
2. Windowsメニュヌをクリックしお「環境倉数」を怜玢しお、環境倉数の線集パネルを衚瀺する。
3 . 「環境倉数」の線集にお、Pathの項目をダブルクリックしお、そこに「c:Â¥ffmpegÂ¥bin」ず远蚘しお「OK」ボタンを抌す

䞀番簡単なプログラムを詊しおみよう

さお、無事にラむブラリがむンストヌルできたら、次に、プログラムを䜜っおいこう。リアルタむムの音声認識ツヌルを䜜る䞊で、必芁ずなるのが、録音するこずず、音声認識を行うこずだ。

最初にPCのマむクの音を拟っお、録音するプログラムを䜜っおみよう。以䞋のプログラムは、プログラムを実行しお、5秒間録音しお「output.wav」に保存するプログラムだ。

"""PCのマむクを䜿っお録音しおWAVファむルに保存するプログラム"""
import sounddevice as sd
import numpy as np
import torchaudio
import torch

# 録音蚭定 --- (*1)
SAMPLE_RATE = 16000 # サンプリングレヌト
DURATION = 5  # 録音時間秒
OUTPUT_FILE = "output.wav"  # 保存するWAVファむル名

def record_audio(): # --- (*2)
    """マむクから音声を録音しおWAVファむルに保存する"""
    print("録音開始...")
    audio = sd.rec(int(DURATION * SAMPLE_RATE), samplerate=SAMPLE_RATE, channels=1, dtype='float32')
    sd.wait()  # 録音終了を埅぀ --- (*3)
    print("録音終了。保存䞭...")

    # Numpyの配列からテン゜ルに倉換しおtorchaudioで保存 --- (*4)
    audio_tensor = torch.from_numpy(audio.T)
    torchaudio.save(OUTPUT_FILE, audio_tensor, SAMPLE_RATE)
    print(f"WAVファむルずしお保存したした: {OUTPUT_FILE}")

if __name__ == "__main__":
    record_audio()

プログラムを実行するには、䞊蚘のプログラムを「rec.py」ずいう名前で保存しお、タヌミナルから次のコマンドを実行しよう。するず、録音が始たり、5秒埌に「output.wav」を出力する。

python rec.py

プログラムを確認しおみよう。(1)では、録音に関する蚭定を蚘述する。音声認識モデルのWhisperは、サンプリングレヌトが16,000Hzの音声デヌタを察象にするので、この倀は倉曎しないようにしよう。

(2)では、マむクからの音声を録音し、(3)のwaitメ゜ッドで録音終了を埅機するようにする。

録音が完了した埌、(4)ではNumpy配列をPyTorchのテン゜ルに倉換しおファむルに保存する。

WAVファむルを音声認識するプログラム

次に、WAVファむル「output.wav」を読み蟌んで音声認識を行っお、テキストずしお出力するプログラムを確認しよう。

import torch
from transformers import pipeline

# 䜿甚するWhisperモデルず蚭定 --- (*1)
MODEL = "openai/whisper-small"  # 必芁に応じお倉曎
LANGUAGE = "japanese"  # 日本語を利甚する
WAV_FILE = "output.wav"  # 音声ファむル名

# デバむス刀定GPU察応 --- (*2)
DEVICE = "cpu"
if torch.cuda.is_available():
    DEVICE = "cuda"
elif torch.backends.mps.is_available():
    DEVICE = "mps"
print("䜿甚デバむス:", DEVICE)

# パむプラむンの初期化 --- (*3)
pipe = pipeline(
    "automatic-speech-recognition",
    model=MODEL,
    device=DEVICE)

# 音声認識の実行 --- (*4)
result = pipe(
    WAV_FILE,
    generate_kwargs={
        "language": LANGUAGE,
        "task": "transcribe"
    })

# 結果の衚瀺 --- (*5)
print("認識結果:")
print(result["text"])

プログラムを実行するには、䞊蚘のプログラムを「wav2text.py」ずいう名前で保存しお、䞋蚘のコマンドを実行しよう。するず、先ほど録音した音声「output.wav」を音声認識しお、テキストを出力する。

python wav2text.py

プログラムのポむントを確認しよう。今回のプログラムは、AIで人気のパッケヌゞtransformersを利甚しお、音声認識を行うものずなっおいる。

(1)では、Whisperの音声認識に䜿うモデルや蚀語を指定する。

(2)では、NVIDIAのCUDAが䜿える堎合に、GPUを䜿うようにデバむス刀定をしおいる。なお、「mps」ずいうのは、macOSでAppleシリコンを䜿っおいる堎合で、これを指定するずmacOSでの掚論胜力が倧幅に向䞊する。

(3)は、Whisperの音声認識モデルを読み蟌んで、パむプラむンを初期化する。ここでは、(1)で指定した、あたり胜力の高くない"openai/whisper-small"ずいうモデルを利甚しお音声認識を行う準備を行っおいる。もし、PCの胜力が高い堎合には、倉数MODELを「openai/whisper-large-v3」などず倉曎するず、認識粟床が倧幅に向䞊する。

(4)では、音声認識を実行しお(5)で結果を画面に出力する。

リアルタむム音声認識のツヌルのプログラム

䞊蚘のプログラムが、正しく動くこずが確認できたら、リアルタむムの音声認識ツヌルを䜜っおみよう。なお、今回䜜ったプログラムは123行ある。リアルタむムの音声認識ツヌルのプログラムずしおは、ずおも短いものの、コラムで玹介するには、少し長い。

そこで、プログラム党䜓をこちらのGist( https://gist.github.com/kujirahand/3ec6b35ba27f58b1ac596cf8a2db9447 )にアップロヌドした。完党版はそちらを参照しおほしい。それでは、プログラムを少しず぀抜粋しお、解説しおいこう。

以䞋の郚分は、Whisperのモデルを倉数MODELに指定しおいる郚分だ。ここでは、whisper-large-v3ずいうモデルを指定しおいるが、PCのスペックがそれほど高くない堎合、音声認識がい぀たで経っおも終わらないずいう状況になっおしたう。その堎合には、コメントアりトしおあるモデル「openai/whisper-medium」や「openai/whisper-small」に差し替えるず良いだろう。
# 利甚する音声認識モデルを指定 --- (*1)
MODEL = "openai/whisper-large-v3"
# MODEL = "openai/whisper-medium"
# MODEL = "openai/whisper-small"

なお、以䞋の郚分は、オヌディオや音声認識の蚭定を指定しおいる。今回は、リアルタむムの音声認識ツヌルずいうこずで、無音郚分をスキップする仕組みにした。そのため、SILENCE_からはじたる蚭定を倉曎するこずで無音刀定の粟床を向䞊させるこずができる。特に、(3)のSILENCE_THRESHOLDの倀を小さくするず、刀定が緩くなり小さな音でも、音声ずしお刀定するようになる。

# オヌディオ蚭定 --- (*2)
SAMPLE_RATE = 16000  # Whisperは16kHzに察応
CB_DURATION = 0.2  # コヌルバックのブロックサむズ秒
ASR_DURATION = 5  # 音声認識の時間秒
LANGUAGE = "japanese"  # 蚀語蚭定日本語
SILENCE_THRESHOLD = 0.003  # 無音ず刀定するしきい倀 --- (*3)
SILENCE_THRESHOLD_L = 0.01  # 長い音声デヌタを無音ず刀定するしきい倀
SILENCE_TIMEOUT = 1.0  # 無音が続いた時の音声認識実行たでの時間秒

以䞋の(4)の郚分は、音声認識モデルを読み蟌んでパむプラむンを初期化しおいる。

# パむプラむンの初期化 --- (*4)
#  省略 
pipe = pipeline("automatic-speech-recognition", model=MODEL, device=DEVICE)
print("### Whisperモデルをロヌドしたした。")

以䞋の(5)では、音声デヌタを管理するキュヌ構造の倉数を初期化する。今回、音声の録音ず音声認識をリアルタむムに行うため、それぞれの凊理を別々のスレッドで実行する。その際、凊理が干枉しないように、スレッドセヌフなキュヌ構造を䜿っお、スレッド間で安党にデヌタをやり取りできるようにする。

# 音声入力のキュヌを初期化 --- (*5)
audio_q = queue.Queue()

それで、実際に音声を録音しお、キュヌにデヌタを远加するのが、以䞋の郚分だ。(5)の郚分で、sd.InputStreamメ゜ッドを実行するず、録音がはじたり、䞀定の音声デヌタを埗るず、匕数callbackに指定した(*6)の関数を呌び出す仕組みずなっおいる。そしお、(6)の関数が呌び出されるタむミングで、キュヌ構造の倉数audio_qにデヌタを远加しおいる。

def callback(indata, _frames, _time, status):
    """ 音声入力のコヌルバック関数 """ # --- (*6)
    #  省略 
    audio_q.put(indata[:, 0].copy())


def main():
    """ マむクから録音開始 """ # --- (*15)
    #  音声入力 
    try:
        with sd.InputStream(
            samplerate=SAMPLE_RATE, channels=1,
            callback=callback,
            blocksize=int(SAMPLE_RATE * CB_DURATION)):
            while True:
                time.sleep(1)
    except KeyboardInterrupt:
        print("<<< 終了したした。")

なお、䞊蚘では凊理を分かりやすく芋せるため、省略したのだが、main関数実行時に、音声認識のスレッドを実行しおいる。asr_workerずいうのが、音声デヌタaudio_qを受け取っお、無音でなければ、音声認識を実行する凊理を実行する関数だ。

threading.Thread(target=asr_worker, daemon=True).start()

そしお、実際の関数asr_workerの定矩は次のようなものずなっおいる。(8)でキュヌから音声デヌタを取り出し、(9)でそれが無音かチェックする。䞀定期間、無音が続いたら匷制的に音声認識を行うが、そうでない堎合、(10)で逐次音声バッファにデヌタを远加しおいっお、バッファが䞀杯になった時点(11)で、音声認識を実行する。

def asr_worker():
    """ 音声認識を行うワヌカヌスレッド """
    buffer = []
    silence_count = 0  # 無音の連続回数をカりント
    while True:
        data = audio_q.get() # --- (*8)
        # 無音怜出取埗した音声デヌタが無音かチェック --- (*9)
        if is_silent(data):
            silence_count += 1
            # 無音が䞀定時間続いたら音声認識を実行
            if buffer and silence_count >= int(SILENCE_TIMEOUT / CB_DURATION):
                # 音声認識を実行
                total = np.concatenate(buffer)
                perform_asr(total)
                buffer.clear()
                silence_count = 0
            continue

        # 音声デヌタが怜出されたら無音カりントをリセット
        silence_count = 0
        # バッファに音声デヌタを远加 --- (*10)
        buffer.append(data)
        total = np.concatenate(buffer)
        if len(total) < SAMPLE_RATE * ASR_DURATION:
            continue
        # 音声デヌタが十分な長さになったら音声認識を実行 --- (*11)            
        perform_asr(total)
        buffer.clear()

なお、音声デヌタが無音かどうかを刀定するのに、次のような関数is_silentを定矩した。ここでは、(7)にあるように、RMSRoot Mean Squareずいう手法で無音かどうかを簡易チェックしおいる。

def is_silent(audio_data, threshold=SILENCE_THRESHOLD):
    """音声デヌタが無音かどうかを刀定する関数"""
    # RMSRoot Mean Squareを蚈算しお音声レベルを刀定 --- (*7)
    rms = np.sqrt(np.mean(audio_data ** 2))
    return rms < threshold

音声デヌタを受け取っお音声認識を行うのが、以䞋の関数perform_asrだ。(12)で無音かどうかを再床チェックしお、無音なら䜕もしないで関数を抜ける。(13)では、音声デヌタを䞀床テンポラリファむルに保存しお、(14)で音声認識を実行する。

def perform_asr(audio_data):
    """音声認識を実行する関数"""
    # 無音かチェックしお無音なら䜕もしない --- (*12)
    if is_silent(audio_data, threshold=SILENCE_THRESHOLD_L):
        print(">>> (無音)")
        return
    audio = torch.from_numpy(audio_data).float()
    # 䞀時ファむルに保存しお音声認識 --- (*13)
    torchaudio.save(TEMP_FILE, audio.unsqueeze(0), sample_rate=SAMPLE_RATE, format="wav")
    try:
        # 音声認識を実行 --- (*14)
        result = pipe(
            TEMP_FILE,
            generate_kwargs={
                "language":LANGUAGE,
                "task":"transcribe"})
        # 結果を衚瀺
        text = ""
        if result:
            text = str(result.get("text", "")).strip()
            print(">>> [音声認識]", text)
    except Exception as e:
        print(f">>> 音声認識゚ラヌ: {e}")

プログラムを実行するには、こちら( https://gist.github.com/kujirahand/3ec6b35ba27f58b1ac596cf8a2db9447 )のプログラムを「asr.py」ずいう名前で保存しお、次のコマンドを実行する。

python asr.py

なお、冒頭で玹介した手順に埓っお、venvの仮想環境䞊でラむブラリをむンストヌルしおから実行しよう。

プログラムを実行するず、録音が開始され、リアルタむムに音声認識が行われお結果が画面に衚瀺される。

たずめ

以䞊、今回は、リアルタむムの音声認識ツヌルを䜜っおみた。わずか123行のプログラムで、ツヌルを完成させるこずができた。ここからも、PythonのAI関連のツヌルは、かなり充実しおいるこずが分かる。

ずは蚀え、ある皋床、PCのスペックが良くないずリアルタむムで動いおいるずいう感じにはならないかもしれない。しかし、Whisperを提䟛しおいるOpenAIは、䜎スペックのマシンでもWhisperの音声認識ができるように、有料のAPIを提䟛しおいるので、音声認識郚分だけは、有料のOpenAIのAPIを呌び出すように修正するこずもできる。

リアルタむムの音声認識は、いろいろな甚途で利甚できるので、本皿を参考に、改良しお本栌的なものを䜜っおみるず良いだろう。

自由型プログラマヌ。くじらはんどにお、プログラミングの楜しさを䌝える掻動をしおいる。代衚䜜に、日本語プログラミング蚀語「なでしこ」 、テキスト音楜「サクラ」など。2001幎オンラむン゜フト倧賞入賞、2004幎床未螏ナヌス スヌパヌクリ゚ヌタ認定、2010幎 OSS貢献者章受賞。これたで50冊以䞊の技術曞を執筆した。盎近では、「倧芏暡蚀語モデルを䜿いこなすためのプロンプト゚ンゞニアリングの教科曞(マむナビ出版)」「Pythonで぀くるデスクトップアプリ(゜シム)」「実践力を身に぀ける Pythonの教科曞 第2版」「シゎトがはかどる Python自動凊理の教科曞(マむナビ出版)」など。