Rutnätsvärlden: din första RL-agent
Kunna implementera en agent som lär sig hitta till målet i ett rutnät.
Förkunskaper
Intuition
Rutnätsvärlden är RL:s motsvarighet till «Hej världen». Ett litet rutnät, en agent, ett mål.
. . . M M = mål (+1)
. # . X X = fälla (−1)
A . . . # = vägg
De fyra begreppen finns alla här, i sin enklaste form:
| Begrepp | I rutnätet |
|---|---|
| Tillstånd | vilken ruta agenten står i |
| Handling | upp, ner, vänster, höger |
| Belöning | +1 i mål, −1 i fällan, annars ett litet minus per steg |
| Episod | från start tills agenten når mål eller fälla |
Agenten vet ingenting i början. Den provar sig fram, får poäng, och kommer långsamt ihåg vilka rutor som är värda att gå till.
Det lilla minuset per steg är viktigare än det låter: utan det spelar det ingen roll hur lång vägen är, och agenten kan lära sig att vandra runt i evighet innan den går i mål.
Kod
import random
BREDD, HOJD = 4, 3
VAGGAR = {(1, 1)}
MAL, FALLA = (0, 3), (1, 3)
HANDLINGAR = ["upp", "ner", "vanster", "hoger"]
DELTA = {"upp": (-1, 0), "ner": (1, 0), "vanster": (0, -1), "hoger": (0, 1)}
def flytta(pos, handling):
dr, dk = DELTA[handling]
ny = (pos[0] + dr, pos[1] + dk)
if not (0 <= ny[0] < HOJD and 0 <= ny[1] < BREDD) or ny in VAGGAR:
return pos # gå in i en vägg → stå kvar
return ny
def belon(pos):
if pos == MAL:
return 1.0, True
if pos == FALLA:
return -1.0, True
return -0.04, False # litet minus per steg → korta vägar lönar sig
Q = {(r, k, h): 0.0 for r in range(HOJD) for k in range(BREDD) for h in HANDLINGAR}
def basta(pos):
return max(HANDLINGAR, key=lambda h: Q[(pos[0], pos[1], h)])
rng = random.Random(0)
for episod in range(2000):
eps = max(0.05, 1.0 - episod / 1000) # utforska mycket först, sedan mindre
pos, steg = (2, 0), 0
while steg < 100:
h = rng.choice(HANDLINGAR) if rng.random() < eps else basta(pos)
ny = flytta(pos, h)
r, klar = belon(ny)
basta_nasta = 0.0 if klar else max(Q[(ny[0], ny[1], a)] for a in HANDLINGAR)
Q[(pos[0], pos[1], h)] += 0.1 * (r + 0.95 * basta_nasta - Q[(pos[0], pos[1], h)])
pos, steg = ny, steg + 1
if klar:
break
ETIKETT = {MAL: " MAL ", FALLA: "FALLA", **{v: " # " for v in VAGGAR}}
for r in range(HOJD):
print(" ".join(ETIKETT.get((r, k), f"{basta((r, k)):^5.5}") for k in range(BREDD)))
# hoger hoger hoger MAL
# upp # upp FALLA
# upp hoger upp vanst
Pilarna pekar mot målet och bort från fällan — utan att någon har sagt var de ligger. Agenten har bara provat, fått poäng, och kommit ihåg.
Interaktivt
Fyra experiment som ändrar vad agenten lär sig. Kör grundversionen först, titta på pilarna, och ändra sedan en sak i taget.
| Ändring | Vad som händer |
|---|---|
Ta bort straffet per steg (-0.04 → 0.0) | pilarna slutar peka raka vägen; alla vägar är lika bra |
Höj straffet till -0.5 | agenten går rakt i fällan — att dö snabbt blir bättre än att gå långt |
Sätt eps = 0.0 | agenten fastnar i den första väg den hittade, ofta en dålig |
Sätt 0.95 → 0.5 | agenten blir närsynt och ser inte mål som ligger långt bort |
Den andra raden är den mest lärorika. Belöningen är inte en önskan — den är en definition. Straffar du varje steg för hårt har du faktiskt bett agenten att avsluta fort, och fällan avslutar fort.
Det är samma fenomen som gör belöningsdesign svårt i verkliga system, och det syns tydligast här i rutnätet där du kan se hela lösningen på en gång.
Behärskning innebär
- Beskriver tillstånd, handling och belöning i ett rutnät
- Implementerar rörelse med väggar
- Förklarar hur belöningen styr vad agenten lär sig
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Sutton & Barto — Reinforcement Learning: An Introduction (2:a uppl.) — fri att läsa online (författarnas utgåva)
- Gymnasium — dokumentation (MIT) — MIT
- Python-dokumentationen (PSF-licens) — PSF