使用actor-critic方法來控制CartPole-V0 游戲詳解
CartPole 介紹
在一個光滑的軌道上有個推車,桿子垂直微置在推車上,隨時有倒的風(fēng)險。系統(tǒng)每次對推車施加向左或者向右的力,但我們的目標(biāo)是讓桿子保持直立。桿子保持直立的每個時間單位都會獲得 +1 的獎勵。但是當(dāng)桿子與垂直方向成 15 度以上的位置,或者推車偏離中心點超過 2.4 個單位后,這一輪局游戲結(jié)束。因此我們可以獲得的最高回報等于 200 。我們這里就是要通過使用 PPO 算法來訓(xùn)練一個強(qiáng)化學(xué)習(xí)模型 actor-critic ,通過對比模型訓(xùn)練前后的游戲運行 gif 圖,可以看出來我們訓(xùn)練好的模型能長時間保持桿子處于垂直狀態(tài)。
Actor Critic 介紹
當(dāng) agent 采取行動并在環(huán)境中移動時,它在觀察到的環(huán)境狀態(tài)的情況下,學(xué)習(xí)兩個可能的輸出:
- 接下來最合適的一個操作,actor 負(fù)責(zé)此部分輸出。
- 未來可能獲得的獎勵總和,critic 負(fù)責(zé)此部分的輸出。
actor 和 critic 通過不斷地學(xué)習(xí),以便使得 agent 在游戲中最終獲得的獎勵最大,這里的 agent 就是那個小車。
庫準(zhǔn)備
tensorflow-gpu==2.10.0 imageio==2.26.1 keras==2.10,0 gym==0.20.0 pyglet==1.5.20 scipy==1.10.1
設(shè)置超參數(shù)
這部分代碼主要有:
(1)導(dǎo)入所需的Python庫:gym、numpy、tensorflow 和 keras。
(2)設(shè)置整個環(huán)境的超參數(shù):種子、折扣因子和每個回合的最大步數(shù)。
(3)創(chuàng)建 CartPole-v0 環(huán)境,并設(shè)置種子。
(4)定義一個非常小的值 eps ,表示的機(jī)器兩個不同的數(shù)字之間的最小差值,用于檢驗數(shù)值穩(wěn)定性。
import gym # 導(dǎo)入Gym庫,用于開發(fā)和比較強(qiáng)化學(xué)習(xí)算法
import numpy as np # 導(dǎo)入NumPy庫,用于進(jìn)行科學(xué)計算
import tensorflow as tf # 導(dǎo)入TensorFlow庫
from tensorflow import keras # 導(dǎo)入keras模塊,這是一個高級神經(jīng)網(wǎng)絡(luò)API
from tensorflow.keras import layers # 導(dǎo)入keras中的layers模塊,用于創(chuàng)建神經(jīng)網(wǎng)絡(luò)層
seed = 42 # 設(shè)定隨機(jī)種子,用于復(fù)現(xiàn)實驗結(jié)果
gamma = 0.99 # 定義折扣率,用于計算未來獎勵的現(xiàn)值
max_steps_per_episode = 10000 # 設(shè)定每個 episode 的最大步數(shù)
env = gym.make("CartPole-v0") # 創(chuàng)建 CartPole-v0 環(huán)境實例
env.seed(seed) # 設(shè)定環(huán)境的隨機(jī)種子
eps = np.finfo(np.float32).eps.item() # 獲取 float32 數(shù)據(jù)類型的誤差最小值 epsilon
Actor Critic 結(jié)構(gòu)搭建
(1)Actor:將環(huán)境的狀態(tài)作為輸入,返回操作空間中每個操作及其概率值,其實總共只有兩個操作,往左和往右。
(2)Critic:將環(huán)境的狀態(tài)作為輸入,返回未來獎勵綜合的估計。
(3)在這里網(wǎng)絡(luò)結(jié)構(gòu)中我們在一開始接收 inputs 之后,我們的 Actor 和 Critic 共用了中間的部分隱藏層 common 層,然后在一個輸出分支上連接了一個全連接進(jìn)行動作分類作為 action ,另一個分支上連接了一個全連接層進(jìn)行未來獎勵計算作為 critic 。
num_inputs = 4 # 狀態(tài)空間的維度,即輸入層的節(jié)點數(shù) num_actions = 2 # 行為空間的維度,即輸出層的節(jié)點數(shù) num_hidden = 128 # 隱藏層的節(jié)點數(shù) inputs = layers.Input(shape=(num_inputs,)) # 創(chuàng)建輸入層,指定輸入的形狀 common = layers.Dense(num_hidden, activation="relu")(inputs) # 創(chuàng)建一個全連接層,包含num_hidden 個神經(jīng)元,使用 ReLU 作為激活函數(shù) action = layers.Dense(num_actions, activation="softmax")(common) # 創(chuàng)建一個全連接層,包含 num_actions 個神經(jīng)元,使用 softmax 作為激活函數(shù) critic = layers.Dense(1)(common) # 創(chuàng)建一個全連接層,包含1個神經(jīng)元 model = keras.Model(inputs=inputs, outputs=[action, critic]) # 創(chuàng)建一個 Keras 模型,包含輸入層、共享的隱藏層和兩個輸出層
訓(xùn)練前的樣子
import imageio
start = env.reset()
frames = []
for t in range(max_steps_per_episode):
frames.append(env.render(mode='rgb_array'))
start = start.reshape(1, -1)
start, reward, done, _ = env.step(np.random.choice(num_actions, p=np.squeeze(action_probs)))
if done:
break
with imageio.get_writer('未訓(xùn)練前的樣子.gif', mode='I') as writer:
for frame in frames:
writer.append_data(frame)

