策略梯度与REINFORCE算法——policy-based的开始

本文将从策略梯度开始介绍,理解策略梯度原理的基础上讲解REINFORCE算法并分析代码,总结其优缺点

一、value-based存在的一些缺点

  • 不能处理连续动作空间或者高维的离散动作空间

二、policy-based的主要思想与策略梯度定理

policy-based不建模Q函数,而是得出累计期望奖励最大的策略,利用神经网络强大的表示能力将策略参数化。对参数采用梯度下降算法以更新策略,以优化累计期望奖励。下面证明为什么更新策略能使得累计期望奖励最大。

最大化目标函数,应该是梯度上升,但本质是一样的,可以实际中取负变为梯度下降问题

我们从期望奖励出发,考虑轨迹 τ = ( s 1 , a 1 , . . . , s T , a T ) \tau = (s_1,a_1,...,s_T,a_T) τ=(s1,a1,...,sT,aT)的概率为:
p θ ( s 0 , a 0 , . . . , s T , a T ) = p ( s 0 ) ∏ t = 0 T π θ ( a t ∣ s t ) p ( s t + 1 ∣ s t , a t ) p_{\theta}(s_0,a_0,...,s_T,a_T)=p(s_0)\prod_{t=0}^T\pi_\theta(a_t|s_t)p(s_{t+1}|s_t,a_t) pθ(s0,a0,...,sT,aT)=p(s0)t=0Tπθ(atst)p(st+1st,at)
我们表示策略 π θ \pi_\theta πθ生成轨迹 τ \tau τ的概率 p ( τ ∣ π ) p(\tau|\pi) p(τπ)改写上式:
P ( τ ∣ π ) = p 0 ( s 0 ) ∏ t = 0 T π ( a t ∣ s t ) p ( s t + 1 ∣ s t , a t ) P(\tau|\pi)=p_0(s_0)\prod_{t=0}^T\pi(a_t|s_t)p(s_{t+1}|s_t,a_t) P(τπ)=p0(s0)t=0Tπ(atst)p(st+1st,at)

对于利用当前策略生成的多条轨迹,每条轨迹都有其累计期望奖励,将这些累计期望奖励取期望即可得到当前策略下的累计期望奖励。积分形式如下:
J ( π θ ) = E τ ∼ π θ [ R ( τ ) ] = ∫ τ P ( τ ∣ θ ) R ( τ ) J(\pi_\theta)=E_{\tau\thicksim\pi_\theta}[R(\tau)]=\int_\tau P(\tau|\theta)R(\tau) J(πθ)=Eτπθ[R(τ)]=τP(τθ)R(τ)

策略梯度定理

对目标函数求导可以得出梯度下降的公式。接下来通过对公式求导。我们考虑没有折扣因子的情况,对上面的目标函数进行求导
∇ θ J ( π θ ) = ∇ θ ∫ τ P ( τ ∣ θ ) R ( τ ) = ∫ τ ∇ θ P ( τ ∣ θ ) R ( τ ) \nabla _\theta J(\pi_\theta)=\nabla _\theta\int_\tau P(\tau|\theta)R(\tau)=\int_\tau\nabla _\theta P(\tau|\theta)R(\tau) θJ(πθ)=θτP(τθ)R(τ)=τθP(τθ)R(τ)
由于 θ \theta θ只会影响策略,不会影响环境中当前奖励大小,因此只需对 P ( τ ∣ π ) P(\tau|\pi) P(τπ)求导。策略梯度定理告诉我们—— P ( τ ∣ π ) P(\tau|\pi) P(τπ)的求导只需对策略求梯度而无需对状态转移概率函数求梯度,证明如下:
∫ τ ∇ θ P ( τ ∣ θ ) R ( τ ) = ∫ τ P ( τ ∣ θ ) ∇ θ P ( τ ∣ θ ) P ( τ ∣ θ ) R ( τ ) = ∫ τ P ( τ ∣ θ ) ∇ θ l o g P ( τ ∣ θ ) R ( τ ) (1) \int_\tau\nabla _\theta P(\tau|\theta)R(\tau)=\int_\tau P(\tau|\theta) \frac{\nabla_\theta P(\tau|\theta)}{P(\tau|\theta)} R(\tau)=\int_\tau P(\tau|\theta) \nabla_\theta logP(\tau|\theta) R(\tau)\tag{1} τθP(τθ)R(τ)=τP(τθ)P(τθ)θP(τθ)R(τ)=τP(τθ)θlogP(τθ)R(τ)(1)
下面对 l o g P ( τ ∣ θ ) logP(\tau|\theta) logP(τθ)展开
l o g P ( τ ∣ θ ) = l o g   p 0 ( s 0 ) + ∑ t = 0 T ( l o g   π ( a t ∣ s t ) + l o g   p ( s t + 1 ∣ s t , a t ) ) logP(\tau|\theta)=log\space p_0(s_0)+\sum_{t=0}^T(log\space\pi(a_t|s_t)+log\space p(s_{t+1}|s_t,a_t)) logP(τθ)=log p0(s0)+t=0T(log π(atst)+log p(st+1st,at))
利用对数将累乘转化为累加,此时 θ \theta θ也不影响环境中状态转移概率的大小,这是环境自身的属性。则求导结果为: ∇ θ l o g P ( τ ∣ θ ) = ∑ t = 0 T ∇ θ l o g   π ( a t ∣ s t ) \nabla_\theta logP(\tau|\theta)=\sum_{t=0}^T\nabla_\theta log \space \pi(a_t|s_t) θlogP(τθ)=t=0Tθlog π(atst)

