【AI文字起こし】kotoba-whisper-v2.2をGoogle Colabで試してみる

## はじめに
**kotoba-whisper-v2.2**(話者分割あり)を使ってフリー環境で文字起こしを試してみたかったので記事にします。

とりあえず試したい人向けに環境がだいたいそろっている**Google Colab**での手順をメモします。

基本的には以下の記事通りですが、Google Colab向けに色々書いています。

https://huggingface.co/kotoba-tech/kotoba-whisper-v2.2

### kotoba-whisper-v2.2とは
『kotoba-whisper-v2.0』は、Kotoba Technologies株式会社と米Amazon社のアプライド・サイエンティストである Asahi Ushio氏が共同開発した日本語ASR（自動音声認識：Automated Speech Recognition）に特化したAIモデルです。

OpenAIが提供する「Whisper」の大型モデル「whisper-large-v3」を蒸留技術で最適化し、同等の精度で約6.3倍の高速化を実現しています。

v2.2はその中で話者分離(しゃべっている人を分ける)機能があるバージョンです。

ライセンスは2025年2月時点でapache-2.0(自由に商用OK。再配布の場合はライセンス表示が必要)です。


### この記事でわかる・できること

- Hugging Faceでの準備
- Colabでのkotoba-whisper-v2.2の実行確認


### この記事の対象者

- kotoba-whisperを試してみたい人
- 議事録の文字起こしとかに興味ある人


### 動作環境・使用するツールや言語
2025/2/7に動作確認しています。
- Google Colab
- Python 3.11.11
- kotoba-whisper-v2.2 
- pyannote/segmentation-3.0
- pyannote/speaker-diarization-3.1
- その他ライブラリ
- Hugging Face

## Hugging Faceでの準備


### token発行
Hugging Faceにサインインして、tokenを発行する必要があります。
アカウントがない場合は作成してください。

https://zenn.dev/protoout/articles/73-hugging-face-setup

### pyannoteのアクセス許可
サインイン後、以下のpyannote2つにアクセスしてOKしておいてください。

[pyannote/segmentation-3.0](https://huggingface.co/pyannote/segmentation-3.0)
[pyannote/speaker-diarization-3.1](https://huggingface.co/pyannote/speaker-diarization-3.1)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/3f9a7409-fb5b-e5fe-128d-9a5384997c66.png)


## Google colabでのハンズオン

Googleアカウントにログインしておいてください。

### Notebookの新規作成
New Notebookで作成開始します。

https://colab.google/

#### 名前変更
上の`untitled`をクリックして好きな名前に変更します`kotoba-whisper-v2.2-handson`など
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/97b04d6a-c82f-4dce-3535-a6fdbc6322ac.png)

#### 接続
右上の接続を押しておきます。成功するとRAMとディスクのステータスが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/07b121d5-990e-be56-49c9-9b759c740526.png)


### 環境変数設定
先ほど発行したtokenを用意します。

#### Colabに環境変数を設定

1. 新しいシークレットを追加
2. HF_TOKENという名前で値に発行したtokenを貼り付け
※その他のシークレットは人によって異なります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/a63521c2-4c40-4a70-8883-99e39b265298.png)


### 1つ目のコードブロック
コードブロックを作成します。コード記入後、再生ボタンで実行されます。いろいろダウンロードされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/21ecf428-ad42-2b3b-28de-adbd7fc2417a.png)

