TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

PCで再生中の音声をWhisperでリアルタイムに文字起こしする

PCで再生中の音声をリアルタイムで文字起こしできると、字幕機能がない動画の再生や、外国とのオンライン会議で便利である。

先日、OpenAIが公開したWhisperは、音声ファイルから文字起こしするするツールが提供されているが、リアルタイムで処理するツールは提供されていない。
そこで、Pythonスクリプトで、リアルタイムで文字起こしするツールを作成した。

ループバック録音

SoundCardを使うと、PCで再生されている音声を録音することができる。

pip install SoundCard

でインストールする。

import soundcard as sc

with sc.get_microphone(id=str(sc.default_speaker().name), include_loopback=True).recorder(samplerate=SAMPLE_RATE, channels=1) as mic:
    while True:
        data = mic.record(BUFFER_SIZE)

のようにして処理を行う。
メインスレッド以外では動作しないので、注意が必要である。

リアルタイム文字起こし

Whisperで音声を認識するには、1つのセンテンスを含むくらいの長さの音声が必要である。
リアルタイムで文字起こしする場合、数秒間隔ごとに音声を区切って処理を行う。

音声を単語の途中で区切ると、誤認識するため、できるだけ無音の区間で区切る方がよい。
そこで、バッファリングした音声の末尾あたりで、無音の区間を探し、その位置で区切るようにする。
区切った残りの音声は、次の処理の先頭に結合するようにする。

無音の区間の探し方は、バッファリングした音声の後半4/5の区間で、音圧の移動平均の最小値の位置とした。

Whisperの処理

Whisperで音声を文字列に変換する処理は、READMEにサンプルコードがあるので、ほぼそのまま流用できる。

float32の音声をwhisper.pad_or_trimでパディングして、whisper.log_mel_spectrogramでログメルスペクトログラムに変換し、whisper.decodeでテキストに変換する。
言語の認識は、model.detect_languageで行える。

録音と文字起こしの非同期処理

文字起こしが間に合わない可能性があるため、録音処理と文字起こしの処理は非同期に行う。
録音処理では音声を処理単位に区切って、queueに追加し、文字起こしの処理は別スレッドで、queueから取り出して処理を行う。

以上の処理を実装したコードは、以下の通りである。

LoopbackWhisper.py
import whisper
import soundcard as sc
import threading
import queue
import numpy as np
import argparse

SAMPLE_RATE = 16000
INTERVAL = 3
BUFFER_SIZE = 4096

parser = argparse.ArgumentParser()
parser.add_argument('--model', default='base')
args = parser.parse_args()

print('Loading model...')
model = whisper.load_model(args.model)
print('Done')

q = queue.Queue()
b = np.ones(100) / 100

options = whisper.DecodingOptions()

def recognize():
    while True:
        audio = q.get()
        if (audio ** 2).max() > 0.001:
            audio = whisper.pad_or_trim(audio)

            # make log-Mel spectrogram and move to the same device as the model
            mel = whisper.log_mel_spectrogram(audio).to(model.device)

            # detect the spoken language
            _, probs = model.detect_language(mel)

            # decode the audio
            result = whisper.decode(model, mel, options)

            # print the recognized text
            print(f'{max(probs, key=probs.get)}: {result.text}')


th_recognize = threading.Thread(target=recognize, daemon=True)
th_recognize.start()

# start recording
with sc.get_microphone(id=str(sc.default_speaker().name), include_loopback=True).recorder(samplerate=SAMPLE_RATE, channels=1) as mic:
    audio = np.empty(SAMPLE_RATE * INTERVAL + BUFFER_SIZE, dtype=np.float32)
    n = 0
    while True:
        while n < SAMPLE_RATE * INTERVAL:
            data = mic.record(BUFFER_SIZE)
            audio[n:n+len(data)] = data.reshape(-1)
            n += len(data)

        # find silent periods
        m = n * 4 // 5
        vol = np.convolve(audio[m:n] ** 2, b, 'same')
        m += vol.argmin()
        q.put(audio[:m])

        audio_prev = audio
        audio = np.empty(SAMPLE_RATE * INTERVAL + BUFFER_SIZE, dtype=np.float32)
        audio[:n-m] = audio_prev[m:n]
        n = n-m

