跳到正文
Hacker News (official RSS)(github.com)·· 3 天前精選AI 評分72

用 DOOM 遊戲構建神經網路:完整教學與實作指南

I built a neural network using DOOM games

AI 導讀

DOOM 被用來執行完整的神經網路推理,作者示範如何將 MLP、Transformer 等模型編譯成遊戲地圖並在 ViZDoom 執行。

  • 模型範例:1,355 個 voodoo dolls 代表 784‑pixel MNIST 輸入,通過 64‑dim 隱藏層、45 個比較和 10 個勝利計數,最終輸出正確數字,100% 精確。
推薦理由

此專案將機器學習模型對映至 DOOM 遊戲引擎,示範如何以遊戲作為精確數值運算平臺,對學習者瞭解硬體實作與離線推理有實際參考。

正文 · AI 翻譯

DOOMFORMER: the marine fights a horde of demons under a hellish sky

在 DOOM 遊戲機制上運作的神經網路:巫毒娃娃、傳送帶、熔岩與門扉。
觀看影片 · 在 Colab 嘗試: 畫一個數字,DOOM 讀取,無需安裝

tests bit-exact: tolerance 0 speed: about 70,000 times slower than NumPy demons: frozen while he thinks MIT

一個數字是傳送帶上巫毒娃娃的群體。啟用函式是一座他們必須攀爬的樓梯。矩陣乘法是娃娃在熔岩線上奔跑,每條線提升房間的光照等級。注意力是娃娃被另一個數字降低的天花板壓縮。當娃娃停止時,光照等級從正在執行的遊戲中讀回,並逐位與獨立的整數參考比較。若有一個值偏差一,執行失敗。

HIS BRAIN IS THE LEVEL: the marine fights a demon; on the right, his policy transformer, built into the same DOOM map, computes and chooses the decision with 1,345 voodoo dolls of himself (the thinking is time-lapsed; the demons are frozen while he thinks)

海軍陸戰隊員的腦袋被編譯進他正在戰鬥的關卡中。右側的點代表他 1,345 個巫毒娃娃。它們執行他的 Transformer,決定下一步;關卡會偵測到惡魔並觸發。觀看影片.

走一個數字,DOOM 讀取它

DRAWPAD: the marine walks a 7 across a 28x28 floor canvas, presses READ, 1,355 voodoo dolls compute the network and pick the winner, and the winner's doll lights a 7 on the level's 7-segment display

輸入是一個 28×28 的地板。海軍陸戰隊員在按下 FIRE 時走過的每一格都會被墨水記錄,並且關卡將該畫素寫入腦中。他按下 READ。1,355 個巫毒娃娃執行 MNIST 網路,比較所有 45 對數字分數並統計勝利。只有勝者的娃娃能通過,並點亮牆上的該數字。Python 先進行時鐘更新,然後讀回 784 個畫素、隱藏層、每一次比較以及答案,並將所有 248 個值與 NumPy 做比對。

doomformer draw               # opens a window: arrow keys to walk, hold Ctrl to draw, Space on the READ switch
doomformer draw --digit 7     # headless: the marine walks a 7 (Python presses his keys in place of a player)
          ▄▄▄▄▄▄▄▄▄▄▄▄▄▄
       ▄████████████████
       ████▀▀     ▀████▀
       ▀▀▀        ▄███▀
                  ████
                  ███
                  ███
                  ███
                 ████
                 ███▀
                 ████
                 ▀███

DOOM reads: 7  (right)   -- the winner's doll lit it on the 7-segment display
EXACT: all 248 values DOOM computed (hidden layer, 45 score comparisons and 10 win counts, each with its twin, and the one-hot answer) equal the NumPy integer reference computed from the 784 pixel ports DOOM holds (tolerance 0); one display firing
pixels: the 784 ports equal the level's canvas (the tiles the pen inked)
READ pass: 5,705 game tics = 163 s of real-time DOOM at 35 tics/s, 79 s wall | Python sent 58 commands, 29 readout scripts each followed by the ack script it waits for (0 waited more than one advance), and nothing else (0 rejected)

每個 0 至 9 的數字都有三種走路變體:共 30 個正確讀取,全部 30 個完全一致。在視窗模式下 DOOM 以即時方式執行,READ 需要 2¾ 分鐘;無頭模式則 76 到 99 秒。工作原理 · 影片

選擇你的技能等級

