この記事の概要を簡単まとめ!
- faster-whisperベースの音声認識”YukariWhisper”を検証した
- 設計上の仕様でNVIDIA製GPU、2000番台以下はcuda_8(int8_bfloat16)を使えなかった
- もしかしてソースコードに不備があるのではと考え、中身を調べることにした
- ソースコードと設定ファイルを改造、最低動作保証1000番台に対応した
- 制作サイドではゆかコネのポート動的取得の対応完了
- int8_float16をデフォルトに使用し、実際の配信で音声認識精度と負荷を確認
- 結果:ヒロアカでは問題なく、伝説のレジェンドでは支障が出た
- 対策:上位モデル3000番台以上か2GPU構成を行う
- 代用:ノイズ除去を他の音声認識にも適用する
- 不完全なものは自分で完全にしてしまえばいい
1週間程度、ゆかコネで使用可能になった音声認識”YukariWhisper”について調査していた。ゆかコネ音声認識比較のデータの1つとして試験後、実際の配信でも使用してみた。しかしcuda_8非対応、VRAMが最低で4GB消費する、ノイズ除去を十分行わないとノイズを音声と誤認する、時々反応が悪いなどの問題があって、使用を休止していた。
GPU問題はこの際、もう無視することにした。もう1つ考えたのは、ソースコードに不備があるということ。元となるfaster-whisperについての解説は探せば出てくるほか、変数を追うことなどは割とやったことがあって得意である。おそらくタブーに手を出している気がするが、だが使いづらさや不備を見過ごせないので中身を見ることにした。
ひとっ飛びできる目次
不完全なものを完全にする
YukariWhisperの調査を継続
YukariWhisperがゆかコネで公式サポート
OpenAIの開発した音声認識モデル”Whisper“を高速化した”faster-whisper“は、GPUを利用することで高精度音声認識をリアルタイムで実行できるというものだ。これをベースとしてゆかりねっとおよびゆかコネに接続して利用できる”YukariWhisper“がTYAPA氏によって作られ、GitHubで公開している。
これについて私は、まずゆかコネ音声認識比較記事において、ゆかコネで使用できる音声認識の1つとして導入・検証した。次に後続の使用者のためにYukariWhisper設定解説記事で、ゆかコネユーザーで特にプログラミング未経験者を想定して解説を行っている。いくらそういう時代だとしても全員が全員、プログラミングできるとは限らない。そして説明するときは、独りよがりな、知っていることを前提とした説明は行ってはならない。そう決めてなるべく分かりやすく、しかし技術面は簡潔にして書いた。
そこで私は、初期設定が多くにとって「そのままでは動かない」設定であることを発見し、それについてのエラーの理由と対応方法を解説した。セットアップのバッチファイルを実行後、特に問題がなければrun.batを実行することでそのまま起動できる、というのがTYAPA氏の説明である。そしてreadme.mdにはNVIDIA系GPU(1000番台以降)で動作すると書いていた。だがこれは後述で明らかな矛盾があり、起動時に確定でエラーが発生する「失敗の組み合わせ」である。
int8_bfloat16は3000番台以降でしか使えない
YukariWhisperの起動時の設定は、run.batと同階層のyukariwhisper.iniにまとめられている。ここから各種設定を変更して保存することによって、自身の環境に合った設定で動作するようになる。その初期設定では、使用デバイスはwhisper_device = cuda_8
で指定されている。これをこのまま実行して起動した人はエラーが発生しないのでなんてことないが、私の環境ではエラーが発生した。その画面が以下である。
このエラーが出たときは、その原因が分からずに悩んでいたのだがなんてことない。int8_bfloat16に注目するとすぐ解決した。これはOpenMNTが公開しているCTranslate2の量子化(Quantization)についてのページで、そこにサポートする量子化の一覧と、CUDA CCのバージョンによって使用できる量子化の暗黙の型変換について書かれている。通常bfloat16はアーキテクチャがAmpere以降、CUDA CC 8.0以上でのみ使用できる浮動小数点の数値表現形式である。ここでRTX 2080TiはTuringアーキテクチャであり、CUDA CCは7.5である。そのためbfloat16はサポートされていないことが分かる。したがって、そのまま起動しても動くはずないのは当然のことである。しかしこれはある程度GPUに興味を持ち、スペック以外の機能について調べている人でなければどうしたらいいのかわからないということが起きる。残念ながらYukariWhisperにはこの「初歩的な説明」が省かれており、対応などについてもあまり書かれていないのは、制作物を提供する側としていかがなものかと思ったほどである。ユーザーが皆「詳しいわけではない」のに。
したがって、1000番台以降で動くというのは実際には間違いであると言えて、正しくは以下のように書くべきであった。
- nVIDIA系GPUは1000番台以降のモデルで動作します。ただし、セットアップ後にそのままrun.batを実行して動作するのは3000番台以降です。それ以前のモデルについては、yukariwhisper.iniの
whisper_device = cuda_8
の部分を以下の通りに変更してください。- 1000番台:
whisper_device = cuda_8
の文頭にセミコロンをつけ、;whisper_device = cuda_8_32
のセミコロンを消して保存。 - 2000番台:
whisper_device = cuda_8
の文頭にセミコロンをつけ、;whisper_device = cuda_8_16
のセミコロンを消して保存。
- 1000番台:
このように書けば、初心者にも優しく分かりやすいものになり、問題なく使えたはずである。
ソースコードの不備を疑う
だが上記の文面において、実はcuda_8_16もcuda_8_32も何も弄っていないyukariwhisper.iniには存在しない。これは私が独自に書き加えたものである。設定ファイルの中身は単なるテキストでしかなく、その記述形式も理解しているため、改造も簡単にできる。だがこれはソースコード自体に変更を加えるものではないので、書き加えただけでは何も変わらない。
そこで私は、ソースコードの不備を疑った。ソースコードは9.7割がPython, 実行時に0.3割のバッチファイルという配分であるため、両方とも中身を見ることができる。もしソースコード自体に不備があるのなら、動くものも動かないはずだ。プログラムとは良くも悪くも「書かれた通りにしか動かない」ものだからだ。ある種、指示待ち人間にも似ている。
Pythonは何度も自分で書いたことがあり、オブジェクト指向で書けるものであるため、mainのスクリプトがあればそれを読むことでどこのスクリプトをクラスとして読み込み、どの関数を呼び出しているかといった動きを知ることができる。そうしていきついたのが、YukariWhisper\settings.py
である。これのある部分に諸悪の根源があった。
ここでCPU/GPUと浮動小数点判定を行っているわけだが、ここにはどこにもint8_bfloat16
がなく、寧ろ謎な記述がcuda_8_1
とint8_float16
である。これは明らかな記述ミスであると考えた。ここでOpenMNTの量子化のページで”Quantize on model loading”の箇所を見てもらうと分かるが、以下のものが使用できるようになっている。
- int8: 8ビット符号付整数型
- int8_float32: 32ビット符号付単精度浮動小数点型で読み込み、8ビット符号付整数型に量子化する
- int8_float16: 16ビット符号付半精度浮動小数点型で読み込み、8ビット符号付整数型に量子化する
- int8_bfloat16: bfloat16(Google独自規格16ビット符号付半精度浮動小数点)で読み込み、8ビット符号付整数型に量子化する
- int16: 16ビット符号付整数型
- float16: 16ビット符号付半精度浮動小数点型
- float32: 32ビット符号付単精度浮動小数点型
- bfloat16: bfloat16(Google独自規格16ビット符号付半精度浮動小数点型)
これらのことを考慮すると、本来はcuda_8_16
と書くところを何故か6をつけ忘れたということになるであろう。もし正しく記述されていればこれが利用できていたわけだが、しかし肝心のyukariwhisper.iniにはcuda_8_16はなく、逆にcuda_8の設定はsettings.pyには存在しないという、あまりにも酷いガバガバ設計である。これはプログラミング未経験者が絶対に気付かないことだ。
流石にここまで酷いと見過ごせないもので、ならば私が改造してしまえばいい。私はGitHubを持っていないのでどうせプルリクは送れない。つまり個人でしか使わないものになるわけだが、それなら何をしても自由だ。そしてここにその結果を置いておくことで、後続への説明資料としてエラーの渦から救えるはずだ。そしてさらに、今まで考えていたDAW経由のノイズ除去を試してみることにした。次項からはその結果も書いていく。
YukariWhisper(無許可)改造計画
settings.pyを改造する
まずは正常に動かない問題を引き起こしているsettings.pyを改造する。改造箇所は関数parse_common
内、設定ファイルから読み込んだモードの条件判定部分である。該当箇所について、元のものと比較する形でソースコードを掲載する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 前に他のコード w_device = ini_common.get('whisper_device') if w_device == 'cpu_8': self.whisper_device = 'cpu' self.compute_type = 'int8' elif w_device == 'cpu_32': self.whisper_device = 'cpu' self.compute_type = 'float32' elif w_device == 'cuda_16': self.compute_type = 'float16' elif w_device == 'cuda_8_1': self.compute_type = 'int8_float16' # 後に他のコード |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 前に他のコード w_device = ini_common.get('whisper_device') if w_device == 'cpu_8': self.whisper_device = 'cpu' self.compute_type = 'int8' elif w_device == 'cpu_32': self.whisper_device = 'cpu' self.compute_type = 'float32' elif w_device == 'cuda_32': self.compute_type = 'float32' elif w_device == 'cuda_8_32': self.compute_type = 'int8_float32' elif w_device == 'cuda_16': self.compute_type = 'float16' elif w_device == 'cuda_8_16': self.compute_type = 'int8_float16' elif w_device == 'cuda_8_b16': self.compute_type = 'int8_bfloat16' # 後に他のコード |
執筆時点で元のコードは既に修正を入れていたため、cuda_8_1
をint8_float16とするように設定ファイルの方にも変更を加えていたようだ。だが私個人としては非常に気持ち悪い書き方である。何故なら、int8_float16ならcuda_8_16
と書いた方が何を意図しているか分かりやすいためで、cuda_8_1
ではその意図が伝わらないと考えるからだ。内部処理的にはそれでいいのかもしれないが、ソースコードの可読性を考えたとき、これはすべきでない書き方だ。
また、1000番台に対応していると言いながら、修正でそのためのコードが一切追加されていないことも謎である。1000番台のPascalアーキテクチャはfloat16はハードウェアとしてサポートしているが、CUDA CCは6.1であり、CTranslate2ではfloat16のサポートはCUDA CC 7.0以上でなければならず、このことからcuda_8_16
では動作しない。利用可能な動作形式はfloat32またはint8_float32であり、現実的なのは後者である。そのためソースコードには別でint8_float32が存在すべきで、理想としては私が書いたものの方になる。言葉に偽りなくするなら、こうでなければならない。
なお、int8ないしint16は単体では殆ど使用されない形式のため省略している。
スポンサーリンク
スポンサーリンク
yukariwhisper.iniにも改造を加える
settings.pyを改造したら、yukariwhisper.iniにも対応するように改造を行う。この場合は追加したモードと対応するようにwhisper_device
の設定を追加する。現在は利用できないが、今後3000番台を導入してbfloat16が利用できるようになった場合に備えてint8_bfloat16も用意している。その上で、以下のように改造した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
;前の他の設定 ;whisper_device = cuda_8_b16 whisper_device = cuda_8_16 ;whisper_device = cuda_16 ;whisper_device = cuda_8_32 ;whisper_device = cuda_32 ;whisper_device=cpu_8 ;whisper_device=cpu_32 ;AI認識の処理をビデオカードで行うのか、cpuで行うのかを選択します。cpu処理はあまりに遅いので、数秒間処理が返ってきません。 ;cuda_8_b16=int8_bfloat16は3000番台(Ampere)以上が利用可能です。 ;cuda_16=float16およびcuda_8_16=int8_float16は2000番台(Turing)以上が利用可能です。 ;1000番台(Pascal)の方はcuda_8_32=int8_float32またはcuda_32=float32でのみ動作します。ただしcuda_32はVRAMを非常に多く消費するため非推奨です。 ;後に他の設定 |
デフォルトはint8_float16であり、それ以外はコメントアウトでいつでも変更できるようにした。cpu認識は精度、速度共に致命的で使い物にならないので使うことはないであろう。また、私のいつもの癖として、パラメータの解説を必ず書き加えるようにしている。こうすることで、何をどうしたらいいかが明確に分かり、この手のものを全く触ったことがない人でも、他のエラーやトラブルを起こすことなく変更できるはずだ。
小レポート:1050Tiでは使い物にならなかった
実はこれを書く前に、1000番台で使えるということで、GT1030というゴミを除いた1000番台エントリーモデルの中でも最安で、追加電源が不要でシンプルにH.264エンコードや軽量な3Dモデルを出力するに向いている1050Tiを調達し、検証しようと考えた。平均相場は5000~6000円という決して安くはない買い物であるが、案外安く手に入った。
それを使ってYukariWhisperの処理を代行させようと考えたが、前述の通りfloat16を使えなかったので、float32で動作させなければならない。だがfloat32はVRAM消費が異常に多くなって停止するので、軽量化のためにはint8_float32しか選択肢がない。それで実行してみたものの、認識精度はint8_float16よりも低く、確定までの処理時間も長く使い物にならなかった。リアルタイム性が重要な配信で使えないと分かったため、早々に取り外して売り飛ばしてしまったのである。
そのため、2GPU構成をしようと考えているのであれば、1000番台ではやめておくべきだ。もしやるのであれば、HEVCが不要ならTU117の1650を使うのが理想である。HEVCが欲しいならTU116またはTU106の1650になるが、補助電源として6ピンが必要になることに注意である。なお、1650の違いはPC Watchがまとめている。
制作サイド:ゆかコネのポート変更の自動追従設定
制作サイドでは、ゆかコネ接続時にポートが変更された場合に自動追従ができるようにコードが追加された。これはNao氏が直接プルリクを出しており、既に反映されたことが分かっている。変更点は次の通りである。
これでポートが変更されたとしても手動変更の煩雑さに悩まされることはなくなる。同時に起動エラーの原因の解消にもなるため、これで下手にソースコードを弄らせることもなくなる。使うならやはり無駄な手を加えなくていい方が、特にプログラミング未経験にとって優しいものだからだ。
制作サイド2:OSCポートを設定ファイルから変更可能に
実は公開直前にコミットがあったため確認すると、ゆかコネでVRC-OSCを有効にして、デフォルトの9000, 9001から変更していない場合に備えて、設定ファイルから使用ポートを変更できるように改良された。設定ファイルにはOSCアドレス(vrc_osc_adsress = 127.0.0.1
)とOSCポート(vrc_osc_port = 9001
)の2つのパラメータが追加され、これを書き換える形で指定するものとなる。基本的にOSCはローカルホストで使用するものなので、ポートだけ9002などに書き換えれば使用できるはずである。これでまた1つ、プログラミング未経験者が困る要素を解消することができたようだ。
実際の配信でのテスト
ハードウェアで使用の可否が問われる要素を除き、現時点で手を加えられるところはこれくらいである。この改造をもとに、実際の配信でテストすることにした。といってもテスト中に数値データを取ることはできていないため、結果はアーカイブに残すことにした。テストしたゲームはヒロアカと伝説のレジェンドである。その結果は以下である。
My Hero Ultra Rumble Season3 (Phase 5.5)
APEX Legedns after of Mid Update No.8
基本的に2080Tiに全てを負担させている関係で、配信関係で2GB前後、ゲーム本体で多くは4~5GB消費する。そこにYukariWhisperをint8_float16で起動させて、これがおおよそ2GB程度である。VRAM的にはかなりギリギリな状態で、最低3GB程度は空きVRAMがなければ動作に影響する。また、同時処理内容が多くなることから、処理の遅れによる遅延やエンコードラグが発生しやすくなり、これが原因でゲーム自体が重くなる、配信側でフレームが飛ぶということに繋がりやすい。そうなってしまうと到底使えるものではなくなってしまう。
結果としては、ヒロアカではギリギリ間に合っていたのだが、伝説のレジェンドはゲーム自体の遅延とフレーム飛びが発生し、使用不可能と判定した。ゲームによっては要求されるGPU性能、消費されるリソースは異なるため、こればかりは実際に使ってみないと使えるかどうかが分からない。伝説のレジェンドに関してはfloat16よりは動くがやはり足りないということになるので、使いたいならやはり1650を使って2GPU構成で負担を軽減させるしかないであろう。なお、2024年2月にはRTX 3050 6GBが発売される予定らしく、それをエンコード・機械学習代用として使う手がある。だが当面は高額なため、逆にそれによって1650が安くなる可能性があるので、それまで待って動くのもありだ。
代用:ノイズ除去を他の音声認識にも適用する
ところで検証中に気付いたのは、ノイズ除去を他の音声認識にも適用することである。これを今までしてこなかったのは、ブラウザ認識の場合は適用させるための設定が難しいためである。ただ、ゆかコネ音声認識比較記事を制作している段階では仮想オーディオデバイス経由でブラウザの音声認識ができたのだが、その後の配信では何故か仮想オーディオデバイス経由での認識ができなくなってしまった。これはEdge限定で起きた謎である。
通常はWindowsのシステムから既定デバイスを仮想オーディオデバイスにセットし、各ブラウザのマイク設定を既定にすることで可能になる。しかしEdgeはその設定を無視し、物理マイクを優先的に使用するように内部処理されているようで、どう頑張っても仮想オーディオデバイスからの音声を受け付けなかった。Chromeの場合はその設定で問題なく認識できたため、意味不明である。マイクソソフトと言わざるを得ない。
ところで、音声認識においてノイズは認識の阻害であることは明白で、そのせいで音声認識の2割ほどは不正確な認識になってしまうことは多い。それを除去すれば、他の音声認識でもほぼ理想とする音声認識結果を得られると思われる。ちなみにノイズ除去の方法としては、DAW等VSTプラグインを利用できるソフトを介して行う。経路は”マイク―ノイズフィルタ―仮想オーディオデバイス(出力)―仮想オーディオデバイス(入力)―各種音声認識”となる。音声認識に通すまでの過程が増えるため結果表示まで若干遅延が生じるということに注意が必要である。
不完全なものは自分で完全にしてしまえばいい
faster-whisperをベースにゆかコネでも使えるように調整し、仮想環境で本体に変更を加えることなく利用できるようにする。構想は確かに素晴らしいYukariWhisperであったが、いざ中身を見てみると不足な部分が多く、機械学習の応用ということもあって性能はGPUに依存する。その上でまともに使えるのはハイエンド・フラッグシップモデルのみで、使用する浮動小数点型の関係からNVIDIA系GPUでも2000番台以上になる。理想は3000番台以上だ。ただし、GPUアーキテクチャがTuring, CUDA CC 7.5となる(1630以外の)1600番台も、専用で使うのであれば間に合う可能性はある。
説明としては1000番台から使えるとは言っていたが、私が検証した結果1000番台では「動きはするが精度と速度は期待できない」という結果になった。しかも1000番台で動かすにはint8_float32でなければならないのだが、その設定はどういうわけか存在しなかった。結局対応させるためには不足部分を自分で記述するしかなかったわけで、元となるfaster-whisperとそれに関連するリファレンスを読みながら書くと、案外簡単に実装できたものである。私が持っているのは2080Tiなのでint8_float16が使用でき、int8_float32は使わなくても大丈夫であるが、1000番台に対応するために追加している。
これは個人で勝手にやったものであるのと、そもそも私はGitHubを持っていないためにプルリクは出すことなく、この改造は公式に反映されない。だがこの改造した結果を記事として残しておくことにより、これを見た人が勝手に自分の持っている方にコードを追加してくれることであろう。私ができるのはこれくらいであるが、うまく動かないと悩む人の助けとなれば幸いである。
以上、ゆかコネ検証報告:YukariWhisperをもっと快適に使う、であった。不完全なものは、改造できるのなら改造して完全にしてしまえばいい。
KIBEKIN at 00:00 Feb. 3rd, 2024
追記1:v0.0.3で改善
果たしてこの記事が効いたのかは不明だが、2/5に本体がv0.0.3にアップデートされた。その際に以下の変更・追加・削除が行われた。
- ゆかコネからポート番号を渡されたらそれを自動反映する
- ゆかコネ起動時にポート番号が変動する場合があり、それに対応するための変更である。
- OSCポート番号をyukariwhisper.iniから変更できるように修正
- VRC-OSCプラグインを有効にしているユーザー向けの対応。デフォルトでローカルホスト(127.0.0.1)、9001にセットされている。
- 2000番台以前で動作しない問題を修正
- settings.pyでCUDA CCでの判定に変更した。これに伴いyukariwhisper.iniにおいて
compute_type
が不要となるため廃止された。また、実用性のないCPU認識も廃止された。
- settings.pyでCUDA CCでの判定に変更した。これに伴いyukariwhisper.iniにおいて
- GPU選択が機能していない問題を修正
- ngwords.txtが機能していない問題を修正
- 一致比較の記述ミスの修正により機能するようになった。また、デフォルトのNGワードも修正された。
- yukariwhisper.iniのテキスト出力をデフォルトでONに変更
- テキスト出力に文字数も追加表示するようになった。
プログラミング未経験者にとってややこしい部分だったGPU問題は、起動時にCUDA CCバージョンを参照して、そこから最適な計算型を自動選択する形で解消された。これで動かないと悩むこともなくなるはずだ。その他各種のエラーも修正されており、これでようやく安心して使えるであろう。
追記情報
2024年2月6日 YukariWhisperがv0.0.3となり、各種問題が解決したことについて追記。
スポンサーリンク