※2022/10/16追記:Ctrl+Cで終了させるため、daemon=Trueを追加した
GitHub: GitHub - TadaoYamaoka/LoopbackWhisper

実行例

コマンドラインからスクリプトを実行し、PCで音声を再生すると、認識されたテキストが表示される。

D:\src\LoopbackWhisper>python LoopbackWhisper.py
Loading model...
Done
en: And so my fellow Americans
en: Ask not.
en: What your country can do for you?
en: Ask what you can do for your country.

使用するモデルを変更する場合は、--modelオプションで指定する。

>python LoopbackWhisper.py --model large

サーバで文字起こしする

PCにGPUがない場合は、処理負荷が大きくなるため、オンライン会議などでは支障がでる可能性がある。
その場合は、PCでは録音のみ行い、音声をサーバにリアルタイムに送信して、サーバで文字起こしするとよい。
PCにはPythonの実行環境がない場合もあるので、C#で録音処理を行い、Socket通信でサーバで文字起こしするツールも作成した。
GitHub - TadaoYamaoka/StreamingWhisper

まとめ

PCで再生中の音声をWhisperでリアルタイムに文字起こしする方法について記述した。
リアルタイム処理するために、音声を単語の途中で区切らないようにすることと、録音と文字起こしを非同期で処理することを考慮して実装した。
また、PCの処理負荷をかけないために、音声をSocketでサーバに送信して、サーバ側で文字起こしする方法についても記載した。

いらすとやでDreamboothを試す

先日、いらすとやの画像でTextual Inversionを試したが、今回は同じく数枚の画像でスタイルを学習するDreamboothを試してみる。