pip install git+https://github.com/mottopanikeiku/doomformer   # Python 3.11+, Linux or macOS

第一次執行會建構 DOOM 的指令碼編譯器,因此你需要 git、cmake 以及 C 編譯器。或者直接跳過:Colab 筆記本會安裝它,讓你畫一個數字讓 DOOM 讀取,並執行 softmax 示範。

技能 指令 DOOM 的功能 時間
我還太年輕不會死 doomformer hello 執行一個小型神經網路 3 秒
嘿,別太嚴苛 doomformer mnist 讀取手寫數字 ~2 分鐘
傷害我不少 doomformer demo 創造一個 DOOM 關卡名稱 20 秒
極端暴力 doomformer ask "what does iddqd do" 回答你的問題 7-11 分鐘
噩夢! doomformer demo --nightmare 106 張關卡,56,390 個巫毒娃娃 2-4 分鐘

時間來自於一臺 16 核 Linux 機器,同時執行 8 個 DOOM 遊戲。

我還太年輕不會死:你將看到的

tiny integer MLP 4-6-5-3 compiled into one DOOM map: 60 voodoo dolls, 1222 sectors
input 1/5  x=[-71, -82, 64, 37] -> y=[11, 4, -15]  EXACT (3 outputs + 22 hidden readouts equal the integer reference; 488 game tics)
...
5/5 EXACT

嘿,別太嚴苛:DOOM 讀取手寫數字

eight MNIST test digits and what DOOM read: 7 2 1 0 4 9 6 3, all exact

784 個畫素 → 64 → 45 次比較 → 10 次勝利計數 → 答案,全部在一張 DOOM 地圖中完成:1,355 個巫毒娃娃和 158,550 個區段。每一個墨水畫素會釋放一個娃娃,沿著隱藏層的權重線奔跑。娃娃接著比較每一對數字分數,只有贏得全部九次比較的數字能通過。DOOM 選擇該數字,Python 進行核對。在 DOOM 中,網路正確讀取了前 100 個 MNIST 測試數字中的 99 個,從全部 10,000 個中抽樣 1,000 個時讀取 966 個,全部都完全一致。對所有 10,000 個,該整數網路的準確率為 97.16%。

$ doomformer mnist --digits 2 --seed 1

          ███
          ████                      ▄▄▄█████████▄▄
           ████                     ▀█████▀▀▀▀▀████
            ████                             ▄▄███▀
             ███▄                       ▄▄▄███████▄▄
             ████                       ████▀▀▀▀▀▀█████
              ████                                  ▀███
               ████                      ▄▄         ▄██▀
               ▀████                     █████▄▄██████▀
                ▀██                        ▀▀▀▀▀▀▀▀▀▀

DOOM reads: 1  right            DOOM reads: 3  right
test #46                        test #51

2/2 read correctly | 2/2 EXACT: every value DOOM computed (248 per digit: hidden layer, comparisons, win counts and
the one-hot answer) equals the NumPy integer reference (tolerance 0); Python only compared | 5,696 game tics per digit | 88 s

要從照片或繪圖中讀取你自己的數字:doomformer mnist --image my7.png。

傷害我不少:transformer 為一個關卡命名

一個訓練於 136 個官方 DOOM 關卡名稱的 transformer,拆分成 38 張小關卡,供一組 DOOM 遊戲執行。Python 在遊戲間複製整數,並從 DOOM 計算的 logits 中挑選每個字母。

DOOM wrote: 'GHEER'   (NOT in its training corpus: the model made it up)
  174 tile runs in 8 parallel DOOM games | 10,462 voodoo dolls in the tile maps |
  6,902 values read back and compared with the NumPy integer reference (tolerance 0) | 15 s
EXACT: every value DOOM exposed equals the integer reference, and the reference writes the same name

--seed 3、--seed 4 等可產生其他名稱。--one-map 將整個 transformer 放入單張關卡(嵌入、注意力、MLP 與 logits,層間無 Python),每個字母大約 90 秒。

--softmax 執行相同的模型,使用 softmax attention 訓練。娃娃計算指數值(階梯式,步階像對數一樣間隔)並於執行時除以總和(比較次數)。其運作方式見於 docs/HOW_IT_WORKS.md.

DOOM wrote: 'FINAL INEALL'   (NOT in its training corpus: the model made it up)
  494 tile runs in 8 parallel DOOM games | 4,886 voodoo dolls in the tile maps |
  106,834 values read back and compared with the NumPy integer reference (tolerance 0) | 59 s
