Chương 9·Bài 9.1

Reinforcement learning

Robot thư viện cần đi từ cửa tới bàn trả sách. Nếu biết bản đồ thì dùng BFS. Nhưng nếu robot không có bản đồ, mỗi lần chỉ được đi một bước, nhìn xem mình tới đâu và nhận một điểm thưởng, thì phải học dần từ các lần thử. Đó là reinforcement learning (học tăng cường). Khó khăn chính là điểm thưởng thường chỉ có khi tới đích, nên phải tìm cách đánh giá cả những bước còn ở xa đích. Bài này học Q-learning và policy gradient trên các bài toán nhỏ.

Từ hành động tới một vòng tương tác

Ở thời điểm tt, agent nhìn trạng thái StS_t, chọn hành động AtA_t. Environment thực hiện hành động và trả về phần thưởng Rt+1R_{t+1} cùng trạng thái St+1S_{t+1}. Chỉ số t+1t+1 nhắc rằng phần thưởng là kết quả sau hành động; nó không phải nhãn đúng của hành động được đưa sẵn trước khi chọn.

Học từ thưởng qua tương tác như vậy là học tăng cường (reinforcement learning): chương trình tự chơi game, được cộng điểm khi thắng, hay hệ thống điều khiển đèn giao thông để xe chạy thông suốt.

Agent chọn hành động; environment trả phần thưởng và trạng thái mới.agentπ(a | s), Q(s, a)environmentstep(s, a)hành động A_tthưởng R_{t+1}, trạng thái S_{t+1}
Agent chọn hành động; environment trả phần thưởng và trạng thái mới.

Một policy, ký hiệu π(a∣s)\pi(a\mid s), cho xác suất chọn hành động aa khi ở trạng thái ss. Policy có thể tất định, như luôn đi phải ở một ô; cũng có thể ngẫu nhiên, như đi phải với xác suất 0.70.7. Phần thưởng nói mục tiêu cần đạt, còn policy nói cách thực hiện. Ta thường chưa biết hành động tốt nhất để đưa làm nhãn huấn luyện.

Muốn vị trí ô đủ mô tả trạng thái, kết quả bước tiếp theo chỉ được phụ thuộc vào vị trí và hành động hiện tại, không cần toàn bộ lịch sử. Đó là tính Markov.

Nếu robot còn pin và pin ảnh hưởng khả năng di chuyển, phải thêm mức pin vào trạng thái. Nếu nhiệm vụ bắt buộc kết thúc sau đúng 20 lượt, thời gian còn lại cũng có thể ảnh hưởng quyết định và phải được biểu diễn. Bỏ một biến cần thiết khiến cùng một “trạng thái” có hai cách diễn biến khác nhau.

Hỏi nhanh

Robot chỉ ghi lại vị trí ô. Khi pin yếu, lệnh “tiến” có lúc không làm robot di chuyển. Nên sửa mô tả trạng thái thế nào?

Đặt một MDP đủ nhỏ để kiểm bằng tay

