30分钟上手策略梯度:从原理到实战案例详解

30分钟上手策略梯度:从原理到实战案例详解

【免费下载链接】easy-rl 强化学习中文教程(蘑菇书🍄),在线阅读地址:https://datawhalechina.github.io/easy-rl/ 【免费下载链接】easy-rl 项目地址: https://gitcode.com/gh_mirrors/ea/easy-rl

你还在为强化学习算法复杂而头疼?本文带你30分钟掌握Policy Gradient(策略梯度)的核心原理,从零实现一个简单的强化学习模型。读完本文你将能够:

  • 理解策略梯度的基本概念和数学原理
  • 掌握REINFORCE算法的实现步骤
  • 用PyTorch实现一个简单的策略梯度模型
  • 在实际环境中训练和评估策略梯度模型

什么是策略梯度

策略梯度是强化学习中一种直接优化策略的方法,它不需要像Q-learning那样估计价值函数,而是直接通过梯度上升来最大化期望奖励。策略梯度算法的核心思想是:如果一个动作带来了正的奖励,那么我们就增加这个动作被选中的概率;反之,如果一个动作带来了负的奖励,我们就减少这个动作被选中的概率。

策略梯度算法通常用一个参数化的策略函数$\pi_\theta(a|s)$来表示智能体的行为,其中$\theta$是策略的参数,$s$是当前状态,$a$是要执行的动作。策略函数输出的是在状态$s$下采取各个动作的概率分布。

策略梯度原理

策略梯度的数学原理

策略梯度算法的目标是最大化期望累积奖励: $$\bar{R}\theta = \mathbb{E}{\tau \sim p_\theta(\tau)}[R(\tau)]$$

其中,$\tau$是一个完整的轨迹(trajectory),$R(\tau)$是该轨迹的总奖励。为了最大化这个期望,我们使用梯度上升的方法,计算$\bar{R}_\theta$对参数$\theta$的梯度:

$$\nabla \bar{R}\theta = \mathbb{E}{\tau \sim p_\theta(\tau)}[R(\tau) \nabla \log p_\theta(\tau)]$$

进一步展开,可以得到策略梯度的具体计算公式:

$$\nabla \bar{R}\theta \approx \frac{1}{N} \sum{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right)$$

这个公式的直观含义是:在每个状态$s_t$下执行动作$a_t$后,如果整个轨迹的总奖励$R(\tau)$是正的,我们就增加在状态$s_t$下执行动作$a_t$的概率;反之则减少这个概率。

策略梯度公式推导

REINFORCE算法

REINFORCE算法是最简单也最经典的策略梯度算法,它使用蒙特卡洛方法来估计梯度。REINFORCE算法的步骤如下:

  1. 利用当前策略$\pi_\theta$采样一条轨迹$\tau = {s_1, a_1, r_1, s_2, a_2, r_2, ..., s_T, a_T, r_T}$
  2. 计算每个时刻$t$的折扣回报$G_t = \sum_{k=t+1}^T \gamma^{k-t-1} r_k$
  3. 计算损失函数$L(\theta) = -\sum_{t=1}^T G_t \log \pi_\theta(a_t|s_t)$
  4. 利用梯度下降更新参数$\theta$

REINFORCE算法的关键在于使用整个轨迹的回报来指导参数更新,这也是它被称为蒙特卡洛策略梯度的原因。

REINFORCE算法流程

策略梯度模型实现

下面我们用PyTorch实现一个简单的策略梯度模型。首先定义策略网络:

import torch
import torch.nn as nn
import torch.nn.functional as F

class PGNet(nn.Module):
    def __init__(self, input_dim, output_dim, hidden_dim=128):
        """ 初始化策略网络,为全连接网络
            input_dim: 输入的特征数即环境的状态维度
            output_dim: 输出的动作维度
        """
        super(PGNet, self).__init__()
        self.fc1 = nn.Linear(input_dim, hidden_dim)  # 输入层
        self.fc2 = nn.Linear(hidden_dim, hidden_dim)  # 隐藏层
        self.fc3 = nn.Linear(hidden_dim, output_dim)  # 输出层

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = torch.sigmoid(self.fc3(x))
        return x

接下来实现策略梯度算法的主体部分:

import torch
from torch.distributions import Bernoulli
from torch.autograd import Variable
import numpy as np

class PolicyGradient:
    
    def __init__(self, model, memory, cfg):
        self.gamma = cfg['gamma']
        self.device = torch.device(cfg['device']) 
        self.memory = memory
        self.policy_net = model.to(self.device)
        self.optimizer = torch.optim.RMSprop(self.policy_net.parameters(), lr=cfg['lr'])

    def sample_action(self, state):
        state = torch.from_numpy(state).float()
        state = Variable(state)
        probs = self.policy_net(state)
        m = Bernoulli(probs)  # 伯努利分布
        action = m.sample()
        action = action.data.numpy().astype(int)[0]  # 转为标量
        return action

    def update(self):
        state_pool, action_pool, reward_pool = self.memory.sample()
        state_pool, action_pool, reward_pool = list(state_pool), list(action_pool), list(reward_pool)
        
        # 计算折扣回报
        running_add = 0
        for i in reversed(range(len(reward_pool))):
            if reward_pool[i] == 0:
                running_add = 0
            else:
                running_add = running_add * self.gamma + reward_pool[i]
                reward_pool[i] = running_add
        
        # 归一化回报
        reward_mean = np.mean(reward_pool)
        reward_std = np.std(reward_pool)
        for i in range(len(reward_pool)):
            reward_pool[i] = (reward_pool[i] - reward_mean) / reward_std
        
        # 梯度下降
        self.optimizer.zero_grad()
        for i in range(len(reward_pool)):
            state = state_pool[i]
            action = Variable(torch.FloatTensor([action_pool[i]]))
            reward = reward_pool[i]
            state = Variable(torch.from_numpy(state).float())
            probs = self.policy_net(state)
            m = Bernoulli(probs)
            loss = -m.log_prob(action) * reward  # 负的得分函数乘以回报
            loss.backward()
        self.optimizer.step()
        self.memory.clear()

