强化学习------Actor-Critic(A2C)算法(附pytorch代码)

本文详细介绍了Actor-Critic算法,包括其原理、如何结合策略梯度和值函数进行学习,以及如何通过PyTorch实现一个基础的Actor-Critic模型,以OpenAIGym的CartPole游戏为例展示了训练过程。
摘要由CSDN通过智能技术生成

一、前言

Actor-Critic算法是一种基于策略梯度(Policy Gradient)和价值函数(Value Function)的强化学习方法,通常被用于解决连续动作空间和高维状态空间下的强化学习问题。该算法将一个Actor网络和一个Critic网络组合在一起,通过Actor网络产生动作,并通过Critic网络估计状态值函数或状态-动作值函数,最终通过策略梯度算法训练Actor网络和Critic网络。Actor-Critic算法的优点是在处理大型状态空间时具有较高的效率和可扩展性。

二、算法原理

Actor-Critic算法中的Actor网络用于学习策略,用于生成动作。Critic网络则用于学习值函数,用于评估状态或状态动作对的价值。ActorCritic网络之间的交互便是Actor-Critic算法的核心机制。
Actor-Critic算法中,我们有两个更新任务:Actor网络的策略梯度更新和Critic网络的值函数更新。

Actor-Critic算法中使用的策略梯度方法是REINFORCE算法,该算法的公式如下:

∇ θ J ( θ ) = E t ​ [ ∇ θ l o g π ( a t ∣ s t ​ ) ( Q π ( s t ​ , a t ​ ) − b t ) ] ∇_θ J(θ)=E_t​[∇_θ logπ(a_t∣s_t​)(Q^π (s_t​ ,a_t​ )−b_t )] θJ(θ)=Et[θlogπ(atst)(Qπ(st,at)bt)]

其中 J ( θ ) J(\theta) J(θ)表示目标策略的性能, ∇ θ J ( θ ) ∇_θ J ( θ ) θJ(θ)表示策略梯度, π ( a t ∣ s t ) π ( a_t ∣ s_t ) π(atst)表示在状态 s t s_t st 下选择动作 a t a_t at 的概率。

虽然REINFORCE算法在Actor-Critic算法中被广泛使用,但它存在两个问题***:高方差计算效率低***。
为了解决这两个问题,我们可以引入一个基准函数 B ( s t ) B ( s_t ) B(st),并将奖励 Q π ( s t , a t ) − B ( s t ) Q^π ( s_t , a_t ) − B ( s_t ) Qπ(st,at)B(st)作为更新中的优势函数 A π ( s t , a t ) A^π ( s_t , a_t ) Aπ(st,at) ,公式变为:

∇ θ J ( θ ) = E t [ ∇ θ l o g π ( a t ∣ s t ) A π ( s t , a t ) ] ∇_θJ(θ)=E_t [∇_θ logπ(a_t ∣s_t )A^π (s_t ,a_t)] θJ(θ)=Et[θlogπ(atst)Aπ(st,at)]

其中, A π ( s t , a t ) = Q π ( s t , a t ) − B ( s t ) A^π ( s_t , a_t ) = Q^π ( s_t , a_t ) − B ( s_t ) Aπ(st,at)=Qπ(st,at)B(st)表示相对于基准函数的优势函数。

参数化的值函数可以通过状态价值函数V(s)或动作价值函数Q(s,a)来表示,取决于我们需要估计的是状态价值函数还是状态-动作价值函数。对于Critic网络的值函数更新,我们可以使用TD误差来计算当前状态值和下一时刻状态值之间的误差:

δ = r + γ V ( s ′ ) − V ( s ) δ=r+γV(s^′)−V(s) δ=r+γV(s)V(s)

其中r是当前时刻的奖励,γ是折扣因子, V ( s ′ ) V ( s^′ ) V(s)是下一时刻的状态值, V ( s ) V (s) V(s)是当前时刻的状态值。

我们可以使用每个状态sTD(Temporal Difference)误差 δ δ δ的平方来衡量当前值函数 V ( s ) V ( s ) V(s)的误差,并用该误差更新Critic网络的参数。