Dreamboothは、元は、GoogleのImagenに対して適用された手法だが、Stable Diffusionに適用できるようにした実装が公開されたので、それを使って試してみた。
GitHub - XavierXiao/Dreambooth-Stable-Diffusion: Implementation of Dreambooth (https://arxiv.org/abs/2208.12242) with Stable Diffusion

Textual Inversionは、新しい単語の埋め込みベクトルを学習するのに対して、Dreamboothは、出現頻度の少ない既存の単語を使用し、それを条件として拡散モデルのパラメータを更新する。
上記のStable Dffusionの実装では、単語として「sks」が使用される。
「sks」は、任意のクラス(「犬」など)を修飾する語として使用される。

学習データ

いらすとやの「猫」の画像を使用する。
以下の12枚の画像を用いた。

正則化のために同じクラスの画像が200枚必要になるため、Stable Diffusionで「a photo of a cat」というプロンプトで生成した。
リポジトリにはプロンプトから画像を生成するスクリプトも用意されている。

学習手順

GitHubのREADMEの通りに実行すれば、問題なく実行できた。

python main.py --base configs/stable-diffusion/v1-finetune_unfrozen.yaml \
        -t --actual_resume /work/stable-diffusion-v-1-4-original/sd-v1-4-full-ema.ckpt \
        -n cat \
        --gpus 0, \
        --data_root training/images \
        --reg_data_root regularization/images \
        --class_word cat

デフォルトで800ステップの学習が行われる。
学習時間はA100で、10分くらいで完了した。

画像生成

学習したモデルは、Stable Diffusionと互換性があるため、Stable Diffusion web UIで読み込むことができる。

起動時に「--ckpt」オプションで、logs/images2022-09-28T08-16-31_cat/checkpoints/last.ckptを指定すればよい。
※images2022-09-28T08-16-31_catの部分は書き換える。

生成例

ファインチューニングしたモデルで、猫の画像を生成した例は以下の通り。
プロンプト:sks cat

いらすとや風の画風で、猫のイラストのバリエーションが生成できている。


sksを付けないと、以下のような画像が生成される。
プロンプト:cat

sksが有効に働いていることが確認できた。

ただし、ファインチューニングにより、既存の単語も色味が変わっており少し影響を受けているように見える。
参考として、既存のStable Dffusionのモデルで猫を生成すると、以下のような画像が生成される。



学習させたクラス(猫)以外の単語でも試した。
プロンプト:dog

ファインチューニングモデル

Stable Dffusionのモデル

ファインチューニングモデルの左下の画像は若干色味が影響を受けていそうである。

猫以外を含んだプロンプト

プロンプトに猫以外の説明を含めて生成した場合、以下のようになる。
プロンプト:木のある庭でボールで遊んでいる猫のイラスト(Illustration of a sks cat playing with a ball in a garden with trees)

猫以外も、猫と調和がとれたイラストが生成されている。

猫以外を修飾した場合

sksで猫以外を修飾した場合、以下のようになる。
プロンプト:sks dog

いらすとやの犬ではないが、イラスト調の画像になっている。

まとめ

いらすとやの画像でDreamboothを試してみた。
数枚の画像で、学習したクラス(今回は猫)の画像を、スタイルを反映していろいろなバリエーションで生成できることが確認できた。
また、学習したクラス以外の説明を含めた場合でも、違和感のない画像が生成できた。

【TIPS】 Stable Diffusion web UIの起動を速くする

AUTOMATIC1111版Stable Diffusion web UIは、起動時にCLIPの学習済みモデルを取得するために、HuggingFaceにアクセスを行う。
初回は、実際に学習済みモデルのダウンロードが必要だが、2回目以降はキャッシュされるために、ローカルストレージのキャッシュが使用される。

しかし、2回目以降にキャッシュを使用する場合でも、HTTP ヘッダーからETagを取得するためHuggingFaceにアクセスを行う。
HuggingFaceは時間帯によってはアクセスに数秒かかる場合があるため、それにより起動が遅くなる。

オフラインモード

環境変数「TRANSFORMERS_OFFLINE」に0より大きい値を設定することで、オフラインモードにすることができる。
オフラインモードでは、HTTP ヘッダーの取得を行わず、ローカルストレージのキャッシュフォルダからファイルを検索する。
HTTP ヘッダーの取得の待ち時間がなくなるため、起動が速くなる。

bashの場合、起動コマンドの前に「TRANSFORMERS_OFFLINE=1 」を付けて起動すればよい。
例)

TRANSFORMERS_OFFLINE=1 python webui.py

Windowsコマンドプロンプトから起動する場合は、setコマンドで環境変数を設定できる。

set TRANSFORMERS_OFFLINE=1

Whisperとゲームバーで会議議事録作成を自動化する

先日、OpenAIが公開したオープンソース音声認識ソフトウェアのWhisperを使って、会議議事録を自動作成する方法について記載する。

昨今、オンライン会議が主流になっているため、会議を録画/録音することが容易になっている。
Windows 10/11には、Xbox Game Barという機能が標準で備わっており、ZoomやGoogle Meets、Teamsなどでの会議が録画が可能である。
ループバック録音も可能で、再生している音声と、マイクからの音声をミックスして録音できる。

Xbox Game Barで録画した音声と、Whisperを組み合わせることで、無料で手軽に会議議事録を自動作成できる。

以下、手順について記載する。

事前準備

Xbox Game Barの設定

Windowsキー+Gで、Xbox Game Barを起動する。

歯車アイコンをクリックして、設定ウィンドウを開く。

左のタブからキャプチャ中を選択し、チェックボックスから「すべて」を選択する。

設定ウィンドウを閉じて、Escキーで、Xbox Game Barを終了する。

Whisperのインストール