```python:コードブロック1
# Colabではpipコマンドは!を付ける必要がある
# 推論実行後、localeが変化されるのでおまじないとしてUTF-8に戻す
import locale
def getpreferredencoding(do_setlocale = True):
    return "UTF-8"
locale.getpreferredencoding = getpreferredencoding

# pipアップデート、Python確認
print("実行開始")
!pip install --upgrade pip
!python --version

# 必要なライブラリインストール
!pip install huggingface_hub
!pip install --upgrade torch
!pip install --upgrade torchvision
!pip install --upgrade transformers accelerate torchaudio

#実行後ランタイム再接続が必要
!pip install "punctuators==0.0.5"

!pip install "pyannote.audio"
!pip install git+https://github.com/huggingface/diarizers.git
#Flash-Attention 2が必要な場合インストール。Colabの場合は課金必要
#!pip install flash-attn --no-build-isolation

#huggingfaceにログイン処理(毎回は不要)
from google.colab import userdata

# シークレットに登録したトークンを確認
HF_TOKEN = userdata.get('HF_TOKEN')

# Hugging Faceにトークンでログイン
!huggingface-cli login --token "$HF_TOKEN"

# サンプル音声をダウンロード
!wget https://huggingface.co/kotoba-tech/kotoba-whisper-v2.2/resolve/main/sample_audio/sample_diarization_japanese.mp3

```
### 再接続
実行するとセッション再接続を求められるので従います。


### 2つ目のコードブロック
先ほどの実行後、再接続後に次のコードをコードブロックで作り実行します。
※初めにコードブロックを用意しても良い

初回実行時はkotoba-whisperをダウンロードするので10分程度かかります。
文字起こし自体の時間はCPUの場合はサンプル音声で5分程度。T4だと一瞬。
```python: コードブロック2
# モジュールインポート
# タイミングにより失敗するので、セッション再起動や待ってみる
import torch
from transformers import pipeline
import pprint

# モデルの設定
model_id = "kotoba-tech/kotoba-whisper-v2.2"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
device = "cuda:0" if torch.cuda.is_available() else "cpu"
# 基本(CPUやT4を使う場合):"sdpa"、強いGPUを使う場合:"flash_attention_2"
# CPUの場合はサンプル音声で5分程度かかる。T4だと一瞬
model_kwargs = {"attn_implementation": "sdpa"} if torch.cuda.is_available() else {}


# モデルを読み込む
pipe = pipeline(
    model=model_id,
    torch_dtype=torch_dtype,
    device=device,
    model_kwargs=model_kwargs,
    batch_size=8,
    trust_remote_code=True,
)

# 推論の実行。初回実行時は5GBダウンロードする(5分くらいかかる)
# 句読点が必要な場合：add_punctuation=True
result = pipe("sample_diarization_japanese.mp3", chunk_length_s=15, add_punctuation=True)
pprint.pprint(result)
```
### Colab実行結果
この画面が出ていれば文字起こし中です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/6a78d231-289c-70a2-3d00-1b8f9ad203cf.png)

5分ほど待つと、サンプル音声の文字起こしが出力されます。
色々試したい場合はとりあえず`sample_diarization_japanese.mp3`を差し替えて2つ目のコードブロックを実行すれば確認できます。

2回目からはダウンロードがないのでその分早いです。

