見出し画像

音声認識モデルWhisper large-v3をGoogle Colabで試した際のメモ


Hugging Face & Google Colab

Whisper large-v3 は、OpenAI が開発した音声認識モデル Whisper の一つのバージョンです。Whisper は多言語対応の音声認識システムで、音声からテキストへの変換を行います。
Hugging Faceのプラットフォームでオープンソースとして公開されています。

※この投稿の文章はChatGPTが生成した回答を主に使用しています。

主な特徴:

  1. 多言語対応: Whisper は複数の言語に対応しており、日本語を含む様々な言語の音声を認識できます。

  2. ノイズ耐性: 騒がしい環境でも正確に音声を認識するためのトレーニングが行われており、クリアでない音声でも比較的良い結果が得られます。

  3. 多目的な利用: Whisper モデルは、音声認識だけでなく、翻訳や要約などにも応用が可能です。

Hugging Faceを参考にGoogle Colabで動かしてみます。
その際、ChatGPTに聞きながらコードをちょっと変えてみました。
Hugging Faceのdatasetsライブラリから音声データセットを読み込み箇所を、ファイルをアップロードする方法に変更しています。

コードは何回かエラーが発生し、それ毎にChatGPTに修正してもらいました。
その主な要因は、今回使った音声ファイルのフォーマットがWhisperモデルの期待する形式と一致していないためでした。
次のように対処していきました。

  1. Google Colabに音声ファイルをアップロードするためにfiles.upload()を使用

  2. Whisperモデルが16,000Hzのサンプリングレートを期待しているため、音声データのサンプリングレートを変換

  3. Whisperモデルはモノラル音声(1チャンネル)を想定しているので、2チャンネル(ステレオ)でデータが読み込まれている場合、チャンネルを1つに変換

  4. 音声データの前処理としてテンソル形式に音声データを変換していたが、前処理をなくし、データを事前にサンプリングレートを調整しpipeにそのまま入力

(1)ファイルアップロードに変更

データセットの読み込みを、Google Colabにアップしたファイルを読み込む方法に変えられますか?


はい、Google Colabにアップロードした音声ファイルを読み込んで同様のパイプラインに適用する方法に変更できます。以下は、そのためのコードです。

変更点:

  1. `load_dataset`の代わりに、Google Colab上で直接アップロードしたファイルを読み込みます。

  2. `torchaudio`ライブラリを使って音声ファイルを読み込む方法を追加します。

必要なインストール

まず、必要であれば`torchaudio`をインストールしてください。

!pip install torchaudio

コード

import torch
import torchaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
from google.colab import files

# デバイスの設定
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# Whisperモデルの設定
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# パイプラインの構築
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    chunk_length_s=15,
    stride_length_s=3,
    max_new_tokens=128,
    return_timestamps=True,
    return_language=True,
    torch_dtype=torch_dtype,
    device=device,
)

# Google Colabに音声ファイルをアップロード
uploaded = files.upload()  # このコマンドでファイルをアップロード
file_path = list(uploaded.keys())[0]  # アップロードしたファイルのパスを取得

# 音声ファイルを読み込み
waveform, sample_rate = torchaudio.load(file_path)

# 音声ファイルをパイプラインに入力可能な形式に変換
inputs = processor(waveform, sampling_rate=sample_rate, return_tensors="pt")# 出力を PyTorch テンソル形式 ("pt") に変換

# 音声データをテキスト化
with torch.no_grad():
    result = pipe(inputs.input_values.to(device)[0])

print(result["text"])

説明

  1. ファイルのアップロード

    • `files.upload()`を使って、Google Colabに音声ファイルをアップロードします。

    • `file_path`にはアップロードされたファイルのパスが格納されます。

  2. 音声ファイルの読み込み

    • `torchaudio.load()`を使用して音声ファイルを読み込み、`waveform`(音波データ)と`sample_rate`(サンプリングレート)を取得します。

  3. 処理用の入力データの変換

    • `processor`で音声データをパイプラインに対応した形式に変換します。

  4. 音声データのテキスト化

    • 最後に、変換したデータをパイプラインに入力してテキスト化し、結果を表示します。

このコードで、Google Colabにアップロードした音声ファイルを読み込み、テキスト化できます。


(2)エラーが出たのでサンプリングレート変更


このエラーは、Whisperモデルがトレーニングに使用したサンプリングレート(16,000Hz)と、アップロードした音声ファイルのサンプリングレート(44,100Hz)が異なるために発生しています。Whisperモデルは16,000Hzのサンプリングレートを期待しているため、音声データのサンプリングレートを変換する必要があります。

