Praxis · Grundlagen
Ein Weltmodell von Hand:
49 Pixel, 8 Zahlen, ein Traum.
Ein Agent sieht 49 Graupixel, lernt daraus die Regeln seiner Umgebung und plant anschließend in seiner eigenen Vorstellung, statt die Umgebung zu fragen. Ich habe das in einer Python-Datei gebaut, ohne PyTorch, ohne GPU, nur mit der Standardbibliothek. Der interessanteste Messwert kam heraus, als das gelernte Modell besser plante als die echte Welt.
Was ein Weltmodell ist
Ein Agent, der lernen soll, probiert normalerweise in der Umgebung herum. Jeder Versuch kostet einen echten Schritt. Ein Weltmodell verschiebt das Probieren nach innen: Der Agent lernt aus seinen bisherigen Beobachtungen eine Nachbildung der Umgebung und spielt Handlungsfolgen darin durch, bevor er sich in der Wirklichkeit bewegt. Ha und Schmidhuber haben das 2018 unter dem Namen World Models populär gemacht, Hafner et al. 2020 mit Dream to Control auf richtige Aufgaben gebracht.
Die veröffentlichten Arbeiten arbeiten mit VAEs, rekurrenten Netzen und mehreren GPU-Tagen. Ich wollte wissen, welcher Teil davon das eigentliche Prinzip trägt, und habe die kleinste Fassung gebaut, die noch funktioniert. Kein Framework, keine Bibliothek, eine Datei mit rund 900 Zeilen. Das Ziel war Durchschaubarkeit, nicht Produktionsreife: Jede Zahl im Ergebnis lässt sich auf eine Zeile Code zurückführen.
Die Welt
Ein Raum aus 7×7 Feldern. # ist Wand, Z das Ziel, o
der Agent:
#######
#..#..#
#..#..#
#..o..#
#.##..#
#....Z#
#######
Vier Aktionen, eine Belohnung von 1 auf dem Zielfeld, sonst nichts. Der Agent bekommt nie eine Koordinate zu sehen. Seine gesamte Wahrnehmung sind diese 49 Felder als Grauwerte: Wand 1,0, Ziel 0,6, er selbst 0,3, freies Feld 0. Wo er steht, wo die Wände sind und wo es Belohnung gibt, muss das Modell aus Bildern herausziehen.
Die Datenlage ist knapp, und das mit Absicht. Gesammelt wird mit einer Zufallspolitik, die in 94 Episoden 38-mal das Ziel erreicht. Von 3000 aufgezeichneten Übergängen tragen 1,3 Prozent überhaupt eine Belohnung. Mit dieser Ausbeute muss das Modell auskommen, so wie in echten Anwendungen auch.
Schritt 1: Sammeln
Der Agent läuft zufällig durch den Raum, und jeder Übergang wird mitgeschrieben: Bild vorher, Aktion, Bild nachher, Belohnung, Endsignal. Das ist der ganze Schritt. Er dauert Sekunden und ist der einzige Moment, in dem die echte Umgebung noch gebraucht wird.
python weltmodell.py sammeln # 3000 Übergänge -> daten.json
Schritt 2: Sehen
Autoencoder
49 Pixel durch 8 Zahlen pressen und zurück
Encoder 49 → 24 → 8 (tanh) · Decoder 8 → 24 → 49 (tanh, sigmoid) · 16 Epochen, Adam, Lernrate 0,01
Der Trick ist der Engpass. Wer 49 Pixel durch 8 Zahlen schicken und danach rekonstruieren muss, kann nichts abspeichern, er muss zusammenfassen. Die Karte ist immer dieselbe, veränderlich ist nur die Agentenposition. Also lernt der Engpass im Kern eine Koordinate. Niemand hat ihm das gesagt.
Die erste Fassung lief 16 Epochen durch und lieferte einen Rekonstruktionsfehler von 0,0015, der nicht mehr fiel. Das sieht nach Konvergenz aus. Tatsächlich war es exakt der Fehler, den man bekommt, wenn man die Wände und das Ziel perfekt malt und den Agenten weglässt: ein Pixel von neunundvierzig, im Mittel unsichtbar. Die Positionsquote lag bei 4 bis 8 Prozent, also schlechter als ein starres Modell, das behauptet, nichts ändere sich.
Die Ursache stand in den Latents:
latent ['-1.00', '1.00', '-1.00', '-1.00', '1.00', '-1.00', '-1.00', '0.03']
Sieben von acht Zahlen kleben an ±1. In fast jedem Pixel steht immer derselbe Wert, die Wände bewegen sich nie. Diese Konstanten treiben die gewichtete Summe im Encoder so weit ins Positive oder Negative, dass tanh sättigt, und dort ist die Ableitung null. Es kam kein Gradient mehr zurück. Die Lösung ist eine Zeile: das Durchschnittsbild abziehen, bevor der Encoder das Bild sieht. Danach steht im Bild nur noch, was sich tatsächlich ändert. Positionsquote nach 16 Epochen: 100 Prozent.
Epoche 5 Rekonstruktionsfehler 0.00146 Agent gefunden 38.3%
Epoche 6 Rekonstruktionsfehler 0.00076 Agent gefunden 85.3%
Epoche 7 Rekonstruktionsfehler 0.00022 Agent gefunden 100.0%
Dieselbe Falle steht in jedem Projekt, in dem ein Durchschnittsmaß über viele Dimensionen als Erfolgskriterium dient. Der Verlust misst, was leicht zu messen ist. Ob er das misst, worauf es ankommt, ist eine getrennte Frage, und sie wird selten gestellt.
Schritt 3: Träumen
Dynamiknetz
Zustand + Aktion → nächster Zustand, Belohnung, Ende
Netz 12 → 32 → 10 (tanh, linear) · 20 Epochen · Belohnung mit Faktor 10 gewichtet
Das eigentliche Weltmodell. Eingabe sind die acht Zahlen aus Schritt 2 plus die Aktion als Vier-Bit-Vektor, Ausgabe der nächste Zustand, die vorhergesagte Belohnung und ob die Episode endet. Die Zielzustände kommen aus dem bereits fertigen Encoder. Das Dynamiknetz sieht kein einziges Pixel.
Dieser Bruch ist der Punkt, an dem Weltmodelle brauchbar werden. Planen kostet danach acht Zahlen statt neunundvierzig, und der Aufwand pro geträumtem Schritt hängt nicht mehr an der Bildgröße. Bei 49 Pixeln ist das eine Fingerübung, bei einem Kamerabild macht es den Unterschied zwischen planbar und nicht planbar.
Eine Kleinigkeit hat mich dabei aufgehalten: Die Belohnung ist in weniger als zwei Prozent der Daten ungleich null. Ohne Gegengewicht lernt das Netz, immer null zu sagen, und liegt damit fast immer richtig. Der Belohnungsanteil im Verlust bekommt deshalb den Faktor 10.
So sieht ein Traum aus. Links die Welt, rechts das, was das Modell sich vorstellt, nachdem es die letzte Beobachtung vor zwei Zügen gesehen hat:
2. rechts Belohnung echt 0 / getraeumt 0.00
Welt Traum
####### #######
#..#..# #..#..#
#..#..# #..#..#
#.....# #.....#
#.##.o# #.##.o#
#....Z# #....Z#
####### #######
Wie weit der Traum trägt
Das Kommando messen gibt dem Modell ein Bild und danach nur noch Aktionen. Gezählt
wird, ob der geträumte Agent auf dem richtigen Feld steht. Zum Vergleich läuft das starre Modell
mit, das behauptet, nichts ändere sich, also die Latte, die ein Weltmodell reißen kann:
| Schritt | Position richtig | Pixelfehler | Starres Modell |
|---|---|---|---|
| 1 | 95,0 % | 0,00014 | 35,0 % |
| 3 | 92,7 % | 0,00027 | 27,1 % |
| 5 | 83,6 % | 0,00052 | 16,9 % |
| 10 | 71,3 % | 0,00091 | 16,0 % |
Der Traum zerfällt langsam, und er zerfällt vorhersehbar. Das ist die Zahl, aus der später der Planungshorizont folgt: Der Agent plant sechs Schritte weit und liest danach wieder die Wirklichkeit ein.
Schritt 4: Planen
Der Planer ist der billigste, der noch funktioniert. Random Shooting mit Model Predictive Control: 100 Aktionsfolgen würfeln, jede im Traum durchspielen, die vorhergesagten Belohnungen mit 0,95 abgezinst aufsummieren, den ersten Zug der besten Folge ausführen und danach neu planen. Das Neuplanen sorgt dafür, dass sich Modellfehler nicht über die ganze Episode aufaddieren.
Zum Vergleich läuft derselbe Planer mit der echten Umgebung als Modell, also mit perfektem Wissen. Ich nenne ihn Orakel, er ist die Obergrenze für dieses Verfahren. Dazu ein Agent ohne Modell, der nur würfelt. 20 Episoden, Abbruch nach 40 Schritten:
| Horizont | Weltmodell | Orakel | Zufall |
|---|---|---|---|
| 3 | 8/20, 25,0 Schritte | 15/20, 17,8 | 9/20, 33,1 |
| 6 | 20/20, 4,5 Schritte | 19/20, 11,3 | 9/20, 33,1 |
| 12 | 20/20, 4,5 Schritte | 20/20, 5,2 | 9/20, 33,1 |
Bei Horizont 6 ist der Träumer besser als die Wirklichkeit. Ein gelerntes Modell kann nicht besser sein als das Original, also habe ich nachgesehen, woher der Vorsprung kommt.
Warum der Träumer das Orakel schlägt
Er kommt von den Feldern, die weiter als sechs Schritte vom Ziel entfernt sind. Dort findet das Orakel in 100 gewürfelten Folgen keine einzige, die das Ziel trifft. Alle Folgen bekommen den Wert 0, der Planer hat nichts zu vergleichen und wählt praktisch zufällig.
Das Weltmodell dagegen halluziniert Belohnungen zwischen 0,00003 und 0,77, und zwar bevorzugt
für Folgen, die in die richtige Richtung laufen. Aus einer Belohnung, die nur auf einem einzigen
Feld existiert, wird ein Gefälle über den ganzen Raum. Das Kommando warum misst
beide Hälften dieser Erklärung nach, auf den drei Feldern jenseits des Horizonts, 30
Wiederholungen, 90 Züge je Planer:
| Planer | Zug verkürzt den Weg zum Ziel |
|---|---|
| Weltmodell | 82,2 % |
| Orakel | 46,7 % |
Bei Horizont 12, wenn das Orakel die Belohnung selbst findet, holt es auf und zieht mit 20 von 20 gleich. Der Vorsprung des Träumers war also geborgt: Seine Ungenauigkeit wirkte wie ein geformtes Belohnungssignal, das ihm niemand gegeben hat.
Das ist bequem und gefährlich zugleich. Dieselbe Unschärfe kann Belohnungen genauso gut dort erfinden, wo keine sind. Bei Horizont 3 passiert genau das: 8 von 20 Episoden, damit schlechter als reiner Zufall mit 9 von 20. Wer eine solche Zahl in einem Projekt sieht und sich darüber freut, hat noch nicht verstanden, wovon sie kommt. Die Frage nach der Ursache ist mir hier dreimal begegnet und war jedes Mal die Arbeit wert.
Was bewusst fehlt
Das Projekt lässt eine Menge weg, und jede Auslassung hat einen Grund. Wer die Fassung erweitert, fängt hier an:
- Stochastik. Jeder Übergang ist hier deterministisch. Ein Weltmodell für eine unsichere Umgebung muss Wahrscheinlichkeiten ausgeben, so wie das MDN-RNN bei Ha und Schmidhuber.
- Rekurrenz. Die Beobachtung zeigt hier den gesamten Zustand, also reicht ein Netz ohne Gedächtnis. Sobald der Agent nur einen Ausschnitt sieht, braucht er eines.
- VAE statt schlichtem Autoencoder. Der Engpass ist hier nicht darauf trainiert, einen zusammenhängenden Zustandsraum zu bilden. Beim Träumen über viele Schritte macht sich das bemerkbar.
- Gelernte Politik und gezielte Exploration. Random Shooting und zufälliges Sammeln funktionieren bei 49 Pixeln und vier Aktionen. Darüber hinaus nicht mehr lange.
Was ich mitgenommen habe
Drei Punkte, die sich auf andere Projekte übertragen lassen.
Erstens misst der Verlust selten das, was zählt. Ein Rekonstruktionsfehler von 0,0015 sah nach einem fertigen Modell aus, während der Agent in der Rekonstruktion gar nicht vorkam. Die zwölf Zeilen, die eine fachliche Kennzahl nebenher mitzählen, sind der beste Zeitinvest im ganzen Projekt gewesen.
Zweitens lohnt sich eine dumme Vergleichslinie. Das starre Modell, das behauptet, nichts ändere sich, kostet vier Zeilen und beantwortet die Frage, ob das gelernte Modell überhaupt etwas gelernt hat. Ohne diese 35 Prozent im ersten Schritt hätte ich die kaputte Fassung für funktionierend gehalten.
Drittens ist ein unerwartet gutes Ergebnis ein Grund zum Nachsehen. Der Träumer, der das Orakel schlägt, war der Moment, an dem das Projekt interessant wurde, und er war ein Fehler des Modells, kein Können.
Der ganze Aufbau läuft in 132 Sekunden auf einer CPU durch, in einer Datei, mit der Python-Standardbibliothek. Wer verstehen will, was Weltmodelle tun, braucht dafür weder GPU noch Framework.
Reinforcement Learning oder LLM-Anwendung im Aufbau?
Ich baue KI-Anwendungen für den Produktivbetrieb und schaue dabei besonders auf die Stelle, an der eine Kennzahl gut aussieht und das System trotzdem nicht tut, was es soll. 30 Minuten Erstgespräch, kostenlos.