最终形式如下:
∇ θ J ( π θ ) = E τ ∼ P ( τ ∣ θ ) [ ∑ t = 0 T ∇ θ l o g   π θ ( a t ∣ s t ) R ( τ ) ] \nabla _\theta J(\pi_\theta)=E_{\tau\thicksim P(\tau|\theta)}[\sum_{t=0}^T\nabla _{\theta}log\space\pi_\theta(a_t|s_t)R(\tau)] θJ(πθ)=EτP(τθ)[t=0Tθlog πθ(atst)R(τ)]
与先前对比,策略梯度定理告诉我们——梯度下降过程中,只需求导策略部分,无需求导状态转移函数部分

三、REINFORCE算法

在实际计算中,我们无法精确计算 ∇ θ J ( π θ ) \nabla _\theta J(\pi_\theta) θJ(πθ)——轨迹难以穷举,REINFORCE算法提出采用蒙特卡洛采样,采样当前策略下的多条轨迹迭代优化策略,每次策略迭代基于一条轨迹。

基本的REINFORCE算法有如下特点:

  • on-policy算法:每次计算只能利用当前策略生成与环境交互的样本进行更新,而不能使用旧样本,因为旧样本不是当前策略产生的,所以这是一种 on-policy 算法。
  • 高方差:高方差由两个部分造成
    • 轨迹随机性:在环境随机性较大的情况下,相近迭代的策略的轨迹也有非常不同的结果,因此REINFORCE策略梯度估计有较高的方差。
    • Monte Carlo方法更新梯度:梯度更新一次需要等整个轨迹结束再进行更新,方差较大

REINFORCE算法的公式为:
∇ θ J ( π θ ) = E τ ∼ P ( τ ∣ θ ) [ ∑ t = 0 T ∇ θ l o g   π θ ( a t ∣ s t ) R ( τ ) ] = 1 N ∑ τ ∑ t ∇ θ l o g   π ( a t ∣ s t ) R ( τ ) \nabla_\theta J(\pi_\theta)=E_{\tau\thicksim P(\tau|\theta)}[\sum_{t=0}^T\nabla _{\theta}log\space\pi_\theta(a_t|s_t)R(\tau)]=\frac{1}{N}\sum_\tau\sum_t\nabla _{\theta}log\space\pi(a_t|s_t)R(\tau) θJ(πθ)=EτP(τθ)[t=0Tθlog πθ(atst)R(τ)]=N1τtθlog π(atst)R(τ)
在pytorch中,反向传播可以自动进行,我们需要定义该反向传播的正向传播公式,即 J ( π θ ) J(\pi_{\theta}) J(πθ),我们将两侧梯度算子摘去:
J ( π θ ) = 1 N ∑ τ ∑ t l o g   π ( a t ∣ s t ) R ( τ ) + C ( 可令 C = 0 ) J(\pi_{\theta})=\frac{1}{N}\sum_\tau\sum_tlog\space\pi(a_t|s_t)R(\tau)+C(可令 C=0) J(πθ)=N1τtlog π(atst)R(τ)+C(可令C=0)
我们考虑单条轨迹 τ \tau τ其中单步 t t t,取负,改为最小化的目标:
J ( π θ ) = − l o g π ( a t ∣ s t ) R ( τ ) J(\pi_\theta)=-log\pi(a_t|s_t)R(\tau) J(πθ)=logπ(atst)R(τ)

四、算法实现

参数化后的策略网络应该如何输出动作呢?我们要求策略的输出一定满足该条件 ∑ π ( a ∣ s ) = 1 \sum\pi(a|s)=1 π(as)=1

  • 如果环境的动作空间是离散的,那么策略网络的输出一般是离散的softmax分布
  • 如果环境的动作空间是连续的,那么策略网络的输出一般是正态分布

我们将policy-based,on-policy下REINFORCE算法和value-based,off-policy DQN算法进行对比

class PolicyNet(torch.nn.Module):
    def __init__(self,state_dim,hidden_dim,action_dim):
        super(PolicyNet,self).__init__()
        self.fc1 = torch.nn.Linear(state_dim,hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim,action_dim)

    def forward(self,x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x),dim=1)

REINFORCE在输出actions时需要将最后一层进行softmax,最终输出对应每个动作的概率