以下のように、`torchaudio.transforms.Resample`を使って音声データのサンプリングレートを16,000Hzに変換してからパイプラインに渡すことで、問題を解決できます。

修正コード

import torch
import torchaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
from google.colab import files

# デバイスの設定
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# Whisperモデルの設定
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# パイプラインの構築
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    chunk_length_s=15,
    stride_length_s=3,
    max_new_tokens=128,
    return_timestamps=True,
    return_language=True,
    torch_dtype=torch_dtype,
    device=device,
)

# Google Colabに音声ファイルをアップロード
uploaded = files.upload()
file_path = list(uploaded.keys())[0]

# 音声ファイルを読み込み
waveform, sample_rate = torchaudio.load(file_path)

# サンプリングレートを16,000Hzに変換
if sample_rate != 16000:
    resampler = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)
    waveform = resampler(waveform)

# 音声ファイルをパイプラインに入力可能な形式に変換
inputs = processor(waveform, sampling_rate=16000, return_tensors="pt")# 出力を PyTorch テンソル形式 ("pt") に変換

# 音声データをテキスト化
with torch.no_grad():
    result = pipe(inputs.input_values.to(device)[0])

print(result["text"])

変更点の説明

  • サンプリングレートの変換:アップロードした音声ファイルのサンプリングレートが16,000Hzでない場合、`torchaudio.transforms.Resample`で16,000Hzに変換しています。

  • サンプリングレート指定:`processor`の`sampling_rate`引数に16,000を指定することで、Whisperモデルのトレーニングに合わせています。

これでエラーが解消し、テキスト変換が実行できるはずです。


(3)またエラーが出たのでステレオをモノラルに変換


このエラーは、音声データの形式がWhisperモデルの期待する形式と一致していないために発生している可能性があります。Whisperモデルはモノラル音声(1チャンネル)を想定しているので、2チャンネル(ステレオ)でデータが読み込まれている場合、チャンネルを1つに変換する必要があります。

`torchaudio`を使ってステレオをモノラルに変換する方法を含め、以下に修正版のコードを示します。

修正コード

import torch
import torchaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
from google.colab import files

# デバイスの設定
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# Whisperモデルの設定
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# パイプラインの構築
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    chunk_length_s=15,
    stride_length_s=3,
    max_new_tokens=128,
    return_timestamps=True,
    return_language=True,
    torch_dtype=torch_dtype,
    device=device,
)

# Google Colabに音声ファイルをアップロード
uploaded = files.upload()
file_path = list(uploaded.keys())[0]

# 音声ファイルを読み込み
waveform, sample_rate = torchaudio.load(file_path)

# ステレオからモノラルに変換
if waveform.shape[0] > 1:
    waveform = torch.mean(waveform, dim=0, keepdim=True)

# サンプリングレートを16,000Hzに変換
if sample_rate != 16000:
    resampler = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)
    waveform = resampler(waveform)

# 音声ファイルをパイプラインに入力可能な形式に変換
inputs = processor(waveform.squeeze(), sampling_rate=16000, return_tensors="pt") # 出力を PyTorch テンソル形式 ("pt") に変換

# 音声データをテキスト化
with torch.no_grad():
    result = pipe(inputs.input_values.to(device)[0])

print(result["text"])

変更点の説明

  • モノラルへの変換:`waveform`が2チャンネルの場合、`torch.mean(waveform, dim=0, keepdim=True)`を使って平均を取り、1チャンネルに変換しています。

  • `squeeze()`の使用:Whisperモデルが受け取る形に合わせて、モノラルに変換した`waveform`に`waveform.squeeze()`を使用してデータを1次元にしています。

これにより、音声データが正しい形式で処理されるようになります。


(4)まだエラーが出たのでパイプラインに直接入力(これで解消)


このエラーは、`pipeline`が`sampling_rate`パラメータを直接受け取らないために発生しています。代わりに、音声データを事前に指定したサンプリングレートで処理してからパイプラインに渡すようにします。

次の修正コードでは、音声データを事前にサンプリングレートを調整し、`pipe`にそのまま入力します。

修正コード

import torch
import torchaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
from google.colab import files

# デバイスの設定
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# Whisperモデルの設定
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# パイプラインの構築
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    chunk_length_s=15,
    stride_length_s=3,
    max_new_tokens=128,
    return_timestamps=True,
    return_language=True,
    torch_dtype=torch_dtype,
    device=device,
)

# Google Colabに音声ファイルをアップロード
uploaded = files.upload()
file_path = list(uploaded.keys())[0]

