【OpenAI】PythonでWhisperを使ってみた【音声書き起こし】

## 目次
[0.結論](#0-結論)
[1.はじめに](#1-はじめに)
[2.前準備](#2-前準備)
[3.音声書き起こし Whisper ](#3-音声書き起こしwhisper)
[4.おわりに](#4-おわりに)

### 0. 結論
Python（whisper）を用い、音声ファイル（MP３、WAV）の音声の書き起こしが可能。

▼作成したスクリプト
[Make_text_from_Audio_For_ Qiita.ipynb](https://colab.research.google.com/drive/1ILyQbKgaErSVx1zf_SgN8ySygArKznxY?usp=sharing)


### 1. はじめに
OpenAIから音声認識モデル「**Whisper**」が発表されていましたので、実際に使用してみました。

▼参考サイト
[OpenAIの文字起こしAI「Whisper」の使い方](https://aismiley.co.jp/ai_news/what-is-whisper/)

なお開発環境ですが**Google Colaboratory**を利用しています。

▼参考サイト
[【Google Colab】GPUが無料で使える!? 基本的な使い方](https://sci-by-py.com/hpc/google-colab/)

### 2. 前準備
ここでは、前準備としてGoogleドライブのマウントとテストする音声のDLについて記載しております。

またMP3→WAVへの変換処理についても記載しています。

不要な部分については適宜削除していただければと思います。

```Python:
#Googleドライブのマウント（音声を自分で準備するときはGoogleドライブ使うとよい）
from google.colab import drive
drive.mount('/content/drive')
```

```Python:
#テストする音声のダウンロード
!wget http://www.arky.co.jp/service/studio_recording/voice_sample/igarashi/asano.wav.zip
!unzip -o asano.wav.zip

#▼参考）テストする音声のダウンロード(MP3の時)
!wget https://www.city.takasaki.gunma.jp/uploaded/attachment/16508.mp3

#以後の処理はWAVで行っていることからMP3→WAVに変換している(最初からWAVの時は不要)
!pip install pydub

import pydub
import glob
import os
import sys

mp3s = glob.glob('*.mp3')
for i, mp3 in enumerate(mp3s):
  audio = pydub.AudioSegment.from_mp3(mp3)
  audio.export(mp3.replace('mp3', 'wav'), format='wav')
  
```

### 3. 音声書き起こしWhisper
ここでは、作成したスクリプトのメインの部分となるWhisperにて音声書き起こし処理について紹介していきたいと思います。

まずは音声書き起こし用のライブラリである**whisper**をインストールします。

```Python:
#whisper(音声書き起こし)のインストール（３分くらい時間かかる(通信環境次第でもっとかかる)）
!pip install git+https://github.com/openai/whisper.git
import whisper
lang = 'ja'
model = whisper.load_model('large')
```

whisperにて音声書き起こしした場合、文章の区切りが上手く認識されていないことがあったので、以下のライブラリを活用し、文章区切り処理を行います。
```Python:
#ja_ginza(文章区切り)のインストール（1分くらい時間かかる)
!pip install ja-ginza
import spacy
nlp = spacy.load('ja_ginza')
```

whisperの音声書き起こしは１ファイル30秒という縛りがあるため、以下のライブラリを活用し、音声ファイルを30秒毎に分割する処理を行います。

```Python:
#音声分割用ライブラリのインストール
import wave
import struct
from scipy import fromstring, int16
```

その他必要なライブラリをインストールします。
```Python:
#その他諸々インストール
import os
import glob
import math
from tqdm.notebook import tqdm
```

色々とPathの設定を記載します。

自身の実行環境において適切に書き換えてください。
```Python:
#上記ダウンロードした音声用のpath、テストしたい音声を準備する場合は自分で記載
#pathの設定
path = ''#初期設定：colab起動中のランタイム内に保存（ランタイム終わったら削除される）
#path = '/content/drive/MyDrive/'#例：Googleドライブを利用する場合
audio_name = '16508'#<-文字起こしに1時間くらいかかる（CPUの場合）
#audio_name = 'asano'
extension = '.wav'
extension_txt = '.txt'
path_audio = path + audio_name + extension
path_txt = path + audio_name + extension_txt
path_txt_separator = path + audio_name +'_Separator_'+ extension_txt
path_txt_summarize = path + audio_name +'_Summarize_'+ extension_txt
```

以下にて音声を分割する関数の定義を行なっています。
```Python:
#音声ファイルの分割
def cut_wav(cut_time, path_audio, audio_name, extension):
    # ファイルを読み込み
    wr = wave.open(path_audio, 'r')
    # waveファイルが持つ性質を取得
    ch = wr.getnchannels()
    width = wr.getsampwidth()
    fr = wr.getframerate()
    fn = wr.getnframes()
    total_time = 1.0 * fn / fr
    integer = math.floor(total_time) # 小数点以下切り捨て
    t = int(cut_time)  # 秒数[sec]
    frames = int(ch * fr * t)
    num_cut = int(integer//t)

    # waveの実データを取得し、数値化
    data = wr.readframes(wr.getnframes())
    wr.close()
    X = fromstring(data, dtype=int16)

    for i in range(num_cut):
        # 出力データを生成
        outf = path + audio_name + '_Output_' + str(i) + extension
        start_cut = i*frames
        end_cut = i*frames + frames
        Y = X[start_cut:end_cut]
        outd = struct.pack('h' * len(Y), *Y)

        # 書き出し
        ww = wave.open(outf, 'w')
        ww.setnchannels(ch)
        ww.setsampwidth(width)
        ww.setframerate(fr)
        ww.writeframes(outd)
        ww.close()
```

以下にてwhisperにて音声書き起こしする関数の定義を行なっています。
```Python:
#音声書き起こし
def transcribe(results, path_audio, audio_name, extension):
  wav_num = len(glob.glob(path + audio_name + '_Output_*.wav'))
  #音声ファイルが30秒以内の時はfor文の必要がないためif文の作成
  if wav_num == 0:
    results.append(model.transcribe(path + audio_name + '_Output_0' + extension))#<-ここで書き起こし、resultsに格納している
  else:
    for i in tqdm(range(wav_num)):
      results.append(model.transcribe(path + audio_name + '_Output_' + str(i) + extension))
  return results
```

以下にて音声書き起こしした内容をテキストに保存する関数の定義を行なっています。
```Python:
#テキスト化
def for_txt(results, path_txt):
  for i, result in enumerate(tqdm(results)):
    #ファイルを新規に作成
    if i == 0:
      with open(path_txt, mode = 'w') as f:
        f.write(result['text'])
    else:
      with open(path_txt, mode = 'a') as f:
        f.write(result['text'])
```

以下にてテキスト化した文章を適切な区切りにする関数の定義を行なっています。
```Python:
#文章区切り
def separator_txt(path_txt, path_txt_separator):
  with open(path_txt, 'r') as f:
    txt_sents = f.read()
  #文章区切り
  doc = nlp(txt_sents)
  for i, sent in enumerate(doc.sents):
    print(sent)
    #ファイルを新規に作成
    if i == 0:
      with open(path_txt_separator, mode = 'w') as f:
        f.write(str(sent) + '\n')
    else:
      with open(path_txt_separator, mode = 'a') as f:
        f.write(str(sent) + '\n')
```

以下にて作成した関数を実行し音声の書き起こしを実行しています。
```Python:
results = []
cut_time = 30#音声ファイルを指定時間(sec)に分割、whisperは30secしか書き起こしできないため、分割が必要

#音声ファイルの分割
cut_wav(cut_time, path_audio, audio_name, extension)
#文章書き起こし
results = transcribe(results, path_audio, audio_name, extension)#GPUを設定しないと遅い(1ファイル5分くらい)
for_txt(results, path_txt)
#文章区切り
separator_txt(path_txt, path_txt_separator)
```
### 4. おわりに
whisperによって音声書き起こしするスクリプトを作成しました。

高崎市の議会時の音声をテスト音声として音声書き起こしに使用してみると、音声書き起こしの精度としては高いと感じました。

これ以降の処理としては、ChatGPT等を用いることで文書要約を実行できると思っています。

ぜひ、本スクリプトを活用し、良い音声書き起こし生活を！

長い文章を最後までお読みいただき、ありがとうございました！！