Value iteration pada chapter sebelumnya menghitung nilai optimal, tetapi dengan syarat yang berat: tabel transisi dan reward harus diketahui di muka. Agent sungguhan tidak punya keistimewaan itu — yang ada hanyalah pengalaman berupa urutan (state, aksi, reward, state_baru). Jembatannya adalah value function: penaksir nilai yang diperbarui dari pengalaman, bukan dari model. Pada chapter ini kita membedakan dua wujud nilai — V(s) dan Q(s,a) — lalu melatih Q-learning tabular pada GridWorld 4x4 dan menyaksikan tabel Q yang mulanya kosong tumbuh menjadi peta panah berisi jalur optimal enam langkah.
Dua Wujud Nilai #
| Simbol | Pertanyaan yang dijawab | Bentuk tabel pada GridWorld 4x4 |
|---|---|---|
V(s) | seberapa menjanjikan state s? | 16 angka |
Q(s,a) | seberapa menjanjikan mengambil a di s? | 16 × 4 = 64 angka |
Q lebih boros memori, tetapi membawa satu keuntungan yang menentukan: kebijakan bisa dibaca langsung tanpa memeriksa transisi. Cukup pilih argmax_a Q(s,a) — kebijakan greedy — dan agent pun siap bertindak. Keduanya terhubung lewat V*(s) = max_a Q*(s,a); tabel V pada akhir demo kita pun diperoleh dengan cara persis seperti itu.
Pembaruan Q-Learning #
Sekali pengalaman (s, a, r, s') memberi satu koreksi terhadap dugaan lama:
Q(s,a) ← Q(s,a) + α · (target − Q(s,a)) dengan target = r + γ · max_a' Q(s',a')
Sisi kanan kurung adalah galat temporal difference: selisih antara dugaan baru (r plus nilai terbaik di s') dan dugaan lama Q(s,a). Learning rate α mengatur seberapa cepat dugaan lama ditimpa — perannya sama dengan learning rate pada Gradient Descent
. Karena target memakai max, pembaruan ini memperkirakan Q* dari kebijakan terbaik apa pun yang kelak ditemukan, tanpa perlu tahu kebijakan itu seperti apa (off-policy). Untuk mengumpulkan pengalaman, agent memakai epsilon-greedy: sesekali memilih aksi acak — mekanisme yang kita bedah tuntas pada chapter Exploration vs Exploitation
.
Demo: Q-Learning Tabular pada GridWorld 4x4 #
Dunianya diperluas dari chapter Markov Decision Process
: papan 4x4, mulai (0,0), target (3,3), reward -0.05 per langkah dan +1 saat tiba, batas 25 langkah per episode. Berkas q_learning.py menjalankan 500 episode:
"""Q-learning tabular pada GridWorld 4x4 dengan kebijakan epsilon-greedy.
Agen mulai di (0,0) dan harus mencapai target (3,3). Empat aksi: atas,
bawah, kiri, kanan; menabrak dinding papan membuat agen diam. Setiap
langkah bernilai -0.01, mencapai target bernilai +1 dan episode berakhir.
Tabel Q berukuran 4 x 4 x 4 diisi lewat pembaruan
Q(s,a) <- Q(s,a) + alfa x (target - Q(s,a)).
"""
import numpy as np
rng = np.random.default_rng(7)
UKURAN = 4
START, GOAL = (0, 0), (3, 3)
AKSI = [(-1, 0), (1, 0), (0, -1), (0, 1)] # atas, bawah, kiri, kanan
PANAH = ["^", "v", "<", ">"]
MAKS_LANGKAH = 25
def step(state, aksi):
"""Satu interaksi; kembalikan (state_baru, reward, selesai)."""
db, dk = AKSI[aksi]
nb, nk = state[0] + db, state[1] + dk
state_baru = (nb, nk) if (0 <= nb < UKURAN and 0 <= nk < UKURAN) else state
selesai = state_baru == GOAL
reward = 1.0 if selesai else -0.05
return state_baru, reward, selesai
Q = np.zeros((UKURAN, UKURAN, 4))
alfa, gamma, epsilon = 0.05, 0.9, 0.1
kunjungan = np.zeros((UKURAN, UKURAN, 4))
riwayat = []
for episode in range(1, 501):
state, total = START, 0.0
for _ in range(MAKS_LANGKAH):
if rng.random() < epsilon: # eksplorasi
aksi = int(rng.integers(0, 4))
else: # eksploitasi
aksi = int(np.argmax(Q[state[0], state[1]]))
state_baru, reward, selesai = step(state, aksi)
q_maks_berikut = 0.0 if selesai else Q[state_baru[0], state_baru[1]].max()
target = reward + gamma * q_maks_berikut
Q[state[0], state[1], aksi] += alfa * (target - Q[state[0], state[1], aksi])
kunjungan[state[0], state[1], aksi] += 1
total += reward
state = state_baru
if selesai:
break
riwayat.append(total)
print("Q-learning tabular, 500 episode, epsilon=0.1, alfa=0.05")
print("Total reward 10 episode pertama (Q masih hampir kosong):")
for i in range(10):
print(f" episode {i + 1:2d}: {riwayat[i]:+.2f}")
print("Mean total reward per blok 50 episode:")
for i in range(0, 500, 50):
blok = riwayat[i:i + 50]
print(f" episode {i + 1:3d}-{i + 50:3d}: {np.mean(blok):+.3f}")
print(f"\nSukses mencapai target (blok awal) : "
f"{sum(r > 0 for r in riwayat[:50])}/50")
print(f"Sukses mencapai target (blok akhir): "
f"{sum(r > 0 for r in riwayat[-50:])}/50")
print("\nKebijakan greedy dari tabel Q (panah = aksi terbaik):")
for b in range(UKURAN):
baris = []
for k in range(UKURAN):
if (b, k) == GOAL:
baris.append(" G ")
else:
baris.append(f" {PANAH[int(np.argmax(Q[b, k]))]} ")
print(" " + "".join(baris))
print("\nV(s) = max_a Q(s,a) dibulatkan 2 desimal:")
for b in range(UKURAN):
nilai = [f"{Q[b, k].max():5.2f}" if (b, k) != GOAL else " 0.00"
for k in range(UKURAN)]
print(" " + " ".join(nilai))
state, rute, total = START, [START], 0.0
for _ in range(MAKS_LANGKAH):
aksi = int(np.argmax(Q[state[0], state[1]]))
state, reward, selesai = step(state, aksi)
rute.append(state)
total += reward
if selesai:
break
print(f"\nJalur greedy dari {START}: "
+ " -> ".join(f"({b},{k})" for b, k in rute))
print(f"Panjang jalur : {len(rute) - 1} langkah, total reward = {total:+.2f}")
print(f"Pasangan state-aksi tak pernah dikunjungi: "
f"{int((kunjungan == 0).sum())}/60 "
f"(state non-target: 15 x 4 aksi)")Output yang Dihasilkan:
Q-learning tabular, 500 episode, epsilon=0.1, alfa=0.05
Total reward 10 episode pertama (Q masih hampir kosong):
episode 1: -1.25
episode 2: -1.25
episode 3: -1.25
episode 4: +0.15
episode 5: -0.20
episode 6: +0.40
episode 7: +0.40
episode 8: -1.25
episode 9: +0.25
episode 10: -1.25
Mean total reward per blok 50 episode:
episode 1- 50: +0.274
episode 51-100: +0.687
episode 101-150: +0.718
episode 151-200: +0.726
episode 201-250: +0.728
episode 251-300: +0.720
episode 301-350: +0.716
episode 351-400: +0.698
episode 401-450: +0.723
episode 451-500: +0.725
Sukses mencapai target (blok awal) : 42/50
Sukses mencapai target (blok akhir): 50/50
Kebijakan greedy dari tabel Q (panah = aksi terbaik):
v v v v
> v > v
> > > v
< > > G
V(s) = max_a Q(s,a) dibulatkan 2 desimal:
0.39 0.29 -0.02 0.07
0.48 0.59 0.43 0.77
0.33 0.71 0.85 1.00
-0.01 0.26 0.81 0.00
Jalur greedy dari (0, 0): (0,0) -> (1,0) -> (1,1) -> (2,1) -> (2,2) -> (2,3) -> (3,3)
Panjang jalur : 6 langkah, total reward = +0.75
Pasangan state-aksi tak pernah dikunjungi: 4/60 (state non-target: 15 x 4 aksi)Perjalanan belajarnya terbaca dari tiga lapis keluaran. Episode awal masih bergantung pada keberuntungan — total reward berayun dari -1.25 hingga +0.40 karena tabel Q nyaris kosong dan 25 langkah sering habis sebelum target. Mean per blok 50 episode naik dari +0.274 lalu stabil di sekitar +0.72; tingkat keberhasilan melompat dari 42/50 menjadi 50/50. Pada lapis kedua, panah-panah greedy membentuk aliran menuju G, dan tabel V(s) = max_a Q(s,a) memperlihatkan lereng nilai yang menanjak dari 0.39 di start menuju 1.00 di sekitar target. Lapis ketiga adalah ujian akhirnya: jalur greedy dari (0,0) menempuh enam langkah dengan total reward +0.75 — enam langkah adalah jarak terpendek yang mungkin, sehingga -0.05 × 5 + 1.0 = +0.75 adalah return optimal dunia ini. Mari kita telaah kode di atas baris per baris:
- Baris 12: generator acak ber-seed 7 dari numpy.random — seluruh episode, aksi eksplorasi, dan hasil demo dapat direproduksi persis.
- Baris 14-17: konstanta dunia — papan 4x4, start, target, empat aksi, dan batas 25 langkah.
- Baris 21-28 (
step): satu interaksi — perpindahan dihitung, dinding membuat agen diam, reward-0.05atau+1, episode selesai di target. - Baris 31-33: tabel
Q4×4×4 nol, hyperparameteralfa=0.05, gamma=0.9, epsilon=0.1, serta tabelkunjunganuntuk menghitung pasangan state-aksi yang belum pernah dicoba. - Baris 39-42: pemilihan aksi epsilon-greedy — dengan peluang
epsilonaksi acak diambil, selain itu aksi greedy dari tabelQ. - Baris 44-46: inti algoritma —
target = reward + gamma · max Q(s')(nol bila episode selesai), lalu pembaruanQ(s,a)menuju target sebesaralfakali galat. - Baris 54-66: ringkasan belajar — 10 episode pertama dicetak mentah, mean per blok 50 episode, dan tingkat keberhasilan blok awal lawan blok akhir.
- Baris 68-82: membaca hasil belajar — peta panah greedy, tabel
V(s), dan keduanya menampilkan target sebagaiG/0.00karena episode berakhir di sana. - Baris 84-97: ujian jalur greedy murni (tanpa eksplorasi) dari start, plus laporan
4/60pasangan state-aksi yang tak pernah dikunjungi selama training — jejak eksplorasi yang belum lengkap namun tidak menghalangi jalur optimal.
Batas Tabel #
Enam belas state muat nyaman dalam tabel, dan dunia kita bahkan deterministik — syarat paling ramah bagi Q-learning. Dua hal akan sekaligus runtuh saat dunia membesar: memori (jumlah state melonjak secara kombinatorial, apalagi bila state berupa citra) dan cakupan (sebagian besar pasangan state-aksi tak akan pernah dikunjungi, seperti 4/60 kita yang akan membesar drastis). Jalan keluarnya adalah mengganti tabel dengan fungsi penaksir. Ada dua arah: menaksir kebijakan langsung dengan jaringan — itu topik chapter Policy Gradient
— atau menaksir Q itu sendiri dengan jaringan, yang kita bangun pada chapter Deep Q-Network
.