Easy RL 策略梯度入门:REINFORCE算法原理与代码实现

Easy RL 策略梯度入门:REINFORCE算法原理与代码实现

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

引言:从价值迭代到策略搜索的范式转换

你是否曾在强化学习实践中遇到这些困境:Q-learning在高维动作空间中陷入维度灾难?DQN的ε-贪婪策略导致探索效率低下?当面对连续动作空间(如机械臂控制、自动驾驶)时,基于价值函数的方法往往需要复杂的函数近似或离散化技巧。而策略梯度(Policy Gradient,PG) 算法直接参数化策略函数,通过梯度上升最大化累积回报,为解决这些问题提供了全新思路。

本文将系统讲解策略梯度的奠基性算法——REINFORCE(蒙特卡洛策略梯度),通过数学推导、代码实现和可视化分析,帮助你掌握:

  • 策略梯度的核心数学原理与无偏估计
  • REINFORCE算法的完整流程与实现技巧
  • 基于PyTorch的策略网络设计与训练全流程
  • 解决实际问题时的回报归一化、基线设置等关键优化

策略梯度核心原理:从期望回报到梯度上升

策略参数化与目标函数

策略梯度算法直接对策略函数进行参数化表示: $$\pi_\theta(a|s) = P(a|s;\theta)$$ 其中$\theta$为策略网络参数,$s$为状态,$a$为动作。对于离散动作空间,通常采用softmax函数输出概率分布;对于连续动作空间,则常用高斯分布建模。

目标函数定义为策略的期望累积回报: $$J(\theta) = \mathbb{E}{\tau \sim \pi\theta} [R(\tau)] = \sum_\tau P(\tau;\theta) R(\tau)$$ 其中$\tau = (s_1,a_1,r_1,...,s_T,a_T,r_T)$为完整轨迹,$R(\tau)=\sum_{t=1}^T \gamma^{t-1}r_t$为折扣累积回报。

策略梯度的数学推导

通过链式法则计算目标函数梯度: $$\nabla_\theta J(\theta) = \mathbb{E}{\tau \sim \pi\theta} [\sum_{t=1}^T \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot G_t]$$ 其中$G_t = \sum_{k=t}^T \gamma^{k-t} r_k$为从时刻$t$开始的折扣回报。这一重要结论表明:策略梯度可通过采样轨迹的对数概率梯度累积回报的乘积来估计。

直观理解:若某动作$a_t$在状态$s_t$下产生正回报$G_t$,则通过梯度上升增大$\pi_\theta(a_t|s_t)$的概率;反之则减小其概率。

REINFORCE算法详解:蒙特卡洛策略梯度

算法流程

REINFORCE算法采用蒙特卡洛方法,在完整采样一回合后进行参数更新,具体步骤如下:

mermaid

关键实现技巧

  1. 折扣回报计算
    采用逆序迭代高效计算:

    running_add = 0
    for i in reversed(range(len(reward_pool))):
        running_add = running_add * gamma + reward_pool[i]
        reward_pool[i] = running_add  # G_t
    
  2. 回报归一化
    通过标准化处理降低梯度估计方差:

    reward_pool = (reward_pool - np.mean(reward_pool)) / (np.std(reward_pool) + 1e-9)
    
  3. 基线减法
    引入状态价值函数$V(s)$作为基线(Baseline),将目标改为优势估计$A(s,a)=G_t - V(s_t)$,进一步减小方差。

代码实现:基于PyTorch的REINFORCE

环境准备与依赖安装

项目依赖项:

pip install torch==1.10.0 gym==0.21.0 numpy==1.21.2 matplotlib==3.5.1

策略网络设计

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):
        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 numpy as np
from torch.distributions import Bernoulli

class PolicyGradient:
    def __init__(self, model, memory, cfg):
        self.gamma = cfg['gamma']  # 折扣因子
        self.device = torch.device(cfg['device'])
        self.policy_net = model.to(self.device)
        self.optimizer = torch.optim.RMSprop(self.policy_net.parameters(), lr=cfg['lr'])
        self.memory = memory  # 经验回放缓冲区

    def sample_action(self, state):
        state = torch.from_numpy(state).float().to(self.device)
        probs = self.policy_net(state)
        m = Bernoulli(probs)  # 伯努利分布采样(适用于二值动作)
        action = m.sample().item()
        return int(action)

    def update(self):
        # 从经验池采样数据
        state_pool, action_pool, reward_pool = self.memory.sample()
        
        # 计算折扣回报
        running_add = 0
        discounted_rewards = []
        for r in reversed(reward_pool):
            running_add = running_add * self.gamma + r
            discounted_rewards.insert(0, running_add)
        discounted_rewards = torch.tensor(discounted_rewards).to(self.device)
        
        # 回报归一化
        discounted_rewards = (discounted_rewards - discounted_rewards.mean()) / \
                            (discounted_rewards.std() + 1e-9)
        
        # 计算损失并更新参数
        self.optimizer.zero_grad()
        loss = 0
        for i in range(len(reward_pool)):
            state = torch.from_numpy(state_pool[i]).float().to(self.device)
            action = torch.tensor(action_pool[i]).float().to(self.device)
            probs = self.policy_net(state)
            m = Bernoulli(probs)
            # 策略梯度损失:-log_prob(a) * G_t
            loss += -m.log_prob(action) * discounted_rewards[i]
        
        loss.backward()
        self.optimizer.step()
        self.memory.clear()  # 清空经验池