``` :出力
sp.model: 100%
 5.07M/5.07M [00:00<00:00, 56.8MB/s]
model.onnx: 100%
 1.11G/1.11G [00:17<00:00, 27.6MB/s]
config.yaml: 100%
 531/531 [00:00<00:00, 31.9kB/s]
{'chunks': [{'speaker_id': 'SPEAKER_00',
             'text': '水をマレーシアから買わなくてはならないのです',
             'timestamp': [22.1, 24.97]},
            {'speaker_id': 'SPEAKER_01',
             'text': 'これも先ほどがずっと言っている自分の感覚的には大丈夫ですけれども',
             'timestamp': [0.03, 13.85]},
            {'speaker_id': 'SPEAKER_01',
             'text': '今は屋外の気温',
             'timestamp': [5.03, 18.85]},
            {'speaker_id': 'SPEAKER_01',
             'text': '昼も夜も上がってますので',
             'timestamp': [7.63, 21.45]},
            {'speaker_id': 'SPEAKER_01',
             'text': '空気の入れ替えだけではかえって人が上がってきます',
             'timestamp': [9.91, 23.73]},
            {'speaker_id': 'SPEAKER_02',
             'text': '愚直にその街の良さをアピールしていくという',
             'timestamp': [13.48, 22.1]},
            {'speaker_id': 'SPEAKER_02',
             'text': 'そういう姿勢が基本にあった上での',
             'timestamp': [17.26, 25.88]},
            {'speaker_id': 'SPEAKER_02',
             'text': 'こういうPR作戦だと思うんですよね',
             'timestamp': [19.86, 28.48]}],
 'chunks/SPEAKER_00': [{'speaker_id': 'SPEAKER_00',
                        'text': '水をマレーシアから買わなくてはならないのです',
                        'timestamp': [22.1, 24.97]}],
 'chunks/SPEAKER_01': [{'speaker_id': 'SPEAKER_01',
                        'text': 'これも先ほどがずっと言っている自分の感覚的には大丈夫ですけれども',
                        'timestamp': [0.03, 13.85]},
                       {'speaker_id': 'SPEAKER_01',
                        'text': '今は屋外の気温',
                        'timestamp': [5.03, 18.85]},
                       {'speaker_id': 'SPEAKER_01',
                        'text': '昼も夜も上がってますので',
                        'timestamp': [7.63, 21.45]},
                       {'speaker_id': 'SPEAKER_01',
                        'text': '空気の入れ替えだけではかえって人が上がってきます',
                        'timestamp': [9.91, 23.73]}],
 'chunks/SPEAKER_02': [{'speaker_id': 'SPEAKER_02',
                        'text': '愚直にその街の良さをアピールしていくという',
                        'timestamp': [13.48, 22.1]},
                       {'speaker_id': 'SPEAKER_02',
                        'text': 'そういう姿勢が基本にあった上での',
                        'timestamp': [17.26, 25.88]},
                       {'speaker_id': 'SPEAKER_02',
                        'text': 'こういうPR作戦だと思うんですよね',
                        'timestamp': [19.86, 28.48]}],
 'speaker_ids': ['SPEAKER_00', 'SPEAKER_01', 'SPEAKER_02'],
 'text/SPEAKER_00': '水をマレーシアから買わなくてはならないのです。',
 'text/SPEAKER_01': 'これも先ほどがずっと言っている。自分の感覚的には大丈夫です。けれども。今は屋外の気温、昼も夜も上がってますので、空気の入れ替えだけではかえって人が上がってきます。',
 'text/SPEAKER_02': '愚直にその街の良さをアピールしていくという。そういう姿勢が基本にあった上での、こういうPR作戦だと思うんですよね。'}
```
### GPUの変更
CPUの動作確認ができたらGPUを使ってみます
ランタイム→ランタイムのタイプを変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/5021dde8-874e-7bee-1d81-667a48121964.png)
T4 GPUを選択して保存
※課金している場合はより強いA100 GPUを選べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755504/e9aeda52-f0ad-2722-9175-f0c13ebe2aa8.png)

A100 GPUの場合はFlash Attention 2が利用可能らしいです。(未検証)
```diff_python
+ !pip install flash-attn --no-build-isolation
```

リセットされるので再度1つ目のコードブロックと2つ目のコードブロックを実行します。

:::note warn

GPUは時間制限があるので最初はCPUを選択して慣れてからT4を選ぶといいと思います。
:::


### ハマりポイント

#### 2つ目のコードブロックでimportできない
なんかimportできずに失敗することがあります。再接続をもう一回したり、少し時間を待つとできました。

#### rocale変化エラー
中途半端に推論を実行するとrocaleが変になってエラーになりました。
`NotImplementedError: A UTF-8 locale is required. Got ANSI_X3.4-1968`

1つ目のコードブロックにlocaleを戻す処理を追加して対策しています。

https://zenn.dev/kun432/scraps/c3111462f8b70f

#### T4でFlash Attention 2が使えない
`FlashAttention only supports Ampere GPUs or newer.`
T4は対応していません。
T4の場合はコードを変更しなくても勝手にGPUを選んで処理してくれます。

https://github.com/huggingface/transformers/issues/28188


## ローカル環境

次回はローカルでの実行を試すつもりですが、ライブラリの用意が必要そうなので複雑だと思います。GPUを使えるようにしたり、HuggingFaceライブラリが必要になると思います。

## まとめ
とりあえず好きな音声ファイルを文字起こしできるようになりました！