【強化學習】Policy Gradient 算法實現案例

【強化學習】Policy Gradient算法詳解

完整代碼:https://github.com/StephenLouis/Reinforcement-Learning---Policy-Gradient
如果覺得不錯,麻煩點顆星哦!

1.算法思想

之前的文章已經介紹了Q-Learning的相關知識及其實例:【強化學習】Q-Learning 迷宮算法案例
Q-Learning 是一個基於價值value的方法,通過計算動作得分來決策的,它在確定了價值函數的基礎上採用某種策略(貪婪-epslion)的方法去選取動作。
不同於基於最優價值的算法,Policy Gradient算法更着眼於算法的長期回報。策略梯度根據目標函數的梯度方向去尋找最優策略。策略梯度算法中,整個回合結束之後纔會進行學習,所以策略梯度算法對全局過程有更好的把握。

2.Cart Pole遊戲介紹

遊戲規則

cart pole即車杆遊戲,遊戲如下,很簡單,遊戲裏面有一個小車,上有豎着一根杆子。小車需要左右移動來保持杆子豎直。如果杆子傾斜的角度大於15°,那麼遊戲結束。小車也不能移動出一個範圍(中間到兩邊各2.4個單位長度)。

action
  • 左移
  • 右移
state variables
  • position of the cart on the track
  • angle of the pole with the vertical
  • cart velocity
  • rate of change of the angle

分別表示車的位置,杆子的角度,車速,角度變化率

遊戲獎勵

在gym的Cart Pole環境(env)裏面,左移或者右移小車的action之後,env都會返回一個+1的reward。到達200個reward之後,遊戲也會結束
在這裏插入圖片描述

3.算法實現

3.1 算法描述如下:

在這裏插入圖片描述

3.2 定義主更新的循環
3.2.1創建環境

在寫循環之前,跟之前Q-Learning一樣,我們首先得創建環境。不過這次我們不像上次那麼自己定義環境了,手動編寫環境是一件很耗時間的事。有這麼一個庫,Open gym,他給我們提供了很多很好的模擬環境,方便我們去在這些環境中嘗試我們的算法。如下,在import gym後,幾行代碼就能很方便的創建環境。

import gym
from RL_brain import PolicyGradient
import matplotlib.pyplot as plt

DISPLAY_REWARD_THRESHOLD = 400  # 當回合總reward大於400的時候顯示模擬窗口
RENDER = False  # 邊訓練邊顯示會拖慢訓練速度,我們等程序先學習一段時間

env = gym.make('CartPole-v0')   # 創建 CardPole這個模擬
env.seed(1)     # 創建隨機種子
env = env.unwrapped # 取消限制

print(env.action_space) #輸出可用的動作
print(env.observation_space)    # 顯示可用 state 的 observation
print(env.observation_space.high)  # 顯示 observation 最高值
print(env.observation_space.low)   # 顯示 observation 最低值
3.2.1主循環

接下來定義主更新循環,之前的Q—Learning在回合的每一步都更新參數,但是在這裏,計算機跑完一整個回合才更新一次,故需要把這回合的transition存儲起來。

# 定義使用 Policy_gradient 的算法
RL = PolicyGradient(
    n_actions=env.action_space.n,
    n_features=env.observation_space.shape[0],
    learning_rate=0.02,
    reward_decay=0.99,
    # output_graph=True,
)

for i_episode in range(3000):

    observation = env.reset()   # 獲取回合 i_episode 第一個 observation

    while True:
        if RENDER: env.render() # 刷新環境

        action = RL.choose_action(observation)  # 選行爲

        observation_, reward, done, info = env.step(action) # 獲取下一個state

        RL.store_transition(observation, action, reward)    # 存儲這一回合的transition

        if done:    # 一個回合結束,開始更新參數
            ep_rs_sum = sum(RL.ep_rs)   # 統計每回合的reward

            if 'running_reward' not in globals():
                running_reward = ep_rs_sum
            else:
                running_reward = running_reward * 0.99 + ep_rs_sum * 0.01
            if running_reward > DISPLAY_REWARD_THRESHOLD: RENDER = True     # 判斷是否開始模擬
            print("episode:", i_episode, "  reward:", int(running_reward))

            vt = RL.learn() # 學習參數,輸出vt

            if i_episode == 0:  # 畫圖
                plt.plot(vt)
                plt.xlabel('episode steps')
                plt.ylabel('normalized state-action value')
                plt.show()
            break

        observation = observation_
3.2 Policy Gradient代碼結構

PolicyGradient算法主結構

class PolicyGradient:
    # 初始化
    def __init__(self, n_actions, n_features, learning_rate=0.01, reward_decay=0.95, output_graph=False):

    # 建立 policy gradient 神經網絡
    def _build_net(self):

    # 選行爲
    def choose_action(self, observation):

    # 存儲回合 transition
    def store_transition(self, s, a, r):

    # 學習更新參數
    def learn(self, s, a, r, s_):

    # 衰減回合的 reward
    def _discount_and_norm_rewards(self):

