Xin chào các bạn. Tiếp nối series về Reinforcement Learning (RL), hôm nay mình xin giới thiệu một ví dụ đơn giản có thể coi như là "Hello world" của RL.
1. Giới thiệu
Trong bài trước Đôi điều cơ bản về học tăng cường mình đã giới thiệu một số khái niệm của RL. Trong đó môi trường và các trạng thái, phần thưởng là những yếu tố quan trọng. Để cho có thể học được những chiến lược tối ưu hoặc tìm được chuỗi hành động tối ưu, tác nhân phải thử nhiều lần và học từ các lần thử đó, và rất khó để cho tác nhân có thể học được trong thế giới thực. Do đó việc mô phỏng lại môi trường là rất quan trọng. Rất may OpenAI đã có một opensource mang tên Gym để mô phỏng lại một số môi trường đơn giản. Trong bài này mình sẽ sử dụng Q-Learning để giải quyết game mountain-car trong thư viện gym.
Với mountain-car, mục tiêu của chúng ta là điều khiển xe ô tô đến lá cờ: Ta xét các thuộc tính của game này:
- Tập trạng thái bao gồm vị trí của xe trên trục ngang và vận tốc của xe, chiều dương từ trái sang phải. Cụ thể hơn:
- Tập hành động của xe:
Với :
- a = 0: tăng tốc sang trái
- a = 1: Không tăng tốc
- a = 2: Tăng tốc sang phải
- Phần thưởng: Với mỗi hành động, nếu chưa tới đích nhận được phần thưởng -1, nếu không thì nhận được 0 và kết thúc game
- Trạng thái khởi tạo và
- Trạng thái kết thúc: Đến được cờ tại hoặc tổng phần thưởng đạt đến -200
Code import thư viện và khởi tạo môi trường:
# import
import gym
import time
import numpy as np
import IPython.display
from tqdm.notebook import tqdm
# create environment
env = gym.make('MountainCar-v0')
Hãy cùng chạy thử 1 ví dụ:
s = env.reset() # reset to starting State
action_list = list(range(env.action_space.n))
while True:
a = np.random.choice(action_list) # random an action
s, r, done, _ = env.step(a) # perform action, get state, reward, is terminated
if done:
break
env.render()
time.sleep(0.05)
2. Code thôi
2.1 Xây dựng Q-function
Hãy cùng xét công thức cuối cùng của bài viết trước:
Trong trường hợp chúng ta đang xét, do thư viện gym mô phỏng lại hiện tượng vật lý từ thực tế nên tại mỗi trạng thái , ta thực hiện hành động sẽ đưa ta đến trạng thái xác định. Do đó hàm trong trường hợp này được viết lại:
Trong game này,không gian trạng thái có 2 chiều ( và ), tập hành động chỉ có 3 hành động, do đó chúng ta có thể mô hình hàm bằng một bảng , với các cột tương ứng với các giá trị khác nhau của vận tốc , các cột tương ứng với các giá trị khác nhau của tọa độ . Tuy nhiên mà số cột và hàng phải hữu hạn nên ta cần lượng tử hóa trạng thái của xe thành trạng thái rời rạc khác nhau, nghĩa là chia chia các giá trị của tọa độ và vận tốc thành đoạn bằng nhau, trạng thái của xe sẽ là chỉ số của các đoạn tương ứng.
Code lượng tử hóa cho 1 chiều của trạng thái
class NBinDiscretizer:
def __init__(self, min_val, max_val, nbins):
self.min_val = min_val
self.max_val = max_val
self.step = (max_val - min_val) / nbins
self.nbins = int(nbins)
def __call__(self, val):
return int(round((val - self.min_val) / self.step)) % self.nbins
Code lượng tử hóa cho không gian trạng thái:
class Dicretezation:
def __init__(self, discretezers):
self.discretezers = discretezers
def __getitem__(self, index):
assert len(index) == len(self.discretezers)
return tuple([self.discretezers[i](index[i]) for i in range(len(index))])
Tạo các quantizer:
n_quantization = 50
x_quantizer = NBinDiscretizer(env.observation_space.low[0], env.observation_space.high[0], n_quantization)
v_quantizer = NBinDiscretizer(env.observation_space.low[0], env.observation_space.high[0], n_quantization)
state_quantizer = Dicretezation([x_quantizer, v_quantizer])
2.2 Xây dựng quá trình học
Các bước của quá trình học như sau:
- Khởi tạo một giá xác xuất là xác suất thực hiện một bước đi ngẫu nhiên, còn lại là xác suất thực hiện theo chiến lược . sẽ giảm dần trong quá trình học bởi ban đầu khi mô hình chưa học được nhiều thì ta cần lấy các bước ngẫu nhiên để tạo thêm dữ liệu cho mô hình học, còn khi học được rồi thì sẽ học từ chính mô hình.
- Tại mỗi epoch:
- Reset môi trường về trạng thái khởi tạo
- Chọn một bước đi với xác suất ngẫu nhiên, và 1-\epsilion tuân theo chiến lược .
- Thực hiện bước đi , nhận về trạng thái tiếp theo , lợi tức tức thời và trạng thái kết thúc.
- Cập nhật theo (1)
Code:
# inititalize some variables
lr = 0.1
gamma = 0.9
epochs = 10000
epsilon = 0.9
epsilon_scale = epsilon / (epochs / 4)
# some metrics
max_reward = -1000
max_pos = -1000
# logging
# inititalize some variables
epochs = 10000
epsilon = 0.9
epsilon_scale = epsilon / (epochs / 4)
# some metrics
max_reward = -1000
max_pos = -1000
# logging
log = display('', display_id=True)
reach_log = display('', display_id=True)
for epoch in tqdm(range(epochs), desc="Epoch"):
ep_max_pos = -1000
ep_reward = 0
# reset environment
obs = env.reset()
done = False
while not done:
# take an action
if np.random.random_sample() > epsilon:
a = np.argmax(Q[state_quantizer[obs]])
else:
a = np.random.randint(0, env.action_space.n)
# perform action
new_obs, r, done, info = env.step(a)
ep_reward += r
if new_obs[0] >= env.goal_position:
reach_log.update(f"Reach goal at epoch {epoch} with reward: {ep_reward}")
# update Q
cur_q_value = Q[state_quantizer[obs]][a]
new_q_value = (1-lr) * cur_q_value + lr * (r + gamma * max(Q[state_quantizer[new_obs]]))
Q[state_quantizer[obs]][a] = new_q_value
obs = new_obs
ep_max_pos = max(obs[0], ep_max_pos)
max_reward = max(ep_reward, max_reward)
max_pos = max(ep_max_pos, max_pos)
epsilon = max(0, epsilon - epsilon_scale)
log.update("epoch {}: ep_reward: {:9.6f}, max_reward: {:9.6f}, ep_max_pos: {:.6f}, max_pos: {:.6f}, epsilon: {:.6f}".format(epoch, ep_reward, max_reward, ep_max_pos, max_pos, epsilon))
3. Tổng kết
Hy vọng với một ví dụ nhỏ trong bài viết này sẽ giúp các bạn hiểu hơn về Q-function và Q-learning. Code đầy đủ trong bài viết này mình sẽ để ở đây. Nếu có gì góp ý thì đừng ngần ngại cho mình biết để hoàn thiện hơn. Còn nếu thấy hay thì cho mình xin 1 upvote 😄.