下記の記事の通り、Whisperをインストールする。
tadaoyamaoka.hatenablog.com

録画

録画開始

オンライン会議のソフトを起動し、ウィンドウがアクティブになっている状態で、Windowsキー+Gで、Xbox Game Barを起動する。

ゲームバーから、キャプチャをクリックする。

マイクがオフになっている場合は、録画中はマイクをオンをクリックする。
忘れると音声が録音されないので注意。

キャプチャウィンドウが表示されるので、録画を開始をクリックする。

録画が開始され、画面の右側にキャプチャ状態ウィンドウが表示される。

Escで、ゲームバーを終了する。
キャプチャ状態ウィンドウは表示されたままになる。

録画終了

キャプチャ状態ウィンドウの録画を停止ボタンをクリックする。

録画されたファイルは、エクスプローラのPC→ビデオ→キャプチャに保存されている。

自動文字起こし

Whisperで文字起こしを行う。
コマンドプロンプトを起動して、「whisper 」(※末尾にスペースあり)と打ち込み、エクスプローラから録画したファイルをコマンドプロンプトにドラッグ&ドロップする。

whisper "C:\Users\xxxx\Videos\Captures\タイトルなし - ペイント 2022-09-23 14-37-34.mp4"

のように入力された状態になる。

末尾に続けて、「 --language Japanese」(※頭にスペースあり)と入力して、Enterを押す。

自動文字起こしした結果が表示される。
例)

R:\>whisper "C:\Users\xxxx\Videos\Captures\タイトルなし - ペイント 2022-09-23 14-37-34.mp4" --language Japanese
[00:00.000 --> 00:06.000] 記事録の自動文字起こしを行います

文字起こしの結果は、ファイルにも出力される。
カレントディレクトリに、「動画ファイル名」に拡張子「.txt」と「.vtt」が付いた2つのファイルが作成される。
.txtは文字列のみ、.vttはタイムスタンプありのテキストファイルである。

.vttの例)

WEBVTT

00:00.000 --> 00:06.000
記事録の自動文字起こしを行います

まとめ

Windowsで、Whisperとゲームバーで会議議事録を自動作成する方法について記載した。

オンライン会議以外でも、翻訳機能がない動画を再生して、文字起こしして翻訳するなど使い道があるので、いろいろ活用できそうである。

WindowsにWhisperをインストールする

先日、OpenAIが公開したオープンソース音声認識ソフトウェアであるWhisperWindowsにインストールする方法について記載する。

Anaconda(Miniconda)のインストール

Anacondaもしくは、Minicondaをダウンロードして、インストールする。
必要に応じて仮想環境を作成して、activateする。

PyTorchインストール

コマンドプロンプトから以下のコマンドでPyTorchをインストールする。

conda install pytorch torchvision torchaudio cudatoolkit=11.6 -c pytorch -c conda-forge

NVIDIAGPUが未搭載のPCの場合は、CPU版をインストールする。

conda install pytorch torchvision torchaudio cpuonly -c pytorch

ffmpegインストール

以下のコマンドでffmpegをインストールする。

conda install ffmpeg -c conda-forge

Whisperインストール

以下のコマンドでWhisperをインストールする。

pip install git+https://github.com/openai/whisper.git

テスト

適当な日本語の音声ファイルで、テストする。

>whisper test.wav --language Japanese
[00:00.000 --> 00:02.560] 音声自動認識ソフトウェアのテストです

まとめ

WindowsにWhisperをインストールする方法について記載した。
比較的簡単にインストールして試すことができる。

Windowsゲームバーの録画機能と組み合わせると、いろいろなケースで便利に使えると思う。

いらすとやでTextual Inversionを試す

Stable Diffusionの追加画像によるスタイル学習の方法として、「Textual Inversion」という方法がある。