模型訓(xùn)練
設(shè)置訓(xùn)練所需要的優(yōu)化器,以及各種參數(shù)來記錄每個時間步上的數(shù)據(jù)。
optimizer = keras.optimizers.Adam(learning_rate=0.01) # 創(chuàng)建 Adam 優(yōu)化器實例,設(shè)置學(xué)習(xí)率為 0.01 huber_loss = keras.losses.Huber() # 創(chuàng)建損失函數(shù)實例 action_probs_history = [] # 創(chuàng)建一個列表,用于保存 action 網(wǎng)絡(luò)在每個步驟中采取各個行動的概率 critic_value_history = [] # 創(chuàng)建一個列表,用于保存 critic 網(wǎng)絡(luò)在每個步驟中對應(yīng)的值 rewards_history = [] # 創(chuàng)建一個列表,用于保存每個步驟的獎勵值 running_reward = 0 # 初始化運行過程中的每輪獎勵 episode_count = 0 # 初始化 episode 計數(shù)器
一直訓(xùn)練下去,直到滿足獎勵大于 195 才會停下訓(xùn)練過程。
while True:
state = env.reset() # 新一輪游戲開始,重置環(huán)境
episode_reward = 0 # 記錄本輪游戲的總獎勵值
with tf.GradientTape() as tape: # 構(gòu)建 GradientTape 用于計算梯度
for timestep in range(1, max_steps_per_episode): # 本輪游戲如果一切正常會進(jìn)行 max_steps_per_episode 步
state = tf.convert_to_tensor(state) # 將狀態(tài)轉(zhuǎn)換為張量
state = tf.expand_dims(state, 0) # 擴(kuò)展維度,以適應(yīng)模型的輸入形狀
action_probs, critic_value = model(state) # 前向傳播,得到 action 網(wǎng)絡(luò)輸出的動作空間的概率分布,和 critic 網(wǎng)絡(luò)預(yù)測的獎勵值
critic_value_history.append(critic_value[0, 0]) # 將上面 critic 預(yù)測的獎勵值記錄在 critic_value_history 列表中
action = np.random.choice(num_actions, p=np.squeeze(action_probs)) # 依據(jù)概率分布抽樣某個動作,當(dāng)然了某個動作概率越大越容易被抽中,同時也保留了一定的隨機(jī)性
action_probs_history.append(tf.math.log(action_probs[0, action])) # 將使用該動作的對數(shù)概率值記錄在 action_probs_history 列表中
state, reward, done, _ = env.step(action) # 游戲環(huán)境使用選中的動作去執(zhí)行,得到下一個游戲狀態(tài)、獎勵、是否終止和其他信息
rewards_history.append(reward) # 將該時刻的獎勵記錄在 rewards_history 列表中
episode_reward += reward # 累加本輪游戲的總獎勵值
if done: # 如果到達(dá)終止?fàn)顟B(tài),則結(jié)束循環(huán)
break
running_reward = 0.05 * episode_reward + (1 - 0.05) * running_reward # 計算平均獎勵
returns = [] # 存儲折扣回報
discounted_sum = 0
for r in rewards_history[::-1]: # 從后往前遍歷獎勵的歷史值
discounted_sum = r + gamma * discounted_sum # 計算折扣回報
returns.insert(0, discounted_sum) # 將折扣回報插入列表的開頭,最后形成的還是從前往后的折扣獎勵列表
returns = np.array(returns) # 將折扣回報轉(zhuǎn)換為數(shù)組
returns = (returns - np.mean(returns)) / (np.std(returns) + eps) # 歸一化折扣回報
returns = returns.tolist() # 將折扣回報轉(zhuǎn)換為列表形式
history = zip(action_probs_history, critic_value_history, returns) # 將三個列表進(jìn)行 zip 壓縮
actor_losses = [] # 存儲 action 網(wǎng)絡(luò)的損失
critic_losses = [] # 存儲 critic 網(wǎng)絡(luò)的損失
for log_prob, value, ret in history:
diff = ret - value
actor_losses.append(-log_prob * diff) # 計算 actor 的損失函數(shù)
critic_losses.append(
huber_loss(tf.expand_dims(value, 0), tf.expand_dims(ret, 0)) # 計算 critic 的損失函數(shù)
)
loss_value = sum(actor_losses) + sum(critic_losses) # 計算總損失函數(shù)
grads = tape.gradient(loss_value, model.trainable_variables) # 計算梯度
optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 更新模型參數(shù)
action_probs_history.clear() # 清空之前的歷史記錄
critic_value_history.clear() # 清空之前的歷史記錄
rewards_history.clear() # 清空之前的歷史記錄
episode_count += 1 # 當(dāng)一輪游戲結(jié)束時, episode 加一
if episode_count % 10 == 0: # 每訓(xùn)練 10 個 episode ,輸出當(dāng)前的平均獎勵
template = "在第 {} 輪游戲中獲得獎勵: {:.2f} 分"
print(template.format(episode_count, running_reward))
if running_reward > 195: # 如果平均獎勵超過195,視為任務(wù)已經(jīng)解決
print("獎勵超過 195 ,訓(xùn)練結(jié)束")
break
打?。?/p>
在第 10 輪游戲中獲得獎勵: 11.17 分
在第 20 輪游戲中獲得獎勵: 17.12 分
...
在第 170 輪游戲中獲得獎勵: 155.02 分
在第 180 輪游戲中獲得獎勵: 171.67 分
...
在第 220 輪游戲中獲得獎勵: 193.74 分
獎勵超過 195 ,訓(xùn)練結(jié)束
訓(xùn)練后的樣子
import imageio
start = env.reset()
frames = []
for t in range(max_steps_per_episode):
frames.append(env.render(mode='rgb_array'))
start = start.reshape(1, -1)
action_probs, _ = model(start)
action = np.random.choice(num_actions, p=np.squeeze(action_probs))
start, reward, done, _ = env.step(action)
if done:
break
with imageio.get_writer('訓(xùn)練后的樣子.gif', mode='I') as writer:
for frame in frames:
writer.append_data(frame)

以上就是使用actor-critic方法來控制CartPole-V0 游戲詳解的詳細(xì)內(nèi)容,更多關(guān)于 actor-critic控制CartPole-V0的資料請關(guān)注腳本之家其它相關(guān)文章!
Python中實現(xiàn)從目錄中過濾出指定文件類型的文件