整体算法流程如下:

在这里插入图片描述

三、算法代码

伪代码如下:
三、算法代码

算法代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from torch.distributions import Categorical


class Actor(nn.Module):
    '''
    演员Actor网络
    '''
    def __init__(self, action_dim, state_dim):
        super(Actor, self).__init__()
        self.fc1 = nn.Linear(state_dim, 300)
        self.fc2 = nn.Linear(300, action_dim)

        self.ln = nn.LayerNorm(300)

    def forward(self, s):
        if isinstance(s, np.ndarray):
            s = torch.FloatTensor(s)
        x = self.ln(F.relu(self.fc1(s)))
        out = F.softmax(self.fc2(x), dim=-1)

        return out


class Critic(nn.Module):
    '''
    评论家Critic网络
    '''
    def __init__(self, state_dim):
        super(Critic, self).__init__()
        self.fc1 = nn.Linear(state_dim, 300)
        self.fc2 = nn.Linear(300, 1)

        self.ln = nn.LayerNorm(300)

    def forward(self, s):
        if isinstance(s, np.ndarray):
            s = torch.FloatTensor(s)
        x = self.ln(F.relu(self.fc1(s)))
        out = self.fc2(x)

        return out


class Actor_Critic:
    def __init__(self, env):
        self.gamma = 0.99
        self.lr_a = 3e-4
        self.lr_c = 5e-4

        self.env = env
        self.action_dim = self.env.action_space.n             #获取描述行动的数据维度
        self.state_dim = self.env.observation_space.shape[0]  #获取描述环境的数据维度

        self.actor = Actor(self.action_dim, self.state_dim)   #创建演员网络
        self.critic = Critic(self.state_dim)                  #创建评论家网络

        self.actor_optim = torch.optim.Adam(self.actor.parameters(), lr=self.lr_a)
        self.critic_optim = torch.optim.Adam(self.critic.parameters(), lr=self.lr_c)

        self.loss = nn.MSELoss()

    def get_action(self, s):
        a = self.actor(s)
        dist = Categorical(a)
        action = dist.sample()             #可采取的action
        log_prob = dist.log_prob(action)   #每种action的概率

        return action.detach().numpy(), log_prob

    def learn(self, log_prob, s, s_, rew):
        #使用Critic网络估计状态值
        v = self.critic(s)
        v_ = self.critic(s_)

        critic_loss = self.loss(self.gamma * v_ + rew, v)
        self.critic_optim.zero_grad()
        critic_loss.backward()
        self.critic_optim.step()

        td = self.gamma * v_ + rew - v          #计算TD误差
        loss_actor = -log_prob * td.detach()
        self.actor_optim.zero_grad()
        loss_actor.backward()
        self.actor_optim.step()

四、训练示例

我们将以OpenAI Gym中的CartPole(倒立摆)游戏为应用场景,基于pytorch实现一个基础的Actor-Critic算法,让算法去玩这个游戏。
程序共分为两个文件:

  • train.py文件:主要负责调用算法实现整体功能
  • model.py文件:主要实现一个完整的Actor-Critic算法(上面的代码)

train.py代码如下:

import gym
from model import Actor_Critic
import matplotlib.pyplot as plt


if __name__ == "__main__":
    env = gym.make('CartPole-v0')
    model = Actor_Critic(env)  #实例化Actor_Critic算法类
    reward = []
    for episode in range(200):
        s = env.reset()  #获取环境状态
        env.render()     #界面可视化
        done = False     #记录当前回合游戏是否结束
        ep_r = 0
        while not done:
            # 通过Actor_Critic算法对当前环境做出行动
            a,log_prob = model.get_action(s)

            # 获得在做出a行动后的最新环境
            s_,rew,done,_  = env.step(a)

            #计算当前reward
            ep_r += rew

            #训练模型
            model.learn(log_prob,s,s_,rew)

            #更新环境
            s = s_
        reward.append(ep_r)
        print(f"episode:{episode} ep_r:{ep_r}")
    plt.plot(reward)
    plt.show()

训练效果如下:
在这里插入图片描述

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

韭菜盖饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值