EXACT: every value DOOM exposed equals the integer reference, and the reference writes the same name

--one-map --softmax 在單一圖中完成,層與層之間無 Python:4,209 個娃娃,每個字母約 2 分鐘。

--model doomnames_modern 在 softmax attention 上加入 RMSNorm 與閘控(ReGLU)MLP。娃娃亦計算平方和(如奇數之和)、整數平方根(階梯式 d·k²)以及除法。它在 170 個 tile 執行中寫出 MILL,所有 62,000 個值皆精確。

它自己寫。 --one-map --loop 建立一張圖,能在不使用 Python 的迴圈中寫完整個名字。每個位置,娃娃比較所有 30 個 logits,勝出的娃娃將下一位置的 token 輸入移至其字母。關卡隨即回溯並自行執行下一輪,共五輪以處理 6 個 token 的上下文。Python 只進行時間同步與讀取:每一輪的每個值皆被檢查,最終文字必須等於貪婪生成。它寫了 DAN(5,875 個娃娃,約 3 分鐘/輪),以及 DAGE 搭配 --softmax。

Ultra-Violence:向 DOOM 提問

一個 2 層 transformer,訓練於 189 個關於 DOOM 的事實。跨越數千個 DOOM 遊戲例項的娃娃計算每個值,且每個值皆被檢查。首字母需數分鐘,之後每 20 秒左右到達一次。部分答案,皆在 DOOM 中精確:

你問 DOOM 回答
iddqd 會做什麼 GOD MODE
什麼是愛 INVULNERABILITY
什麼是娃娃 BERSERK
它能跑 DOOM 嗎 SIXTY SECONDS

它知道自己的作弊碼。再問其他問題,你會得到自信的 DOOM 無稽之談。

Nightmare!:大腦

這個 2 層、24,279 引數的 transformer,訓練於 11,341 個真實 DOOM 關卡名稱,編譯成 106 張圖、56,390 個娃娃與 785,460 個區域。以此設定,它寫出了 DEMON CANYON,這並非其 12,934 個訓練名稱之一:

106 tile maps (56,390 voodoo dolls, 785,460 sectors) built in 6 s; running them in 8 DOOM game processes ...
DOOM wrote: 'DEMON CANYON'
  1,690 tile runs in 8 parallel DOOM games | 56,390 voodoo dolls in the tile maps |
  120,666 values read back and compared with the NumPy integer reference (tolerance 0) | 135 s
EXACT: every value DOOM exposed equals the integer reference, and the reference writes the same name

關閉位於一個神經元前的門(L1.m 單元 33),同一腦部寫出 DEMON DEATH。

運作方式

each voodoo doll crossing lava weight lines voodoo dolls jammed under attention gates
矩陣乘法。每個娃娃是啟用的一個單位,越過的每條熔岩線為房間的光照級別加上權重。 注意。娃娃在天花板閘門下擠壓,另一個活值已降低;計算誰能通過即為乘法。
seven voodoo dolls stopped by a slammed door the level DEMON CANYON
切除即為門。射擊開關,門關閉,某個神經元的娃娃永不到達。 DEMON CANYON,根據 DOOM 腦部寫出的名稱構建的關卡,由一名大兵(其腦也在 DOOM 上執行)遊玩。
flowchart LR
  NET["your network<br/>(PyTorch / NumPy)"] --> INT["integers<br/>(quantised)"]
  INT --> MAP["a DOOM map<br/>lanes, gates, lava lines, doors"]
  MAP --> RUN["ViZDoom runs it<br/>voodoo dolls walk"]
  RUN --> READ["read back<br/>sector light levels"]
  READ --> CHECK{"== NumPy integer<br/>reference?"}
  CHECK -->|"every value"| EXACT["EXACT"]
  CHECK -->|"off by one"| ERR["MismatchError"]

載入

DOOM 將每個額外的 player-1 開始點轉化為娃娃:玩家的副本,沿輸送帶行進並觸發其越過的每條線。啟用值 5 表示五個娃娃。階梯式允許娃娃在每一步僅在值超過該步高度時通過,這即是 ReLU、四捨五入與限制於一個形狀。總和累積於區域光照級別,結果為飽和 int16 值。

取得精確結果意味著測量引擎。同一 tic 中同一區域的兩次地板移動:一個會靜默遺失。傳送線會取消同一 tic 中先前越過的線。寬度超過約 34,000 單位的圖會將物件放至錯誤房間。完整清單見於 docs/HOW_IT_WORKS.md 與 docs/engine_limits.md。