class REINFORCE():
    def __init__(self,state_dim,hidden_dim,action_dim,lr,gamma,device):
       ...

    def take_action(self,state):
        state = torch.tensor([state],dtype=torch.float).to(self.device)
        probs = self.PolicyNet(state)
        action_dist = torch.distributions.Categorical(probs) # 生成一个分布,用于采样,probs是概率
        action = action_dist.sample()
        return action.item()

DQN向网络传入state,输出当前states下所有动作的Q值,gather(1,action)可取出指定动作的Q值

REINFORCE向网络传入state,输出softmax后的概率,Categorical利用概率建立一个动作分布,sample从动作中抽样一个动作并返回

def update(self,transition_dict):
        reward_list = transition_dict['rewards']
        state_list = transition_dict['states']
        action_list = transition_dict['actions']

        G = 0
        self.optimizer.zero_grad()
        for i in reversed(range(len(reward_list))):
            reward = reward_list[i]
            state = torch.tensor([state_list[i]],dtype=torch.float).to(self.device)
            action = torch.tensor([action_list[i]],dtype=torch.long).view(-1,1).to(self.device)

            log_prob = torch.log(self.PolicyNet(state).gather(1,action))
            G = reward + self.gamma * G 
            loss = -log_prob * G
            loss.backward()
        self.optimizer.step()
        return loss

下面是最重要的update函数

DQN每个step均进行一次update,从经验池中抽样多个样本(s,a,s’,r) 。off-policy:这些样本不一定来自本次策略,每次step均更新Qnet,DQN的核心代码如下:

       #从replybuffer中选择多组states。计算每个state不同action的Q值,选取指定actions的Q(s,a)
    	q_values = self.q_net(states).gather(1,actions)
        #利用多组next_states,计算每个s'不同action的值,返回每组s'最大的Q(s,a)
        max_next_q_values = self.target_q_net(next_states).max(1)[0].view(-1,1)
		
        q_targets = rewards + self.gamma * max_next_q_values * (1 - dones)
        #计算损失函数
        dqn_loss = torch.mean(F.mse_loss(q_values,q_targets))
        dqn_loss.backward()
        self.optimizer.step()
    def update(self,transition_dict):
        reward_list = transition_dict['rewards']
        state_list = transition_dict['states']
        action_list = transition_dict['actions']

        G = 0
        self.optimizer.zero_grad()
        for i in reversed(range(len(reward_list))):
            reward = reward_list[i]
            state = torch.tensor([state_list[i]],dtype=torch.float).to(self.device)
            action = torch.tensor([action_list[i]],dtype=torch.long).view(-1,1).to(self.device)

            log_prob = torch.log(self.PolicyNet(state).gather(1,action)) #计算state采取该已知动作的概率,取对数
            G = self.gamma * G + reward #计算奖励
            loss = -log_prob * G #计算loss
            loss.backward()
        self.optimizer.step()
        return loss

REINFORCE一条轨迹结束进行一次update,使用整条轨迹的全部样本(s,a,s’,r)。on-policy:这些样本均来自本次策略。

由于一条轨迹的整个样本都能收集,我们可以从最终状态开始,累加计算reward即 R ( τ ) R(\tau) R(τ) ,将一条轨迹上的损失函数反向传播结束后,更新一次参数。单个元组计算的 Δ J \Delta J ΔJ如下
J ( π θ ) = − l o g π ( a t ∣ s t ) R ( τ ) J(\pi_\theta)=-log\pi(a_t|s_t)R(\tau) J(πθ)=logπ(atst)R(τ)

五、REINFORCE改进

上文提到,REINFORCE算法方差太大,训练不稳定,下面根据主要原因分别进行优化

  • 相近策略采样轨迹不同:随机性策略,相近策略动作可能不同,对奖励的影响较大,所以计算 J ( π θ ) = 1 N ∑ τ ∑ t l o g   π ( a t ∣ s t ) R ( τ ) J(\pi_{\theta})=\frac{1}{N}\sum_\tau\sum_tlog\space\pi(a_t|s_t)R(\tau) J(πθ)=N1τtlog π(atst)R(τ)时,主要影响因素在于 R ( τ ) R(\tau) R(τ)

    有多种方法对 R ( τ ) R(\tau) R(τ)进行优化:

    • 基线:我们希望 l o g π log\pi logπ R ( τ ) R(\tau) R(τ)的大小在同一个数量级,使得奖励的变化对整体数值的影响减小,因此考虑将其减去一个基线 b b b
    • 修改:不考虑全程奖励 R ( τ ) R(\tau) R(τ),每次利用单个元组,计算Loss进行反向传播时,只计算该元组之后轨迹的奖励(上文的程序使用就是此方法)
  • 蒙特卡洛方式更新:轨迹结束才进行更新,方差较大,考虑修改为时序差分更新——后续Actor-Critic借鉴了该思路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值