ハードウェアからサーバ・アプリまでワンストップで開発

Orange Pi 5でWhisperを試す

うまく認識してくれるかな?

先日、Orange Pi 5でCode Llamaを動かしてみた話を書きました。

Orange Pi 5でCode Llamaを試す

ここまで来たら、whisperも試さないではいられませんね。「プロジェクト (目玉親父|一反もめん)」としても、精度の高い音声認識が使えるのは必須ですから。

whisper.cppはOpenAIの作ったwhisperをC/C++のコードにしたものです。

whisper.cpp

Llama.cppよりもこちらの方が古いです。と言うか、whisper.cppのコードを元にLlama.cppが作られました。

それぞれについては、それぞれの情報を当たって下さい。

実はwhisper.cppは以前Mayumiが試してみたのですが、その時はまだ古いコードでもあったので速度がイマイチでした。そこであらためて試してみることにします。

インストール

Orange Pi 5でインストールするのは、そんなに難しいことではありません。

git cloneしてmakeするだけです。

$ git clone https://github.com/ggerganov/whisper.cpp.git
$ cd whisper.cpp
$ make -j 3

本当にこれだけ。今回はCPUだけでやります。

モデルが必要なので取って来ましょう。

$ ./models/download-ggml-model.sh large
$ ./models/download-ggml-model.sh medium
$ ./models/download-ggml-model.sh small

今回はパフォーマンス比較のために、3種類取ってみました。

これだけです。

動作確認がしてみたいので、とりあえずサンプル音源を探します。Hugging Faceに

openai/whisper-large-v2

というページがあって、ここにサンプル音源と内容が書いてあるので、これを試してみます。

音源はflac形式なのと、whisper.cppはwavしか受けつけないので、

$ ffmpeg -i sample2.flac sample2.wav

とやって変換します。

これを使って、

$ ./main -m models/ggml-large.bin sample2.wav 

とやって変換してやると、

$ ./main -m models/ggml-large.bin sample2.wav 
whisper_init_from_file_no_state: loading model from 'models/ggml-large.bin'
whisper_model_load: loading model
whisper_model_load: n_vocab       = 51865
whisper_model_load: n_audio_ctx   = 1500
whisper_model_load: n_audio_state = 1280
whisper_model_load: n_audio_head  = 20
whisper_model_load: n_audio_layer = 32
whisper_model_load: n_text_ctx    = 448
whisper_model_load: n_text_state  = 1280
whisper_model_load: n_text_head   = 20
whisper_model_load: n_text_layer  = 32
whisper_model_load: n_mels        = 80
whisper_model_load: ftype         = 1
whisper_model_load: qntvr         = 0
whisper_model_load: type          = 5
whisper_model_load: mem required  = 3557.00 MB (+   71.00 MB per decoder)
whisper_model_load: adding 1608 extra tokens
whisper_model_load: model ctx     = 2951.27 MB
whisper_model_load: model size    = 2950.66 MB
whisper_init_state: kv self size  =   70.00 MB
whisper_init_state: kv cross size =  234.38 MB

system_info: n_threads = 4 / 8 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | COREML = 0 | OPENVINO = 0 | 

main: processing 'sample2.wav' (227440 samples, 14.2 sec), 4 threads, 1 processors, lang = en, task = transcribe, timestamps = 1 ...


[00:00:00.000 --> 00:00:05.500]   Before he had time to answer, a much-encumbered Vera burst into the room with the question,
[00:00:05.500 --> 00:00:09.000]   "I say, can I leave these here?"
[00:00:09.000 --> 00:00:13.720]   These were a small black pig and a lusty specimen of black-red game-cock.


whisper_print_timings:     load time =  2127.27 ms
whisper_print_timings:     fallbacks =   0 p /   0 h
whisper_print_timings:      mel time =    57.46 ms
whisper_print_timings:   sample time =    61.95 ms /    57 runs (    1.09 ms per run)
whisper_print_timings:   encode time = 36251.94 ms /     1 runs (36251.94 ms per run)
whisper_print_timings:   decode time =  6692.32 ms /    57 runs (  117.41 ms per run)
whisper_print_timings:    total time = 45531.46 ms

というように処理されます。左側にタイムスタンプのようなものが出ている行が、処理結果です。

[00:00:00.000 --> 00:00:05.500]   Before he had time to answer, a much-encumbered Vera burst into the room with the question,
[00:00:05.500 --> 00:00:09.000]   "I say, can I leave these here?"
[00:00:09.000 --> 00:00:13.720]   These were a small black pig and a lusty specimen of black-red game-cock.

と出ていますね。

ちなみに、元の文章は、

