Skip to content

v0.3.0 — 実機で漢字を喋り、実時間の要件を満たした

Latest

Choose a tag to compare

@ayutaz ayutaz released this 03 Sep 07:53
· 34 commits to main since this release
8155cb2

v0.3.0 — 実機で漢字を喋り、実時間の要件を満たした

2026-09-03。モデルの重みは v0.1.0 から 1 bit も変わっていません(v3 = Stage 3 を 80,000 step)。
変わったのは端末側のコード配布物です。


何が変わったか

1. スタックチャン(M5Stack CoreS3)で漢字かな交じり文を喋ります

ESP32-S3 の上だけで G2P が完結します。 ホスト側の前処理は要りません。
辞書 13.7 MB を flash から読み、形態素解析(Viterbi)→ アクセント規則 → NJD の各段 →
フルコンテキストラベル → 音素 ID まで、すべて端末の C 実装です。

! の印は要らなくなりました。 行をそのまま打つと、端末が 3 通りに振り分けます。

打った行 経路
今日は良い天気ですね。 辞書(漢字経路)
コンニチハ 辞書
きょ][おわよ][いて][んきです°ね かな(中間表現)
きょ][おわよ][んきです°ね。(中間表現に が混ざる) 位置つきで拒否(黙って別経路に回さない)

判定はかな G2P のトークナイザそのもので、ホスト側の scripts/to_intermediate.py
held-out 298 文 + 中間表現 298 行で 596/596 一致します。

2. 実時間の要件(RTF ≤ 0.5)を満たしました

v0.2.0 相当(9/2 夜) v0.3.0
満チャンク 1 pull の xRT 0.926 0.446
1 step 18,378,513 cyc 11,659,500 cyc
アンダーラン 1 / 14 0
鳴らし始めまで 719 ms 384 ms
内部 RAM の空き(起動直後) 99,987 B 132,039 B
arena(静的確保) 212,992 B 180,224 B

出力の波形は 1 bit も変わっていません(checksum 0xa69a7ebbb5ccb05f が 9/2 から同一)。
速くなったのは、捨てられる出力を計算しない・token を持ち越す・GELU のコード生成を直す・
D-cache の行を 64 B にする、といった出力を変えない変更の積み重ねです。

3. 音を聴きました

これまでの一致はすべて「OpenJTalk と同じ出力か」という機械的な照合で、
日本語として通用するかは誰も確かめていませんでした。実機の音を聴いて確認しました。

⚠️ 弱い証拠です: 聴取者 1 名、対照なし、盲検なし、尺度なし。
**「品質を測った」ではなく「破綻していないことを人が 1 度確かめた」**と読んでください。


⚠️ v0.2.0 から必ず入れ替えてください

配布していた int8 の重みを、現行コードが拒みます

v0.2.0 の saanotts-jp-v3-int8.binblob v1(643,936 B)で、
現行コードは起動時に SAAN_ERR_VERSION で止まります。v0.3.0 は v2(654,032 B)です。

blob v2 は int8 の conv 重みを [cout][k][align16(cin)] に 0 埋めして並べ直したもので、
PIE 命令に転置コピー無しで渡せるようにしたものです(+10,096 B)。数値は同じです。

配布していた firmware が、native USB だけの板で操作できませんでした

v0.1.1 / v0.2.0 のイメージはコンソール入力が UART0 でした。CoreS3 や AtomS3 は
USB-シリアル変換を持たないので、起動はするのに かな> に何を打っても届きません
v0.3.0 のイメージは USB Serial/JTAG 入力です。


配布物

資産 大きさ 何に使うか
m5-cores3-firmware-kanji-16mb.bin 16 MB M5Stack CoreS3 / スタックチャン。焼くだけ。漢字 + スピーカー + 画面
esp32s3-firmware-kanji-16mb-usbjtag.bin 16 MB ESP32-S3 DevKit(16 MB flash)。漢字。I2S DAC で発声
esp32s3-firmware-w8a8-pie-usbjtag.bin 8 MB 同(8 MB flash)。かな中間表現のみ(辞書なし)
saanotts-jp-v3-int8.bin 654,032 B 端末用の重み。blob v2
saanotts-jp-v3-fp32.bin 2,249,792 B ホストで動かす / ゴールデンテスト用
golden-v3-int8.bin / golden-v3-fp32.bin 各 779,584 B 参照出力(make -C csrc int8-golden / test
saanotts-jp-v3-stage4.pt 2,744,874 B PyTorch のチェックポイント(上の blob はここから再現できます)
k1-dict-438750.bin 13,702,320 B 漢字辞書(438,750 entries)。firmware に同梱済み

SHA256SUMS.txt に全資産のハッシュがあります。

焼き方(スタックチャン)

# ⚠️ 先に元のファームを退避してください
esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX read_flash 0 0x1000000 backup.bin

esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX write_flash 0x0 m5-cores3-firmware-kanji-16mb.bin

⚠️ --flash_mode qio を渡さないでください。 ヘッダが QIO になると ROM ローダが読めず、
ets_loader.c 78 を繰り返してブートループします。そのまま焼けば bootloader が起動後に
qio_mode: Enabling default flash chip QIO で切り替えます。

⚠️ 辞書 13.7 MB を含むので焼くのに約 4 分かかります。

焼けたら USB Serial/JTAG(115200)で繋ぎ、かな> に日本語の行をそのまま打ってください。

起動時に見える行

esp_psram: Found 8MB PSRAM device
saanotts: W8A8 + PIE 有効 / int8 blob を確認
saan_dict: esp_mmu_map OK: vaddr 0x3c960000
saan_dict: 辞書 OK: 見出し語 355768 / エントリ 438750 / 行列 1377x1377
saanotts: 起動直後: 内部 DRAM free 132039 B

今日は良い天気ですね。 を打つと 0xa69a7ebbb5ccb05f が出ます(この値は QEMU とも一致します)。


⚠️ できないこと・分かっていない食い違い

  • 音の品質は測っていません。 聴いたのは 1 名・数文・対照なしです。
  • 辞書を削ったので、ホストと音素の 0.32%が違います(n=298)。落ちた語は無音にならず、
    短く切り直されて誤読されます上毛 + )。
  • 端末に載らない後処理が 3 段あります(「何」の読み推定 / Sudachi の漢字読み修正 / 踊り字)。
    中間表現で 2.00% [1.15, 3.45] の文が食い違います。
  • アクセントの起伏が教師より 19% 大きいmagnitude_ratio 1.193)。過剰強調かは未判断です。
  • 発話全体で見た RTF は 0.55〜0.71 で、まだ 0.5 を超えます(最初の 38 フレーム分の助走が
    初回 pull に乗るため)。要件の分母が定義されていないので、両方を記録しています。
  • I2S の実サンプルレートの誤差は未測定です(ESP32-S3 に APLL がありません)。
  • 動作を確認した実機は M5Stack CoreS3 1 台だけです。