名人堂

更大的實驗,全部準確。程式碼在 demos/,訓練資料在 training/,每個訓練好的模型在 models/。每個的數字和備註在 docs/DEMOS.md 和 docs/FLY.md。

the DOOM brain writing DEMON CANYON across a wall of DOOM games 一個名為 DOOM 地圖的 Transformer。 DEMON CANYON,跨越 1,594 個 DOOM tile 例項編寫。film
DOOM plays DEMON CANYON DOOM 玩 DOOM。 一名海軍陸戰隊員,其政策基於 55 場 DOOM 遊戲,走過名為 DOOM 的地圖,雖未見過:門、兩次擊殺、出口。102 個決策,全部準確。film
ONE DOOM GAME: a blank brain learns from an aimbot inside the game DOOM 學習。 權重是天花板高度,學習則是通靈娃娃升降它們。一個空白大腦在一場遊戲內由瞄準機器人教導,第一生命得 4 次擊殺,之後 24、26、22、24。931/931 個決策皆精確。film
THE FLY IS ALIVE: the fly circuit's MN9 neurons spike, the marine lunges at the health bonus and eats it 一隻果蠅。 來自 FlyWire 連結圖的 255 個神經元迴路,在一張地圖內引導海軍陸戰隊員:吃、拒絕、梳理、逃跑。169/169 步驟皆精確。film · code
ONE MAP, ONE TRANSFORMER: every dot is a voodoo doll; they march layer by layer through one forward pass and the model writes H 一張地圖中的完整 Transformer。 嵌入、注意力、MLP 與 logits,層間無 Python:3,548 個娃娃寫出 SHIL。film
  • 他的腦袋就是地圖。 地圖指令碼感知最近的惡魔,1,345 個通靈娃娃執行他的 Transformer 並挑選動作,地圖觸發。Python 推進時鐘並讀取。50/50 個決策皆精確。film · code
  • 腦切除術。 一發 7 砲彈霰彈槍擊中他的 DOOM 大腦,將他變成零擊殺的和平主義者。此病灶來自 3,000 次隨機擊中的掃描。film
  • 最大的那個。 一個四層 Transformer 在 10,166 個 DOOM tile 例項與 1.9 百萬遊戲滴答上寫出 UNHOLY STRONGHOLD。film
  • Showreel。

自行打造

將你自己的網路編譯進 DOOM 地圖,讓輸出開門、閃燈或推動海軍陸戰隊員。

import doomformer as df

net = df.MLP.from_torch(my_torch_mlp, calib_x, levels=8)   # or df.MLP(...) by hand, or df.MLP.from_numpy(...)
print(net.quantization_report(test_x))                     # how close the integer model is to your float model
with df.compile(net, actions={"y0": [df.act.door_open(tag=7)]}, world=my_room) as brain:
    y = brain(net.encode(x))                               # computed by voodoo dolls, verified exactly
    brain.record([net.encode(x)], "dolls.gif")             # film the dolls doing it

Spiking networks (df.SpikingNet)、transformers (df.Transformer.load("doomnames"))、卷積網路 (from_torch 精確降低 Conv1d/2d) 以及在地圖內更新權重的學習者 (df.Perceptron) 運作方式相同。df.with_argmax 讓娃娃選擇勝出輸出。df.Agent 將感官與肌肉也放入地圖。模型過大無法放在一張地圖,會分散在 DOOM 遊戲池中使用 mode="tiles",doomformer plan 告訴你需要哪一個。

指南在 docs/BUILD_YOUR_OWN.md,還有十二個小型可執行 範例。做出什麼詛咒?Show and tell。

更多 DOOM

一張地圖即一場遊戲,一場遊戲使用一個核心。df.run_many 將每個輸入傳給下一個閒置遊戲,檢查每一次通過,並按順序返回輸出。遊戲在你的機器或任何你自行啟動的 Ray 叢集上執行。

在一臺 16 核機器上,14 場 DOOM 遊戲處理 1,000 個 MNIST 測試數字花了 2 小時:966 個正確讀取,1,000 個完全正確, 5,696,000 個遊戲滴答,即 45 小時即時 DOOM。每個數字的結果都在 models/mnist/doom_1000.json。