# 音声ファイルを読み込み
waveform, sample_rate = torchaudio.load(file_path)

# ステレオからモノラルに変換
if waveform.shape[0] > 1:
    waveform = torch.mean(waveform, dim=0, keepdim=True)

# サンプリングレートを16,000Hzに変換
if sample_rate != 16000:
    resampler = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)
    waveform = resampler(waveform)

# 音声データを直接パイプラインに入力
with torch.no_grad():
    result = pipe(waveform.squeeze().numpy())

print(result["text"])

修正点の説明

  • サンプリングレートの事前設定:音声データを指定されたサンプリングレート(16,000 Hz)に変換した後、`pipe`にそのまま渡すようにしました。

  • 不要な`sampling_rate`パラメータの削除:`pipe`の呼び出しから`sampling_rate`パラメータを削除しました。


processorについて

最終的に`processor` を使用せず、変換した音声データ(`waveform`) を直接 `pipe` (pipeline)に渡しています。

# 音声ファイルをパイプラインに入力可能な形式に変換
inputs = processor(waveform.squeeze(), sampling_rate=16000, return_tensors="pt")

# 音声データをテキスト化
with torch.no_grad():
result = pipe(inputs.input_values.to(device)[0])

print(result["text"])

↓ ↓ ↓

# 音声データを直接パイプラインに入力
with torch.no_grad():
result = pipe(waveform.squeeze().numpy())

print(result["text"])

この場合、`pipe` は入力データの形式が想定通りであることが前提になります(通常は NumPy array で、適切なサンプリングレートであること)。

pipelineを使う場合は、通常processorによる事前加工を明示的に行わなくても大丈夫です。pipelineは、内部でモデルが期待するフォーマットに自動的に処理を行います。つまり、音声データの前処理(特徴抽出、正規化など)はpipeline内で完結するため、データが適切な形式で入力されさえすれば、pipeline単独で音声認識を正しく実行できます。

(processorの役割)

processorの役割

サンプリングレートの調整:多くの音声モデルは特定のサンプリングレート(例: 16kHz)で訓練されています。processorは入力データのサンプリングレートが異なる場合、自動的に変換して互換性を確保します。

正規化:音声データを適切な範囲にスケーリングし、モデルが入力として扱いやすい形に整えます。これにより、入力データのばらつきが抑えられ、モデルの予測が安定します。

特徴抽出:多くの音声モデルは、入力データをそのまま渡すのではなく、周波数領域の特徴(メルスペクトログラムなど)に変換したデータを期待しています。processorを使うと、この特徴抽出が自動で行われ、モデルが最適に動作する入力形式が得られます。

(なぜprocessorを使用しなかったのか?)

なぜprocessorを使用しなかったのか?

音声が「シングルチャネル(モノラル)」でないためエラーが発生し英多様です。
waveform = torch.mean(waveform, dim=0, keepdim=True)を使ってモノラルに変更したのですが、processor で音声を処理するときに次元が変わったようです。

processor の処理によって、音声は特徴量(input_features)に変換されます。この特徴量は、モデルに入力される前に多くの処理が施されており、特に音声信号から「メル周波数ケプストラム係数(MFCC)」や「メルスペクトログラム」などの特徴量を抽出することが一般的です。

これにより、音声は次元が変わった「特徴量」の形式になります。もともとモノラル音声(1 チャンネル)の形状は (1, N) ですが、特徴量変換後には、音声のフレームごとに抽出された特徴量が input_features として保存され、torch.Size([128, 3000]) のような形状になります。(今回使った音声では128, 3000]となりました)

・128 は、特徴量の次元数(例:メルフィルタバンクの数など)を示しています。

:3000 は、時間軸上のフレーム数を示します。音声信号が時間的に分割され、それぞれのフレームに対応する特徴量が含まれます。

pipeの代わりにmodel.generateを使用見出し

もし特別な前処理をしたい場合や、`processor`でカスタム設定をしたい場合は、手動で`processor`を使い、`model.generate`で生成を行う方が適しています。
すでに自前で正確に前処理されたデータを持っている場合は、pipelineの内部処理を経ずに直接モデルに渡すことで細かく制御できるため、以下のようなコードが推奨されます。

# processorで特徴抽出したinput_featuresをmodel.generateに直接入力する
inputs = processor(waveform.squeeze().numpy(), sampling_rate=16000, return_tensors="pt")
with torch.no_grad():
    result = model.generate(inputs["input_features"].to(device))
text = processor.batch_decode(result, skip_special_tokens=True)[0]
print(text)

