説教を録音してGTX980Tiで文字起こし。GPTに過失割合を判定してもらう

# この記事の意図
文字起こしをローカルで実行する手順の解説がメインです。説教されている時点で悪いのは明らかに筆者です。

**あくまでエンタメとして読んでもらったら嬉しいです。**

## 準備するもの
+ NVIDIA製GPU搭載PC (CUDAが搭載されているもの)
+ Python3 実行環境
+ 説教音声ファイル (今回は16bit, 44.1KHz 1ch, wavです)


また、筆者の実行環境を以下に記します。

```:spec
CPU : Ryzen5 4500 6C12T
RAM : DDR4 16GiB 3200MHz
GPU : NVIDIA GeForce GTX980Ti 6GB VRAM Reference Model
SSD : nvme 2.0TB
```

```sh:zsh
$ cat /lib/os-release
PRETTY_NAME="Ubuntu 24.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.2 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

$ uname -a
Linux goto 6.11.0-19-generic #19~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Feb 17 11:51:52 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

$ python3 --version
Python 3.12.3
```

## インストール

https://github.com/openai/whisper

今回はWhisper AIを使用します。選定理由としてはレスポンス速度よりも求められるのは精度です。

VOSKは高精度高速でお気に入りでしたがどちらかというとリアルタイム性が強いモデルです。またCPU単体で実行できる点からリソースの限られた環境に本領を発揮すると考えます。従って、CPUのみの方はVOSKを使用すると良いでしょう。今回はVOSKの説明を完全に割愛しますので各自で調査してください。

GoogleのSpeechRecognitionも視野に入ります。しかし、APIを利用する点から料金の問題、長さの問題に懸念があります。**説教は決して短い音声ファイルで済まされません。** 長い音声ファイルも気兼ねなく処理するには不適でしょう。

余談はさておき、インストールします。

**pip3の人は適宜pipをpip3に変更してください**

```sh:shell
pip install -U openai-whisper
```

あとはffmpegをインストールします。

+ apt系

```sh:apt
sudo apt install ffmpeg
```

+ pacman系

```sh:pacman
sudo pacman -S ffmpeg
```

加えて、setuptools-rustをインストールします。

```sh:shell
pip install setuptools-rust
```

### NVIDIAドライバー

https://wiki.archlinux.jp/index.php/Nouveau

https://wiki.archlinux.jp/index.php/NVIDIA

https://documentation.ubuntu.com/server/how-to/graphics/install-nvidia-drivers/index.html

ここでは詳しく解説しませんが、Nouveauドライバーまたはnvidia-openドライバーが一般的でしょう。

とにかく

```sh:shell
nvidia-smi
```

が実行できればOKです！


## 説教ファイルの文字起こし

whisperの使い方は

```sh:shell
whisper path-to-sound-file.wav
```

で良い訳ですが、言語指定、モデル指定する場合は

```sh:shell
whisper path-to-sound-file.wav --language Japanese --model turbo
```

これでOKです。筆者の実行環境ではlargeモデルを実行するとOutOfMemmoryErrorになりました。

<details>
<summary>実行結果(失敗)</summary>

```
/home/haruki-goto/.local/lib/python3.12/site-packages/whisper/__init__.py:150: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
  checkpoint = torch.load(fp, map_location=device)
Traceback (most recent call last):
  File "/home/haruki-goto/.local/bin/whisper", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/whisper/transcribe.py", line 595, in cli
    model = load_model(model_name, device=device, download_root=model_dir)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/whisper/__init__.py", line 160, in load_model
    return model.to(device)
           ^^^^^^^^^^^^^^^^
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/torch/nn/modules/module.py", line 1340, in to
    return self._apply(convert)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/torch/nn/modules/module.py", line 900, in _apply
    module._apply(fn)
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/torch/nn/modules/module.py", line 900, in _apply
    module._apply(fn)
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/torch/nn/modules/module.py", line 927, in _apply
    param_applied = fn(param)
                    ^^^^^^^^^
  File "/home/haruki-goto/.local/lib/python3.12/site-packages/torch/nn/modules/module.py", line 1326, in convert
    return t.to(
           ^^^^^
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 254.00 MiB. GPU 0 has a total capacity of 5.93 GiB of which 46.56 MiB is free. Including non-PyTorch memory, this process has 5.71 GiB memory in use. Of the allocated memory 5.31 GiB is allocated by PyTorch, and 314.03 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)
```
</details>

さて、先程のコマンドでは実行してもstdoutに垂れ流してす。ファイルに書き込みましょう。

```sh:shell
whisper path-to-sound-file.wav --language Japanese --model turbo > output.txt
```

こんな感じで文字起こしした音声をファイルに書き込むことができるはずです。

ちなみにGPUは100%張り付きでした。

```:shell
$ nvidia-smi
Sun Mar  9 20:30:10 2025
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.183.01             Driver Version: 535.183.01   CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce GTX 980 Ti      Off | 00000000:01:00.0  On |                  N/A |
| 40%   82C    P0             167W / 250W |   4373MiB /  6144MiB |    100%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      3238      G   /usr/lib/xorg/Xorg                          114MiB |
|    0   N/A  N/A      3541      G   /usr/bin/gnome-shell                         64MiB |
|    0   N/A  N/A     10048      C   /usr/bin/python3                           4186MiB |
+---------------------------------------------------------------------------------------+
```

一部誤字がありますが、このように文字起こしが完了しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/f2520013-fd1c-4728-a007-64fe7b0cf7fb.png)

余談ですが、watchコマンドを駆使すれば生成途中のファイルを監視できます。

## タイムスタンプの削除

タイムスタンプが不要である場合は削除してしまいましょう。

```sh:shell
sed 's/\[[^]]*\]//g' output.txt > final.txt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/568b6435-b156-4996-b241-220666bee9d6.png)

このようにタイムスタンプが削除できました。

参考までに1時間23分の説教の文字起こしを行い、タイムスタンプ削除するとファイルサイズは72KBでした。
```sh:shell
$ du -h final.txt
72K     final.txt
```

### 【おまけ】GPTに投げてみる

+ Deepseek AI (DeepThink R1)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/95165ca1-0056-4613-aeb7-04e8f7d4f81b.png)


+ Gemini

一回で出力せず、戸惑っていたのでファイルの頭しか読み込んでいない可能性が高いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/5fd6c4be-790b-448d-b0fd-fa541998c070.png)

+ ChatGPT 4o

推論をONにすると悩みに悩んだ挙句100%/0%になったので割愛。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/41dd2eb9-59fa-4159-96b1-d0405665a9cf.png)

## 感想
個人的にはこの説教の前の背景情報を追加したいですが...。

以前4時間の説教ファイルでしっかり追加したときは5%/95%の判定になったのでしっかりプロンプトでAIに過去の情報を追加するのも大切なのかな...と考えます。

実際にはバイアスが掛かるのでアンフェアです。

そもそも72KBのテキストファイルを読み込んでいるかも不明です。あと、文字起こしするだけでは誰が話しているかは憶測でしかありません。

そこは手直しする必要があるのかなと考えます。

簡単にそういった面白いことができるんだなと感じてもらえたらと思います。

以上です。