CartPole环境应用示例

import gym
from collections import deque

class Memory:
    def __init__(self):
        self.state_pool = deque()
        self.action_pool = deque()
        self.reward_pool = deque()

    def push(self, state, action, reward):
        self.state_pool.append(state)
        self.action_pool.append(action)
        self.reward_pool.append(reward)

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

    def clear(self):
        self.state_pool.clear()
        self.action_pool.clear()
        self.reward_pool.clear()

# 配置参数
cfg = {
    'gamma': 0.99,
    'lr': 0.01,
    'device': 'cpu',
    'episodes': 500,
    'max_steps': 200
}

# 初始化环境和智能体
env = gym.make('CartPole-v1')
input_dim = env.observation_space.shape[0]
output_dim = 1  # 二值动作空间
model = PGNet(input_dim, output_dim)
memory = Memory()
agent = PolicyGradient(model, memory, cfg)

# 训练主循环
rewards = []
for episode in range(cfg['episodes']):
    state = env.reset()
    total_reward = 0
    for step in range(cfg['max_steps']):
        action = agent.sample_action(state)
        next_state, reward, done, _ = env.step(action)
        agent.memory.push(state, action, reward)
        state = next_state
        total_reward += reward
        if done:
            break
    agent.update()  # 回合结束后更新策略
    rewards.append(total_reward)
    
    # 打印训练进度
    if episode % 50 == 0:
        print(f"Episode {episode}, Avg Reward: {np.mean(rewards[-50:]):.2f}")

实验分析与可视化

训练曲线分析

REINFORCE在CartPole环境中的典型训练曲线如下: mermaid

关键观察

  • 初期奖励波动较大(蒙特卡洛估计方差导致)
  • 中期奖励稳步上升(策略逐渐优化)
  • 后期收敛至最大奖励(CartPole最大步数500)

超参数敏感性分析

超参数推荐值范围对性能影响分析
学习率(lr)0.001-0.01过小导致收敛慢,过大致使训练不稳定
折扣因子(γ)0.9-0.99过小过度关注即时奖励,过大增加方差
隐藏层维度64-256过小表达能力不足,过大易过拟合

进阶方向与算法改进

方差减小技术

  1. 优势函数估计:使用时序差分(TD)方法估计$A(s,a) = Q(s,a) - V(s)$,如A2C算法
  2. 广义优势估计(GAE):结合多步TD和蒙特卡洛的优势估计: $$\hat{A}t^{GAE(\lambda)} = \sum{k=0}^\infty (\gamma\lambda)^k \delta_{t+k}$$
  3. 异步更新:A3C算法通过多线程并行采样降低梯度估计方差

连续动作空间扩展

对于连续动作空间,策略网络输出高斯分布参数: $$\pi_\theta(a|s) = \mathcal{N}(\mu_\theta(s), \sigma^2 I)$$ 其中$\mu_\theta(s)$为均值向量,通过神经网络直接输出。

总结与展望

REINFORCE作为最基础的策略梯度算法,展示了直接策略优化的强大潜力:

  • 优势:天然支持连续动作空间,无需价值函数迭代
  • 局限:蒙特卡洛更新导致高方差,收敛速度较慢

后续改进可关注:

  1. 结合价值函数的演员-评论员(Actor-Critic)架构
  2. 信任区域策略优化(TRPO、PPO)等稳定性提升方法
  3. 分布式强化学习框架(如IMPALA)的工程实现

通过掌握REINFORCE算法,你已迈出策略梯度方法的第一步。建议进一步实践PPO算法(当前最流行的策略梯度变种),并尝试在Mujoco等高维连续控制环境中应用。

扩展资源

  • 项目代码仓库:https://gitcode.com/datawhalechina/easy-rl
  • 推荐论文
    • 《Policy Gradient Methods for Reinforcement Learning with Function Approximation》
    • 《Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning》
  • 实践环境
    • OpenAI Gym:经典控制任务
    • MuJoCo:连续控制物理仿真环境

行动建议:立即克隆仓库,在CartPole环境复现本文实验,然后尝试修改代码实现带基线的REINFORCE算法,观察方差减小效果!

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

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

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

抵扣说明:

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

余额充值