通常は`pipeline`で事前処理を含めたすべてを一度に実行する方が便利で簡潔です。

`pipe`と`model.generate`は、どちらもモデルによる音声認識を行いますが、使用方法や内部の処理が異なります。以下にそれぞれの違いを詳しく説明します。

1. `pipe`の使用

`pipe`は、Hugging Faceのパイプラインオブジェクトで、一連の処理を簡単に実行できる高レベルのインターフェースです。パイプラインを使用すると、音声データの前処理からモデルへの入力、生成された結果の後処理までを一貫して行うため、初心者や標準的な処理をしたい場合に適しています

特徴とメリット

  • 簡単なコード: `pipe`は内部で`processor`を自動的に呼び出し、音声データの前処理(特徴抽出、トークナイズ)や後処理(デコード)を行うため、処理をシンプルに書けます。

  • エラー処理の簡略化: 特徴抽出、トークナイズ、モデル入力などが自動で行われるため、フォーマットやエラーの扱いが簡単です。

  • 推論のオプションが豊富: パイプラインオブジェクトは`max_new_tokens`や`return_timestamps`などのオプションをサポートしており、標準的な処理には適しています。

注意点

  • 柔軟性の制約: `pipe`は基本的な処理を行うため、細かい制御やカスタムの前処理・後処理には向きません。

  • パフォーマンス上の最適化に限界: `pipe`は簡便さを重視しているため、モデルの設定やハイパーパラメータの調整を細かく行いたい場合には適していません。

2. `model.generate`の使用

`model.generate`は、モデルの生成プロセスに直接アクセスする低レベルのメソッドで、パイプラインを介さずに音声データをモデルに渡し、出力を取得する方法です。これにより、推論プロセスの詳細な制御が可能です。

特徴とメリット

  • 柔軟な制御: `generate`は`pipe`と異なり、トークン生成時の挙動やハイパーパラメータの微調整、生成プロセスの細かなカスタマイズが可能です。

  • モデルに直接アクセス: 音声データの`input_features`を直接モデルに入力するため、前処理や後処理を自分で指定できます。このため、`processor`でのカスタム前処理や後処理を施す場合には最適です。

  • 特殊用途に対応: 特定のデコード方法やバッチ処理が必要な場合、`generate`メソッドに直接アクセスすることで対応可能です。

注意点

  • 追加の前処理・後処理が必要: `generate`では前処理・後処理を手動で行う必要があり、特にデコード処理を`processor.batch_decode`などで行う必要があります。

  • 使い方にやや難易度がある: `generate`メソッドの詳細な設定を理解する必要があり、シンプルに音声認識を行いたい場合には、`pipe`ほど直感的でない場合があります。

| 特徴 | パイプライン(pipe) | model.generate |
|----------|----------------------|----------------|
| コードの複雑さ | シンプル |    やや複雑 |
| 制御の柔軟性     | 限定的    |    高い |
| 前処理の要否     | 自動       |    手動 |
| 適用場面        | 標準的な音声認識 | カスタム処理が必要な場合 |

適切な使い分け

  • 簡単に音声認識を行いたい場合は、`pipe`を使うのが便利です。特に、特徴抽出やトークナイズ、デコードなどが自動で処理されるため、標準的な音声認識タスクには向いています。

  • 細かい制御やカスタム処理が必要な場合は、`model.generate`を使い、独自の前処理・後処理を組み合わせて実行するのがよいでしょう。

import torch
import torchaudio
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor
from google.colab import files

# デバイスの設定
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# Whisperモデルの設定
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# Google Colabに音声ファイルをアップロード
uploaded = files.upload()
file_path = list(uploaded.keys())[0]

# 音声ファイルを読み込み
waveform, sample_rate = torchaudio.load(file_path)

# ステレオからモノラルに変換
if waveform.shape[0] > 1:
    waveform = torch.mean(waveform, dim=0, keepdim=True)

# サンプリングレートを16,000Hzに変換
if sample_rate != 16000:
    resampler = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)
    waveform = resampler(waveform)

# processorを使って音声データを特徴抽出し、モデルに入力可能な形式に変換
inputs = processor(waveform.squeeze().numpy(), sampling_rate=16000, return_tensors="pt")

# input_featuresをモデルに入力し、結果を取得
with torch.no_grad():
    # generateに必要なinput_featuresをモデルに渡す
    result = model.generate(inputs["input_features"].to(device))

# 結果をデコード
text = processor.batch_decode(result, skip_special_tokens=True)[0]
print(text)

いいなと思ったら応援しよう!