これは、3~5枚の画像を用いて、スタイルを表す新規の単語の埋め込みベクトルを学習する方法である。
既存のパラメータは一切更新しないで、既存モデルに新規の単語を追加することで、その単語が追加画像のスタイルを表すように学習される。

使用する画像は、配色やスタイルが統一されている必要がある。

ここでは、いらすとやの画像を使って、Textual Inversionを試してみた。

手順は、↓こちらのページの解説を参考にした。
note.com

追加画像

どこまで配色やスタイルが統一されている必要があるのか知りたかったため、いくつかのパターンを試した。

男の子

服の色が同じ、男の子の画像3枚を使用する。

女の子

服の色が同じ、女の子の画像3枚を使用する。

男の子と女の子

上記の男の子の画像3枚と女の子の画像3枚を合わせた、計6枚

適当な期間の画像118枚

2021年1月の画像+αの118枚
例:

結果

各パターンでの生成結果は以下の通り。
単語は、「irasutoya」に学習している。

男の子

プロンプト:「a boy, irasutoya」

・追加画像:男の子3枚

いらすとやにそっくりかと言われると違うが、雰囲気はでている。

「, irasutoya」をなくして、単に「a boy」とすると以下のようになる。

既存モデルには影響していないことが確認できる。

・追加画像:女の子3枚

配色のスタイルは反映されているが、性別不明な人物が生成されている。

・追加画像:男の子と女の子 計6枚

男の子っぽい画像と女の子っぽい画像が生成されている。

・追加画像:適当な期間の画像118枚

キャプションを無視して、よく分からない画像になっている。
配色もグレーな画像になっている。
やはり追加画像のスタイルが統一されている必要があるようだ。

人物以外の動物でも試してみた。
プロンプト:「a cat, irasutoya」

・追加画像:男の子3枚

猫が人間になってしまっている。
やはり追加画像にあるオブジェクトの影響を受けてしまうようだ。

・追加画像:女の子3枚

左1枚は猫がイラストぽくなっている。
右3枚は背景の配色のみスタイルが反映されている。

・追加画像:男の子と女の子 計6枚

猫が人物になっている。
右1枚は、文字のみのよくわからない画像が生成されたりしている。
追加画像のスタイルが統一されていないことが影響していそうである。

・追加画像:適当な期間の画像118枚

イラスト調だが、かわいくない猫が生成された。
配色も暗めである。

芝生の庭で走り回る犬のイラスト

少し複雑な説明文でも試してみた。
プロンプト:「Illustration of a dog running around in a lawn garden, irasutoya」

・追加画像:男の子3枚

説明の概念は理解できているが、犬が人物になる場合がある。

・追加画像:女の子3枚

あまりスタイルが反映されていない。
説明文が複雑になると、スタイルの指示が弱まるようだ。

・追加画像:男の子と女の子 計6枚

イラスト風になっているが、犬が人物になっている。

・追加画像:適当な期間の画像118枚

キャプションの意味は解釈できている。
イラスト調だが、いらすとやの雰囲気はない。

irasutoya

irasutoyaのみでも試してみた。
プロンプト:「irasutoya」

・追加画像:男の子3枚

デフォルメされた人物と配色が学習されていそうである。

・追加画像:女の子3枚

人物よりも配色が多く学習されていそうである。

・追加画像:男の子と女の子 計6枚

画像が統一されていないためか、よくわからない画像も生成されている。

・追加画像:適当な期間の画像118枚

グレーの配色で、よくわからない画像が生成される。

まとめ

いらすとやの画像でTextual Inversionを試してみた。
追加画像のスタイルが統一されている場合は、配色やイラストの雰囲気は反映することができた。
ただし、画像にあるオブジェクトも反映されてしまうため、別のオブジェクトを生成する場合は影響が出ることが分かった。
追加画像の配色やオブジェクトが統一されていない場合、いらすとやの雰囲気とは外れて意図したスタイルを反映することはできなかった。

