0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IBM Granite Speech を試してみた — RTX 3060 Ti 8GBでローカル音声認識

0
Posted at

IBM Granite Speech を試してみた — RTX 3060 Ti 8GBでローカル音声認識

はじめに

IBMが公開している音声認識モデル Granite Speech 5.0 2B をローカル環境で動かしてみました。

今回は、NVIDIA RTX 3060 Ti(VRAM 8GB)を搭載したPCで、BF16(bfloat16)によるGPU推論を試しています。

単純に音声ファイルを文字起こしするだけでなく、PythonとGradioを使用して、ブラウザから操作できるWebアプリケーションも作成しました。

実装した機能は以下のとおりです。

  • 音声ファイルをアップロードして文字起こし
  • マイク入力による疑似リアルタイム文字起こし
  • 日本語・英語など6言語の選択
  • GPU推論時間、RTF、VRAM・RAM使用量の表示
  • 無音時の不要な推論を減らす簡易VAD(音声区間判定)

実際に動かしてみると、8GBのVRAMでもBF16モデルをロードでき、日本語の文字起こしも実行できました。一方で、無音時の誤認識や音声ライブラリの依存関係など、いくつか注意点も見つかりました。

1. Granite Speech 5.0 2Bとは

今回使用したのは、IBMの音声モデルです。

モデル: ibm-granite/granite-speech-5.0-2b

Hugging Face Transformersからロードでき、音声を入力してテキストを生成できます。

今回のアプリでは、入力音声の言語を指定して文字起こしする方式にしました。

音声認識用のプロンプトは、次のようなものです。

prompt_text = (
    f"<|audio|>The speech is in {language}. Transcribe it exactly; "
    "do not translate. transcribe the speech with proper "
    "punctuation and capitalization."
)

例えば language に Japanese を指定すると、日本語の音声を翻訳せずに文字起こしするよう指示します。

2. 動作環境

今回の検証環境は以下のとおりです。

項目 環境
OS Windows 11 + WSL2 Ubuntu 22.04
GPU NVIDIA GeForce RTX 3060 Ti
VRAM 8GB
メインメモリ 128GB
Python 3.10(今回の仮想環境)
CUDA(PyTorchビルド) 12.6
推論精度 BF16
Web UI Gradio

主要ライブラリは以下のバージョンで動作を確認しました。

ライブラリ バージョン
PyTorch 2.14.1+cu126
TorchAudio 2.11.0+cu126
TorchCodec 0.17.0+cu126
Transformers 4.57.6
Gradio 6.17.3
Accelerate 1.15.0

これらは今回の検証環境で使用したバージョンです。特にPyTorchとTorchAudioは通常、対応するリリース系列を揃える必要があります。上記の組み合わせは一般的な互換性を保証するものではないため、別環境に再現する場合は依存関係の確認が必要です。

3. モデルをGPUにロードする

モデルのロードには AutoProcessor と AutoModelForSpeechSeq2Seq を使用しました。

import torch
from transformers import AutoProcessor, AutoModelForSpeechSeq2Seq

MODEL_ID = "ibm-granite/granite-speech-5.0-2b"

processor = AutoProcessor.from_pretrained(MODEL_ID)

model = AutoModelForSpeechSeq2Seq.from_pretrained(
    MODEL_ID,
    device_map="cuda",
    dtype=torch.bfloat16,
)

model.eval()

今回の実装では、起動時にCUDAとBF16への対応をチェックしています。

if not torch.cuda.is_available():
    raise RuntimeError("CUDA GPU not found.")

if not torch.cuda.is_bf16_supported():
    raise RuntimeError("GPU does not report native BF16 support.")

実際の起動ログは以下のとおりです。

[Init] Checking GPU device support...
[Init] Loading processor for 'ibm-granite/granite-speech-5.0-2b'...
[Init] Processor loaded in 4.49s
[Init] Loading model weights for
'ibm-granite/granite-speech-5.0-2b' (BF16) into VRAM...
[Init] Model loaded in 2.89s | VRAM allocated: 4.31 GB
[Init] SpeechEngine initialized successfully.

モデルのロード直後のVRAM使用量は約4.31GBでした。

RTX 3060 Tiの8GB VRAMでも、少なくとも今回のモデルロードと短い音声の推論は実行できています。

4. 音声認識を実行する

今回のコードでは、音声を16kHz・モノラルに変換してからモデルに入力します。

音声認識処理の中心部分は以下のとおりです。

chat = [
    {
        "role": "user",
        "content": prompt_text,
    }
]

prompt = processor.tokenizer.apply_chat_template(
    chat,
    tokenize=False,
    add_generation_prompt=True,
)

inputs = processor(
    prompt,
    wav,
    device="cuda",
    return_tensors="pt",
).to("cuda")

with torch.inference_mode():
    outputs = model.generate(
        **inputs,
        max_new_tokens=512,
        do_sample=False,
        num_beams=1,
    )

prefix = inputs["input_ids"].shape[-1]
tokens = outputs[:, prefix:]

