Weltmodell im Code · Teil 2 von 3

Wie aus 49 Pixeln acht Zahlen werden

24. August 2026 · Lesezeit ca. 7 Minuten

Das Dynamikmodell soll nicht mit vollständigen Bildern rechnen. Ein Autoencoder baut deshalb ein kleines internes Datenformat und lernt dabei, welche Bildteile erhalten bleiben müssen.

Encoder und Decoder teilen sich den Engpass

Der Encoder nimmt 49 Grauwerte entgegen und gibt acht Zahlen aus. Der Decoder versucht, aus diesen acht Zahlen wieder 49 Grauwerte zu erzeugen.

Encoder: 49 → 24 → 8
Decoder:  8 → 24 → 49

Beide sind gewöhnliche kleine neuronale Netze. Die Funktion netz erzeugt die Schichten, vorwaerts berechnet ihre Ausgabe und rueckwaerts verteilt den Fehler zurück auf die Gewichte. Das Projekt schreibt diese Teile selbst, damit keine Bibliothek die Mechanik versteckt.

Warum erst 24 Neuronen?

Die 24 Neuronen bilden eine Zwischenebene. Sie sehen noch alle 49 Pixel und können daraus Merkmale zusammensetzen, bevor die acht Werte den Zustand verdichten. In dieser Rasterwelt können das Kombinationen sein, die den beweglichen Agenten von Wänden und Ziel unterscheiden oder seine Lage im Raster eingrenzen.

Das Netz ist hier voll verbunden. Deshalb steht kein einzelnes der 24 Neuronen verlässlich für ein bestimmtes Bildmuster. Im Training teilen sie die Aufgabe unter sich auf. Die tanh-Aktivierung nach 49 → 24 und 24 → 8 gibt dem Encoder zwei Rechenschritte, in denen solche Kombinationen entstehen können.

Ein direkter Encoder 49 → 8 wäre kleiner und könnte für diese Karte reichen. Er hätte nur einen gewichteten Rechenschritt bis zum Engpass. Ob die Zwischenebene nötig ist, entscheidet hier der Vergleich: Rekonstruiert die kleinere Variante die Agentenposition ebenso zuverlässig, ist sie die passendere Architektur.

Warum acht Werte?

Acht ist ein Parameter dieses Versuchs, keine feste Regel für Weltmodelle. Weniger Werte würden stärker komprimieren und könnten wichtige Information verlieren. Mehr Werte gäben dem Netz mehr Platz, machten den Engpass aber weniger wirksam.

GROESSE = len(KARTE)
PIXEL = GROESSE * GROESSE  # bei dieser 7×7-Karte: 49
LATENT = 8
VERSTECKT_AE = 24

encoder = netz([PIXEL, VERSTECKT_AE, LATENT],
               ["tanh", "tanh"], zufall)
decoder = netz([LATENT, VERSTECKT_AE, PIXEL],
               ["tanh", "sigmoid"], zufall)

Die acht Zahlen sind auch keine Felder mit Namen. Wert 1 ist nicht automatisch die x-Koordinate und Wert 2 nicht die y-Koordinate. Das Training darf die Information beliebig verteilen. Entscheidend ist, ob Decoder und Dynamikmodell damit arbeiten können.

Der praktische Test für die Größe. Trainieren Sie Varianten mit vier, sechs, acht und zwölf Werten. Messen Sie anschließend die rekonstruierte Agentenposition und die Vorhersage über mehrere Schritte. Die kleinste zuverlässig funktionierende Variante ist für diese Aufgabe groß genug.

Das Durchschnittsbild wird abgezogen

Wände und Ziel stehen in jedem Trainingsbild an derselben Stelle. Der Agent ändert nur ein Feld. Das Durchschnittsbild trennt diese beiden Arten von Information: Für jede der 49 Positionen wird berechnet, welchen Grauwert sie im Mittel hat.

Position              Bild jetzt   Durchschnitt   nach dem Abzug
Wand oben links           1,0          1,0              0,0
Freies Feld               0,0          0,0              0,0
Feld mit Agent            0,3          0,006            0,294

