2026年 02月 03日

続 Conformer モールスデコーダ

-11dB(2500Hz BW) ぐらいまで90%、-15dB で50%デコードぐらいまではきたけどもういいかな……

モデル構造もちょっと変えた。Signal Head とかを Conformer の途中から出すようにして、2層分の Conformer をCTC Head だけに使うようにしてみた。ただあんまり意味があるのかは…… Conformerの層ごとの寄与度的なやつを出してみたけど、ほぼ Conv。Attn が意外に最初のほうで使われていてよくわからない。

あと内部の次元数も半分(256→128)にした。あんまり変わらないみたい。もっと減らして深くしたほうがいいのかも。わからない。

200ms 分の推論がスマフォ(Pixel7)で20ms前後。ちゃんと実用的に10バッチ同時にやるとかできたら格好いいかもだけどそこまでのモチベがなし……


人間だと -15dB (2500Hz BW) までは完コピできるみたいな論文?がでてくるんだけど、絶対盛ってるだろと思う。100Hz のフィルタ入れて 15wpm ならなんとか多少聞き覚えのある単語がわかるぐらい。CQはわかるけどコールは聞きとれない。https://cho45.github.io/morse-decoder-2026/demo.html で -15dB 設定して聞いてみてほしい。

2026年 02月 02日

Ossanのキマグレパーティ

Ossan.fm & キマグレエフエム 合同ファンミーティングに参加してきた。楽しくおしゃべりしました。

もともと ninjinkun の企画らしい。あいかわらず行動力たかくてすごい。

渋谷のタワレコのしばらくぶりに行ったけど雰囲気は変わらない感じ。1Fは推し活エリアみたいになってたけど。

というかとにかく渋谷がこわすぎて、ちょっと早めについたら居場所がなさすぎてタワレコに逃げ込んだという感じ。

アニメのポップアップストアとかアニメタイアップとかも多くて、なんかこのへんの融合がすすんでるなと思った

とにかく出不精が極まっていて、病的なというぐらいかもしれない。builderscon もファンミも行って酒飲むまで気がむちゃくちゃ重い。そして帰ってきてからしばらく気が重い。

イベントは行けば楽しいことはわかっていて、実際ほとんど杞憂なんだけど、この前後の気の重さがなあ……

2026年 02月 01日

builderscon::3DP #1

からくりすとさん来るぞというので、ビタビタのターゲティングを勝手に感じ取り絶対に行くぞと思い行ってきた。大変良かった。

カメラ付きでアラート出すプリンタとか、マルチカラーのプリンタとか羨ましいと思いつつ、まあそこまで今のプリンタに不満ないんだよなという……


Fusion の派生機能を知らなかったので試してみたい。エクスポートしたときどうなるのか不安に思ったけど個別に書かれるらしい。


転職ドラフトのビールスポンサーすごいなと思った。なんとなく世の中景気悪いイメージだったけど少人数(5人から)から支援しているとのこと。

会場のアンドパッド案内板がおしゃれだった。関係ないけどビルのエレベーターがうちのリビンクの広さぐらいあり、でかすぎて怖かった。


からくりすとさん、よく知らないCADを使ってるなと思ってたけど、 Rhinocerosらしい。ある程度シミュレーションしてるのかな?と思ってたけど、「動かせないCADなのでからくり作るには向かない」と言っていてびっくりした。なんとなく動くだろうい頭のなかでやってるらしい。天才すぎる。

動画にはなってない実用品の3Dプリントも格好良くて良かった。ますますファンになった。

あとよく軸に使っている虫ピンみたいなの、なんだろうなと思ってついに直接訊いてみたら、真鍮釘とのこと。釘で調べたことなかった


あと小さい3Dプリンタの話、3Dプリンタは「大は小を兼ねない」という感じの話もしていて良かった。完全に同意見です。自分もVoron0.1気に入ってる。

2026年 01月 31日

reload した後 h2oの古いプロセスが残り続ける場合

h2o は graceful restart をするのだが、稀に古いプロセスがずっと残ることがあって困ってた。(正確には start_server が restart させていて、h2o 自体は graceful shutdown しているだけ)

一例としてはこういう感じ (これは単に SIGHUP 直後なだけだけど、ときどき reload するとプロセスが残っていく)

 $  sudo systemctl status h2o