text = processor.tokenizer.batch_decode(
    tokens,
    add_special_tokens=False,
    skip_special_tokens=True,
)[0].strip()

print(text)

今回は生成時のサンプリングを無効にして、同じ入力に対して結果がなるべく安定する設定にしました。

5. GradioでWebアプリケーションを作成

モデルの動作を確認できたので、Gradioを使用してWeb UIを作成しました。

画面は2つのタブで構成しています。

File transcription

音声ファイルをアップロードし、言語を選択して文字起こしします。

with gr.Tab("File transcription"):
    file_audio = gr.Audio(
        sources=["upload"],
        type="filepath",
        label="Upload audio",
    )

    file_language = gr.Dropdown(
        LANGUAGES,
        value="Japanese",
        label="Language",
    )

    file_button = gr.Button(
        "Transcribe",
        variant="primary",
    )

    file_text = gr.Textbox(
        label="Transcript",
        lines=8,
    )

    file_metrics = gr.Textbox(
        label="Timing / status",
        interactive=False,
    )

現在のアプリでは、日本語、英語、フランス語、ドイツ語、スペイン語、ポルトガル語を選択肢として用意しています。

ただし、今回実際に動作を確認したのは主に日本語です。6言語すべての認識精度を検証したわけではありません。

Live microphone

もう1つは、ブラウザのマイクから音声を入力する機能です。

mic = gr.Audio(
    sources=["microphone"],
    type="numpy",
    streaming=True,
    label="Microphone",
)

Gradioのストリーミング機能を使用して、マイクから音声チャンクを受け取ります。

mic.stream(
    live_chunk,
    [mic, live_state, live_language],
    [live_text, live_status, live_state],
    stream_every=2.0,
    concurrency_limit=1,
    concurrency_id="gpu",
)

今回の方式では、約2秒ごとに音声を受け取り、直近最大8秒の音声を再認識しています。

WINDOW_SECONDS = 8
UPDATE_SECONDS = 2

これはモデル自体が逐次的に音声を処理するネイティブストリーミングではありません。

直近の音声を繰り返し文字起こしする、疑似リアルタイム方式です。

そのため、表示される文字列は暫定結果であり、音声が進むと内容が変化することがあります。また、現在の実装では会話全体の確定済み文字起こしを蓄積していません。

6. 実際の文字起こし性能

日本語音声ファイルで試したところ、次の結果が得られました。

今回の実測例

指標 結果
音声の長さ 約11.8秒
推論時間 約4.10秒
RTF 約0.35
VRAMピーク 約4.36GB
VRAM予約量 約4.39GB
プロセスRAM使用量 約1.77GB

文字起こし結果の例です。

テストです今日はいい天気です明日学校に行きます

RTF(Real-Time Factor)は、推論時間を音声の長さで割った値です。

rtf = inference_seconds / audio_seconds

今回の場合、

RTF = 4.10 / 11.8 ≒ 0.35

となります。

RTFが1未満であれば、音声の再生時間より短い時間で処理できたことを意味します。

今回の短い日本語音声では、RTX 3060 Tiでも実時間より速く文字起こしできました。

ただし、これは1つの実測例であり、音声の長さ、内容、言語、GPUの状態などによって推論時間は変わります。

7. 無音なのに文字起こしされる問題

マイク入力を試していて、興味深い問題がありました。

何も話していないのに、意味のある文章のような文字起こし結果が生成されることがあります。

例えば、無音に近い状態で次のような文章が出力されました。

このような状況にあるとき、どのような方法で対処するのか、
ということです。

実際には、このような発言はしていません。

これは音声認識モデルで発生することがある、いわゆるハルシネーションの一例と考えられます。

最初はリモートデスクトップ(RDP)経由のマイク入力が原因かもしれないと思いましたが、PCを直接操作した場合にも同様の現象が発生しました。

そこで、簡易的な無音判定を追加しました。

RMSによる簡易VAD

音声波形のRMS(Root Mean Square)を計算し、一定のしきい値未満であればモデルへの入力をスキップします。

import numpy as np

DEFAULT_VAD_THRESHOLD = 0.01

def calculate_rms(samples):
    arr = np.asarray(samples, dtype=np.float32)

    if arr.size == 0:
        return 0.0

    return float(np.sqrt(np.mean(arr ** 2)))

判定部分は次のようにしています。

rms = calculate_rms(audio)

if rms < DEFAULT_VAD_THRESHOLD:
    print("Silence detected. Inference skipped.")
else:
    # 音声認識を実行
    pass

この方法は非常に簡単ですが、あくまで音量による判定です。

環境ノイズが大きい場合には誤判定する可能性がありますし、小さな声を無音と判定してしまう可能性もあります。

さらに、今回の8秒ローリングウィンドウでは、直近2秒が無音でも、その前の音声がウィンドウに残っていることがあります。

そのため、RMS判定を追加するだけでハルシネーションを完全に防げるわけではありません。

今後はSilero VADなどの音声区間検出モデルも試してみたいと思います。

8. TorchCodecとCUDAの依存関係に苦労した

