Python-OpenAI「Whisper」を使った音声認識
OpenAIの音声認識モジュール「Whisper」を使って文字起こしを行う場合のプログラムの紹介です.プログラムにはPythonを使用します.
精度
モデルサイズ(tiny~large)によって精度は異なりますが,largeを指定した場合,経験的に非常に精度が高いと思います.
処理時間
モデルサイズ、処理がCPUかGPUかで、処理時間が大幅に違います(GPU>CPU)
モデルサイズが小さいほど処理時間は早くなりますが,精度が救いようがない状態になります
(体験談)1時間の音声ファイル:CPU(Intel(R) Core(TM) i7-8700)処理,largeモデル→1.5時間以内留意点
現段階(2022年11月現在)で話者認識が無いため,その点の後処理が必要です。
Google Colaboratory
最近取り扱って,環境構築をしなくてもよい便利さに心打たれたため,プログラミングになじみのない方であればこちらを使うのがとっつきやすいと思います.
以下のサイトの通り進めればできると思います.
もしWebのエディターに懸念を覚える人は,ローカルで環境構築するのがいいでしょう.
上記のブログで書かれたプログラムで,テキストファイルの中身が見づらい!!!!と不満を覚えた人は,ぜひ下の記事を読み進めて必要なコードを足していってください.
Anacondaの仮想環境+VSCode
※最後に、プログラムが載っています。
Visual Studio Codeというエディターがあり,筆者がお気に入りなので,これを使って環境構築しました.
Pythonの環境構築の方法にもいくつかあるのですが,仮想環境の構築が楽そうだったのでAnacondaを使用しています.
また,グラボがGeForce GTX 1050 Ti(4GB)なので,基本的にはsmall以下のモデルしか動作できません...(新しいグラボ欲しいけど高い)
もしlargeモデルで動かしたい場合は,Google Colaboratory(有料版になるかも)か16GB以上のグラボを使って下さい!
ちなみに,smallモデルでも声がはっきりと録音されていれば(反響とかなく),smallでもそれなりの精度で,1時間の音声が5~10分で書き起こされます.すごいね.
環境構築
環境構築の方法はこちらのブログたちを参照してください.
Anaconda
【初心者向け】Anacondaで仮想環境を作ってみる←モジュールのインポートまで解説している良記事
VSCode
以上がそろえば,Google Collaboratoryのプログラムを実行すれば,同様の結果が得られると思います.
それだけだと味気がないので,もう少し遊んでみましょう.
SRT:Subrip Subtitle (Wiki)
今回はSRTファイルという動画編集ソフトで,動画に字幕を追加するときに使用されるファイルを使います.
これを使えば,準備された関数で必要な情報を取り出してくれますので,使わない手はありません.
SRTとWhisperの組み合わせで参考にしたブログです.
これで文字起こしをすると以下のような出力が得られます.
テキストが改行もなく出力されるよりはるかに見やすいです!!!(個人差あり)
ただ,SRTファイルにはIDがそれぞれについています.文字起こしでは特に必要性を感じません.
話者認識ができない分,余計な後処理が増えてしまうことを恐れ,IDを除いたファイルを作りたいですが,SRTファイルを作成するためにはid,start,end,textの4種類は必要です.
ならば,いったんSRTファイルを作成してしまい,そのファイル内に格納されている情報から必要な情報だけを取り出せばいいじゃないか,と筆者は思い至り,以下のコードを追加します.
#SRTファイルを開いて、変数subripに格納
subrip = pysrt.open(f"{basename}.srt")
#同じ名前のtxtファイルを作成して、開きます。
f_out = open(f"{basename}.txt", mode="w", encoding="utf-8")
#subripに入っている情報の中から、テキストだけをtxtファイルに書き込みます。
for sub in subrip:
f_out.write(sub.text + '\n')
こうすると,以下のようなテキストだけがtxtファイルに書き込まれることになります
もし,タイムスタンプも付けて出力したい場合は以下のコードになります.
#タイムスタンプ、テキスト(ID無し)
#for sub in subrip:
# f_out.write(str(sub.start) + ' --> ' + str(sub.end) + '\n')
# f_out.write(sub.text + '\n')
こうすると,タイムスタンプとテキストが以下の状態でtxtファイルに書き込まれます.
以上のように,SRTファイルを介することで少し厄介になったかもしれませんが,result[segments]にアクセスするのを考えるよりも楽だったので私は良いと思っています.
GPU環境を構築したうえで,CPUで処理させる(2023年5月7日追記)
録音環境が良くない場合,largeモデルを使ってでも文字起こしをしたいと思うことがあるかもしれません.そういった場合は,CPUで処理をさせれば,時間はかかるもののlargeモデルで処理をすることができます.
※GPUのメモリ容量が10GB以上あれば問題ないのですが,ノートPCだったりするとグラボが載っていないという状況もあるかと思います.そういった場合は,インタープリター側で勝手にCPUで処理させようとしてくれます.
しかし,私のようにとりあえずGPU環境を構築してしまった人間は,切り替えをどうするのか?方法は2つです.
仮想環境を新しく作って,WhisperとSRT関係のモジュールだけをインストールする
プログラムでCPUに強制的に切り替える
1つ目は,CUDAとcuDNNとかPytorchとか気にせず入れればよいでしょうから,難しいことはありません.利点としては,準備させしておけばインタープリター側で切り替えられます.今回のプログラムに限らず,汎用性はあるので良いのではないでしょうか.
2つ目は,プログラムファイル内に強制的にCPUで処理させるプログラムを書くことになりますが,今回はこちらで試してみます.
以下の記事を参考にしました.
CUDA環境上のOpenAI WhisperをCPUで動かす方法
追加するのは,以下の2行です.
import torch
torch.cuda.is_available = lambda: False
これで実行すればCPUで処理してくれます.Warningが出ます.
簡単ですね.
プログラム例
以上をまとめてプログラムを作成し,Anacondaの仮想環境内で必要なモジュールたちをインポートして実行しました.
ご参考にどうぞ(Google Colaboratoryでも同じようにできると思います)
追記(2023.9.25)
一つのプログラムで実行した時,最後のtxtファイルに変換するときに全部変換してくれないバグがあるので,分けるのが良いかと思われました.
以下の例の下に追加しております.
#whisper_transcribe.py
import torch
import whisper
import os #ファイル操作用
#音声認識のresult[segments]を扱うのに便利なSRTのモジュールをインポートします
from datetime import timedelta
from srt import Subtitle
import srt
import pysrt
#CPUで処理させる場合は,このコメントアウトを外す
#torch.cuda.is_available = lambda: False
#Size | Parameters | English-only | Multilingual | Required VRAM | Relative speed
#tiny | 39 M | tiny.en | tiny | ~1 GB | ~32x
#base | 74 M | base.en | base | ~1 GB | ~16x
#small | 244 M | small.en | small | ~2 GB | ~6x
#medium | 769 M | medium.en | medium | ~5 GB | ~2x
#large | 1550 M | N/A | large | ~10 GB | 1x
filepath = "C:\\Users\\*省略*\\test.m4a" #ファイル指定。
lang = "ja" #音声ファイルの言語(ja=日本語)
basename = os.path.splitext(os.path.basename(filepath))[0] #音声ファイルの名前(拡張子なし)
model = whisper.load_model("small") #モデルサイズの指定(上の表参照)
#audioファイルを読み込む
audio = whisper.load_audio(file=filepath)
#音声認識
result = model.transcribe(audio, verbose=True, language=lang)
#結果の出力はtextかsegments。今回はタイムスタンプにも対応可能なようにsegmentsを使う
segments = result["segments"]
# resultの中身
# {'language': 'ja',
# 'segments': [{
# "id": len(all_segments),←取り出すやつ
# "seek": seek,
# "start": start, ←タイムスタンプ:開始時間
# "end": end, ←タイムスタンプ:終了時間
# "text": text, ←テキスト
# "tokens": result.tokens,
# "temperature": result.temperature,
# "avg_logprob": result.avg_logprob,
# "compression_ratio": result.compression_ratio,
# "no_speech_prob": result.no_speech_prob,
# },
# 'text': '********'}
subs = []
for data in segments: #segmentsの中から、id, start, end, textを取り出していく。
index = data["id"] + 1
start = data["start"]
end = data["end"]
text = data["text"]
sub = Subtitle( #SRTモジュールのSubtitle関数を使って、情報を格納していく
index = 1,
start = timedelta(seconds = timedelta(seconds=start).seconds, microseconds=timedelta(seconds=start).microseconds),
end = timedelta(seconds = timedelta(seconds=end).seconds, microseconds=timedelta(seconds=end).microseconds),
content=text,
proprietary=''
)
subs.append(sub)
#格納した情報をSRTファイルとして書き出す
with open(f"{basename}.srt", mode="w", encoding="utf-8") as f:
f.write(srt.compose(subs))
#SRTファイルから必要な情報だけ取り出してtxtファイルで保存する
#subrip = pysrt.open(f"{basename}.srt")
#f_out = open(f"{basename}.txt", mode="w", encoding="utf-8")
#テキスト(IDとタイムスタンプ無し)
#for sub in subrip:
# f_out.write(sub.text + '\n')
#タイムスタンプ、テキスト(ID無し)
#for sub in subrip:
# f_out.write(str(sub.start) + ' --> ' + str(sub.end) + '\n')
# f_out.write(sub.text + '\n')
SRT→TEXTファイルに変換する.(2023.9.25)
#SRTtoTEXT.py
import os #ファイル操作用
#音声認識のresult[segments]を扱うのに便利なSRT(Subrip)のモジュールをインポートします
from datetime import timedelta
from srt import Subtitle
import srt
import pysrt
filepath = "C:\\Users\\*省略*\\test.m4a" #ファイル指定。
basename = os.path.splitext(os.path.basename(filepath))[0] #音声ファイルの名前の取得(拡張子なし)
#SRTファイルから必要な情報だけ取り出してtxtファイルで保存する
subrip = pysrt.open(f"{basename}.srt")
f_out = open(f"{basename}.txt", mode="w", encoding="utf-8")
#テキスト(IDとタイムスタンプ無し)
# for sub in subrip:
# f_out.write(sub.text + '\n')
#タイムスタンプ、テキスト(ID無し)
for sub in subrip:
f_out.write(str(sub.start) + '\n')
f_out.write(sub.text + '\n')
読んでいただきありがとうございました.それでは.