Die Wand verschwindet aus der Eingabe, weil ihr Wert immer gleich ist. Ein freies Feld mit dem Agenten darauf bleibt deutlich sichtbar: Der Agent steht dort nur selten, deshalb ist der Durchschnitt an dieser Position fast null. Genau diese Abweichungen soll der Encoder in seinen acht Zahlen festhalten.

def zentriere(modell, bild):
    return [wert - mittel
            for wert, mittel in zip(bild, modell["mittel"])]

def kodiere(modell, bild):
    z, _ = vorwaerts(modell["encoder"],
                     zentriere(modell, bild))
    return z

zip(bild, modell["mittel"]) geht beide Listen Feld für Feld durch und bildet jeweils aktueller Wert − Durchschnitt. Zentriert wird nur die Eingabe des Encoders. Der Decoder wird weiter gegen das ursprüngliche Bild trainiert. Die festen Wände kann er aus seinen Gewichten wieder erzeugen; die acht latenten Werte müssen sich vor allem um den beweglichen Teil kümmern.

Das ist ein Fix für diese Karte. Mean-Centering löst nicht jedes Repräsentationsproblem. Hier hilft es, weil fast das ganze Bild konstant ist. Bei einer beweglichen Kamera oder wechselnden Karten müsste die Vorverarbeitung anders aussehen.

Trainiert wird gegen das Eingabebild

Für jedes Bild läuft zuerst der Encoder, dann der Decoder. Der Fehler ist die Differenz zwischen Rekonstruktion und Original. Beim Rückwärtslauf werden zunächst die Decodergewichte und danach die Encodergewichte angepasst.

z, cache_e = vorwaerts(encoder, x)
rekonstruktion, cache_d = vorwaerts(decoder, z)

fehler = [(aus - soll) * 2.0 / PIXEL
          for aus, soll in zip(rekonstruktion, bild)]

dz = rueckwaerts(decoder, cache_d, fehler)
rueckwaerts(encoder, cache_e, dz)

Der Decoder ist damit ein Trainingshelfer. Für die spätere Planung wird er nicht benötigt. Dort arbeitet das Dynamikmodell direkt mit dem latenten Zustand. Der Decoder macht einen geträumten Zustand nur wieder sichtbar, damit wir ihn kontrollieren können.

Warum der erste Messwert täuschte

Der Rekonstruktionsfehler ist der Durchschnitt der quadrierten Abweichungen über alle 49 Pixel. Der Agent belegt davon nur eines. Fehlt er vollständig, während alle anderen 48 Pixel stimmen, bleibt der Durchschnitt klein:

richtiger Wert am Agentenfeld:  0,3
Ausgabe ohne Agent:             0,0

(0,3 − 0,0)² / 49 = 0,00184

Ein Fehler von etwa 0,0018 kann also bedeuten: Wände, Ziel und leerer Boden sehen richtig aus, der Agent fehlt aber. Für ein normales Bild wäre das oft ein kleiner Fehler. Für dieses Weltmodell fehlt damit genau die Information, die nach einer Aktion weitergegeben werden soll.

Epoche 5  Fehler 0.00146   Agent gefunden 38,3 %
Epoche 6  Fehler 0.00076   Agent gefunden 85,3 %
Epoche 7  Fehler 0.00022   Agent gefunden 100,0 %

Darum misst das Programm zusätzlich die Agentenposition. Es sucht im Original und in der Rekonstruktion das Feld des Agenten und zählt nur dann als Treffer, wenn beide Felder gleich sind. In Epoche 5 lag der mittlere Pixelfehler schon bei 0,00146, doch nur 38,3 Prozent der Agenten standen richtig. Erst die zweite Kennzahl zeigt, ob der latente Zustand für das Dynamikmodell brauchbar ist.

Was an Teil 3 übergeben wird

Nach dem Training kann kodiere(modell, bild) jedes Bild in acht Werte übersetzen. Der nächste Teil hängt vier weitere Werte für die Aktion an und lernt daraus den Zustand nach dem Zug, die Belohnung und das Ende der Episode.