今回、モデルの実行よりも時間がかかったのがPythonライブラリの依存関係でした。

最初は次のようなエラーが発生しました。

TorchCodec is required for load_with_torchcodec

TorchCodecをインストールすると、今度は共有ライブラリのロードに失敗しました。

Could not load this library:
.../torchcodec/libtorchcodec_image.so

調べると、インストールされていたTorchCodecが参照するCUDAランタイムと、現在のPyTorch環境のCUDAバージョンが一致していませんでした。

具体的には、CUDA 13系のライブラリが要求されている一方で、PyTorchはCUDA 12.6のビルドを使用していました。

最終的には、CUDA 12.6向けのTorchCodecを導入することで解決しました。

torch:      2.14.1+cu126
torchcodec: 0.17.0+cu126
CUDA:       12.6

確認には次のコマンドを使用しました。

python - <<'PY'
import torch
import torchcodec

print("torch:", torch.__version__)
print("torchcodec:", torchcodec.__version__)
print("CUDA:", torch.version.cuda)
print("CUDA available:", torch.cuda.is_available())
PY

今回の経験から、GPUを使うPythonアプリでは、単に pip install するだけでなく、PyTorch、TorchCodec、CUDAランタイムの対応を確認することが重要だと感じました。

また、音声ファイルの読み込みをTorchAudioからSoundFileに置き換える実験もしましたが、M4Aファイルで次のエラーが発生しました。

Format not recognised.

SoundFileとTorchAudioでは対応する音声形式が異なる場合があるため、依存ライブラリを減らす際にも注意が必要です。

今回は元のTorchAudioベースの実装を採用しています。

9. メモリ使用量も記録する

今回のアプリでは、GPUメモリとプロセスのRAM使用量を表示するようにしました。

GPUメモリはPyTorchから取得できます。

vram_peak_mb = (
    torch.cuda.max_memory_allocated("cuda") / 1024**2
)

vram_reserved_mb = (
    torch.cuda.memory_reserved("cuda") / 1024**2
)

RAM使用量は psutil を利用しています。

import os
import psutil

process = psutil.Process(os.getpid())

ram_rss_mb = (
    process.memory_info().rss / 1024**2
)

これらの数値はWeb画面にも表示し、ログファイルにも保存しています。

GPUメモリのピーク値はPyTorchが管理するメモリの値であり、GPU全体のメモリ使用量と完全に一致するものではありません。

10. 今回のアプリの構成

Pythonコードは次のように分割しました。

hello-ibm-granite-speech-5/
├── app.py
├── requirements.txt
└── granite_web/
    ├── __init__.py
    ├── audio.py
    ├── metrics.py
    ├── model.py
    └── streaming.py

それぞれの役割は次のとおりです。

ファイル 役割
app.py Gradio Web UI
audio.py 音声読み込み、16kHz変換、RMS判定
metrics.py VRAM・RAM使用量、ログ記録
model.py Granite Speechモデルのロードと推論
streaming.py マイク入力、ローリングウィンドウ管理

Webアプリは以下のコマンドで起動します。

python app.py

起動すると、ローカルのWebブラウザからアクセスできます。

http://127.0.0.1:7860

今回の実装では、GPU推論を同時に複数実行しないよう、Gradioのキューとロックで制御しています。

11. 今後試したいこと

今回の検証では、短い日本語音声の文字起こしが正常に動作しました。

一方で、実用的な音声認識アプリにするには、まだ改善の余地があります。

特に次の点を試したいと思います。

  1. Silero VADなどによる無音・発話区間の判定精度向上
  2. マイク入力での確定済み文字起こしの蓄積
  3. 長い音声ファイルの自動分割と連結
  4. 複数言語での認識精度と推論速度の比較
  5. クリーンなPython環境から再現できるインストール手順の整備

まとめ

IBM Granite Speech 5.0 2Bを、RTX 3060 Ti(VRAM 8GB)で動かしてみました。

今回確認できたことは以下のとおりです。

  • BF16モデルを約4.31GBのVRAM使用量でロードできた
  • 短い日本語音声の文字起こしに成功した
  • 約11.8秒の音声を約4.10秒で処理できた(RTF約0.35)
  • Gradioで音声ファイルとマイク入力のWeb UIを構築できた
  • 無音時のハルシネーション対策が必要だった
  • TorchCodecとCUDAのバージョン整合性が重要だった

個人的には、8GBのVRAMでもローカルで音声認識モデルを実用的な速度で動かせたことが印象的でした。

今後はVADやストリーミング処理を改善し、より使いやすいローカル音声認識ツールにしていきたいと思います。

image.png

参考リンク

追記

RTX 3060 Ti(VRAM 8GB)でGranite Speech 5.0 2Bを動作させたところ、モデルロード直後のVRAM使用量は約4.31GBでした。一方、ディスク容量はモデル本体が約4.4GB、Python仮想環境が約6.8GBで、合計約11.2GBでした。仮想環境の大部分はPyTorch、NVIDIA CUDA関連ライブラリ、Tritonが占めています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?