Before he had time to answer, a much-encumbered Vera burst into the room with the question,—'I say, can I leave these here?' These were a small black pig and a lusty specimen of black-red game-cock.

ということなので、うまく処理できているようですね。

モデルをいろいろ試してみる

この例を見ると、処理には

whisper_print_timings:    total time = 45531.46 ms

ということで、約45秒かかったようです。元の音声が14秒ほどなので、リアルタイムよりも余分にかかっているようです。これだと厳しいので、もうちょっと速くならないか実験してみましょう。

まずは、mediumモデルを試してみます。

$ ./main -m models/ggml-medium.bin sample2.wav 
whisper_init_from_file_no_state: loading model from 'models/ggml-medium.bin'
whisper_model_load: loading model
whisper_model_load: n_vocab       = 51865
whisper_model_load: n_audio_ctx   = 1500
whisper_model_load: n_audio_state = 1024
whisper_model_load: n_audio_head  = 16
whisper_model_load: n_audio_layer = 24
whisper_model_load: n_text_ctx    = 448
whisper_model_load: n_text_state  = 1024
whisper_model_load: n_text_head   = 16
whisper_model_load: n_text_layer  = 24
whisper_model_load: n_mels        = 80
whisper_model_load: ftype         = 1
whisper_model_load: qntvr         = 0
whisper_model_load: type          = 4
whisper_model_load: mem required  = 1899.00 MB (+   43.00 MB per decoder)
whisper_model_load: adding 1608 extra tokens
whisper_model_load: model ctx     = 1462.58 MB
whisper_model_load: model size    = 1462.12 MB
whisper_init_state: kv self size  =   42.00 MB
whisper_init_state: kv cross size =  140.62 MB

system_info: n_threads = 4 / 8 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | COREML = 0 | OPENVINO = 0 | 

main: processing 'sample2.wav' (227440 samples, 14.2 sec), 4 threads, 1 processors, lang = en, task = transcribe, timestamps = 1 ...


[00:00:00.000 --> 00:00:05.760]   Before he had time to answer, a much-encumbered veerer burst into the room with the question.
[00:00:05.760 --> 00:00:08.960]   I say, "Can I leave these here?"
[00:00:08.960 --> 00:00:13.760]   These were a small black pig and a lusty specimen of black-red gamecock.


whisper_print_timings:     load time =  4494.99 ms
whisper_print_timings:     fallbacks =   0 p /   0 h
whisper_print_timings:      mel time =    44.64 ms
whisper_print_timings:   sample time =    57.32 ms /    57 runs (    1.01 ms per run)
whisper_print_timings:   encode time = 19317.77 ms /     1 runs (19317.77 ms per run)
whisper_print_timings:   decode time =  4392.28 ms /    57 runs (   77.06 ms per run)
whisper_print_timings:    total time = 28571.97 ms

結果は正しいようですが、約28秒かかってます。

ではsmallモデルだとどうでしょうかね。

$ ./main -m models/ggml-small.bin sample2.wav 
whisper_init_from_file_no_state: loading model from 'models/ggml-small.bin'
whisper_model_load: loading model
whisper_model_load: n_vocab       = 51865
whisper_model_load: n_audio_ctx   = 1500
whisper_model_load: n_audio_state = 768
whisper_model_load: n_audio_head  = 12
whisper_model_load: n_audio_layer = 12
whisper_model_load: n_text_ctx    = 448
whisper_model_load: n_text_state  = 768
whisper_model_load: n_text_head   = 12
whisper_model_load: n_text_layer  = 12
whisper_model_load: n_mels        = 80
whisper_model_load: ftype         = 1
whisper_model_load: qntvr         = 0
whisper_model_load: type          = 3
whisper_model_load: mem required  =  743.00 MB (+   16.00 MB per decoder)
whisper_model_load: adding 1608 extra tokens
whisper_model_load: model ctx     =  464.68 MB
whisper_model_load: model size    =  464.44 MB
whisper_init_state: kv self size  =   15.75 MB
whisper_init_state: kv cross size =   52.73 MB

system_info: n_threads = 4 / 8 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | COREML = 0 | OPENVINO = 0 | 

main: processing 'sample2.wav' (227440 samples, 14.2 sec), 4 threads, 1 processors, lang = en, task = transcribe, timestamps = 1 ...


[00:00:00.000 --> 00:00:05.720]   Before he had time to answer, a much encumbered Vera burst into the room with the question,
[00:00:05.720 --> 00:00:09.000]   "I say, can I leave these here?"
[00:00:09.000 --> 00:00:13.760]   These were a small black pig and a lusty specimen of black-red gamcock.


