【強化學習】DDPG(Deep Deterministic Policy Gradient)算法詳解

http://www0.cs.ucl.ac.uk/staff/d.silver/web/Teaching.html
引用莫凡老師的素材 https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/6-2-DDPG/
引自Reinforcement Learning:An Introduction強化學習名著2018新編版
DPG論文http://www0.cs.ucl.ac.uk/staff/d.silver/web/Applications_files/deterministic-policy-gradients.pdf
PolicyGradient論文https://www.ics.uci.edu/~dechter/courses/ics-295/winter-2018/papers/2000-nips12-sutton-mcallester-policy-gradient-methods-for-reinforcement-learning-with-function-approximation.pdf
DDPG論文http://www0.cs.ucl.ac.uk/staff/d.silver/web/Applications_files/ddpg.pdf
DQN論文http://web.cse.ohio-state.edu/~wang.7642/homepage/files/Peidong_Wang_DQN.pdf

【強化學習】DDPG(Deep Deterministic Policy Gradient)算法詳解

之前我們在Actor-Critic上面提到過了DDPG,現在本篇詳細講解,因爲Actor-Critic收斂慢的問題所以Deepmind 提出了 Actor Critic 升級版 Deep Deterministic Policy Gradient,後者融合了 DQN 的優勢, 解決了收斂難的問題。

1、算法思想

DDPG我們可以拆開來看Deep Deterministic Policy Gradient
這裏寫圖片描述
Deep:首先Deep我們都知道,就是更深層次的網絡結構,我們之前在DQN中使用兩個網絡與經驗池的結構,在DDPG中就應用了這種思想。
PolicyGradient:顧名思義就是策略梯度算法,能夠在連續的動作空間根據所學習到的策略(動作分佈)隨機篩選動作
Deterministic : 它的作用就是用來幫助Policy Gradient不讓他隨機選擇,只輸出一個動作值

  • 隨機性策略,π(as)=1\sum\pi(a|s)=1 策略輸出的是動作的概率,使用正態分佈對動作進行採樣選擇,即每個動作都有概率被選到;優點,將探索和改進集成到一個策略中;缺點,需要大量訓練數據。
  • 確定性策略,π(s)SA\pi(s) S→A 策略輸出即是動作;優點,需要採樣的數據少,算法效率高;缺點,無法探索環境。然而因爲我們引用了DQN的結構利用offPolicy採樣,這樣就解決了無法探索環境的問題
    這裏寫圖片描述
    從DDPG網絡整體上來說:他應用了 Actor-Critic 形式的, 所以也具備策略 Policy 的神經網絡 和基於 價值 Value 的神經網絡,因爲引入了DQN的思想,每種神經網絡我們都需要再細分爲兩個, Policy Gradient 這邊,我們有估計網絡和現實網絡,估計網絡用來輸出實時的動作, 供 actor 在現實中實行,而現實網絡則是用來更新價值網絡系統的。再看另一側價值網絡, 我們也有現實網絡和估計網絡, 他們都在輸出這個狀態的價值, 而輸入端卻有不同, 狀態現實網絡這邊會拿着從動作現實網絡來的動作加上狀態的觀測值加以分析,而狀態估計網絡則是拿着當時 Actor 施加的動作當做輸入。
    這裏寫圖片描述
    DDPG 在連續動作空間的任務中效果優於DQN而且收斂速度更快,但是不適用於隨機環境問題。

2、公式推導

再來囉唆一下前置公式
sts_t:在t時刻,agent所能表示的環境狀態,比如觀察到的環境圖像,agent在環境中的位置、速度、機器人關節角度等;
ata_t:在t時刻,agent選擇的行爲(action)
r(st,at)r(s_t,a_t):函數: 環境在狀態st 執行行爲at後,返回的單步獎勵值;
RtR_t:是從當前狀態直到將來某個狀態中間所有行爲所獲得獎勵值的之和當然下一個狀態的獎勵值要有一個衰變係數 γ\gamma 一般情況下可取0到1的小數
Rt=i=tγitr(si,ai)R_t=∑_{i=t}γ^{i−t}r(s_i,a_i)