完整代码实现

策略梯度训练技巧

在实际实现策略梯度算法时,有一些技巧可以提高训练效果:

添加基线

策略梯度的一个问题是如果所有的奖励都是正的,那么所有动作的概率都会被增加,这可能导致未被采样到的动作概率下降。为了解决这个问题,我们可以在奖励中减去一个基线$b$:

$$\nabla \bar{R}{\theta} \approx \frac{1}{N} \sum{n=1}^{N} \sum_{t=1}^{T_{n}}\left(R\left(\tau^{n}\right)-b\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right)$$

基线$b$通常设置为平均奖励,这样可以让$R(\tau)-b$有正有负,从而使得好的动作概率增加,差的动作概率减少。

分配合适的分数

另一个改进是给每个动作分配不同的权重,而不是整个轨迹使用相同的奖励。具体来说,我们使用从时刻$t$开始的折扣回报$G_t$来代替整个轨迹的总奖励$R(\tau)$:

$$\nabla \bar{R}{\theta} \approx \frac{1}{N} \sum{n=1}^{N} \sum_{t=1}^{T_{n}}\left(\sum_{t^{\prime}=t}^{T_{n}} \gamma^{t^{\prime}-t} r_{t^{\prime}}^{n}-b\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right)$$

这样可以更精确地评估每个动作的好坏,而不是将整个轨迹的结果平均分配给每个动作。

策略梯度训练技巧

实战案例:CartPole环境

下面我们在OpenAI Gym的CartPole环境中测试我们实现的策略梯度算法。CartPole环境的目标是控制一个小车,使车上的杆子保持竖直不倒。

首先,我们需要创建一个经验回放缓冲区来存储轨迹数据:

class Memory:
    def __init__(self):
        self.state_pool = []
        self.action_pool = []
        self.reward_pool = []
        self.pool_size = 0

    def push(self, state, action, reward):
        self.state_pool.append(state)
        self.action_pool.append(action)
        self.reward_pool.append(reward)
        self.pool_size += 1

    def sample(self):
        return self.state_pool, self.action_pool, self.reward_pool

    def clear(self):
        self.state_pool = []
        self.action_pool = []
        self.reward_pool = []
        self.pool_size = 0

然后,我们可以开始训练模型:

import gym
import numpy as np
import torch

# 配置参数
cfg = {
    'gamma': 0.99,
    'lr': 0.01,
    'train_eps': 1000,
    'device': 'cpu'
}

# 创建环境
env = gym.make('CartPole-v0')
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n

# 初始化模型和记忆库
model = PGNet(state_dim, action_dim)
memory = Memory()
agent = PolicyGradient(model, memory, cfg)

# 训练模型
rewards = []
for i_ep in range(cfg['train_eps']):
    state = env.reset()
    ep_reward = 0
    while True:
        action = agent.sample_action(state)
        next_state, reward, done, _ = env.step(action)
        agent.memory.push(state, action, reward)
        state = next_state
        ep_reward += reward
        if done:
            agent.update()
            rewards.append(ep_reward)
            if (i_ep+1) % 100 == 0:
                print(f"Episode: {i_ep+1}, Reward: {np.mean(rewards[-100:])}")
            break

# 绘制奖励曲线
import matplotlib.pyplot as plt
plt.plot(rewards)
plt.title('Training Reward')
plt.xlabel('Episode')
plt.ylabel('Reward')
plt.show()

在CartPole环境中,策略梯度算法通常可以在1000个episode内收敛,达到200分的最大奖励。

策略梯度训练结果

总结

策略梯度是一种直接优化策略的强化学习方法,它通过梯度上升来最大化期望奖励。本文我们介绍了策略梯度的基本原理,实现了REINFORCE算法,并在CartPole环境中进行了测试。策略梯度算法的优点是可以处理连续动作空间,并且具有较好的收敛性;缺点是训练过程中方差较大,通常需要较多的采样数据。

在实际应用中,策略梯度算法有很多改进版本,如A2C、A3C、PPO等,这些算法通过引入价值函数估计或其他技巧来减少方差,提高训练效率。如果你想深入了解策略梯度算法,可以继续学习这些高级变种。

希望本文能够帮助你理解策略梯度算法的核心思想和实现方法。如果你有任何问题或建议,欢迎在评论区留言讨论!

点赞收藏本文,关注作者获取更多强化学习实战教程!

【免费下载链接】easy-rl 强化学习中文教程(蘑菇书🍄),在线阅读地址:https://datawhalechina.github.io/easy-rl/ 【免费下载链接】easy-rl 项目地址: https://gitcode.com/gh_mirrors/ea/easy-rl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值