Raspberry Pi Pico 2 Wで動作する、ゲーム用超小型ワールドモデル
A tiny world model running games on a Raspberry Pi Pico 2 W (github.com)
要約
本記事は、Raspberry Pi Pico 2 W上で動作する、2つのニューラルネットワークを用いた小型ワールドモデル(World Model)とエージェント(Actor)のシステムについて解説しています。このシステムは、ゲームの状態を予測するワールドモデルと、その予測に基づいて行動を選択するエージェントを組み合わせ、限られたリソースのマイクロコントローラー上でゲームを生成・プレイします。
全文翻訳
Raspberry Pi Pico 2 Wで動作する、ゲーム用超小型ワールドモデル
Raspberry Pi Pico 2 W上で、同時にゲームを生成・プレイする2つの小型ニューラルネットワークを使用します。1つ目のネットワークはゲームを生成します。現在のフレームとアクションを入力として、次のフレームと報酬を予測します。2つ目のネットワークはそのゲームをプレイします。生成されたフレームを使用し、アクションを選択します。
[画像: tinyworldformer: games created by a tiny looped transformer on a Raspberry Pi Pico 2 W]
上の各フレームはワールドモデルネットワークによってサンプリングされ、アクションはアクターネットワークによって取られました。
なぜ
ループ型トランスフォーマー(Looped Transformer)に興味を持ちました。これは、異なる層を積み重ねる代わりに、1つのトランスフォーマーブロックを同じ重みで複数回適用するものです。重みを再利用することはメモリ効率が良いので、ループ型ワールドモデルがマイクロコントローラーで実行できるほど小さいかどうかを確認したかったのです。Pico 2 Wを選んだのは、安価でFPUを搭載しているためです。また、小型ゲームを選んだのは、実際のバージョンをNumPyで直接記述し、生成された各フレームを検証できたからです。その後、ストリーミングRLエージェントを追加し、ボードが自身のドリーム内で学習できるようにしました。
目次: 2つのネットワーク · ゲーム · ワールドモデル · エージェント · 量子化 · チップ上 · フラッシュ · ブラウザ · 再現 · コードレイアウト
ワールドモデルとアクター
| ネットワーク1: ワールドモデル | ネットワーク2: アクター | |
|---|---|---|
| 仕事 | フレーム + アクション → 次のフレーム + 報酬 | フレーム → アクション |
| アーキテクチャ | ループ型トランスフォーマー, q int8 | 2つの隠れ層を持つMLP, float32 |
| サイズ | 26k–131k パラメータ | 9k–16k パラメータ |
| フレームあたりのコスト | 2–8M 乗算・加算 | 9k–16k 乗算・加算 |
| 学習方法 | 記録された実ゲームで学習 (方法) | Stream-x を使用、実ゲーム内またはドリーム内で学習 (方法) |
[画像: the world model and the actor]
サイズはゲームによって異なります。プレイ可能にするためには、一部のゲームではより多くのパラメータが必要です。パラメータ数を小さく保つように努めました。
チップ上では、どちらのネットワークも実ゲームを使用しません。アクターはワールドモデルからのフレームのみを参照し、ワールドモデルはアクターからのアクションのみを受け取ります。両方ともRP2350のコア1で実行され、コア0はUSBとディスプレイを処理します。
事前学習済みアクターは、新しいStream-xエージェントと交換可能で、ボード上でゼロから学習します。また、ボタンを押せばいつでも操作権を奪うことができるため、自分でドリームをプレイすることも可能です。
ゲーム
各ゲームは src/tinyworldformer/games/ にある数百行のNumPyコードで構成されています。小さなトークンIDのグリッドにレンダリングされ、1つのフレームが常にゲーム状態全体を保持するため、隠れ状態はフレームに描画される必要があります。ボールの進行方向はトークンの一部であり、Doom-liteにはミニマップがあります。
| ゲーム | フレーム | ルール | ランダムイベント | ワールドモデル | 正確なフレーム、2ループ |
|---|---|---|---|---|---|
| Breakout | 20×16 | パドル、ボール、56個のブロック、5回のライフ | ミス後のサーブ | 26k パラメータ、4.3M MACs | 99.99% |
| Doom-lite | 32×32 | 8×8の迷路のレイキャスト一人称視点;敵が最短経路であなたに向かって歩く;あなたは撃つ | 敵のリスポーン | 55k パラメータ、4.2M MACs | 83.6% (下記参照) |
| Flappy Bird | 24×24 | 重力、フラップ、スクロールするパイプ | 次のパイプの間隔の高さ | 46k パラメータ、2.0M MACs | 100% |
| Pong | 20×32 | 遅れて反応するCPUパドルとの対戦、5点先取 | ポイント後のサーブ | 46k パラメータ、2.3M MACs | 99.45% |
| Pac-Man lite | 48×48 | 23×21の迷路、249個のペレット、最短経路をたどる2体のゴースト、3回のライフ | ゴーストのリスポーン | 131k パラメータ、8.2M MACs | 86.4% |
「正確なフレーム」は、ホールドアウトされた遷移のうち、予測されたフレームの全セルが実際のゲームと一致した割合です。ランダムイベント直後のフレームは、サンプリングのみ可能で予測できないため除外されます。Doom-liteの3Dビューは768セルを持ち、単一のシェーディングされた壁ピクセルがずれるだけでフレーム全体が誤りとみなされるため、その数値は実際のプレイよりも悪く見えますが、ゲーム状態はほとんど常に正しいです(ドリームの検証を参照)。
ターミナルで実際のゲームをプレイできます: uv run python -m tinyworldformer.games.play --game pacman (または breakout, doomlite, flappy, pong)。
ワールドモデルの仕組み
フレームをトークンに分割
フレームは正方形のパッチに分割されます。Breakoutでは2×2セル、Doom-lite、Flappy Bird、Pongでは4×4、Pac-Manでは6×6です。これにより、ゲームごとに36から80のトークンが得られます。パッチの埋め込み(embedding)は、セルごとの学習済みベクトルと、パッチ内の各位置に対する個別のテーブルの合計であり、セルの順序が失われないようにします。各トークンには位置ベクトルとアクションのベクトルも追加されます。
ループ型トランスフォーマー
モデルは、単一のトランスフォーマーブロック(アテンションとReLU MLP)で、1セットの重みを使用し、1から4回適用されます。各パスの前に、入力が再度追加されます。各パス後の出力は次のフレームに対して学習されるため、実行時にはループ回数によって速度と精度のトレードオフが発生します。Picoでは、まともなフレームレートを維持するために2回のループを使用しています。
アニメーションは、ホールドアウトされたDoom-liteの1ステップを示しています。1回のパス後、モデルは存在しない敵を描画し、さらにパスを重ねるごとにその一部が除去されます。誤りは92、35、12、そして0となります。右側のバーは、各ループ回数が正確に正解したホールドアウトフレームの割合です。
以下は、1、2、3、4回のループ後にデコードされたドリームのDoom-liteステップで、赤いボックスは実際のルールと異なるセルを示しています。
ループ回数ごとの正確なホールドアウトフレーム:
| ループ数 | Breakout | Doom-lite | Flappy Bird | Pong | Pac-Man |
|---|---|---|---|---|---|
| 1 | 99.68% | 59.8% | 99.99% | 98.47% | 79.6% |
| 2 | 99.99% | 83.6% | 100% | 99.45% | 86.4% |
| 3 | 100% | 85.9% | 100% | 99.60% | 85.7% |
| 4 | 99.96% | 84.7% | 100% | 99.53% | 84.4% |
デコーディング
モデルは、各セルと各トークンに対するスコアを出力します。これらのスコアをフレームに変換することは、モデル自体と同じくらい重要であることが判明し、3つのルール(上記のPongで示されている)になりました。
- 背景セル(壁、ブロック、ペレット、床)は、それぞれ独立に最も可能性の高いトークンを選択します。
- シングルトン(ボール、プレイヤー、各ゴースト)は、すべての(セル、トークン)ペアの中から1つの選択で配置されるため、各シングルトンは常に1つだけ存在します。ランダムイベントの後、選択はベストから2.2ロジット以内の候補の中からサンプリングされるため、ワールドモデルはそれらを平均するのではなく、新しいサーブやリスポーンをサンプリングします。
- Pac-Manとゴーストは2×2のスプライトですが、状態を持つのは各スプライトの1つのセルのみです(口が進行方向を保持し、ゴーストの左下セルがその下にペレットがあるかどうかを保持します)。他の3つのセルはそこからスタンプされ、予測されません。
ルール2を実装したのは、最初のBreakoutモデルが2つのボールを描画したためです。グローバルな単一選択により、正確なフレームが91.6%から99.7%に向上しました。
ワールドモデルの検証
記録されたデータに対する精度だけでは不十分でした。エージェントは、データには存在しない状態にドリームを誘導し、そこでのあらゆる間違いは悪用される可能性があります。そのため、主な検証はエージェントがプレイ中に実行されます。ドリームフレームからゲーム状態を読み取り(games/<game>.py: read)、その状態とエージェントのアクションに実際のルールを適用し、結果を次のドリームフレームと報酬と比較します。
ゲームごとに5,000ドリームステップ(2ループ)を、出荷されているエージェント(training/evalwm.py --agent)で実行します。
| ゲーム | 不正なゲーム状態 | 不正な報酬 | ステップあたりの不正なセル |
|---|---|---|---|
| Breakout | 読み取り不可;不可能なトークンを含むフレームなし | 6 (ブロックが壊れずに支払われた) | – |
| Doom-lite | 1 | 0 | 1.50 |
| Flappy Bird | 0 | 0 | 0.00 |
| Pong | 1 | 0 | 0.00 |
| Pac-Man | 742 | 38 | 0.39 |
トレーニング
まず、エージェントが実ゲームをプレイし、各ステップが1つの例(フレーム、アクション、次のフレーム、報酬)として記録されます。ゲームごとに100万〜300万件です。一部のエピソードは、ブロックに穴が開いている、またはランダムな位置にあるなどの異常な状態から開始するため、データはゲームのより多くの部分をカバーします。
次に、ワールドモデルはセルごとのクロスエントロピー損失で次のフレームを予測するように学習します。30,000ステップで、最初の3〜4kステップはfloatで実行され、その後int8丸めがオンになります(QAT)。
int8の重みはRustソースコードに書き込まれ、floatは正確なビットとして保存されます。cargo testで検証し、Pythonの整数仕様とビット単位で一致することを確認してから、ファームウェアをPicoで実行します。
ドリーム内で学習したエージェントは、記録されたデータには存在しない状態に到達するため、そのエージェントで実ゲームを記録し、すべてをファインチューニングします。
異なるゲームのエージェントは、それらを悪用し始めたため、いくつかの修正を行いました。
- 「このセルが変化した」という情報で損失を重み付けすると、モデルは変化を予測する傾向があり、2つのボールを描画し、2つのブロックを破壊しました。入力フレームで何が起こりうるかという場所で重み付けし、グローバルなシングルトン選択と組み合わせることで修正されました。
- ドリーム内のBreakoutエージェントは、ブロックがコーナーに残ったままになったため、26個のブロックが残った状態で154点を獲得しました。Doom-liteエージェントは5マス離れた場所からスナイプすることを学習しました。
- ドリーム内でのみ学習したエージェントは、ドリーム内で210点(実ゲームの最大値は56点)を獲得しましたが、実ゲームでは28点でした(World Models (Ha & Schmidhuber) の予測通り)。各収集・ファインチューニングラウンドで、エージェントが見つけたエクスプロイトが修正されました。
- Doom-liteのドリームでは、敵のスプライトが失われ、二度と描画されなくなることがありました。モデルがミニマップから再描画する代わりに、前のビューをコピーしていたためです。ノイズ拡張で修正されました(
train --noise 0.3、GameNGenと同様)。入力ビューにはランダムなピクセルが追加され、スプライトが失われることがあります。 d=32では、より広いBreakoutデータを吸収できませんでしたが、d=48では可能でした。Pac-Manの48×48フレームにはd=64が必要でした。
エージェント: Stream-x
エージェントはStream AC(λ)(Elsayed, Vasan & Mahmood, 2024)を使用しており、1回の遷移ごとに学習します。リプレイバッファ、バッチ、ターゲットネットワークはありません。エリジビリティトレースがクレジットを過去に遡って伝達し、ObGDはTDターゲットを超過するステップを縮小します。ポリシーネットワークとバリューネットワークはそれぞれ、64ユニット(Pico上では32ユニット)の2つの隠れ層とLayerNormを備えています。
入力は、フレームから読み取られた特徴ベクトルです(training/streamx.py: observe_*、Rustではtinyworldformer-rs/src/agent.rsでミラーリング)。例えば、Breakoutではボールの行、列、進行方向、Pac-Manでは各方向について、最も近いペレットまでの最短経路距離と各ゴーストの近さです。
以下は、各ゲームでの1回のトレーニングラン(実ゲーム、50万ステップ)を6つの段階で示しています。学習前、終了時、およびスコアが25%、50%、75%、95%に達した時点のスナップショットです。各パネルは同じ開始状態から貪欲にプレイし、フレームごとに数ステップをスキップします。「試行回数」はこれまでにプレイされたエピソード数を示しており、クラッシュの様子もわかります。
Breakout [画像]
Doom-lite [画像]
Flappy Bird [画像]
Pac-Man lite [画像]
Pong [画像]
同じランのスナップショットにおける、実ゲームでの貪欲なリターン(10エピソードの平均):
| ゲーム | 0 | 1k | 2k | 5k | 10k | 20k | 50k | 100k | 150k | 250k | 500k | 最大 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Breakout | 8.2 | 17.0 | 15.9 | 21.9 | 29.9 | 29.9 | 29.9 | 31.1 | 35.0 | 39.9 | 45.6 | 56 |
| Doom-lite | −5.0 | −5.0 | −5.0 | −5.0 | −5.0 | 59.5 | 69.9 | 78.1 | 81.8 | 82.1 | 82.9 | – |
| Flappy Bird | −1.0 | −1.0 | −1.0 | 1.9 | 24.6 | 24.9 | 96.2 | 123.7 | 309.2 | 332 | 332 | 332 |
| Pac-Man | 7.0 | 3.8 | 17.0 | 17.7 | 8.2 | 46.8 | 72.9 | 138.9 | 146.6 | 190.3 | 217.4 | 249 |
| Pong | −4.3 | −3.9 | −3.9 | −3.9 | −1.7 | −2.0 | −0.3 | 1.1 | 1.7 | 1.1 | 1.1 | 5 |
シェーピングなしでは、Doom-liteエージェントは開始コーナーに留まって発砲します。なぜなら、敵は常にそちらに向かってくるからです。--idle-penalty 0.1は、過去20ステップで6回以上訪れたセルに費やされた各ステップに0.1のコストを課します。位置はミニマップから読み取られるため、ペナルティはドリーム内でも機能します。これを使用すると、エージェントはパトロールし、キル数を2倍にします。
tinyworldformer-rs/src/learner.rsは、no_std Rustにおける同じアルゴリズムで、バックワードパスは手書きされています。PyTorchの200回の更新に3.5e-6以内で一致します。ホスト上では、Rustのint8 Flappy Birdドリーム内の新しいエージェントは、25kステップ後に平均6パイプ、100kステップ後に121パイプ、250kステップ後に300パイプ以上をエピソードごとに獲得します。
量子化とPico
ワールドモデル内のすべての行列はint8で、出力行ごとに1つのスケール(scale = max|w| / 127)を持ちます。アクティベーションもint8で、テンソルごと、ループインデックスごとに1つのスケールを持ちます。ループは異なる範囲を見るためです。Matmulはint8 × int8をint32に合計し(RP2350のSMLAD命令で2つの乗算・加算)、その後2つのスケールによるfloat乗算を適用します。Softmaxは1024エントリのexpテーブルを使用し、確率はuint8で格納されます。Norm、Residual、Embeddingはfloat32のままですが、RP2350はFPUを搭載しています。
トレーニングも同じ丸め(ストレートスルー推定器付きQAT)を適用するため、int8モデルはトレーニングされたモデルです。rustexport/intref.pyは、固定順序のすべてのfloat演算を持つNumPyでのint8モデルです。Rustエンジンはそれをビット単位で再現し、cargo testはエクスポートされた各環境のゴールデンベクトルをチェックします。
| ゲーム | int8重み | float32 (埋め込み、正規化、スケール) | ゲームデータ | エージェント (float32) | ファームウェアフラッシュ | ファームウェアRAM |
|---|---|---|---|---|---|---|
| Breakout | 19.7 KB | 31.6 KB | 1.1 KB | 54.1 KB | 361 KB | 299 KB |
| Doom-lite | 33.9 KB | 91.9 KB | 12.0 KB | 61.3 KB | 471 KB | 357 KB |
| Flappy Bird | 30.1 KB | 69.5 KB | 10.1 KB | 50.8 KB | 409 KB | 227 KB |
| Pong | 30.1 KB | 70.4 KB | 10.9 KB | 52.6 KB | 414 KB | 244 KB |
フラッシュとRAMは、Display PackドライバでビルドされたファームウェアELF(size -A)から測定されており、コード、USB、ディスプレイドライバ、オンデバイス学習器が含まれています。Pico 2 Wは4MBのフラッシュと520KBのSRAM(メインメモリマップに512KiB)を備えています。float32埋め込みテーブルが最大の部分であり、int8にすることも可能です。
src/tinyworldformer/rustexport/はNumPyとTorchのみをインポートするため、他のプロジェクトもエクスポートを再利用できます。rustexport.qat(QLinear, LoopedBlock)からモデルをビルドし、floatウォームアップ後にActQuant.enabled = Trueでトレーニングし、rustexport.convert.looped_modelで変換し、int_model_arrays, mlp_arrays, codegen.write_moduleでRustソースを書き込みます。rustexport.Goldenは、Rustテストがチェックするベクトルを書き込みます。
チップ上
ファームウェア(tinyworldformer-rs/firmware)は、Embassy上のno_std Rustです。実行時に何も割り当てられません。重みはフラッシュ内のstatic配列であり、すべてのバッファはRAM内の固定サイズのstaticです。
コア1はゲームを実行します。そのループ(firmware/src/main.rsのdream_loop)の1パスが1フレームです。
- アクターは現在のフレームから特徴を読み取り(
agent::observe)、MLPを実行してボタンを選択します。オンデバイス学習が有効な場合は学習エージェントが選択し、ボタンを押している場合はそのボタンが使用されます。 - int8トランスフォーマーは、フレームとボタンに対して2回のループ(
Engine::forward)を実行し、デコーディングがそのスコアを次のフレームに変換します。背景はセルごとにargmax、各シングルトンは1回配置されます(ランダムイベントではxorshift RNGでサンプリング)、その後スプライトがスタンプされます。報酬は報酬ヘッドのargmaxであり、ライフとエピソード終了はそれらの上のカウンターです。 - 学習がオンの場合、学習器はこの1回の遷移から更新し、それを忘れます。
- フレームは2スロットチャネルを介してコア0に送信されます。コア0が前のフレームでまだ処理中の場合、このフレームはドロップされるため、コア1は画面を待つことはありません。
プレイ時には、コア1はプレイ可能な速度(ゲームに応じて66〜150ミリ秒/フレーム)に低下します。エージェントはチップが許す限り速く実行されます。
コア0は、他のすべてのためにEmbassyの非同期タスクを実行します。USBシリアル経由で、各フレームはステータスライン(モデル時間、アクター時間、FPS、環境とエージェント名)とともに色付きANSIテキストとして送信され、キーが戻ってきます。オプションのDisplay Pack(--features display)は、最大62.5MHzのSPI経由の320×240 ST7789で、前回のフレームから変更されたセルのみが再描画されます。そのボタンは20ミリ秒ごとにポーリングされます。2つのコアは、フレームチャネルと、誰がプレイしているか、押されたキー、学習のオン/オフ、ループ回数といったいくつかのアトミック変数のみを共有します。
メモリの使い道:
| フラッシュ内 | RAM内 | |
|---|---|---|
| ワールドモデル | int8行列、float32埋め込み、正規化、スケール、ゲームの開始フレームと色(52–138 KB) | 1回のフォワードパス用のスクラッチバッファ:36〜80トークンのアクティベーション、matmulが読み取るint8コピー、次のフレームのセルごとのスコア(Flappy Birdは114 KB、Pac-Manは最大368 KB、そのセルごとのスコアだけで184 KB) |
| アクター | float32 MLP重みと特徴正規化(51–61 KB) | その特徴と隠れ層(約1 KB) |
| オンデバイス学習器 | コードのみ | 2つの32ユニットネットワーク用の重み、エリジビリティトレース、正規化(Pac-Manは49 KB、Doom-liteは最大106 KB。これは入力特徴の数に比例します) |
| ファームウェア | コード、USB、ディスプレイドライバ(約150 KB) | スタック(コア1用16 KB)、USBバッファ、フレームチャネル |
各ゲームは、4MBのフラッシュと512KiBのSRAMに収まります。Breakoutは361KBのフラッシュと299KBのRAMを使用します。Pac-Manは最大で600KBと460KBを使用します。
ほとんどの時間はint8ドット積(dot_i8、src/lib.rs)に費やされます。1つの32ビットロードで4つの重みをフェッチし、SXTB16でバイト0/2と1/3を16ビットハーフに展開し、SMLADで1つの命令で2つの乗算・加算を実行します。アテンションはuint8確率とint8値を乗算するため、代わりにUXTB16を使用します。Norm、expテーブルルックアップ、最終的な出力ごとのスケールはFPU上のfloat32のままです。2ループのフレームは2〜4M乗算・加算(Pac-Manは8.2M)です。デスクトップCPUでは、同じRustコードでフレームを0.7ミリ秒(Flappy Bird)から1.5ミリ秒(Breakout)でドリームできます。Pico自体でのフレームレートはまだ測定していません。
ボードは、新しいエージェントがデバイス上で学習した状態で起動します。USB経由でlを押すか、Display PackでBを1秒間長押しすると、事前学習済みアクターに切り替わり、もう一度押すと元に戻ります。
ゲームとエージェントのフラッシュ
各エクスポートはクレート内で並んで配置されています。ワールドモデルはtinyworldformer-rs/models/envs/<env>/に、エージェントはmodels/agents/<game>/<agent>/にあります。ビルド時に両方を選択します。
cd tinyworldformer-rs
./flash.sh --list # エクスポートされているものリスト
./flash.sh --env doomlite --agent doom_agent_roam_p0.3 # ビルドとフラッシュ(接続時にBOOTSELを長押し)
./flash.sh --env breakout --display # Display Packを使用
./flash.sh --env pong --host # このターミナルでプレイ
./flash.sh --env flappy --default # 通常のcargoのデフォルトにする
TWF_ENV=doomlite TWF_AGENT=doom_agent_roam_p0.1 cargo test --release # そのペアのゴールデンテスト
--agentなしの場合、models/defaultにリストされている環境のエージェントが使用されます。存在しない場合は、名前順で最初のものが使用されます。異なるゲームのエージェントは、有効なもののリストとともに拒否されます。画面上のステータスラインには、実行中の環境とエージェントが表示されます。
USB経由のキー: Breakout a/d/s、Doom-lite w/a/d/space/s、Flappy space、Pac-Man w/a/s/d、Pong w/s。その後、gはエージェントに戻り、1〜4はループ回数を設定し、lはオンデバイス学習を切り替え、rは再起動します。
ブラウザで
同じno_std int8エンジンはWebAssembly(tinyworldformer-rs/web/)にもコンパイルされます。モジュールにはインポートがなく、wasm-bindgenも使用しません。ファームウェアのループ(ドリーム、エージェント、Stream-x学習)をオンザフライで実行します。静的ページがキャンバスにフレームを描画します。
rustup target add wasm32-unknown-unknown # 一度だけ
cd tinyworldformer-rs/web
./build.sh # 各環境ごとに1つのモジュールをwww/に(または./build.sh pong flappy)
python3 -m http.server -d www # http://localhost:8000を開く、?env=pacmanでゲームを選択
同じシードで、wasmビルドはネイティブビルドと同じエピソードをフレームごとにドリームし、ゲームに応じて1〜5ミリ秒/フレームで実行されます。各モジュールには、ゲームのデフォルトエージェントが含まれています。TWF_AGENT=... ./build.sh <env>で別のものを選択できます。キーはPicoと同じですが、矢印キーと画面上のボタン、およびf(学習中に役立つ、ドリームを可能な限り速く実行する)が追加されています。
再現
uv sync
G=pong # breakout, doomlite, flappy, pacman, pong
uv run python -m tinyworldformer.training.data --game $G --transitions 2000000 --varied 0.3 # -> data/$G
uv run python -m tinyworldformer.training.train --data data/$G --d 48 --heads 3 --ff 96 --out models/${G}_wm.pt
uv run python -m tinyworldformer.training.streamx --env real --game $G --out models/${G}_agent.pt
uv run python -m tinyworldformer.training.evalwm --ckpt models/${G}_wm.pt --agent models/${G}_agent.pt # ドリームの検証
uv run python -m tinyworldformer.training.streamx --env dream --wm models/${G}_wm.pt --out models/${G}_dream_agent.pt
uv run python -m tinyworldformer.training.export --ckpt models/${G}_wm.pt
uv run python -m tinyworldformer.training.streamx --out models/${G}_agent.pt --export tinyworldformer-rs
cd tinyworldformer-rs && TWF_ENV=$G cargo test --release && TWF_ENV=$G cargo run --release
ゲームごとに異なる設定:
| ゲーム | 設定 |
|---|---|
| Breakout | 1M遷移、--miss-penalty 5、その後エージェントで収集(data --learned <agent> --holes 0.5 --out data/breakout_learned)し、すべてでファインチューニング(train --init ... --data data/breakout data/breakout_learned --steps 15000 --warmup-float 0 --lr 1e-3)、4ラウンド |
| Doom-lite | --varied 0.5, train --noise 0.3, streamx --max-ep-steps 500 --idle-penalty 0.1 |
| Flappy Bird | streamx --max-ep-steps 3000 |
| Pac-Man | 1.2M遷移、--varied 0.4, train --d 64 --heads 4 --ff 128 --steps 40000 --warmup-float 4000, streamx --miss-penalty 5 --max-ep-steps 1500 |
| Pong | streamx --max-ep-steps 5000 |
コードレイアウト
| パス | 内容 |
|---|---|
src/tinyworldformer/games/ | 5つのゲーム(ルール、レンダリング、read)、Gameレジストリ、スクリプト化されたエージェント、ターミナルプレイ |
src/tinyworldformer/training/ | データ、ワールドモデル、トレーニング、ドリームチェック、Stream-x、Rustクレートへのエクスポート |
src/tinyworldformer/rustexport/ | 再利用可能なPyTorch → no_std Rustエクスポート:QATレイヤー、int8仕様、変換、コード生成 |
tinyworldformer-rs/ | no_std int8エンジン、ドリームループ、エージェントと学習器;ホストデモ(src/main.rs) |
tinyworldformer-rs/firmware/ | Pico 2 Wファームウェア |
tinyworldformer-rs/web/ | WebAssemblyビルドとブラウザページ(www/) |
最近の数学の実践に関する雑感
これは奇妙な瞬間です。後になって、物事がどこに向かっていたのかが明白だったような物語を語り合うことになるだろうという予感があります。そのため、この時点での混乱ぶりを記録するために、小さなタイムカプセルを残しておきたいと思います。これは現場からの個人的な視点です。
1.
普通の人間であれば、AIは長い間数学が得意だと思ってきたかもしれません1。しかし、「真の」数学に関しては、おおよそ以下のようでした。
| 時期 | 感覚 |
|---|---|
| 2024年 | LLMはオモチャ |
| 2025年夏 | LLMは面白いオモチャ |
| 2025年初秋 | LLMは時々役に立つことができる |
| 2025年秋中頃 | …多くのことを |
| 2025年晩秋 | …非常に多くのことを |
| 2026年初頭 | 神よ! |
過去のAIの進歩に驚かされてきたにもかかわらず、AIが数ヶ月のうちに、ほとんど役に立たないオモチャから、今や多くの人が「なくてはならないもの」と考えるレベルにまで進歩したのを見るのは、やはり驚くべきことでした。
2.
個人的には、現在のAIによって「数学を行う」生産性が約50%向上したと言えます。これは、AIがかつて私が何時間もかけていたことを数分でこなせるようになったにもかかわらずです。
私の「数学を行う」という概念には、定理のアイデアを思いつく、それを証明しようとする、書き留める、より明確にするためにすべてを再編成するなど、さまざまなステップが含まれます。AIはこれらのうちの一部は得意ですが、他の部分はまだかなり苦手なので、全体的なスピードアップは期待ほど高くありません。
(飛行機が10倍速く飛べたとしても、ロンドンのアパートからローマの友人の家までのドアツードアの移動時間はそれほど大幅には短縮されないでしょう。空港への移動や搭乗待ちの時間は依然としてかかるからです。)
3.
しかし、私はまだ(ある意味で、後述しますが)古い「数学を行う」ワークフローに従おうとしており、その一部を自動化しているだけです。それは正しいのでしょうか?
もし、現在のコストの1%でバッテリーを製造する方法を見つけたとしましょう。私たちは単に「すごい、バッテリーが安くなった」と言うだけでしょうか?いいえ、以前は意味をなさなかった、バッテリーのあらゆる種類の新しい使い方を見つけ、これらの新しいバッテリーを考慮して発電方法を変更するでしょう。おそらく、私たちのエネルギーシステム全体が変革されるでしょう。同様に、AIが証明を得意とすることは、非常に異なる均衡状態につながるはずです。しかし、それは何でしょうか?2
4.
現時点では、「数学を行う」ことの意味について流動的な見解を維持することは健全なようです。しかし、最低限、それは私が「行っている」数学を理解することを確実に含みます。それがワークフローの一部である限り、「私」がどれほど速く数学を「行う」ことができるかには限界があるように思われます。AIがどれほど優れていてもです。
一つの明白な結論は、私の小さな有機脳をワークフローから完全に排除すべきだということです。そうかもしれません!しかし、人間の脳をループ内に保ちながら、どれほど速く物事を進めることができるかを推測するのは興味深いです。答えはこうです:たくさんです。
5.
あなたが聞いたこととは反対に、人間は数学のために廃止されていません。まだです。最も明白な理由は、AIがまだ文章を書くのが苦手であることです。
人々はしばしば、AIの文章が様々なパターンを使いすぎたり、独自の声を欠いていたりすると不平を言います。私も同意しますが、いつも、指で押さえることができない、より深い問題があるように感じていました。数学においては、これは非常に明確になります:問題は、それが全く意味をなさないということです。
本当に、これは「文章を書くのが下手」ということよりも深いと思います。AIは、文章の悪さとして現れる特定の方法で考えるのが苦手なようです。
6.
初心者が数学を行うとき、彼らはしばしば「スパゲッティコード数学」を作成します。たくさんの数式が含まれる長い文書を想像してみてください。しかし、もしあなたが数式の一つを指差しても、それがどのような仮定に基づいているのか、あるいは大きな構図の中でどのように適合するのかを言うのは難しいです。17ページ前に言及された任意のアイデアが、警告なしにいつでも再び使用される可能性があります。経験を積むと、人々は、各部分の仮定と結論が比較的単純であるという点で、良い「API」を持つモジュール化された部分に複雑な議論を分解することを学びます。
どういうわけか、AIはこれらの驚くべき技術スキルを開発しましたが、抽象化を作成するのがまだ苦手です。そのため、AIが提示する議論は長く、絡み合い、当惑させるものになります。これがAIの数学が非常に異質に見える理由です。
7.
さて、あなたが考えていることはわかっています。おそらくAIは抽象化を作成するのが下手なのではなく、あなた、人間(小さな有機脳を持つ)にとって機能する抽象化を作成するのが下手なのでしょう。
遠くから見ると、それはもっともらしく見えます。そして、ある程度は真実かもしれません。私は人間脳への高帯域幅アクセスを持っています。これが、人間脳がアイデアの提示にどのように反応するかを予測する上で私に利点があることを想像するのは、それほど突飛ではありません。しかしそれでも、消費者が抽象化であるかどうかにかかわらず、現在のAIは抽象化を作成するのが客観的に下手であると主張します。
理由は以下の通りです。もしAIに難しいことを証明するように頼むと、それは単純なものから始まり、徐々に複雑さの暴走へと下降する証明を生成します。もしそれを自己完結型の部分に分解するように指示すると、各補題を述べるだけで1ページかかるようなものを生成します。「なぜ?なぜ私にこんなことをするんだ?」と尋ねると、AIは「これ以上単純にはできない。それほど悪くない。何が問題なんだ?」と答えるでしょう。
しかし、AIとしばらく議論すると、議論の基本的な輪郭を理解し始めることができます。そして、「なぜまず、blahblahblahを心配せずに、blahという仮定だけを使ってblahを証明しようとしないのですか?それはもっと簡単ではありませんか?」と尋ねます。しばしば、これはうまくいきません。私のアイデアが悪かったか、あるいはAIがそれを実行するほど賢くないかのどちらかです。(どちらでしょうか?誰が知る!)しかし、数回の試行の後、私は通常、機能するものを見つけることができ、AIはそれを使用して証明をいくらか単純な方法で書き直すことができます。これを何度も繰り返すと、複雑さを徐々に削り取ることができます。
しかし、それはまだ証明を人間が理解しやすくしているだけでしょうか?いいえ!一つには、このプロセスは通常、証明を短くし、時には劇的に短くします。さらに重要なことに、このプロセス中に、誰か(私かもしれませんし、AIかもしれません)が、異なる証明戦略がより強力な結果をもたらすことに気づくことがよくあります。x < 9.7を証明する代わりに、今ではx < 7.1を証明できるようになります。したがって、職人的な人間の抽象化は「客観的に」役立ちます。
8.
一部の人々は、私がやっていることの一部を、巧妙な「AGENTS.MD」やカスタムスキル、ハーネスに時間を費やすことは時間の無駄ではないと私に言った「オートメーター」automatorのように、マルチエージェントのルーブ・ゴールドバーグ装置で自動化できると主張するかもしれません。私は確信がありません。なぜなら、私の経験では、そのようなものに時間を投資することはあまり良い時間の使い方ではないからです。それはある程度役立ちますが、物事は非常に速く変化するため、知識の半減期は短いです。また、これらのトリックに関する情報の大部分は、技術自体に興奮している人々から来ています。
一方で、オートメーターは、「独立したエージェントに敵対的なレビューを依頼する」と入力するように私に言いました。そして、私はその7つの言葉が非常に役立つことを発見しました。最先端から離れていたい人々のための、退屈なベストプラクティスを見つけることができればと思います。
9.
AIが私の生産性をそれほど向上させないもう一つの理由は、それがしばしば「緑色の光」、私から遠ざかる陶酔的な未来だからです。AIに何かを依頼すると、それはほとんどそれをやったように見えます。そのため、変更を依頼し、それが役立つように見えますが、まだ完全には正しくありません。数時間後、私は何も達成しておらず、何も学んでいません。最終的に、私は自分で、または非常に厳しく管理されたAIでやり直すことになり、30分で完了します。このことが起こるたびに、私は愚かに感じ、二度としないように自分に言い聞かせます。しかし、リアルタイムで判断するのは非常に困難です。
10.
もちろん、AIが本質的に文章、思考、または抽象化の作成が下手だとは主張しません。これらのすべてにおいて改善されており、今後もそうすると予想されます。私が言いたいのは、2026年秋現在、人間はまだ貢献できることがたくさんあり、それは単に優れた表現力だけではないということです。
11.
AIは2025年秋に本当に数学が大幅に上手くなったのでしょうか?あるいは、全体的に見て、人間の数学能力は狭い範囲に収まるのでしょうか?
12.
新しい現実に反応して、AIを使って多くの半ばまともな論文を生成し、arXivに大量に投稿している人もいます。私はこれを推進するインセンティブを理解しています。ある意味では、それは新しい現実への論理的な対応のように思えます。(自動車の発明後、私たちはより多くの低価値な旅行をするようになりました。)しかし、私はこれが社会的に有用だとは思いませんし、すぐに誰もがこれが恥ずかしいことだと同意するだろうと考えています。しかし…確信はありません。もしかしたら社会的に有用なのかもしれません?そして、この種のことに「視野が狭い」と感じるのではなく、機会を見つけて略奪する人々に対して、漠然とした嫉妬を感じています。
13.
楽観的なAI賛成、進歩賛成の意見が欲しいですか?OKです。もし私が古い「数学を行う」ワークフローを取るとしたら、AIはそれを確実に改善します。大幅に改善します。それは、多くの点で私よりも賢く、信じられないほどの速度で働き、ほぼすべてを知っている、超勤勉なインターンを持っているようなものです。
はい、AIは文章作成や抽象化作成が苦手です。しかし、私が規律を守り、AIに助けを求めながら、主に「自分で」数学を行うように努めれば、以前よりもはるかに速くアイデアを探求でき、間違いをはるかに少なくし、以前と同じくらい明確なものを作成しながら、同じだけの理解を深めることができます。「コフニ展開をしたらどうなる?」と尋ねて、すぐに答えを得ることができます。イラストやインタラクティブな視覚化を依頼して、すぐにそれらを見ることができます。確かに、これらの新しいツールがこの新しい領域を開拓してくれたこの瞬間に生きている私たちは、信じられないほど幸運です。
14.
そして、これが私が望んでいたことではありませんか?2024年に、私はAIが最も苦労しているように見えたのは、事実を正確に把握することだと嘆きましたbemoaned:
おそらく、古いSFロボットは私たちの幻想の投影でした。私は自分のスタイルと声が貴重で模倣不可能であることを望みます。私が創造性とセンスと魔法を提供するAIアシスタントを望みます。しかし、現実はそうではありません。したがって、覚えておくべきです:AIが有用だが人間が比較優位を保持するシナリオでは、その比較優位がどこにあるのか気に入らないかもしれません。
少なくとも数学においては、そして少なくとも現時点では、私がカークであり、AIはスポックです。(他の種類の文章でも、2024年以降、AIはスポックの次元よりもカークの次元に沿ってより改善されたという印象です。)それは良いことです。
15.
ここ数ヶ月、共同研究者がAIを使って何かを生成し、それを私に送って役立つかどうかチェックするように依頼することが頻繁にあります。なぜこれが合理的だと思われるかはわかります。(本当に、わかります。)しかし、それが頻繁に起こると、何のために人間の共同研究者がいるのか疑問に思わずにはいられません。
16.
一方で、創作においては、AIを全く使って書かないことをお勧めします。質問はしてもいいですが、一字一句自分で書いてください3。数学に関しては、それほど厳格な線引きは意味をなさないと感じます。良いものを作成するには、依然としてかなりの人間の関与が必要であり、表記法に苦しみ、繰り返し再編成し、個々の文章を大幅に編集する必要があります。それでも、人々がその努力をすれば、手作業で作成できたものと同じくらい良いものを作成できると感じますが、おそらく50%はAIで書かれているかもしれません4。
したがって、人々が「ケンタウロス数学」を送ってきても気にしません。ただし、内容については責任を負うことを条件とします。問題は、AIと協力するとき、理解を超えて物事が滑り落ちるのを避けるためには、絶え間ない偏執的な警戒が必要だということです。私はしばしば、人々におずおずと尋ねたくなることがあります。「さて、あなたは明らかにAIを使ってこれを作成しましたね…それはOKですが…このうちどれくらいを保証しますか?もし議論するための会議を開いたら、私が質問し始めた瞬間に崩壊しますか?」
17.
抽象化作成モードに入るとき、私は知的なことをしているように感じます。AIに悪い表記法や文章について怒鳴るとき、私は味覚を提供しているように感じます。しかし、私はしばしば、非常に基本的なように思えるフィードバックを与えていることに気づきます。例えば、「これはめちゃくちゃだ、片付けろ」とか、「その仮定は必要か?」とか、「この問題を解決できないなら、もっと簡単なバージョンを作成して最初に解決してみてはどうだろう?」といったことです。
一方では、良い人間のマネージャーの多くは、非常に基本的なフィードバックを提供することに相当すると推測できます。他方では、この種のことを行う上で私が多くの持続的な優位性を持っていると想像するのは困難です。したがって、抽象化作成や味覚においても、私がどれほどの持続的な優位性を持っているのか疑問に思います。
18.
AIが証明を得意とするようになったことに対する一般的な反応は、人間はより表現に焦点を当てる必要があるということです。これには、明白なブルデュー_風の批判があります。人間の画家はかつて写実主義を強く重視していましたが、カメラが発明された後、彼らは「ああ、写実主義は重要ではない、重要なのは表現だ、それは偶然にもカメラにはできないことだ」と皆を説得しました。数学における表現への移行は、単に人間がゴールポストを動かして、自分たちの関連性を正当化する方法なのではないでしょうか?
その批判を回避する唯一の信頼できる方法は、実際に、AIが登場する前から数学はすでに表現にもっと焦点を当てるべきだったと言うことでしょう。個人的には、これは真実だと強く信じています。
19.
私がそれほど生産的ではない最大の理由は、何の意味があるのか?ということです。過去には、エレガントな証明を思いついたら、「自分で何かを作った!他の人が私のものを見て使うだろう!私は人類の進歩を前進させている」と自分に言い聞かせたでしょう。今でもそう言えます。しかし、疑問に思わざるを得ません。誰かが実際にあなたの証明を読むでしょうか?それとも、別のAIに説明を求めるだけでしょうか?あなたの証明は、AIがそれをより良く説明するのに役立つでしょうか?どれくらいの期間?もし1年待ったら、別のAIが独立して同じ証明を生成するでしょうか?
- あるいは、普通の人間であれば、LLMはまだ基本的な算術で苦労していると思うかもしれません?↩
- AIの進歩は、社会の適応能力に比べて非常に速いため、「現在の均衡」がどのようなものになるのか、実際には決して見ることができないかもしれません。↩
- このエッセイは、もしあなたが疑問に思っているなら、一字一句自分で書いてAIには何も頼んでいません。↩
- そのパーセンテージが何を意味するのかについて、完全な議論をすることができます。私の意見では、AIによって書かれた単語の数よりも、アイデアの全体的な構造に責任があるのは誰かの方が重要です。↩