Markov decision process (MDP) gồm tập trạng thái S\mathcal S, tập hành động A\mathcal A, quy luật chuyển và thưởng p(s′,r∣s,a)p(s',r\mid s,a), cùng hệ số chiết khấu γ\gamma. Ta dùng lưới 3 hàng, 4 cột; tọa độ viết (hàng, cột), bắt đầu từ 0 ở góc trên trái. Robot xuất phát ở (2,0)(2,0), đích ở (0,3)(0,3), ô (1,1)(1,1) bị chắn. Mỗi ô còn lại là một trạng thái, nên có 11 trạng thái.

Giá trị tối ưu V* của từng ô và một hành động tối ưu; từ S cần 5 bước.6.208.0010.00G0.004.588.0010.00S3.124.586.208.00mỗi bước −1vào G +10, kết thúcγ = 0.9V*(S) = 3.122
Giá trị tối ưu V* của từng ô và một hành động tối ưu; từ S cần 5 bước.

Bốn hành động là lên, xuống, trái, phải. Va tường hoặc đi ra ngoài lưới thì giữ nguyên vị trí. Mỗi bước chưa vào đích nhận −1-1 điểm; bước vào đích nhận 1010 điểm và kết thúc episode, tức một lần thực hiện nhiệm vụ. Đích không còn hành động cần học.

Chuyển trạng thái ở đây tất định; xác suất của kết quả đúng bằng 1. Trong môi trường có trượt chân, cùng một hành động có thể dẫn tới nhiều kết quả và ta phải lấy kỳ vọng theo pp.

Ta cài environment trước, rồi kiểm hai trường hợp dễ nhầm: va ô chắn và vào đích. Toàn bộ code trong bài chạy nối tiếp; chỉ cần NumPy.

Python
import numpy as np

states = [(r, c) for r in range(3) for c in range(4) if (r, c) != (1, 1)]
state_id = {cell: i for i, cell in enumerate(states)}
start, goal = state_id[(2, 0)], state_id[(0, 3)]
moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
action_names = ["U", "D", "L", "R"]
gamma = 0.9

def step(state, action):
    assert state != goal, "Không bước tiếp sau khi đã tới đích."
    row, col = states[state]
    dr, dc = moves[action]
    next_cell = (row + dr, col + dc)
    next_state = state_id.get(next_cell, state)
    terminated = next_state == goal
    reward = 10.0 if terminated else -1.0
    return next_state, reward, terminated

wall_step = step(state_id[(1, 0)], 3)
goal_step = step(state_id[(0, 2)], 3)
assert wall_step == (state_id[(1, 0)], -1.0, False)
assert goal_step == (goal, 10.0, True)
print("Số trạng thái:", len(states))
print("Va ô chắn:", states[wall_step[0]], wall_step[1:])
print("Vào đích:", states[goal_step[0]], goal_step[1:])

Dòng va ô chắn phải giữ vị trí cũ và vẫn trừ một điểm. Dòng vào đích phải có True; nếu quên điều kiện ấy, robot có thể tiếp tục thu thưởng hoặc bị phạt sau khi nhiệm vụ đã xong.

Hỏi nhanh

Đổi luật chơi: robot có hướng nhìn là N, E, S hoặc W, và chỉ còn ba hành động là quay trái, quay phải, và tiến một ô theo hướng đang nhìn. Trên lưới của bài, MDP mới có bao nhiêu trạng thái và bao nhiêu hành động?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q3.1

Vì sao nhìn thưởng một bước chưa đủ?

Một bước xa đích chỉ có thưởng −1-1, dù nó mở đường tới bàn trả sách. Ta cần đánh giá phần thưởng tương lai. Với episode kết thúc sau TT hành động, return từ thời điểm tt là

Gt=Rt+1+γRt+2+⋯+γT−t−1RT.G_t = R_{t+1} + \gamma R_{t+2} + \cdots + \gamma^{T-t-1}R_T.

γ=0.9\gamma=0.9 làm một điểm ở bước kế tiếp chỉ còn giá trị 0.90.9 khi nhìn từ hiện tại. Nó không phải xác suất thành công, cũng không phải learning rate. Với bài tiếp diễn vô hạn, thưởng bị chặn và 0≤γ<10\leq\gamma<1 giúp tổng hội tụ; một episode hữu hạn có thể dùng γ=1\gamma=1 nếu mục tiêu là tổng thưởng không chiết khấu.

Đường 5 hành động trong hình có chuỗi thưởng (−1,−1,−1,−1,10)(-1,-1,-1,-1,10). Vì thưởng cuối đến sau bốn lần chiết khấu,

G0=−1−0.9−0.92−0.93+10⋅0.94=3.122.G_0=-1-0.9-0.9^2-0.9^3+10\cdot0.9^4=3.122.

Tổng không chiết khấu là 66. Hai số đo hai mục tiêu khác nhau, đều có đơn vị điểm thưởng. Đừng đọc 3.122 thành số bước.

Thưởng càng xa càng bị chiết khấu: tổng thưởng 6 nhưng return chỉ 3.122.0510t = 0-1t = 1-0.9t = 2-0.81t = 3-0.729t = 46.56tổng thưởng 6G₀ = 3.122thưởng (nhạt) và γᵗ · thưởng (đậm)
Thưởng càng xa càng bị chiết khấu: tổng thưởng 6 nhưng return chỉ 3.122.
Python
rewards = np.array([-1, -1, -1, -1, 10], dtype=float)
return_hand = float(np.sum(rewards * gamma ** np.arange(len(rewards))))
print(f"Tổng thưởng: {rewards.sum():.3f}")
print(f"Return chiết khấu: {return_hand:.3f}")
assert np.isclose(return_hand, 3.122)

Phần thưởng và γ\gamma cùng quyết định thế nào là “tốt nhất”. Ở lưới này, mỗi bước chưa tới đích bị trừ 1 nên đường dài mất thêm điểm; γ<1\gamma<1 còn làm thưởng 10 đến muộn có giá trị nhỏ hơn. Cả hai cơ chế đều đẩy robot đi đường ngắn. Ngược lại, cộng thưởng dương cho mỗi bước có thể khiến robot muốn kéo dài episode. Thiết kế thưởng là một phần của đề bài, không phải chi tiết cài đặt.

Hỏi nhanh

Một bước đi vào terminal nhận thưởng 10. Return tính từ ngay trước bước đó bằng bao nhiêu?

Hỏi nhanh

Đổi thiết kế: vào đích nhận 10, mọi bước khác nhận 0, và γ=1\gamma = 1. Policy nào là tối ưu?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q3.8

Bellman: tính việc xa bằng việc gần

Vπ(s)V^\pi(s) là kỳ vọng return khi bắt đầu ở ss và làm theo policy π\pi. Còn Qπ(s,a)Q^\pi(s,a) là kỳ vọng khi bắt đầu bằng hành động aa, sau đó làm theo π\pi. Kỳ vọng cần thiết vì cả policy và environment có thể ngẫu nhiên. V∗V^* là giá trị tốt nhất có thể đạt.

Trong lưới tất định này, Bellman optimality viết thành

V∗(s)=max⁡a[r(s,a)+γV∗(s′)],V∗(terminal)=0.V^*(s)=\max_a\left[r(s,a)+\gamma V^*(s')\right],\qquad V^*(\text{terminal})=0.

Với chuyển ngẫu nhiên, thay biểu thức trong ngoặc bằng ∑s′,rp(s′,r∣s,a)[r+γV∗(s′)]\sum_{s',r}p(s',r\mid s,a)[r+\gamma V^*(s')]. Dấu max chọn hành động tốt nhất; dấu tổng tính kỳ vọng kết quả của một hành động. Hai thao tác có vai trò khác nhau.

Với một policy cố định, bỏ max và lấy trung bình theo chính policy đó:

Vπ(s)=∑aπ(a∣s)∑s′,rp(s′,r∣s,a)[r+γVπ(s′)].V^\pi(s)=\sum_a\pi(a\mid s)\sum_{s',r}p(s',r\mid s,a)\left[r+\gamma V^\pi(s')\right].

Đây là hệ ∣S∣|\mathcal S| phương trình tuyến tính với ẩn là các Vπ(s)V^\pi(s). Khi đồ thị có vòng, các ẩn phụ thuộc lẫn nhau, nên phải giải cả hệ hoặc lặp đến khi hội tụ.

Từ đích đi ngược: ô cách một bước có giá trị 1010; cách hai bước có −1+0.9⋅10=8-1+0.9\cdot10=8; cách ba bước có 6.26.2, rồi 4.584.58, rồi 3.1223.122. Giống dynamic programming, giá trị của bài toán lớn được viết qua bài toán nhỏ. Nhưng có vòng trong đồ thị nên không có thứ tự nào tính một lần là xong; ta lặp cập nhật trên mọi trạng thái, gọi là value iteration. Bắt đầu từ V=0V=0, mỗi vòng đưa thông tin từ đích lùi thêm một bước.

Một vòng xét mọi trạng thái, mọi hành động và mọi trạng thái kế tiếp có thể: lưu pp thành bảng đầy đủ thì tốn O(∣S∣2∣A∣)O(|\mathcal S|^2|\mathcal A|), còn lưới tất định chỉ có một trạng thái kế tiếp nên mỗi vòng là O(∣S∣∣A∣)O(|\mathcal S||\mathcal A|).

Python
V = np.zeros(len(states))
for _ in range(200):
    old_V = V.copy()                # cập nhật đồng bộ từ vòng trước
    for state in range(len(states)):
        if state == goal:
            continue
        candidates = []
        for action in range(4):
            next_state, reward, terminated = step(state, action)
            future = 0.0 if terminated else old_V[next_state]
            candidates.append(reward + gamma * future)
        V[state] = max(candidates)
    if np.max(np.abs(V - old_V)) < 1e-12:
        break

print(f"Value iteration tại S: {V[start]:.3f}")
print(f"Giá trị terminal: {V[goal]:.3f}")
assert np.isclose(V[start], return_hand)

Value iteration gọi step cho mọi cặp trạng thái–hành động tùy ý, tức dùng mô hình của environment: đây là planning với mô hình đã biết, chưa phải học từ trải nghiệm robot tự thu được. Đáp án V∗V^* của nó là mốc để kiểm phần học tiếp theo. Khi biết bản đồ và chỉ cần đường ngắn nhất, dùng BFS trực tiếp thường hợp lý hơn huấn luyện một agent.

Hỏi nhanh

Chạy value iteration đồng bộ trên lưới của bài, bắt đầu từ V=0V = 0. Sau đúng 2 vòng, giá trị tại S bằng bao nhiêu?

Value iteration: phải tới vòng 5, thưởng ở G mới lan về tới S.-1.0-1.010.0-1.0-1.010.0-1.0-1.0-1.0-1.0sau 1 vòngS: -1.000-1.98.010.0-1.98.010.0-1.9-1.9-1.98.0sau 2 vòngS: -1.9006.28.010.0-2.78.010.0-2.7-2.76.28.0sau 3 vòngS: -2.7106.28.010.04.68.010.03.14.66.28.0sau 5 vòngS: 3.122mỗi vòng đưa thông tin từ G lùi thêm một bước
Value iteration: phải tới vòng 5, thưởng ở G mới lan về tới S.
Mỗi vòng, mỗi ô lấy thưởng tốt nhất của một bước cộng giá trị chiết khấu của ô kế; giá trị lan dần từ G về S.

Q-learning: cập nhật từ một trải nghiệm

Bây giờ agent chỉ gọi step cho hành động đã chọn. Nó lưu bảng Q(s,a)Q(s,a) shape (11, 4); mỗi hàng là một ô, mỗi cột là một hành động. Sau quan sát (s,a,r,s′)(s,a,r,s'), target là r+γmax⁡a′Q(s′,a′)r+\gamma\max_{a'}Q(s',a'). Sai lệch giữa target và giá trị hiện tại gọi là TD error (sai lệch thời gian). Ta cập nhật

Q(s,a)←Q(s,a)+α[r+γmax⁡a′Q(s′,a′)−Q(s,a)].Q(s,a)\leftarrow Q(s,a)+\alpha\left[r+\gamma\max_{a'}Q(s',a')-Q(s,a)\right].

α\alpha là learning rate. Viết cách khác, Q(s,a)←(1−α)Q(s,a)+α⋅targetQ(s,a)\leftarrow(1-\alpha)Q(s,a)+\alpha\cdot\text{target}: α=1\alpha=1 ghi đè bằng target mới nhất, α\alpha nhỏ giữ lại phần lớn giá trị cũ và lấy trung bình có trọng số các target đã gặp. Nếu bước vừa đi kết thúc episode (terminal), target chỉ còn rr.

Ví dụ Q(s,a)=2Q(s,a)=2, r=−1r=-1, giá trị tốt nhất ở trạng thái kế tiếp là 5, γ=0.9\gamma=0.9, α=0.2\alpha=0.2: target là 3.53.5, TD error là 1.51.5, giá trị mới là 2.32.3. Nếu đó là bước terminal thưởng 10 thì giá trị mới là 2+0.2(10−2)=3.62+0.2(10-2)=3.6.

Hỏi nhanh

Đề chỉ cấp env.reset() và env.step(a), trả về trạng thái mới, phần thưởng và cờ kết thúc. Không có bảng chuyển, cũng không có bản đồ. Cách nào chạy được ngay?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q5.1(iv)

Policy lấy hành động có QQ lớn nhất có thể bỏ lỡ hướng chưa thử. Ta dùng epsilon-greedy: xác suất ϵ\epsilon chọn đều trong cả bốn hành động, còn lại chọn một hành động đạt cực đại. Hành động tốt nhất cũng có thể được chọn trong nhánh ngẫu nhiên.

Việc thử hướng mới này gọi là khám phá (exploration), để thu thông tin về environment. Nó khác với tìm kiếm (search) ở bài 9.2, nơi trạng thái được mở rộng bằng quy tắc đã biết.

Python
def td_target(reward, next_values, terminated):
    if terminated:
        return reward
    return reward + gamma * np.max(next_values)

assert np.isclose(td_target(-1.0, np.array([5.0, 2.0]), False), 3.5)
# Giá trị cố ý sai ở terminal cũng không được lọt vào target.
assert td_target(10.0, np.full(4, 999.0), True) == 10.0

rng = np.random.default_rng(7)
Q = np.zeros((len(states), 4))
alpha, epsilon = 0.3, 0.2
for episode in range(3000):
    state = start
    for turn in range(100):
        if rng.random() < epsilon:
            action = int(rng.integers(4))
        else:
            best = np.flatnonzero(Q[state] == Q[state].max())
            action = int(rng.choice(best))
        next_state, reward, terminated = step(state, action)
        target = td_target(reward, Q[next_state], terminated)
        Q[state, action] += alpha * (target - Q[state, action])
        state = next_state
        if terminated:
            break

print("Shape bảng Q:", Q.shape)
print(f"Q tốt nhất tại S: {Q[start].max():.3f}")
assert np.all(Q[goal] == 0)

Giới hạn 100 lượt trong code là dừng thu mẫu để chương trình luôn chạy nhanh, không phải terminal của MDP. Ở lượt bị dừng ấy, ta vẫn dùng target có giá trị tương lai, rồi reset để thu một episode mới. Gymnasium trả riêng hai cờ terminated và truncated; chỉ terminal thật làm mất bootstrap.

Q-learning tiến dần tới giá trị V*(S) = 3.122 mà value iteration đã tính.-2023.1220100200300episodeV*(S)max Q(S, ·)
Q-learning tiến dần tới giá trị V*(S) = 3.122 mà value iteration đã tính.

Hỏi nhanh

Epsilon-greedy có 4 hành động, đúng một hành động tốt nhất, ϵ=0.2\epsilon = 0.2, và nhánh explore chọn đều cả 4 hành động. Xác suất chọn hành động tốt nhất là bao nhiêu?

Epsilon-greedy 0.2 với 4 hành động: hành động tốt nhất được chọn 85% số lần.00.510.85tốt nhất0.05khác 10.05khác 20.05khác 3khai thác 0.8explore 0.2 / 4
Epsilon-greedy 0.2 với 4 hành động: hành động tốt nhất được chọn 85% số lần.

Mỗi cập nhật chỉ nhìn trước một bước, nên tin tốt ở đích lan ngược về S chậm. Với α=1\alpha=1 và QQ khởi tạo 0, trong episode đầu tiên tới đích chỉ cặp trạng thái–hành động cuối cùng nhận target có thưởng 10; các cặp trước đó đã được cập nhật khi giá trị phía sau còn bằng 0. Mỗi lần đi lại cùng đường, thông tin lùi thêm một bước.

Experience replay lưu các transition đã gặp vào bộ nhớ rồi cập nhật lại nhiều lần, nhờ đó dùng mỗi lần tương tác hiệu quả hơn; đây cũng là một thành phần của DQN, Q-learning với mạng neural.

Hỏi nhanh

Episode đầu tiên đi 5 transition từ S tới đích, và tất cả đều được lưu lại. Q-learning với α=1\alpha = 1, QQ khởi tạo bằng 0. Phát lại 5 transition này theo thứ tự nào để QQ tại S đạt 3.1223.122 chỉ sau một lượt quét?

Phát lại ngược đưa thưởng về S sau một lượt; phát lại xuôi thì không.phát lại xuôi000010Q(S) = 0phát lại ngược3.1224.586.2810Q(S) = 3.122S→ G
Phát lại ngược đưa thưởng về S sau một lượt; phát lại xuôi thì không.

Đọc policy, không chỉ đọc một ô Q

Con số ở S gần đáp án chuẩn chưa chứng minh robot đi được. Ta tắt exploration, chạy policy greedy và in cả đường đi. Evaluation không cập nhật QQ; tránh việc kiểm tra cũng làm thay đổi agent.

Python
state = start
path = [states[state]]
actions_taken, observed_rewards = [], []
for _ in range(30):
    action = int(np.argmax(Q[state]))
    state, reward, terminated = step(state, action)
    actions_taken.append(action_names[action])
    observed_rewards.append(reward)
    path.append(states[state])
    if terminated:
        break

discounted = sum(gamma ** t * r for t, r in enumerate(observed_rewards))
print("Hành động:", " ".join(actions_taken))
print("Đường đi:", path)
print(
    f"Số bước: {len(actions_taken)}, "
    f"return: {discounted:.3f}"
)
assert state == goal and len(actions_taken) == 5
assert np.isclose(discounted, V[start])

Nhìn Số bước trước: đường ngắn nhất từ S dài 5 bước (đếm trên hình hoặc bằng BFS), nên từ S agent đã đạt mức tối ưu. Lưới có nhiều đường ngắn nhất, vì vậy chuỗi hành động có thể khác mũi tên trong hình mà return vẫn là 3.122. Một đường đi chỉ kiểm policy tại S; muốn kiểm cả policy, chạy evaluation từ mọi ô và so với V∗V^* của value iteration.

Định lý hội tụ của Q-learning dạng bảng cần mọi cặp trạng thái–hành động được thử vô số lần và learning rate giảm dần thích hợp. Ở lưới tất định của bài, α=0.3\alpha=0.3 cố định đã cho đúng kết quả; khi chuyển trạng thái ngẫu nhiên, α\alpha cố định khiến QQ luôn dao động theo các mẫu gần nhất.

Hỏi nhanh

Một bảng Q mới học 20 episode. Đánh giá greedy từ S cho đường (2,0)→(2,1)→(2,0)→(2,1)→⋯(2, 0)\to(2, 1)\to(2, 0)\to(2, 1)\to\cdots cho đến hết 30 lượt mà không tới đích. Ta kết luận gì, và nên làm gì?

SARSA, Monte Carlo và TD

Target của Q-learning lấy max⁡a′Q(s′,a′)\max_{a'}Q(s',a'), giá trị của hành động tốt nhất ở s′s', dù lúc chạy epsilon-greedy có thể chọn hành động khác. Agent hành động theo một policy (epsilon-greedy) nhưng học giá trị của một policy khác (greedy). Kiểu học này gọi là off-policy.

SARSA chọn trước hành động kế tiếp a′a' bằng chính policy đang chạy, rồi đưa nó vào target:

Q(s,a)←Q(s,a)+α[r+γQ(s′,a′)−Q(s,a)].Q(s,a)\leftarrow Q(s,a)+\alpha\left[r+\gamma Q(s',a')-Q(s,a)\right].

Tên gọi lấy từ năm thứ cần cho một lần cập nhật: s,a,r,s′,a′s, a, r, s', a'. Target dùng hành động thật sự được chọn, kể cả bước khám phá, nên SARSA học giá trị của chính policy đang chạy. Kiểu học này gọi là on-policy.

Ví dụ: r=1r = 1, ở s′s' hai hành động có QQ là 6 và 2, policy chọn hành động có Q=2Q = 2, γ=0.5\gamma = 0.5. Target của Q-learning là 1+0.5⋅6=41 + 0.5\cdot 6 = 4, của SARSA là 1+0.5⋅2=21 + 0.5\cdot 2 = 2.

Khác biệt hiện rõ trên lưới vách đá (cliff walking): 4 hàng, 12 cột, xuất phát ở góc dưới trái, đích ở góc dưới phải, mười ô giữa hàng dưới là vách. Mỗi bước nhận −1-1; bước vào vách nhận −100-100 và bị đưa về ô xuất phát. Cả hai thuật toán học 500 episode với ϵ=0.1\epsilon = 0.1, α=0.1\alpha = 0.1, γ=1\gamma = 1, cùng seed.

Python
CLIFF_ROWS, CLIFF_COLS = 4, 12
CLIFF_START, CLIFF_GOAL = (3, 0), (3, 11)
CLIFF = {(3, c) for c in range(1, 11)}

def cliff_step(cell, action):
    r, c = cell
    dr, dc = moves[action]
    nxt = (min(max(r + dr, 0), CLIFF_ROWS - 1), min(max(c + dc, 0), CLIFF_COLS - 1))
    if nxt in CLIFF:
        return CLIFF_START, -100.0, False
    return nxt, -1.0, nxt == CLIFF_GOAL

def egreedy(values, gen, eps):
    if gen.random() < eps:
        return int(gen.integers(4))
    return int(gen.choice(np.flatnonzero(values == values.max())))

def train_cliff(method, episodes=500, alpha=0.1, eps=0.1, seed=0):
    gen = np.random.default_rng(seed)
    Qc = np.zeros((CLIFF_ROWS, CLIFF_COLS, 4))
    returns = []
    for _ in range(episodes):
        cell, total = CLIFF_START, 0.0
        action = egreedy(Qc[cell], gen, eps)
        for _ in range(500):
            nxt, reward, done = cliff_step(cell, action)
            total += reward
            next_action = egreedy(Qc[nxt], gen, eps)
            if done:
                target = reward
            elif method == "SARSA":
                target = reward + Qc[nxt][next_action]   # hành động sẽ làm thật
            else:
                target = reward + Qc[nxt].max()          # hành động tốt nhất
            Qc[cell][action] += alpha * (target - Qc[cell][action])
            cell, action = nxt, next_action
            if done:
                break
        returns.append(total)
    return Qc, returns

def cliff_greedy(Qc):
    cell, path = CLIFF_START, [CLIFF_START]
    for _ in range(60):
        cell, _, done = cliff_step(cell, int(np.argmax(Qc[cell])))
        path.append(cell)
        if done:
            break
    return path

for method in ["Q-learning", "SARSA"]:
    Qc, returns = train_cliff(method)
    path = cliff_greedy(Qc)
    rows_used = sorted({r for r, _ in path[1:-1]})
    print(f"{method:10s} greedy {len(path) - 1} bước, qua hàng {rows_used}; "
          f"return lúc học (100 episode cuối): {np.mean(returns[-100:]):.1f}")

Q-learning học đường ngắn nhất, 13 bước, sát ngay trên vách. Đó là đường tốt nhất cho policy greedy. Nhưng lúc học, cứ khoảng 10 bước lại có một bước ngẫu nhiên, và đứng sát vách thì một bước xuống là mất 100 điểm: return trung bình khi học chỉ −64.8-64.8. SARSA tính cả những bước ngẫu nhiên ấy vào giá trị, nên học đường cách vách thêm một hàng, dài 15 bước, và khi học đạt −25.1-25.1.

Cần điểm tốt ngay trong lúc còn khám phá, như robot thật không được phép ngã, thì chọn SARSA. Chỉ cần policy greedy cuối cùng tốt nhất thì Q-learning cho đường ngắn hơn. Khi ϵ\epsilon giảm dần về 0, cả hai cùng tiến về đường tối ưu.

Q-learning đi sát vách, SARSA đi cách vách một hàng.vách: −100, về lại SSGQ-learning: 13 bước, sát váchSARSA: 15 bước, cách vách một hàng
Q-learning đi sát vách, SARSA đi cách vách một hàng.

Hỏi nhanh

Sau khi học xong, tắt khám phá (ϵ=0\epsilon = 0) và cho cả hai policy greedy đi từ ô xuất phát. Return của Q-learning và của SARSA lần lượt là bao nhiêu?

Q-learning và SARSA đều cập nhật ngay sau một bước, dựa vào giá trị đang ước lượng ở bước kế (bootstrapping). Đó là học temporal difference (TD). Dạng TD cho VV là

V(s)←V(s)+α[r+γV(s′)−V(s)].V(s)\leftarrow V(s)+\alpha\left[r+\gamma V(s')-V(s)\right].

Monte Carlo làm khác: chạy hết episode, tính return thật GtG_t cho từng trạng thái đã đi qua, rồi lấy trung bình các GtG_t của mỗi trạng thái làm V(s)V(s). Nó không dựa vào ước lượng nào, nên giá trị khởi tạo sai không làm lệch kết quả. Đổi lại, nó phải chờ episode kết thúc, và GtG_t cộng nhiều bước ngẫu nhiên nên dao động mạnh. TD cập nhật được ngay, dùng được cả cho bài không có điểm kết thúc, nhưng target phụ thuộc vào chính giá trị đang học.

Hỏi nhanh

Bài toán điều khiển đèn giao thông chạy liên tục, không có lúc kết thúc. Cách học giá trị nào dùng được?

Policy gradient: học trực tiếp xác suất hành động

Q-learning học bảng giá trị rồi suy ra policy. Một cách khác là đặt tham số cho policy và tăng kỳ vọng return. Để thấy đúng phép toán, ta tạm dùng nhiệm vụ một bước: chọn trái nhận 1 điểm, chọn phải nhận 3 điểm, rồi episode kết thúc. Đây là một bandit, trường hợp đơn giản nhất của tương tác có thưởng; nó không minh họa khó khăn gán công lao qua nhiều bước.

Đặt xác suất đi phải p=σ(θ)=1/(1+e−θ)p=\sigma(\theta)=1/(1+e^{-\theta}). Kỳ vọng thưởng là J(θ)=1(1−p)+3p=1+2pJ(\theta)=1(1-p)+3p=1+2p. Do σ′(θ)=p(1−p)\sigma'(\theta)=p(1-p),

J′(θ)=2p(1−p).J'(\theta)=2p(1-p).

Tại θ=0\theta=0, p=0.5p=0.5, gradient bằng 0.50.5. Gradient ascent với η=0.1\eta=0.1 cho θ=0.05\theta=0.05, tăng xác suất phải lên khoảng 0.51250.5125. Dấu cộng khác với gradient descent của loss vì ta muốn tăng thưởng.

Nếu chưa biết thưởng của cả hai hướng, một mẫu REINFORCE dùng (R−b)∇θlog⁡πθ(A)(R-b)\nabla_\theta\log\pi_\theta(A) để ước lượng gradient. bb là baseline không phụ thuộc vào hành động đang chọn.

Với A∈{0,1}A\in\{0,1\} và xác suất phải là pp, đạo hàm log-policy là A−pA-p. Lấy b=2b=2: chọn phải cho (3−2)(1−p)(3-2)(1-p); chọn trái cho (1−2)(−p)(1-2)(-p). Kỳ vọng hai trường hợp đúng bằng 2p(1−p)2p(1-p).

Ta kiểm tính tay trước, rồi dùng 400 mẫu để cập nhật policy.

Python
def sigmoid(theta):
    return 1.0 / (1.0 + np.exp(-theta))

p0 = sigmoid(0.0)
theta_hand = 0.1 * 2 * p0 * (1 - p0)
print(f"Một bước exact gradient: theta={theta_hand:.4f}, p={sigmoid(theta_hand):.4f}")

rng_policy = np.random.default_rng(11)
theta_policy = 0.0
for _ in range(400):
    probability = sigmoid(theta_policy)
    action = int(rng_policy.random() < probability)
    reward = 3.0 if action else 1.0
    sample_gradient = (reward - 2.0) * (action - probability)
    theta_policy += 0.05 * sample_gradient

p_final = sigmoid(theta_policy)
print(f"Policy sau 400 mẫu: p(phải)={p_final:.4f}, E[R]={1 + 2 * p_final:.4f}")
assert p_final > 0.9

E[R] ở dòng cuối tính bằng công thức 1+2p1+2p của bài toán, không phải trung bình các thưởng đã nhận khi huấn luyện. Sau 400 mẫu, pp mới tới khoảng 0.960.96 vì kỳ vọng gradient 2p(1−p)2p(1-p) nhỏ dần khi pp gần 1: policy càng chắc chắn thì càng học chậm.

Trong môi trường lớn, policy là một mạng neural nhận trạng thái và trả xác suất hành động; ít tham số hơn một bảng khổng lồ, nhưng không còn đáp án chính xác để đối chiếu.

Xác suất chọn hành động tốt tăng nhanh lúc đầu rồi chậm dần khi gần 1.0.50.7510100200300400số mẫu0.9641p(phải)gradient kỳ vọng 2p(1 − p) nhỏ dần khi p gần 1
Xác suất chọn hành động tốt tăng nhanh lúc đầu rồi chậm dần khi gần 1.

Với episode nhiều bước, mỗi thời điểm tt dùng return tính từ chính thời điểm đó:

g^=∑t(Gt−b(St))∇θlog⁡πθ(At∣St).\widehat g=\sum_{t}\left(G_t-b(S_t)\right)\nabla_\theta\log\pi_\theta(A_t\mid S_t).

Hành động ở bước tt không ảnh hưởng tới các thưởng đã nhận trước nó, nên dùng GtG_t thay cho tổng thưởng cả episode.

Baseline không làm lệch kỳ vọng, vì EA∼πθ[∇θlog⁡πθ(A∣s)]=∇θ∑aπθ(a∣s)=0\mathbb E_{A\sim\pi_\theta}[\nabla_\theta\log\pi_\theta(A\mid s)]=\nabla_\theta\sum_a\pi_\theta(a\mid s)=0, nhưng làm các mẫu bớt dao động; lựa chọn thường gặp là một ước lượng của V(St)V(S_t).

Nếu thay GtG_t bằng Rt+1+γVϕ(St+1)R_{t+1}+\gamma V_\phi(S_{t+1}), với VϕV_\phi do một mạng thứ hai học, ta được actor-critic: actor là policy, critic là hàm giá trị.

Hỏi nhanh

Ta muốn cực đại hóa kỳ vọng thưởng J(θ)J(\theta), và đã có gradient bằng J′(θ)J'(\theta). Dòng cập nhật nào đúng?

Hỏi nhanh

Bandit của bài, với θ=0\theta = 0 nên p=0.5p = 0.5, và baseline b=2b = 2. Mẫu vừa rút chọn trái và nhận 1 điểm. Sau một cập nhật REINFORCE như trong code, xác suất chọn phải thay đổi thế nào?

Khi đi thi

  • Đề cho đủ quy tắc chuyển trạng thái thì dùng BFS, DP hay value iteration, không cần học từ tương tác.
  • Kiểm step bằng tay trước khi train: va tường, vào đích, hết lượt.
  • Đánh giá agent bằng policy greedy trên nhiều episode, không bằng thưởng lúc đang khám phá.

Tóm tắt

  • MDP làm rõ trạng thái, hành động, quy luật chuyển và thưởng; trạng thái phải giữ các biến cần cho bước tiếp theo. Thưởng và γ\gamma quyết định thế nào là tối ưu.
  • Return cộng thưởng tương lai với hệ số chiết khấu. Terminal có giá trị tương lai 0 nhưng không xóa thưởng của bước kết thúc; bước bị cắt vì hết lượt vẫn bootstrap.
  • Bellman nối giá trị hiện tại với giá trị tương lai. Value iteration dùng mô hình và lan thông tin một bước mỗi vòng; Q-learning cập nhật từ từng transition, có thể tăng tốc bằng replay.
  • Q-learning là off-policy, target lấy max⁡\max; SARSA là on-policy, target dùng hành động thật sự chọn, nên tránh xa chỗ nguy hiểm khi còn khám phá. TD cập nhật sau từng bước bằng bootstrap; Monte Carlo chờ hết episode để có return thật.
  • Exploration thu thêm thông tin; evaluation greedy đo policy đã học. Policy gradient tăng kỳ vọng thưởng bằng cách chỉnh xác suất hành động; baseline giảm dao động mà không đổi kỳ vọng.

Kiểm tra cuối bài

Câu 1

Bốn đội mô tả bài toán cờ của mình. Bài nào là reinforcement learning?

Phỏng theoNOAI 2025 Singapore · Phần 1 · Câu 3

Câu 2

Environment bị viết sai: bước vào đích nhận 10 nhưng không kết thúc. Đứng ở đích mà đi lên thì va tường, vẫn ở đích, và lại nhận 10. Với γ=0.9\gamma = 0.9, value iteration cho giá trị của ô đích bằng bao nhiêu?

Câu 3

Trên lưới của bài, mỗi bước bị −1-1, bước vào đích được +10+10, và γ=0.9\gamma = 0.9. Đổi thưởng vào đích thành 00, các bước khác vẫn −1-1. Policy tối ưu và V∗(S)V^*(S) thay đổi thế nào?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q3.7

Câu 4

Mặt sàn trơn. Robot ở ô X, cách đích 2 ô trên một đường thẳng, với ô Y nằm giữa. Lệnh “tiến” có xác suất 0.80.8 đi một ô và 0.20.2 trượt đi hai ô. Từ Y thì cả hai trường hợp đều vào đích. Vào đích nhận 10, bước khác nhận 0, γ=0.9\gamma = 0.9, và “tiến” là hành động tốt nhất. V∗(X)V^*(X) bằng bao nhiêu?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q3.9

Câu 5

Một policy cố định đưa robot đi qua đi lại mãi giữa hai ô. Từ A sang B nhận 0, từ B sang A nhận 1, và γ=0.8\gamma = 0.8. Vπ(A)V^\pi(A) bằng bao nhiêu?

Phỏng theoMIT 6.036 · Final, Spring 2022 · Q6(a)

Câu 6

Value iteration lưu p(s′∣s,a)p(s'\mid s, a) thành một bảng đầy đủ, với ∣S∣=2000|\mathcal S| = 2000 trạng thái và ∣A∣=5|\mathcal A| = 5 hành động. Một vòng cập nhật mọi trạng thái tốn cỡ bao nhiêu phép nhân cộng, và chuyển trạng thái ngẫu nhiên có làm đổi con số ấy không?

Phỏng theoCMU 10-601 · Exam 3 Practice · Q4.1

Câu 7

MDP có bốn trạng thái A, B, C, D và hai hành động Move, Stay chưa rõ tác dụng. Q-learning với QQ khởi tạo bằng 0, α=1\alpha = 1, γ=0.9\gamma = 0.9 lần lượt nhận các transition (A,Move,0,B)(A, \text{Move}, 0, B), (B,Move,0,C)(B, \text{Move}, 0, C), (C,Move,1,A)(C, \text{Move}, 1, A), (A,Move,0,B)(A, \text{Move}, 0, B) và (B,Move,0,C)(B, \text{Move}, 0, C). Sau năm lần cập nhật, những ô nào của bảng QQ khác 0?

Phỏng theoMIT 6.036 · Final, Spring 2022 · Q7(a)

Câu 8

Tiếp tục với bảng QQ của câu trước, α=1\alpha = 1, γ=0.9\gamma = 0.9. Agent nhận thêm (A,Move,0,B)(A, \text{Move}, 0, B) rồi (B,Move,0,D)(B, \text{Move}, 0, D), và mọi Q(D,⋅)Q(D, \cdot) vẫn bằng 0. Giá trị mới của Q(A,Move)Q(A, \text{Move}) và Q(B,Move)Q(B, \text{Move}) là bao nhiêu, và cần sửa điều gì?

Phỏng theoMIT 6.036 · Final, Spring 2022 · Q7(c)–(d)

Câu 9

Environment cắt mỗi episode ở lượt 100, với truncated=True, dù robot chưa tới đích. Đoạn cập nhật dưới đây sai ở đâu?

s2, r, terminated, truncated, _ = env.step(a)
done = terminated or truncated
target = r if done else r + gamma * Q[s2].max()
Q[s, a] += alpha * (target - Q[s, a])

Câu 10

Với bandit của bài, trái được 1 điểm, phải được 3 điểm, p=0.5p = 0.5, và mẫu REINFORCE là (R−b)(A−p)(R - b)(A - p). So sánh baseline b=0b = 0 với b=2b = 2.

Phỏng theoCMU 10-601 · Exam 3 Practice · Q5.3(a)

Đọc thêm