開発者向け

コマンドの名前が変わりました。 工程番号(K1〜K7)で付いていたファイル名を責務ベースにしました。

変更前 変更後
make -C csrc k2 / k4 / k4b / k5 / k6 / k7 jdict / accent / njd-rules / oj-heap / kanji-e2e / label-ids
csrc/k1dict.c k4_accent.c k4b_njd.c k7_label2ids.c jdict.c accent.c njd_rules.c label_ids.c

ESP32-S3 では W8A8 + PIE が既定になりました-DSAAN_ENABLE_PIE=1 は不要)。
W8A32 で測るときは -DSAAN_ENABLE_PIE=0 を明示してください。

CI が増えました: 参照実装との一致(リリースの fp32 資産を落として実行・陽性対照つき)と、
ゲートが CI で回るかの機械検査を足しました。

訂正(この版で見つけた欠陥)

  • C-055: 命令数が減っても実機で遅くなることがあります。GELU の最適化で objdump の命令数は
    70 → 59 に減ったのに、実機では 118 → 211 cyc/要素とになっていました
    (符号処理がスタック往復に落ちた)。QEMU でもマイクロベンチでも見えませんでした。
  • C-056: ゴールデンテストが NaN を「完全一致」と報告して通していました。
    出力の 27,136 サンプル中 26,368 が NaN なのに Pearson 1.000000 / SNR inf dB / 一致 と出ていました。
    比較関数の 3 つの分岐が揃って NaN を「一致」側に落としていたためです。修正し、
    毎回走る陽性対照を組み込みました。

全文は docs/decisions.md にあります。