Khi nhiều hành động có giá trị ước lượng gần nhau, lấy số lớn nhất dễ chọn trúng một sai số dương. Trong thí nghiệm NumPy dưới đây, tám hành động có cùng giá trị thật và hai bộ sai số độc lập. DQN cho target cao hơn chuẩn trung bình 0,705345, còn Double DQN lệch 0,000581. Khi sai số chọn và sai số đánh giá tương quan hơn, lợi thế này giảm. Khi giá trị thật của các hành động khác nhau, Double DQN còn có thể cho target thấp hơn chuẩn vì chọn nhầm hành động.
DQN và Double DQN khác nhau ở bước nào?
Target là giá trị dùng để cập nhật ước lượng $Q(s,a)$ sau khi quan sát một chuyển tiếp. Với chuyển tiếp chưa kết thúc, gọi $r$ là phần thưởng vừa nhận, $s'$ là trạng thái kế tiếp và $\gamma$ là hệ số chiết khấu.
DQN dùng mạng online để học và một mạng target có trọng số được đồng bộ định kỳ. Riêng khi tạo target, phép lấy cực đại dùng cùng các ước lượng của mạng target để chọn hành động kế tiếp và lấy giá trị của hành động đó. Bài báo DQN của Mnih và cộng sự
$$ y_{\mathrm{DQN}} = r + \gamma \max_b \widehat{Q}_{\mathrm{target}}(s',b) $$Double DQN lấy chỉ số hành động tốt nhất từ mạng online, rồi tra giá trị ở đúng chỉ số đó trên mạng target. Hai mạng có thể dùng cùng kiến trúc. Thay đổi ở đây là mạng nào thực hiện bước chọn. Với chuyển tiếp kết thúc, target chỉ gồm $r$. Bài báo Double DQN của van Hasselt, Guez và Silver
$$ b_{\mathrm{online}} = \operatorname*{arg\,max}_b \widehat{Q}_{\mathrm{online}}(s',b), \qquad y_{\mathrm{DDQN}} = r + \gamma \widehat{Q}_{\mathrm{target}}(s',b_{\mathrm{online}}) $$Tạo một bài toán có giá trị chuẩn tính được
Môi trường có hai trạng thái ra quyết định $s$ và $s'$ cùng một điểm kết thúc. Ở $s$, hành động duy nhất nhận thưởng 0 và đưa tác nhân tới $s'$. Ở $s'$, mỗi hành động nhận phần thưởng xác định rồi kết thúc. Vì vậy giá trị tối ưu $q_b = Q^*(s',b)$ của mỗi hành động ở $s'$ chính là phần thưởng khi chọn hành động đó.
Trường hợp đầu có tám hành động đều thưởng 1. Trường hợp sau có một hành động thưởng 1 và bảy hành động thưởng 0,8. Cả hai có cùng giá trị chuẩn cho chuyển tiếp từ $s$ là $0 + 0{,}99 \times 1 = 0{,}99$. Phép đo tính trung bình của target ước lượng trừ target chuẩn, nên số dương biểu thị ước lượng cao.
Thay đổi tương quan giữa hai bộ ước lượng
Thay cho đầu ra từ một mạng đã huấn luyện, chương trình tạo hai bộ ước lượng bằng cách cộng nhiễu lên các giá trị $q_b$ đã biết. $Z_b$ và $W_b$ là hai mẫu chuẩn tắc độc lập. Mỗi hành động và mỗi lượt mô phỏng dùng mẫu riêng.
$$ \widehat{Q}_{\mathrm{online}}(s',b) = q_b + \sigma Z_b, \qquad \widehat{Q}_{\mathrm{target}}(s',b) = q_b + \sigma\left(\rho Z_b + \sqrt{1-\rho^2}W_b\right) $$Cả hai bộ sai số đều có trung bình 0 và độ lệch chuẩn $\sigma=0{,}5$. $\rho$ điều khiển tương quan của sai số online và target ở cùng một hành động. Khi $\rho=0$, hai bộ độc lập theo cách tạo này. Khi $\rho=1$, chúng trùng nhau từng phần tử. Giữ nguyên độ lệch chuẩn giúp so sánh ảnh hưởng của tương quan mà vẫn giữ độ lớn nhiễu của từng bộ.
Mỗi seed tạo 50.000 cặp vector, mỗi vector có tám giá trị. Cùng cặp mẫu chuẩn được dùng lại trong các cấu hình để so sánh trên cùng cơ sở ngẫu nhiên. Chương trình chạy năm seed và tính trung bình của năm ước lượng sai lệch.
Kết quả đo trên năm seed
Số đo dưới đây chạy bằng NumPy 2.2.5, Python 3.10.11 trên CPU arm64. Các số là độ lệch của target so với 0,99, tính theo đơn vị phần thưởng.
| Giá trị thật ở $s'$ | $\rho$ | Sai lệch DQN | Sai lệch Double DQN |
|---|---|---|---|
| Tám hành động đều bằng 1 | 0 | +0,705345 | +0,000581 |
| Tám hành động đều bằng 1 | 0,5 | +0,705298 | +0,352535 |
| Tám hành động đều bằng 1 | 1 | +0,704064 | +0,704064 |
| Một hành động bằng 1, bảy hành động bằng 0,8 | 0 | +0,540046 | −0,155961 |
Cùng giá trị thật, phép max vẫn kéo trung bình lên
Ở DQN, phép max giữ lại ước lượng cao nhất. Số lớn nhất trong mỗi lượt ít nhất bằng ước lượng của từng hành động cụ thể. Do đó kỳ vọng của max ít nhất bằng giá trị lớn nhất trong các kỳ vọng riêng lẻ. Với tám hành động bằng nhau và nhiễu độc lập theo hành động, mô phỏng thu được sai lệch dương khoảng 0,705 ở cả ba mức tương quan.
Ở Double DQN với $\rho=0$, biết mạng online đã chọn hành động nào không cho biết sai số của mạng target tại hành động đó. Vì giá trị thật của tám hành động bằng nhau, kỳ vọng của sai lệch Double DQN trong thiết lập này bằng 0. Số đo +0,000581 gần mức đó. Độ lệch chuẩn mẫu giữa năm ước lượng theo seed là 0,001113.
Khi $\rho=0{,}5$, phần sai số do $Z_b$ vẫn đi theo hành động được chọn, còn phần từ $W_b$ có kỳ vọng 0. Với các giá trị thật bằng nhau, sai lệch kỳ vọng Double DQN bằng $\rho$ lần sai lệch kỳ vọng DQN trong mô hình này. Kết quả +0,352535 xấp xỉ một nửa mức DQN. Ở $\rho=1$, chương trình xác nhận hai target giống nhau trong từng lượt lấy mẫu.
Tách chọn và đánh giá vẫn có thể chọn nhầm
Khi chỉ một hành động có giá trị thật bằng 1, mạng online chọn đúng hành động đó 20,93% số lượt. Mỗi lần chọn nhầm làm giá trị thật của hành động giảm 0,2, tương đương giảm 0,198 sau chiết khấu. Phần hụt do chọn hành động đo được là −0,156559. Sai số đánh giá độc lập có kỳ vọng 0, nên sai lệch Double DQN đo được là −0,155961, gần phần hụt này.
Cơ chế giảm xu hướng chọn trúng sai số dương và khả năng tìm được hành động tốt nhất là hai việc khác nhau. Trong triển khai DQN, target thường được cập nhật từ online, nên việc tách vai trò không bảo đảm sai số độc lập. Thí nghiệm chỉ đo một bước tạo target dưới mô hình nhiễu đã nêu. Nó không huấn luyện agent, không đo điểm chơi và không dự báo một mức cải thiện cố định trong môi trường thật. Phân tích sai lệch và cách cập nhật mạng trong bài báo Double DQN
Chạy lại bằng NumPy
Cài NumPy 2.2.5, lưu mã thành experiment.py rồi chạy python3 experiment.py. Cột best chosen là tỷ lệ chọn được một hành động tối ưu theo giá trị thật. Cột selection part đo phần hụt do hành động đã chọn, trước khi cộng sai số đánh giá. Hai số cuối là độ lệch chuẩn mẫu của sai lệch trung bình giữa các seed.
1import platform
2
3import numpy as np
4
5
6ACTIONS = 8
7DRAWS = 50_000
8SEEDS = (11, 23, 47, 89, 2026)
9GAMMA = 0.99
10SIGMA = 0.5
11
12
13def estimate(true_q, z, w, rho):
14 online_q = true_q + SIGMA * z
15 target_q = true_q + SIGMA * (rho * z + np.sqrt(1 - rho**2) * w)
16 chosen = online_q.argmax(axis=1)
17 chosen_target_q = target_q[np.arange(z.shape[0]), chosen]
18
19 true_target = GAMMA * true_q.max()
20 dqn = GAMMA * target_q.max(axis=1)
21 double_dqn = GAMMA * chosen_target_q
22 if rho == 1:
23 np.testing.assert_array_equal(dqn, double_dqn)
24
25 return [
26 np.mean(dqn - true_target),
27 np.mean(double_dqn - true_target),
28 np.mean(true_q[chosen] == true_q.max()),
29 GAMMA * np.mean(true_q[chosen] - true_q.max()),
30 ]
31
32
33def main():
34 equal = np.ones(ACTIONS)
35 unequal = np.array([1.0] + [0.8] * (ACTIONS - 1))
36 cases = [
37 ("equal-0.0", equal, 0.0),
38 ("equal-0.5", equal, 0.5),
39 ("equal-1.0", equal, 1.0),
40 ("unequal-0.0", unequal, 0.0),
41 ]
42 results = {name: [] for name, _, _ in cases}
43 for seed in SEEDS:
44 rng = np.random.default_rng(seed)
45 z = rng.standard_normal((DRAWS, ACTIONS))
46 w = rng.standard_normal((DRAWS, ACTIONS))
47 for name, true_q, rho in cases:
48 results[name].append(estimate(true_q, z, w, rho))
49
50 print(f"Python {platform.python_version()}, NumPy {np.__version__}, CPU {platform.machine()}")
51 print(f"actions={ACTIONS}, draws_per_seed={DRAWS}, seeds={SEEDS}")
52 print(f"gamma={GAMMA}, sigma={SIGMA}, true_target={GAMMA:.6f}")
53 print("case DQN bias Double bias best chosen selection part seed SD DQN/Double")
54 for name, observations in results.items():
55 values = np.array(observations)
56 mean = values.mean(axis=0)
57 spread = values[:, :2].std(axis=0, ddof=1)
58 print(
59 f"{name:12} {mean[0]:+9.6f} {mean[1]:+12.6f}"
60 f" {mean[2]:11.6f} {mean[3]:+15.6f}"
61 f" {spread[0]:.6f}/{spread[1]:.6f}"
62 )
63
64
65if __name__ == "__main__":
66 main()
Đọc thêm
- Để xem cách ghép replay buffer, cập nhật mạng và vòng lặp môi trường. Huấn luyện Double DQN trên Mario
- Để đặt DQN vào bối cảnh các phương pháp học máy. Tổng quan các nhóm thuật toán học máy và học tăng cường
Bình luận