● h2o.service - H2O HTTP Server
     Loaded: loaded (/etc/systemd/system/h2o.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-01-10 17:36:22 JST; 2 weeks 4 days ago
    Process: 902234 ExecReload=/bin/kill -s HUP $MAINPID (code=exited, status=0/SUCCESS)
   Main PID: 1288 (perl)
      Tasks: 23 (limit: 2274)
     Memory: 53.0M (peak: 1015.0M swap: 12.2M swap peak: 95.1M)
        CPU: 1h 38min 58.048s
     CGroup: /system.slice/h2o.service
             ├─  1288 perl -x /usr/local/share/h2o/start_server --pid-file=/var/run/h2o.pid --port=0.0.0.0:80 "--port=[::]:80" --port=0.0.0.0:u443 "--port=[::]:u443" --port=0.0.0.0:443 "--port=[::]:443" -- /usr/local/bin/h2o -c /srv/www/h2o.conf.yaml
             ├─897714 /usr/local/bin/h2o -c /srv/www/h2o.conf.yaml
             ├─897716 neverbleed
             ├─897735 perl -x /usr/local/share/h2o/annotate-backtrace-symbols
             ├─902237 /usr/local/bin/h2o -c /srv/www/h2o.conf.yaml
             ├─902238 neverbleed
             └─902257 perl -x /usr/local/share/h2o/annotate-backtrace-symbols

結論: http*-graceful-shutdown-timeout を設定しよう

まず以下のような設定をし忘れないように。この例だと30秒で強制切断。デフォルトが0(無効)

http2-graceful-shutdown-timeout: 30
http3-graceful-shutdown-timeout: 30

メモ: タイムアウトの仕組み

HTTP/2 (lib/http2/connection):
  1. initiate_graceful_shutdown → 最初のGOAWAY送信
  2. 1000ms後 → graceful_shutdown_resend_goaway → 2回目のGOAWAY送信
  3. もし http2-graceful-shutdown-timeout > 0なら、その時間後に強制終了
HTTP/3 (lib/http3/server.c):
  1. 同様に1000ms後に2回目のGOAWAY
  2. もし http3-graceful-shutdown-timeout > 0なら、その時間後に強制終了
HTTP/1 (lib/http1.c):
  1. Keep-Alive をオフにしてレスポンス終了時 close

とにかくタイムアウトは全部設定しておこう!

調査

ただこの2つを設定していても接続が残るケースが発生して悩んだ

$  sudo ss -tapn | grep 902501                                                                                                                                                                                                              
ESTAB      0      0                              127.0.0.1:54080                                 127.0.0.1:5001  users:(("h2o",pid=902501,fd=66))                                                                                             
ESTAB      0      0                              127.0.0.1:46754                                 127.0.0.1:3000  users:(("h2o",pid=902501,fd=72))                                                                                             
CLOSE-WAIT 1      0                              127.0.0.1:52768                                 127.0.0.1:80    users:(("h2o",pid=902501,fd=74))                                                                                               

CLOSE-WAIT が残っている。

→ 最終的にこれは grafana の websocket への接続がずっと残っているというものだった。ブラウザの grafana タブを閉じたらプロセスが消えた。なんで CLOSE-WAIT?

websocket の proxy.timeout.io はデフォルト30秒らしいけど、通信が続くと一生切断されないのかも?

モールスにおける「SNR」や「受信限界」について曖昧なので Gemini にリサーチさせてみたけど、Gemini も困っていた。

SNR

SNR は帯域幅によって変化するので、帯域幅も同時に明示する必要があるが、書いてないことが多い。アマチュア無線の文脈だと 2500Hz (SSB帯域) 換算で述べられることが多いらしい。JT8 とかもそう。

機械学習だとサンプルレート基準で書いてあったりそもそも書いてなかったりなのでよくわからんが、とりあえず 2500Hz 表記に統一するのがよさそう。

dB/Hz 使うのが一番良いっぽいが、馴染みがない。(オフセットするだけだけど)

受信限界

CER (文字エラーレート) で何%を「受信限界」としているのかが人によって違う。

* 商業的定義(ITU-R F.339): 1%の文字誤り率(99%正解)を維持できる最小SNR。
* 限界性能定義: 50%程度の文字誤り率で、意味のある単語やコールサインの断片が抽出できる最小SNR。

2026年 01月 30日

モールス(時系列データ)の機械学習

機械に機械学習のモデルを設計してもらう(何もわからない)の続きで、モールスの機械学習をちまちま諦めずにやってる。

PCEN (Per-Channel Energy Normalization)

音声データは非常にダイナミックレンジが広いので、なんらかの方法でノーマライズしないと、いざ実世界の、スケール 40dBぐらい違うデータを推論しようとしてもうまくいかない。

PCEN はなんかそういう圧縮してスケールする正規化をモデルの中に組込むというやつ。ちゃんと実装できているかはわかなないが挙動からするとできているのだろう……

もう少し実践的なデモ

実際にオーディオストリームから広域をFFTしつつ、一部をクロップして連続推論するというデモをつくった (マイク入力に対応)。

「しばらく動かすと推論がエラーで止まる」という現象が発生してこまった。onnx のエラーが出るのだけど、WASM のポインタアドレスが throw されてくるのでまるで情報がない。

Python 側では起きないので、ずっとJS側の実装でメモリ管理がおかしいことを疑っていたが、結局 Claude Opus 君が「モデルのマスク管理がおかしいです!」ということに気付いてくれてなおすことができた。ひさびさに Claude に感動したわ。Python 側で再現しなかったのは長時間連続のストリーム推論することがないからだった。

先読みの実装

当初から「先読み」を実装していたつもりだったのに、この期に及んで、まったく先読みできていなかったことが発覚。わたくし衝撃です。先読みせずにそこそこ精度が出てたのはすごいが…… 一応文字が確定するタイミングでCTC発火するように調整していたので、非因果的なのは長点・短点・単語間空白などを予測するヘッドだけだったはずではある。機械学習、こういう間違った実装されてても、ある程度はなんとかなっちゃうことがあるので本当に怖い。

ということで、時系列データの場合とにかく因果性というのが問題になって、常に頭を悩ませる。こんがらがってくる。ラベル自体をオフセットするのか? アテンションに未来を見せるのか? そして、コーディングエージェントは時系列というものが苦手なので、人間がちゃんと理解してテストを書かせてないとダメなのだ…… 普通につらい。

前回も言ったけどコーディングエージェントはとにかくテストを書きたがらない。いくら言ってもダメ。AGENTS で指定しても意味あるのは初回ぐらい。テストで動作を保証する発想が一切ない。ゴミスクリプトをチョチョッっと作って実行してはゴミ掃除もせず放置する。

2026年 01月 28日

WebAudio-Signal-Generator

むか〜し書いた WebAudio-Signal-Generatorをモダンにした。AngularJS 1.2 + Bootstrap 3 だったのを Vue3 + Vanilla CSS に。というかそもそも ScriptProcessorNode 使っててマジで古かった。

ブラウンノイズ こんな感じで設定をURLに反映するようにして、リンクを貼れるように。

まぁもうこんなの欲しいとおもったときにLLMに書かせりゃいいという気はするけど