前回試した潜在拡散モデルの追加画像と比較すると、反映されるスタイルは弱くなり、異なるオブジェクトにスタイルを反映させるのは難しい場合があることがわかった。
Textual Inversionを使う場合は、生成するオブジェクトと同じオブジェクトの画像で学習した方が良さそうである。

Stable Diffusionを「いらすとや」で追加学習する

前回、Stable Diffusionをdanbooruデータセットを使用して追加学習することを試した。

今回は、自分でデータセットを作成して追加学習することを試す。
データセットには、画像とキャプションが揃っている必要があり、キャプションがない場合はCLIPで疑似ラベルを生成するなどする必要がある。
今回は、画像に対するキャプションがある「いらすとや」をスクレイピングして、データセットを作成した。

データセット作成

「いらすとや」から全ての画像とキャプションを取得した。
画像に複数バリエーションがある場合は、1つ目の画像のみを使用した。

キャプションは、日本語になっているため、Googleスレッドシートで、「=GOOGLETRANSLATE(XX,"ja","en")」を使って英語に変換を行った。

合計で、22891枚の画像とキャプションのセットが用意できた。

画像サイズ変換

画像は、512x512に変換した。

キャプションの加工

キャプションには、末尾に「, irasutoya」を付与して、「いらすとや」の画像であることを説明に加えることにした。

学習

前回、学習率をデフォルトとして追加学習のスタイルが反映され過ぎたため、学習率は5.0e-6とした。

結果

各エポックで訓練データのキャプションから生成された画像は、以下の通り。
Stable Diffusionが5エポック学習されているため、6エポック目から開始している。

6エポック目

また、写真ぽさがある。

7エポック目

絵画風になってきた。

8エポック目

人物がいらすとやぽくなってきた。

9エポック目

だいぶ特徴が表れている。

10エポック目

このあたりでほぼ特徴を学習していそうである。

19エポック目

19エポック目では、人物の特徴はほぼ学習できている。

画像生成

9エポック目と19エポック目のモデルを使用して画像生成して、スタイルを学習できているか確認する。

猫の画像を生成した結果は以下の通り。
プロンプト:「cat」

9エポック目

プロンプトに「, irasutoya」を付与しなくても、ほぼ「いらすとや」の画風になっている。

「, irasutoya」を付けた場合は、以下の通り。

ほとんど変わっていないように見える。
追加学習する場合、学習時に「, irasutoya」を付与するのは意味がなさそうである。

19エポック目

一番左の画像は、本物と区別がつかないくらいになっている。


次に、評価用データにあるキャプションで試してみた。
プロンプト:「It is an illustration of a girl playing with Rubikube(ルービックキューブで遊んでいる女の子のイラストです)」

9エポック目

ルービックキューブが描けていないが、人物は「いらすとや」ぽくなっている。

19エポック目

やはりルービックキューブは難しいようである。


次に、比較的分かりやすそうなキャプションで試してみた。
プロンプト:「Illustration of a boy who is happy to raise his hands and say banzai(両手を挙げて万歳をして喜んでいる男の子のイラスト)」

9エポック目


19エポック目

手が増え過ぎている。
数の制約の概念は苦手なようである。



次に、抽象的で難しめのプロンプトで試してみた。
プロンプト:「Illustration of a human wearing a collar and being pulled by a string by an artificial intelligence(首輪をつけて人工知能に紐で引っ張られている人間のイラスト)」

9エポック目


19エポック目

少し解釈が難しかったようである。

まとめ

「いらすとや」の画像とキャプションを使って、Stable Diffusionの追加学習を試した。
4エポック学習することで、ほぼ特徴が学習でき、生成画像も単純な画像であれば本物と区別がつかないクオリティになった。

ただし、抽象的な説明は、難しかったようである。

今回、オートエンコーダーとCLIPのパラメータは固定で、Latent Diffusionのみ学習したが、オートエンコーダーとCLIPもチューニングすることでさらに良くできる可能性はある。