Policy Gradient
通過概率的分佈函數確定最優策略,在每一步根據該概率分佈獲取當前狀態最佳的動作,產生動作採取的是隨機性策略
atπθ(stθπ)a_t ∼π_θ(s_t|θ^π)
目標函數:J(πθ)=Sρπ(s)Aπθ(s,a)r(s,a)dads=Esρπ,aπθ[r(s,a)]J(\pi_\theta)=∫_Sρ^\pi(s)∫_A\pi_\theta(s,a)r(s,a)dads=E_{s∼ρ^\pi,a∼\pi_\theta}[r(s,a)](注意dads不是什麼未知的符號,而是積分的 da ds)
梯度:θJ(πθ)=Sρπ(s)Aθπθ(s,a)Qπ(s,a)dads=Esρπ,aπθ[θlogπθ(as)Qπ(s,a)]∇_θJ(π_θ)=∫_Sρ^\pi(s)∫_A∇_θ\pi_\theta(s,a)Q^\pi(s,a)dads=E_{s∼ρ^\pi,a∼\pi_\theta}[∇_θlog\pi_\theta(a|s)Q^\pi(s,a)]

Deterministic Policy Gradient
因爲Policy Gradient是採取隨機性策略,所以要想獲取當前動作action就需要對最優策略的概率分佈進行採樣,而且在迭代過程中每一步都要對整個動作空間進行積分,所以計算量很大
在PG的基礎上採取了確定性策略,根據行爲直接通過函數μ確定了一個動作,可以吧μ理解成一個最優行爲策略

at=μ(stθμ)a_t=μ(s_t|θ^μ)

performance objective爲
J(μθ)=Sρμ(s)r(s,μθ(s))dsJ(μ_\theta)=∫_Sρ^μ(s)r(s,μ_\theta(s))ds
J(μθ)=Esρμ[r(s,μθ(s))]J(μ_\theta)=E_{s∼ρ^μ}[r(s,μ_\theta(s))]
deterministic policy梯度
θJ(μθ)=Sρμ(s)θμθ(s)Qμ(s,a)a=μθds=Esρβ[θμθ(s)Qμ(s,a)a=μθ]▽_\theta J(μ_\theta)=∫_Sρ^μ(s)▽\theta μ_\theta(s)Q^μ(s,a)|_{a=μ_\theta}ds=E_{s∼ρ^β}[▽\theta μ_\theta(s)Q^μ(s,a)|_{a=μ_\theta}]

DDPG就是用了確定性策略在DPG基礎上結合DQN的特點建議改進出來的算法

Deep Deterministic Policy Gradient
所以基於上述兩種算法
DDPG採用確定性策略μ來選取動作 at=μ(stθμ)a_t=μ(s_t|θ^μ) 其中θμθ^μ是產生確定性動作的策略網絡的參數。根據之前提到過的AC算與PG算法我們可以想到,使用策略網絡μ來充當actor,使用價值網絡來擬合(s,a)函數,來充當critic的角色,所以將DDPG的目標函數就可以定義爲

J(θμ)=Eθμ[r1+γr2+γ2r3+]J(θ^μ)=E_{θ^μ}[r_1+γr_2+γ^2r_3+⋯]

此時Q函數表示爲在採用確定性策略μ下選擇動作的獎勵期望值,在DDPG我們就採用DQN的結構使用Q網絡來擬合Q函數

Qμ(st,at)=E[r(st,at)+γQμ(st+1,μ(st+1))]Q^μ(s_t,a_t)=E [r(s_t,a_t)+γQ^μ(s_{t+1},μ(s_{t+1}))]

Q網絡中的參數定義爲θQ\theta^QQμ(s,μ(s))Q^μ(s,μ(s))表示使用μ策略在s狀態選選取動作所獲取的回報期望值,又因爲是在連續空間內所以期望可用積分來求,則可以使用下式來表示策略μ的好壞