whisper_print_timings:     load time =   460.09 ms
whisper_print_timings:     fallbacks =   0 p /   0 h
whisper_print_timings:      mel time =    43.00 ms
whisper_print_timings:   sample time =    53.67 ms /    56 runs (    0.96 ms per run)
whisper_print_timings:   encode time =  6089.59 ms /     1 runs ( 6089.59 ms per run)
whisper_print_timings:   decode time =  2289.77 ms /    56 runs (   40.89 ms per run)
whisper_print_timings:    total time =  9072.40 ms

結果はちょっと微妙ですが、約9秒になりました。リアルタイムよりも速くなったので、実用できますね。

日本語

日本語がどうなるか試してみましょう。

あまり意地悪なのを使ってもアレなので、ちゃんとしたアナウンサーがしゃべったものを使ってみます。

秋を感じる「四季の茶道具 月光満天」田部美術館で始まる(島根・松江市)

私(生越)の前々職の番組から。1分21秒の長さがあるようです。しゃべっている内容は↑のページに出ています。

まず音声をダウンロードします。whisper.cppは16kHzのサンプルレートの音声しか処理できないので、まずはサンプルレート変換

$ ffmpeg -i sampleTSK.mp3 -ar 16000 sampleTSK.wav

これを試してみます。

まずはlargeモデル。言語指定をしてやってます。

$ ./main -l ja -m models/ggml-large.bin sampleTSK.wav 
whisper_init_from_file_no_state: loading model from 'models/ggml-large.bin'
whisper_model_load: loading model
whisper_model_load: n_vocab       = 51865
whisper_model_load: n_audio_ctx   = 1500
whisper_model_load: n_audio_state = 1280
whisper_model_load: n_audio_head  = 20
whisper_model_load: n_audio_layer = 32
whisper_model_load: n_text_ctx    = 448
whisper_model_load: n_text_state  = 1280
whisper_model_load: n_text_head   = 20
whisper_model_load: n_text_layer  = 32
whisper_model_load: n_mels        = 80
whisper_model_load: ftype         = 1
whisper_model_load: qntvr         = 0
whisper_model_load: type          = 5
whisper_model_load: mem required  = 3557.00 MB (+   71.00 MB per decoder)
whisper_model_load: adding 1608 extra tokens
whisper_model_load: model ctx     = 2951.27 MB
whisper_model_load: model size    = 2950.66 MB
whisper_init_state: kv self size  =   70.00 MB
whisper_init_state: kv cross size =  234.38 MB

system_info: n_threads = 4 / 8 | AVX = 0 | AVX2 = 0 | AVX512 = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | COREML = 0 | OPENVINO = 0 | 

main: processing 'sampleTSK.wav' (1299017 samples, 81.2 sec), 4 threads, 1 processors, lang = ja, task = transcribe, timestamps = 1 ...


[00:00:00.000 --> 00:00:08.000]  松江市の田辺美術館で開かれている季節展「四季の茶道具月光満点」。
[00:00:08.000 --> 00:00:20.000]  田辺美術館では季節に合わせた茶道具などを紹介する展覧会を開催していて、今回は月光満点をテーマに秋にまつわる作品11点が展示されています。
[00:00:23.000 --> 00:00:40.000]  「まだまだ残暑が厳しいということもありますが、こういう秋を感じさせるようなお道具を見ていただいて、秋の深まりをちょっとずつ意識していただきたいなと思っております」
[00:00:40.000 --> 00:00:51.000]  こちらは染め付けぶどう棚「水差し」。この時期、島根でも出荷の再生期を迎えるぶどうが表現されています。
[00:00:52.000 --> 00:01:02.000]  またこちらは、花濃伊勢院室「弓張月図」。三日月が描かれ、秋らしい優雅な雰囲気を感じさせます。
[00:01:02.000 --> 00:01:10.000]  大名茶人松平不舞が月が出ているのどかな風景を読んだ歌も添えられています。
[00:01:10.000 --> 00:01:19.000]  「四季の茶道具月光満点」は松江市の田辺美術館で11月5日まで開かれています。
[00:01:19.000 --> 00:01:23.000]  [音楽]


whisper_print_timings:     load time =  2122.67 ms
whisper_print_timings:     fallbacks =   0 p /   0 h
whisper_print_timings:      mel time =   138.78 ms
whisper_print_timings:   sample time =   492.30 ms /   376 runs (    1.31 ms per run)
whisper_print_timings:   encode time = 164799.44 ms /     4 runs (41199.86 ms per run)
whisper_print_timings:   decode time = 54798.51 ms /   374 runs (  146.52 ms per run)
whisper_print_timings:    total time = 222698.48 ms

処理に4分弱かかったようですね。内容を見ると、「田辺」とか「満点」のような同音のものを間違えているようですね。これは人間が聞いてもそうだと思うので、しょうがないと思います。