初始化,定義參數
self.ep_obs,self.ep_as,self.ep_rs分別存儲了當前episode的狀態動作獎勵

     self.n_actions = n_actions
     self.n_features = n_features
     self.lr = learning_rate
     self.gamma = reward_decay

     self.ep_obs, self.ep_as, self.ep_rs = [], [], []

建立 policy gradient 神經網絡

    def _build_net(self):	# 定義了兩層的神經網絡
        with tf.name_scope('inputs'):	# 模型的輸入包括三部分,分別是觀察值,動作和獎勵值
            self.tf_obs = tf.placeholder(tf.float32, [None, self.n_features], name="observations")
            self.tf_acts = tf.placeholder(tf.int32, [None, ], name="actions_num")
            self.tf_vt = tf.placeholder(tf.float32, [None, ], name="actions_value")
        # fc1
        layer = tf.layers.dense(
            inputs=self.tf_obs,
            units=10,	# 輸出個數
            activation=tf.nn.tanh,  # 激活函數
            kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
            bias_initializer=tf.constant_initializer(0.1),
            name='fc1'
        )
        # fc2
        all_act = tf.layers.dense(
            inputs=layer,
            units=self.n_actions,
            activation=None,
            kernel_initializer=tf.random_normal_initializer(mean=0, stddev=0.3),
            bias_initializer=tf.constant_initializer(0.1),
            name='fc2'
        )

        self.all_act_prob = tf.nn.softmax(all_act, name='act_prob')  # 使用softmax激活函數將行爲轉爲概率

        with tf.name_scope('loss'):
            # 最大化 總體 reward (log_p * R) 就是在最小化 -(log_p * R), 而 tf 的功能裏只有最小化 loss
            neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act, labels=self.tf_acts)
            # or in this way:
            # neg_log_prob = tf.reduce_sum(-tf.log(self.all_act_prob)*tf.one_hot(self.tf_acts, self.n_actions), axis=1)
            loss = tf.reduce_mean(neg_log_prob * self.tf_vt)  # (vt = 本reward + 衰減的未來reward) 引導參數的梯度下降

        with tf.name_scope('train'):
            self.train_op = tf.train.AdamOptimizer(self.lr).minimize(loss)

選行爲
 如果對Q-Learning還有印象的話,Q-Learning是採用90%的貪婪策略,10%隨機搜索。而這裏,我們直接根據概率選取行爲。

    def choose_action(self, observation):
        # 算出所有行爲的概率
        prob_weights = self.sess.run(self.all_act_prob, feed_dict={self.tf_obs: observation[np.newaxis, :]})
        # 根據概率選行爲
        action = np.random.choice(range(prob_weights.shape[1]), p=prob_weights.ravel())
        return action

存儲回合
 之前說過,policy gradient是在一個完整的episode結束後纔開始訓練的,因此,在一個episode結束前,我們要存儲這個episode所有的經驗,即狀態,動作和獎勵。

    def store_transition(self, s, a, r):
        self.ep_obs.append(s)
        self.ep_as.append(a)
        self.ep_rs.append(r)

計算整個回合的獎勵值
 我們之前存儲的獎勵是當前狀態s採取動作a獲得的即時獎勵,而當前狀態s採取動作a所獲得的真實獎勵應該是即時獎勵加上未來直到episode結束的獎勵貼現和。
 換一種說法,當前的獎勵很大,並不代表着這就一定是正確的方向。在圍棋對抗中,機器人會只以最後的輸贏爲評判標準,如果最後贏了,則之前每一步的都是正確的,如果最後輸了,則之前的每一步都是錯誤的。對於Policy Gradient來說,他追求的是整個回合的獎勵總和。

    def _discount_and_norm_rewards(self):
        # 統計一個回合的獎勵
        discounted_ep_rs = np.zeros_like(self.ep_rs)
        running_add = 0
        for t in reversed(range(0, len(self.ep_rs))):
            running_add = running_add * self.gamma + self.ep_rs[t]
            discounted_ep_rs[t] = running_add

        # 標準化回合獎勵值
        discounted_ep_rs -= np.mean(discounted_ep_rs)
        discounted_ep_rs /= np.std(discounted_ep_rs)
        return discounted_ep_rs

學習
 在定義好上面所有的部件之後,我們就可以編寫模型訓練函數了,這裏需要注意的是,我們餵給模型的並不是我們存儲的獎勵值,而是在經過上一步計算的獎勵貼現和。另外,我們需要在每一次訓練之後清空我們的經驗池。

    # 學習更新參數
    def learn(self):
        # 衰減, 並標準化這回合的 reward
        discounted_ep_rs_norm = self._discount_and_norm_rewards()

        # train on episode
        self.sess.run(self.train_op, feed_dict={
             self.tf_obs: np.vstack(self.ep_obs),  # shape=[None, n_obs]
             self.tf_acts: np.array(self.ep_as),  # shape=[None, ]
             self.tf_vt: discounted_ep_rs_norm,  # shape=[None, ]
        })

        self.ep_obs, self.ep_as, self.ep_rs = [], [], []     # 清空回合 data
        return discounted_ep_rs_norm    # 返回這一回合的 state-action value

以上就是我對Policy Gradient 算法實現的理解,如果不對的地方歡迎指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章