Jβ(μ)=Sρβ(s)Qμ(s,μ(s))ds=Esρβ[Qμ(s,μ(s)]J_β(μ)=∫_Sρ^β(s)Q^μ(s,μ(s))ds=E_{s∼ρ^β}[Q^μ(s,μ(s)]

behavior policy β: 在常見的RL訓練過程中存在貪婪策略來平衡exploration和exploit與之類似,在DDPG中使用Uhlenbeck-Ornstein隨機過程(下面簡稱UO過程),作爲引入的隨機噪聲:UO過程在時序上具備很好的相關性,可以使agent很好的探索具備動量屬性的環境exploration的目的是探索潛在的更優策略,所以訓練過程中,我們爲action的決策機制引入隨機噪聲:
過程如下圖所示:
這裏寫圖片描述

Silver大神證明了目標函數採用μ策略的梯度與Q函數採用μ策略的期望梯度是等價的:

J(θμ)θμ=Es[Q(s,aθQ)θμ]\frac{∂J(θ^μ)}{∂θ^μ}=E_s[\frac{∂Q(s,a|θ^Q)}{∂θ^μ}]

因爲是確定性策略a=μ(sθμ)a=μ(s|θ^μ)所以得到Actor網絡的梯度爲

J(θμ)θμ=Es[Q(s,aθQ)aπ(sθμ)θμ]\frac{∂J(θ^μ)}{∂θ^μ}=E_s[\frac{∂Q(s,a|θ^Q)}{∂a} \frac{∂π(s|θ^μ)}{∂θ^μ}]

θJβ(μθ)=Sρβ(s)θμθ(s)Qμ(s,a)a=μθds=Esρβ[θμθ(s)Qμ(s,a)a=μθ]▽_\theta J_β(μ_\theta)=∫_Sρ^β(s)▽_\theta μ_\theta(s)Q^μ(s,a)|_{a=μ_\theta}ds=E_{s∼ρ^β}[▽_\theta μ_\theta(s)Q^μ(s,a)|_{a=μ_\theta}]

在另一方面Critic網絡上的價值梯度爲
L(θQ)θQ=Es,a,r,sD[(TargetQQ(s,aθQ)Q(s,aθQ)θQ]\frac{∂L(θ^Q)}{∂θ^Q}=E_{s,a,r,s′∼D}[(TargetQ−Q(s,a|θ^Q)\frac{∂Q(s,a|θ^Q)}{∂θ^Q}]
TargetQ=r+γQ(s,π(sθμ)θQ)TargetQ=r+γQ′(s′,π(s′|θ^{μ′})|θ^{Q′})

損失函數採取均方誤差損失MSE,另外在計算策略梯度期望的時候仍然選擇蒙特卡羅法來取無偏估計(隨機採樣加和平均法)

我們有了上述兩個梯度公式就可以使用梯度下降進行網絡的更新
網絡結構圖如下因爲引用了DQN的結構,所以就多了一個target網絡
這裏寫圖片描述

3、代碼實現

https://morvanzhou.github.io/tutorials/引用morvan老師的源碼

import tensorflow as tf
import numpy as np
import gym
import time


#####################  hyper parameters  ####################

MAX_EPISODES = 200
MAX_EP_STEPS = 200
LR_A = 0.001    # learning rate for actor
LR_C = 0.002    # learning rate for critic
GAMMA = 0.9     # reward discount
TAU = 0.01      # soft replacement
MEMORY_CAPACITY = 10000
BATCH_SIZE = 32

RENDER = False
ENV_NAME = 'Pendulum-v0'


###############################  DDPG  ####################################


class DDPG(object):
    def __init__(self, a_dim, s_dim, a_bound,):
        self.memory = np.zeros((MEMORY_CAPACITY, s_dim * 2 + a_dim + 1), dtype=np.float32)
        self.pointer = 0
        self.sess = tf.Session()

        self.a_dim, self.s_dim, self.a_bound = a_dim, s_dim, a_bound,
        self.S = tf.placeholder(tf.float32, [None, s_dim], 's')
        self.S_ = tf.placeholder(tf.float32, [None, s_dim], 's_')
        self.R = tf.placeholder(tf.float32, [None, 1], 'r')

        self.a = self._build_a(self.S,)
        q = self._build_c(self.S, self.a, )
        a_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='Actor')
        c_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope='Critic')
        ema = tf.train.ExponentialMovingAverage(decay=1 - TAU)          # soft replacement

        def ema_getter(getter, name, *args, **kwargs):
            return ema.average(getter(name, *args, **kwargs))

        target_update = [ema.apply(a_params), ema.apply(c_params)]      # soft update operation
        a_ = self._build_a(self.S_, reuse=True, custom_getter=ema_getter)   # replaced target parameters
        q_ = self._build_c(self.S_, a_, reuse=True, custom_getter=ema_getter)

        a_loss = - tf.reduce_mean(q)  # maximize the q
        self.atrain = tf.train.AdamOptimizer(LR_A).minimize(a_loss, var_list=a_params)

        with tf.control_dependencies(target_update):    # soft replacement happened at here
            q_target = self.R + GAMMA * q_
            td_error = tf.losses.mean_squared_error(labels=q_target, predictions=q)
            self.ctrain = tf.train.AdamOptimizer(LR_C).minimize(td_error, var_list=c_params)

        self.sess.run(tf.global_variables_initializer())

    def choose_action(self, s):
        return self.sess.run(self.a, {self.S: s[np.newaxis, :]})[0]

    def learn(self):
        indices = np.random.choice(MEMORY_CAPACITY, size=BATCH_SIZE)
        bt = self.memory[indices, :]
        bs = bt[:, :self.s_dim]
        ba = bt[:, self.s_dim: self.s_dim + self.a_dim]
        br = bt[:, -self.s_dim - 1: -self.s_dim]
        bs_ = bt[:, -self.s_dim:]

        self.sess.run(self.atrain, {self.S: bs})
        self.sess.run(self.ctrain, {self.S: bs, self.a: ba, self.R: br, self.S_: bs_})

    def store_transition(self, s, a, r, s_):
        transition = np.hstack((s, a, [r], s_))
        index = self.pointer % MEMORY_CAPACITY  # replace the old memory with new memory
        self.memory[index, :] = transition
        self.pointer += 1

    def _build_a(self, s, reuse=None, custom_getter=None):
        trainable = True if reuse is None else False
        with tf.variable_scope('Actor', reuse=reuse, custom_getter=custom_getter):
            net = tf.layers.dense(s, 30, activation=tf.nn.relu, name='l1', trainable=trainable)
            a = tf.layers.dense(net, self.a_dim, activation=tf.nn.tanh, name='a', trainable=trainable)
            return tf.multiply(a, self.a_bound, name='scaled_a')

    def _build_c(self, s, a, reuse=None, custom_getter=None):
        trainable = True if reuse is None else False
        with tf.variable_scope('Critic', reuse=reuse, custom_getter=custom_getter):
            n_l1 = 30
            w1_s = tf.get_variable('w1_s', [self.s_dim, n_l1], trainable=trainable)
            w1_a = tf.get_variable('w1_a', [self.a_dim, n_l1], trainable=trainable)
            b1 = tf.get_variable('b1', [1, n_l1], trainable=trainable)
            net = tf.nn.relu(tf.matmul(s, w1_s) + tf.matmul(a, w1_a) + b1)
            return tf.layers.dense(net, 1, trainable=trainable)  # Q(s,a)


###############################  training  ####################################

env = gym.make(ENV_NAME)
env = env.unwrapped
env.seed(1)

s_dim = env.observation_space.shape[0]
a_dim = env.action_space.shape[0]
a_bound = env.action_space.high

ddpg = DDPG(a_dim, s_dim, a_bound)

var = 3  # control exploration
t1 = time.time()
for i in range(MAX_EPISODES):
    s = env.reset()
    ep_reward = 0
    for j in range(MAX_EP_STEPS):
        if RENDER:
            env.render()

        # Add exploration noise
        a = ddpg.choose_action(s)
        a = np.clip(np.random.normal(a, var), -2, 2)    # add randomness to action selection for exploration
        s_, r, done, info = env.step(a)

        ddpg.store_transition(s, a, r / 10, s_)

        if ddpg.pointer > MEMORY_CAPACITY:
            var *= .9995    # decay the action randomness
            ddpg.learn()

        s = s_
        ep_reward += r
        if j == MAX_EP_STEPS-1:
            print('Episode:', i, ' Reward: %i' % int(ep_reward), 'Explore: %.2f' % var, )
            # if ep_reward > -300:RENDER = True
            break

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