同じことをsmallでやってみましょう。長いので結果だけ。

[00:00:00.000 --> 00:00:08.000]  松井市の田舗美術館で開かれている季節展「四季の砂道具 月光満点」
[00:00:08.000 --> 00:00:13.000]  田舗美術館では季節に合わせた砂道具などを紹介する展覧会を開催していて
[00:00:13.000 --> 00:00:20.000]  今回は月光満点をテーマに「秋にまつわる作品11点」が展示されています
[00:00:23.000 --> 00:00:33.000]  まだまだ残書が厳しいということもありますが、こういう秋を感じさせるようなお道具を見ていただいて
[00:00:33.000 --> 00:00:39.000]  秋の深まりをちょっとずつ感じていただきたいな、意識していただきたいなと思っております
[00:00:39.000 --> 00:00:45.000]  こちらは「染めつけ武道」だな水差し
[00:00:45.000 --> 00:00:50.000]  この時期、島根でも出荷の再生期を迎える武道が表現されています
[00:00:52.000 --> 00:00:57.000]  またこちらは「可能遺跣院室」「弓張りづき酢」
[00:00:57.000 --> 00:01:02.000]  「三日月が描かれ、明らしい有がな雰囲気を感じさせます」
[00:01:02.000 --> 00:01:10.000]  大名茶人松田良不眉が月が出ている喉かな風景を呼んだ歌も添えられています
[00:01:10.000 --> 00:01:18.000]  「四季の砂道具 月光満点」は松井市の田舗美術館で11月5日まで開かれています
[00:01:18.000 --> 00:01:20.000]  ん
[00:01:20.000 --> 00:01:21.180]  「オリジナル」


whisper_print_timings:     load time =   385.59 ms
whisper_print_timings:     fallbacks =   1 p /   0 h
whisper_print_timings:      mel time =   158.63 ms
whisper_print_timings:   sample time =   422.80 ms /   404 runs (    1.05 ms per run)
whisper_print_timings:   encode time = 31726.23 ms /     5 runs ( 6345.25 ms per run)
whisper_print_timings:   decode time = 17976.09 ms /   401 runs (   44.83 ms per run)
whisper_print_timings:    total time = 50823.27 ms

処理には50秒ほど。結果を見ると、いろいろつっこみどころがありますね。確かにまぁ、そう聞こえなくもない。もっともこれはちゃんと訓練されたアナウンサーのしゃべったものなので、その分を思ってもちょっと残念かも?

同じものを、OpenBLASを使ってbuildしたもので試してみました。

whisper_print_timings:     load time =   403.95 ms
whisper_print_timings:     fallbacks =   1 p /   0 h
whisper_print_timings:      mel time =   158.68 ms
whisper_print_timings:   sample time =   407.26 ms /   399 runs (    1.02 ms per run)
whisper_print_timings:   encode time = 175140.17 ms /     5 runs (35028.04 ms per run)
whisper_print_timings:   decode time = 77944.77 ms /   396 runs (  196.83 ms per run)
whisper_print_timings:    total time = 254196.48 ms

なんと、逆に遅くなってしまいました。既にCPUだけを使う範囲では十分に最適化されているようですね。

まとめ

whisper.cppは、smallモデルを使うのであれば、Orange Pi 5でも実用になる速度となりました。問題は認識精度ですが、これくらいなら許容範囲ではないかと、個人的には思います。そもそも、音声認識は人間でも下手な人は少なくないですからね。

モデルは大きいほど精度は上がるようですが、処理時間が伸びます。オフラインなら我慢していれば良いかも知れません。

現状、CPUだけを使った処理では、下手なライブラリを使うよりは、そのままコンパイルしたものの方が速いようです。この辺は、また事情で変わるでしょう。

とりあえずはこんなところです。

最近のエントリー

最近のできごと

Hieronymusのインボイス番号対応について

会計システム「Hieronymus」の現状

OrangePi5にZabbixをインストールする

レビュー等の依頼について

オープンソースのノートアプリ「SiYuan」 - CasaOS AppStoreレビュー

お気に入りの色さがし1

創立記念日

現在の営業品目(2)

現在の営業品目(1)

SPDX License Listをデータ化した

Orange Pi5でC3TR-Adapterを試す

CasaOS上で会計システム「Hieronymus」を動かす

会計システム「Hieronymus」v1.0.0 リリースしました

CasaOSでファイル同期アプリSyncthingをセットアップする

第11回 Freshmeat

オープンソースノーコード「Activepieces」でワークフローを作る

RaspberryPiにパーソナルクラウドOS「CasaOS」を導入する

sequelize-cliでdb:migrateすると「SyntaxError: Unexpected token ':'」が出る

LED行燈の試作(2)