CartPole-v0
規(guī)則
我們要操縱我們的小車左右移動慷嗜,使它上面不斷變長的木棒能夠保持平衡森枪。
狀態(tài)
狀態(tài)應包含以下四個因素:
編號 | 名稱 | 最小值 | 最大值 |
---|---|---|---|
0 | 小車的位置 | -2.4 | 2.4 |
1 | 小車的速度 | -inf | inf |
2 | 木棒的角度 | -41.8° | 41.8° |
3 | 木棒的速度 | -inf | inf |
行動
對于某一個狀態(tài)s采取的行動A(s)應該包括以下兩種:
編號 | 名稱 |
---|---|
0 | 小車向左移動 |
1 | 小車向右移動 |
報酬
木棒每保持平衡1個時間步,就得到1分贫堰。
每一場游戲的最高得分為200分
每一場游戲的結束條件:木棒傾斜角度大于41.8°或者已經達到200分
最終獲勝條件為:最近100場游戲的平均得分高于195
代碼雛形
import gym
import numpy as np
env = gym.make('CartPole-v0')
max_number_of_steps = 200 # 每一場游戲的最高得分
#---------獲勝的條件是最近100場平均得分高于195-------------
goal_average_steps = 195
num_consecutive_iterations = 100
#----------------------------------------------------------
num_episodes = 5000 # 共進行5000場游戲
last_time_steps = np.zeros(num_consecutive_iterations) # 只存儲最近100場的得分(可以理解為是一個容量為100的棧)
# 重復進行一場場的游戲
for episode in range(num_episodes):
observation = env.reset() # 初始化本場游戲的環(huán)境
episode_reward = 0 # 初始化本場游戲的得分
# 一場游戲分為一個個時間步
for t in range(max_number_of_steps):
env.render() # 更新并渲染游戲畫面
action = np.random.choice([0, 1]) # 隨機決定小車運動的方向
observation, reward, done, info = env.step(action) # 獲取本次行動的反饋結果
episode_reward += reward
if done:
print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1, last_time_steps.mean()))
last_time_steps = np.hstack((last_time_steps[1:], [episode_reward])) # 更新最近100場游戲的得分stack
break
# 如果最近100場平均得分高于195
if (last_time_steps.mean() >= goal_average_steps):
print('Episode %d train agent successfuly!' % episode)
break
print('Failed!')
上面的代碼只是使用np.random.choice([0, 1])
隨機決定小車運動的方向死陆,并沒有進行任何的智能學習措译。
運行代碼,發(fā)現(xiàn)每場游戲的平均得分僅22左右饰序。
使用
改進代碼
如果利用每次行動后得到的反饋進行強化學習建模,選擇Q-learning算法求豫,那么如果在時間步t時塌衰,狀態(tài)為st诉稍,我們采取的行動為at,本次行動的有利程度記為Q(st,at)最疆,則有下式:
上面的α被稱為學習系數(shù)杯巨,γ被稱為報酬衰減系數(shù),rt為時間步為t時得到的報酬努酸。
import gym
import numpy as np
env = gym.make('CartPole-v0')
max_number_of_steps = 200 # 每一場游戲的最高得分
#---------獲勝的條件是最近100場平均得分高于195-------------
goal_average_steps = 195
num_consecutive_iterations = 100
#----------------------------------------------------------
num_episodes = 5000 # 共進行5000場游戲
last_time_steps = np.zeros(num_consecutive_iterations) # 只存儲最近100場的得分(可以理解為是一個容量為100的棧)
# q_table是一個256*2的二維數(shù)組
# 離散化后的狀態(tài)共有4^4=256中可能的取值服爷,每種狀態(tài)會對應一個行動
# q_table[s][a]就是當狀態(tài)為s時作出行動a的有利程度評價值
# 我們的AI模型要訓練學習的就是這個映射關系表
q_table = np.random.uniform(low=-1, high=1, size=(4 ** 4, env.action_space.n))
# 分箱處理函數(shù),把[clip_min,clip_max]區(qū)間平均分為num段获诈,位于i段區(qū)間的特征值x會被離散化為i
def bins(clip_min, clip_max, num):
return np.linspace(clip_min, clip_max, num + 1)[1:-1]
# 離散化處理仍源,將由4個連續(xù)特征值組成的狀態(tài)矢量轉換為一個0~~255的整數(shù)離散值
def digitize_state(observation):
# 將矢量打散回4個連續(xù)特征值
cart_pos, cart_v, pole_angle, pole_v = observation
# 分別對各個連續(xù)特征值進行離散化(分箱處理)
digitized = [np.digitize(cart_pos, bins=bins(-2.4, 2.4, 4)),
np.digitize(cart_v, bins=bins(-3.0, 3.0, 4)),
np.digitize(pole_angle, bins=bins(-0.5, 0.5, 4)),
np.digitize(pole_v, bins=bins(-2.0, 2.0, 4))]
# 將4個離散值再組合為一個離散值,作為最終結果
return sum([x * (4 ** i) for i, x in enumerate(digitized)])
# 根據(jù)本次的行動及其反饋(下一個時間步的狀態(tài))舔涎,返回下一次的最佳行動
def get_action(state, action, observation, reward):
next_state = digitize_state(observation) # 獲取下一個時間步的狀態(tài)镜会,并將其離散化
next_action = np.argmax(q_table[next_state]) # 查表得到最佳行動
#-------------------------------------訓練學習,更新q_table----------------------------------
alpha = 0.2 # 學習系數(shù)α
gamma = 0.99 # 報酬衰減系數(shù)γ
q_table[state, action] = (1 - alpha) * q_table[state, action] + alpha * (reward + gamma * q_table[next_state, next_action])
# -------------------------------------------------------------------------------------------
return next_action, next_state
# 重復進行一場場的游戲
for episode in range(num_episodes):
observation = env.reset() # 初始化本場游戲的環(huán)境
state = digitize_state(observation) # 獲取初始狀態(tài)值
action = np.argmax(q_table[state]) # 根據(jù)狀態(tài)值作出行動決策
episode_reward = 0
# 一場游戲分為一個個時間步
for t in range(max_number_of_steps):
env.render() # 更新并渲染游戲畫面
observation, reward, done, info = env.step(action) # 獲取本次行動的反饋結果
action, state = get_action(state, action, observation, reward) # 作出下一次行動的決策
episode_reward += reward
if done:
print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1, last_time_steps.mean()))
last_time_steps = np.hstack((last_time_steps[1:], [episode_reward])) # 更新最近100場游戲的得分stack
break
# 如果最近100場平均得分高于195
if (last_time_steps.mean() >= goal_average_steps):
print('Episode %d train agent successfuly!' % episode)
break
print('Failed!')
(感謝注釋部分讓英語不好的我也能夠讀懂代碼)
運行代碼终抽,發(fā)現(xiàn)雖然分數(shù)是有所提高,但效果并沒有十分顯著桶至,隨著訓練的進行昼伴,在過擬合之前,最高的平均分數(shù)也僅有32左右镣屹。
貪心策略
接下來看作者引入了貪心策略圃郊,即每次要移動小車時,以ε的概率以均勻概率隨機選一個方向進行移動女蜈;以1-ε的概率選擇目前為止探索到的對于當前狀態(tài)的最佳行動方向進行移動持舆。
然后把get_action函數(shù)更改成了下面這個樣子:
def get_action(state, action, observation, reward):
next_state = digitize_state(observation)
epsilon = 0.2 # ε-貪心策略中的ε
if epsilon <= np.random.uniform(0, 1):
next_action = np.argmax(q_table[next_state])
else:
next_action = np.random.choice([0, 1])
接下來嘗試著修改一下報酬評價函數(shù),讓模型對一些會直接導致最終失敗的錯誤行動伪窖,其報酬值要減200
observation, reward, done, info = env.step(action)
# 對致命錯誤行動進行極大力度的懲罰逸寓,讓模型恨恨地吸取教訓
if done:
reward = -200
action, state = get_action(state, action, observation, reward, episode)
if done:
print('%d Episode finished after %f time steps / mean %f' % (episode, t + 1,
last_time_steps.mean()))
last_time_steps = np.hstack((last_time_steps[1:], [t + 1]))
break
經過改進,1000場左右游戲過后覆山,模型的平均分終于達到了195分竹伸。