ys = df.run_many(net, xs, workers=8)                  # 8 DOOM games on this machine
ys = df.run_many(net, xs, backend="ray")              # every CPU of your Ray cluster
pip install "doomformer[ray]"
ray start --head                                      # then `ray start --address=HEAD:6379` on the other machines
doomformer mnist --all --ray --record mnist.json      # all 10,000 MNIST test digits, every result in one file

每場遊戲預算一個核心和 1.25 GB RAM 用於 MNIST 地圖。詳情見 docs/scaling.md。

盒子裡包含

doomformer/ 工具包:地圖編譯器、DOOM 執行環境、精確檢查、CLI
demos/ 研究程式碼:他的腦袋就是地圖、行走器與地圖產生器、果蠅迴路、學習場、相機
training/ 對每個模型進行量化感知訓練,以及包含來源的地圖名稱和 DOOM 事實語料庫
models/ 每一個訓練好的模型及其訓練日誌與評估
examples/ 十二個單檔範例,最多 15 秒
tests/ 187 次測試,其中 88 次在實際無頭 DOOM 上,包括必須失敗的篡改控制
docs/ 工作原理、每個演示與其數值、引擎事實、工具包指南

作弊程式碼(常見問題)

IDCLIP 這真的就是 DOOM 嗎?

它是 ViZDoom 1.3.2,一個基於 ZDoom 的引擎,使用 Freedoom 的免費 資產,無頭執行。地圖使用 ZDoom 的 UDMF 格式和 ACS 等級指令碼。ACS 移動輸入閘門,重置傀儡並複製結果。 在封閉迴路等級中,它還感知(最近的惡魔、海軍陸戰隊員走過的畫布瓦片)並執行傀儡選擇的動作。每個乘加和啟用都是傀儡穿過線條, DRAWPAD、mnist 和 他的腦是關卡 之後,最終選擇也是如此。

IDKFA 那麼 Python 做了什麼?

它編譯地圖,寫入輸入,等待,讀取結果並檢查。在 DRAWPAD 和 他的腦是關卡 中,關卡寫入自己的輸入。對於分佈在多張地圖上的模型,Python 在遊戲之間複製整數,對於文字,它從 DOOM 計算的 logits 中挑選每個字母,除非使用 demo --one-map --loop,此時傀儡選擇每個字母並自行寫回。注意力是 ReLU 風格的整數注意力,或由傀儡計算的 softmax 注意力(demo --softmax);doomnames_modern 新增 RMSNorm 和一個門控 MLP。訓練在 PyTorch 中離線進行,除了在地圖內更新自身權重的學習者。

IDDQD 你怎麼知道是 DOOM 計算的,而不是 Python?

每個值都從正在執行的遊戲中讀回,並與從未見過地圖的 NumPy 整數參考進行比較,容差為 0。測試還嘗試破壞它。它們將編譯地圖中的一行權重從 5 提升到 6,檢查在傀儡穿過該線的輸入上恰好 +1 失敗。它們讓讀出回聲 Python 寫入的值,構建不含引擎所需障礙的地圖,並讓 settle 檢查信任 ViZDoom 的 4,096 個物件列表。這些都必須被捕捉。

IDSPISPOPD 它快嗎?

不。一次小型 MLP 通過在 DOOM 中耗時 726 毫秒,在 NumPy 中耗時 11 微秒,大約慢 70,000 倍。讀取一個手寫數字耗時 5,705 個遊戲滴答:76 到 99 秒無頭,2 ¾ 分鐘即時 DOOM。當海軍陸戰隊員思考時,惡魔被凍結。速度從未是目標。

IDBEHOLD 海軍陸戰隊員真的學會玩了嗎?

他模仿一個指令碼化的自動瞄準器,能看到精確的惡魔位置,並將其分為 86 種可能的觀測。這是監督式模仿。沒有基於獎勵的 RL,也沒有從畫素學習。這裡重要的是網路執行的位置。

IDDT 為什麼?

人們曾在計算機、孕檢試紙和割草機上執行 DOOM。這次則相反:DOOM 成為電腦。

授權

程式碼與檔案:MIT。隨附的關卡名稱列表(Doom Wiki,CC BY-SA 4.0;id Software 的原始碼,GPL-2.0)以及 100 個樣本 MNIST 數字(CC BY-SA 3.0)保持其授權;請參閱 THIRD_PARTY.md。Freedoom 圖形採 BSD 授權。DOOM 是 id Software / ZeniMax 的商標,本專案與其無關。