文字起こしAI「Faster Whisper」を Amazon EC2 で使う

# この記事の目的

[Amazon EC2](https://aws.amazon.com/jp/ec2/) を利用し、音声データをそこで話されている内容を表すテキストに変換します。

# サンプルデータ

[『e-Gov法令検索』に載っている日本国憲法](https://elaws.e-gov.go.jp/document?lawid=321CONSTITUTION)の前文を、[WEB版VOICEVOX（複数）](https://www.voicevox.su-shiki.com/multiple)で読み上げさせました。
VOICEVOX:ずんだもん を使用しています。

前文全体を1個の入力欄に入れると文字数制限をオーバーしてしまうようだったので、1段落ずつ分けて入れました。
その結果を結合・変換したデータ `nikezun.mp3` を、ZIPファイルとしてそのまま展開できるPNGファイルで配布します。

![nikezun.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/cd4a01e7-7815-03b2-3cbc-d8839c2e4131.png)

この画像を保存し、拡張子を `zip` に変更して展開することで、データを取り出すことができます。
(展開ソフトウェアによっては、拡張子を変更しなくても展開できるでしょう)

# Faster Whisper を呼び出すプログラム

[Faster Whisper](https://github.com/SYSTRAN/faster-whisper) はライブラリであり、文字起こしを実行するにはこれを呼び出すプログラムが必要なようです。
そこで、呼び出すプログラム `fw_runner.py` を作成しました。

```python:fw_runner.py
import sys
from faster_whisper import WhisperModel

model_size = "large-v3"
input_file = None
use_cpu = False
use_int = False
beam_size = 5
show_help = False

i = 1
while i < len(sys.argv):
    if sys.argv[i] == "--model":
        i += 1
        model_size = sys.argv[i]
    elif sys.argv[i] == "--beam":
        i += 1
        beam_size = int(sys.argv[i])
    elif sys.argv[i] == "--cpu":
        use_cpu = True
    elif sys.argv[i] == "--int":
        use_int = True
    elif sys.argv[i] == "-h" or sys.argv[i] == "--help":
        show_help = True
    else:
        input_file = sys.argv[i]
    i += 1

if show_help or (input_file is None):
    print("Usage: %s [options] input_file" % sys.argv[0])
    print()
    print("options:")
    print("  --model model_size : set model to use")
    print("  --cpu              : use CPU instead of CUDA")
    print("  --int              : use int8 model instead of float16")
    print("  --help / -h        : show this help")
    sys.exit(0)

if use_cpu:
    device = "cpu"
    compute_type = "int8" if use_int else "float16"
else:
    device = "cuda"
    compute_type = "int8_float16" if use_int else "float16"

model = WhisperModel(model_size, device=device, compute_type=compute_type)

segments, info = model.transcribe(input_file, beam_size=beam_size)

def format_time(t):
    t_int = int(t)
    return "%d:%02d:%02d" % (t_int // 3600, t_int // 60 % 60, t_int % 60)

for segment in segments:
    print("%s -> %s : %s" % (format_time(segment.start), format_time(segment.end), segment.text))
```

公式のサンプルプログラムと比べ、以下の点を工夫しました。

* モデルと計算デバイスの種類、ビーム幅、入力ファイルをコマンドラインで指定できるようにし、プログラムを書き換えずに変更できるようにしました
* 時刻表示を秒だけでなく、わかりやすい「時:分:秒」形式にしました

このプログラムは、[CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/deed.ja) でライセンスします。

# Faster Whisper を実行できる EC2 インスタンスを起動する

Faster Whisper は環境構築をきちんと行えばローカルでも実行できる可能性がありますが、今回は環境構築の手間を省くため Amazon EC2 を利用します。

## クォータを確認・引き上げする

今回は、GPU を用いて Faster Whisper を実行するため、[P3 インスタンス](https://aws.amazon.com/jp/ec2/instance-types/p3/)を用います。
このタイプのインスタンスは、デフォルトではクォータが 0 になっており、起動することができません。
そこで、必要に応じてクォータを引き上げ、起動できるようにします。

まず、AWS コンソールの「Service Quotas」にアクセスします。
たとえば、画面左上の検索欄に「quota」と入力するとリンクが表示されるようです。

画面左側のメニューから「AWS のサービス」を開きます。
そして、「ec2」で検索し、「Amazon Elastic Compute Cloud (Amazon EC2)」を開きます。
表示される項目のうち、P3 インスタンスの起動に関係するのは

* All P4, P3 and P2 Spot Instance Requests
* Running On-Demand P instances

で、それぞれスポットインスタンス、オンデマンドインスタンスとして起動するインスタンスの vCPUs 数の上限を表します。

[スポットインスタンス](https://aws.amazon.com/jp/ec2/spot/)はオンデマンドインスタンスと比べて安価に利用できることがあるかわりに、需要が高くなった場合などに実行が中断される可能性があるようです。
また、スポットインスタンスの方がオンデマンドインスタンスよりクォータ引き上げの申請が通りやすいかもしれません。(昔やったときは、スポットインスタンスの上限は簡単に引き上げられたが、オンデマンドインスタンスの上限の引き上げは拒否されたと思います。今はどうかわかりません)

クォータを引き上げる項目を開き、画面右上の「アカウントレベルでの引き上げをリクエスト」を押します。
「クォータ値を引き上げる」の欄に新しい値、たとえば「16」などを入力し、右下の「リクエスト」を押します。
あまり大きい値を指定するとサポートに回され、拒否される可能性が高くなるようなので、欲張らないほうがいいでしょう。

## インスタンスを起動する

AWS コンソールで「EC2」を開きます。
検索か、「コンピューティング」カテゴリから開くことができます。
画面左中央の「インスタンスを起動」からインスタンスを起動できます。

### AMI を選択する

どのような OS やアプリケーションが入ったインスタンスを起動するかを決める AMI を選択します。
今回は、GPU を用いた計算ができるものを選びます。

検索欄に「CUDA」と入れて検索します。
そして、「コミュニティ AMI」を開きます。
名前に「Ubuntu 20.04」と入っていて、新しそうなやつを選びます。
今回は

> Deep Learning AMI GPU CUDA 11.4.3 (Ubuntu 20.04) 20221115
> ami-069309de05f87770a

を選びました。
今後新しいバージョンが出たり、リージョンによって使える AMI が変わったりすることで、適切な選択は変わるかもしれません。

:::note warn
> Deep Learning AMI GPU CUDA 11.4.3 (Ubuntu 18.04) 20230323
> ami-09112fd440f42f0bc

を選ぶと、入っている Python のバージョンが 3.6.9 で、Faster Whisper の対応環境の Python 3.8 or greaterを満たせなそうでした。
:::

### インスタンスタイプを選択する

インスタンスタイプの検索欄に「p3」と入力し、`p3.2xlarge` を選択します。
上位のインスタンスタイプは利用料金も高くなります。

:::note warn
かわりに[G3 インスタンス](https://aws.amazon.com/jp/ec2/instance-types/g3/)の `g3s.xlarge` を選んだところ、以下のエラーが出て Faster Whisper の実行が失敗しました。

> ValueError: Requested float16 compute type, but the target device or backend do not support efficient float16 computation.
:::

### SSH アクセスができるようにする

SSH アクセスの認証に用いる鍵を表すキーペアを設定します。
さらに、セキュリティグループの設定で SSH アクセス (TCP 22番ポートへのアクセス) を許可します。

### インスタンスの種類を設定する

スポットインスタンスを用いる場合は、「高度な詳細」を開き、「購入オプション」を「スポットインスタンス」に設定します。

### インスタンスを起動する

設定が完了したら、画面右側の「インスタンスを起動」ボタンを押します。
うまくいけば、インスタンスが作成されるはずです。
エラーが出た場合は、クォータの設定やインスタンスの種類の設定 (オンデマンドインスタンス / スポットインスタンス) などを確認しましょう。
なお、インスタンスを終了してもしばらくはカウントされ、クォータに余裕が無いと次のインスタンスの起動ができないようでした。

# Faster Whisper を実行する

インスタンスを起動したら、接続して Faster Whisper を実行します。

## インスタンスに接続する

AWS コンソールの画面左「インスタンス」などからインスタンス一覧を開き、起動したインスタンスを選択します。
画面下に表示される「パブリック IPv4 アドレス」を用いて、インスタンスに SSH 接続をします。
接続のしかたは、用いる SSH クライアントソフトウェアによるでしょう。
今回使用する AMI の場合、接続に用いるユーザー名は `ubuntu`、鍵はインスタンスの起動時に設定したキーペアに対応する鍵です。
また、インスタンスの状態が「実行中」になっても数分間は接続ができないようなので、接続できるようになるまで待ちましょう。

## Python のバージョンを確認する

```
python3 --version
```

コマンドにより、Python のバージョンが確認できます。
今回の AMI では

```
Python 3.8.10
```

と出力されました。

## Faster Whisper をインストールする

```
pip install faster-whisper
```

コマンドを実行し、Faster Whisper をインストールします。
実行には1分程度かかりました。
また、スクリプトに PATH が通っていないという WARNING が何件も出ましたが、今回は無視していいでしょう。

## 使用するファイルをアップロードする

前述のプログラム `fw_runner.py` および処理対象の音声データ `nikezun.mp3` をインスタンスにアップロードします。
SCP などを用いるか、プログラムはコピペしてもいいでしょう。

## Faster Whisper を実行する

`fw_runner.py` と `nikezun.mp3` があるディレクトリで、コマンド

```
python3 fw_runner.py nikezun.mp3
```

を実行します。
すると、文字起こし結果が標準出力に出力されます。
最初の実行時は、モデルのダウンロードに加えて何らかの初期化処理があるようで、文字起こしの結果が出力され始めるまでに5分程度かかりました。
2回目に実行すると、すぐに結果が出力されました。

# 実行結果例

> 0:00:00 -> 0:00:22 : 日本国民は、正当に選挙された国会における代表者を通じて行動し、我らと我らの子孫のために諸国民との共和による成果と、我が国全土に渡って自由のもたらす計画を確保し、政府の行為によって再び戦争の惨禍が起こることのないようにすることを決意し、
> 0:00:22 -> 0:00:28 : ここに主権が国民に損することを宣言し、この憲法を確定する。
> 0:00:29 -> 0:00:43 : そもそも国政は、国民の厳粛な信託によるものであつて、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。
> 0:00:43 -> 0:00:49 : これは人類不変の原理であり、この憲法は、かかる原理に基づくものである。
> 0:00:49 -> 0:00:55 : 我らは、これに反する一切の憲法、法令及び省畜を排除する。
> 0:00:56 -> 0:01:13 : 日本国民は、高級の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであつて、平和を愛する諸国民の公正と真偽に信頼して、我らの安全と生存を保持しようと決意した。
> 0:01:13 -> 0:01:18 : しかし、我らは平和を維持し、先制と礼中。
> 0:01:19 -> 0:01:27 : 人を地上から変に除去しようと努めている国際社会において、名誉ある地位を占めたいと思う。
> 0:01:27 -> 0:01:37 : 我らは、全世界の国民が、等しく恐怖と欠乏から免れ、平和のうちに生存する権利を有することを確認する。
> 0:01:37 -> 0:01:48 : しかし、我らはいずれの国家も、自国のことのみに専念して他国を無視してはならないのであつて、政治道徳の法則は普遍的なものであり、
> 0:01:49 -> 0:01:58 : 自国に従えふことは、自国の主権を維持し、他国と対等関係に立たあとする各国の責務であると信ずる。
> 0:01:58 -> 0:02:06 : 日本国民は、国家の名誉にかけ、全力を挙げてこの崇高な理想と目的を達成することを誓いふ。

パッと見は自然ですが、間違いも何箇所か見られます。
文字起こしによりどこでどのような内容が語られているかがすぐにわかるようになるので、重要な内容は時刻情報を参考にもとの音声を聞いて確認するのがよいでしょう。

# 起動したインスタンスの後始末をする

今回利用するインスタンスタイプは、無料利用枠の対象外です。
さらに、今回利用する AMI はストレージをデフォルトで 35 GiB 使用し、無料利用枠の 30 GiB を超えます。
したがって、無料利用枠がある場合でも、**インスタンスの実行中は料金が発生します。**

そのため、文字起こし作業が完了し、他にインスタンスを使う予定が無ければ、すぐにインスタンスの実行を止めるのがよいでしょう。

近いうちにまた作業をしそうであれば、「インスタンスを停止」するのがいいかもしれません。
これを行うと、後で同じ (Faster Whisper やモデルの準備ができている) インスタンスを再実行できますが、停止中もストレージ (EBS) の料金は発生します。

しばらく作業をしなそうであれば、「インスタンスを終了」するのがよさそうです。
これを行うと、インスタンスとストレージを削除し、ストレージの料金の発生も回避できますが、EC2 で再び文字起こし作業を行うには新たにインスタンスを起動し、準備を行わなければなりません。

# 結論

Amazon EC2 を用いることで、使用量に応じた料金がかかるかわりに、ローカルでの環境構築の手間を省き、Faster Whisper を用いて音声の文字起こしをすることができました。