Amazonのアソシエイトとして、ラズパイダ(raspida.com)は適格販売により収入を得ています。詳しくは当サイトの プライバシーポリシーをご覧ください。
今ならローカル環境でSTT(Speach to Text)ができるのかな、と考えていたら、ちょうどMoonshineが話題になっていました。
Moonshineはエッジデバイスでも動くことが紹介されていて、紹介にRaspberry Piとちゃんとあった。
結論からいうと、Pi 5でも動くけれど非力なCPUでは推論の処理とリアルタイム制の両立は難しいということです。
ただ、使用したUSBマイクが、44.1kHz固定のステレオ2chだったため、16kHz固定mono1chで別の方法を試したいと思いました。
コードはChatGPTとGeminiの両方で生成させました。最終的にGeminiの方が優れたコードを提案してきましたね。
Pi 5 × Moonshine STT 実装の試行錯誤
AIに提案してコードを書いてもらいました。色々な条件で試行錯誤してやったことは大きく5つです。
1.wavファイルリレー
最初にテストとして、コマンド(CLI)で、録音したwavファイル(44.1kHz固定のステレオ2ch)を16kHzのmono1chにリサンプリングして、そのwavファイルを読み込ませたら、結構な認識と速度でテキストが出力されました。
どうもPython内でリサンプリングは処理が大きい。
だったら、一旦3~5秒くらいのwavにして、それをリサンプリングしてからテキスト化のために読み込ませればいいのでは?と考えました。
wavにするけどメモリー内で保存すれば速度も間に合うかなと。
そこで、録音と推論の間に「RAMディスク(/dev/shm)」と「FFmpeg(外部プロセス)」を介在させるファイルリレー方式で試しました。これだとシステムの安定性と推論精度の両立において最も効果的でした。
とはいえ、そこまでまだ精度も上がりませんが。 以下、出力できたテキストです。まだハルシネーションが目立つ。
**[13:47:53]** では。MDファイルのテストを行います。はっきりとしゃべらないとなかな一つの
**[13:48:04]** けれども。1つの文章として表示されれば成功です。
**[13:48:07]** 「表示されれば成功」です。このしゃべったあとにですねえファイルがMD
**[13:48:14]** あファイルがMDファイルとして保存され
**[13:48:18]** 疲れ皆様に配る以上、終了です。。
2.メモリ(16GB)の有効活用
今回のPi 5はメモリー16GBモデルを選びました。大きなメモリ容量をうまく使いたいと考えたからです。
ファイルリレーで録音wavファイルをSDカードに書かず、メモリ上にWAVファイルとしてマッピングすることで、ストレージの寿命を気にせず、ディスクI/Oのボトルネックを排除した高速な受け渡しが可能になりました。
今回はUSBマイクの都合で、16kHzのmono1chでの録音ではないためデータ量は増えてしまいます。もし、16kHzのmono1chで最初から録音できればリサンプリング処理自体が必要ありません。
AIによると、仮に16kHzのmono1chならば、1時間くらい溜め込んでも600MBくらいだそうですから余裕ですね。
3.ハルシネーションの調整
推論中にテキストの複雑な加工(正規表現など)処理を行うと、推論エンジンへのリソース供給が不安定になり、AIが意味不明な言葉を生成する「ハルシネーション」が増大することが確認されました。
そのため、Raspberry PiのようなエッジデバイスでのSTTは、推論プロセスには極力計算のみに専念させ、後処理は最小限にすることが、結果的に認識精度を維持することができました。と、言っても精度は苦しいですけどね。
メモリーに保存するのに加えて、録音と推論を別プロセスに分離することで、オーバーフローは出なくなり、エラーがない安定性は確保できました。
リサンプリング処理を別コアで並列処理させる(thread)のもハルシネーションには有効でした。
加えて、Raspberry Piの「電源プラン」をパフォーマンス優先にしました。
ondemandだったので一時的にPerformanceの設定です。これも推論の処理の遅延を防止するのに有効でした。
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
4.USBマイク選びとALSAのバッファを拡張
Moonshineで使うモデルの誤認率はマイクの入力品質に大きく依存します。16kHzのmono1chが選べるUSBマイクを使いたいところです。
特に日本語のモデルはbaseしかなく、日本語が難しい言語でもあるため、マイクにかなり依存します。
今回、マイクの詳細は以下のコマンドで確認しました。
python3 -c "import sounddevice as sd; print(sd.query_devices())"
出力されたAK5371がUSBマイクです。
0 vc4-hdmi-0: MAI PCM i2s-hifi-0 (hw:0,0), ALSA (0 in, 2 out)
> 1 AK5371: USB Audio (hw:2,0), ALSA (2 in, 0 out)
2 sysdefault, ALSA (0 in, 128 out)
3 hdmi, ALSA (0 in, 2 out)
< 4 default, ALSA (0 in, 128 out)
念のため、次のコマンドでALSAのバッファを拡大して試しました。
export PA_MIN_LATENCY_MSEC=2000
export ALSA_CARD=1
まだ試していませんが、16kHzで直接録音ができるマイクなら、リサンプリングは必要なくなるため精度向上と低負荷を実現できそうです。
5.Markdownファイルへのリアルタイム追記
最終的には文字起こししたテキストは、mdファイルでSDカードに保存させました。wavファイルも書き出し保存させて後から修正できるようにします。
その他の知見
他にもいくつか試しています。エラーこそないものの調整が必要です。
「5秒録音・2秒重複」のスライディング方式
短い時間(3~5秒など)で区切っているため、文の途中で録音が切れてしまいます。これを次の録音で文脈が補完されるように、5秒録音したら2秒は次のために残す方法を採りました。
2秒分も含めて推論させることで、途切れた発言の取りこぼしを抑える効果と、文脈が繋がる助けになります。 これは取り入れた方が良いとは思います。有効でした。
しかし、そのままだと2秒分もテキスト出力されてしまいます。重複した部分を出力させないため、一旦比較して重複していたら出力しないようにもしました。
この重複除去はひとまず失敗。
勝手に付与される句読点の有無だったり、全く同じに比較はできないため、あまり意味がありませんでした。
重複除去は別の方法を試したいですね。
正規表現によるテキスト整形
出力されるテキストを整形させるのは、推論中に行わせることになるため、テキスト処理負荷が推論そのものを圧迫してハルシネーションが逆に増大してしまいました。 何言っているのか分からない状態です。
前述のテキスト比較処理などでもダメです。
表示ロジックの簡素化(生出力+最小限の置換)
CPUを推論に集中させることで精度は回復します。 発言を繋げて1行表示にしたのですが、それでも認識精度はダダ下がりです。 それよりも「データの正確性」を優先して、箇条書きなどで出力だけさせる方が無難でした。
終了処理のタイミングで整形させる方が良いですね。
スレッド処理(thread)とマルチプロセス処理(process)
この辺は詳しくないので、うまくAIに指示できませんでした。 Pi5もせっかく4コアありますから、コアの並列処理で割り振ると認識速度や推論速度は上がります。
だいぶ試行錯誤して、2つくらいは並列処理させて、プロセスも分けたりすることで速度はリアルタイム処理に近くなりました。2〜3秒。
■Pi 5は8GBモデルがオススメ
リアルタイム・リサンプリングは厄介だった
冒頭のように、44.1kHz固定のステレオ2chのUSBマイクではPi 5の処理能力を持ってしても、Python内での44.1kHzから16kHzへのリアルタイム・リサンプリング処理の負荷は無視できませんでした。
しかし、処理しないと扱えません。これはMoonshineが16kHz、mono1chで処理させる仕様だからです。
CPU負荷が高まるとバッファオーバーフローが発生し、推論が崩壊しました。何も推論できずにスタックしたみたいに映ります。スレッドやプロセスを分けて処理させても、リアルタイムのリサンプリング処理は結構なリソースを喰いますね。
そのまま処理できる音声データならこの処理は要らないので、音声認識ではUSBマイク選びが大事ですね。
3.5mmジャックがあればマイク選びは簡単でも、Pi 5のようにUSBマイクで16kHz対応の製品選びは少し難しい。
音声認識の精度向上
完全には思ったようなコードは実現できませんでしたが、精度向上のため次のような処理があると良いです。
- 解析間隔(DURATION)を少し広げる
- 重複除去などのバッファを強化
- 信頼度の低い極短文の無視
Moonshineは少し長めの音声を食わせたほうが、文脈判断が正確になり、謎めいた日本語が減ります。
「あー」「えー」などの単発の音を推論が拾いすぎて、仮に重複除去をさせたときに、判断が狂い全部出力されてしまいます。意味のない言葉を除去するのは必要ですね。
最後に
冒頭で触れたように、使用したAIはChatGPTとGeminiです。 結果的にGeminiの方がエラーの数は少なかったですし、すぐに調整に入ることができたので驚きました。
OpenAI API+VSCodeなどで作業させた方がコード作成は楽ですが、今回はチャット形式だけで済ませました。無料枠内です。(OpenAI APIは有料)
非エンジニアの私でも、AIさえ使えばそれなりにラズベリーパイで試行錯誤できます。 STTも一応は動作させられました。 ローカルSTTなので課金の心配はありません。通信詰まりもありません。
まぁ、精度に関してはコード次第といったところです。ハードウェアの性能から処理が遅いことは仕方ありません。それでも3秒くらいのタイムラグでテキスト出力させられます。 精度を上げれば速度が犠牲になりますから、歯がゆいですね。
16kHz、monoでキャプチャーできるマイクでまた試してみたいと思います。



