(5-2-02)直接优化策略:REINFORCE:基线 (Baseline) 的引入与作用

5.2.2  基线 (Baseline) 的引入与作用​

在强化学习的策略梯度方法中,基线 (Baseline) 的引入是一项重要改进,它在不影响策略梯度无偏性的前提下,显著降低了梯度估计的方差,从而加速算法收敛。

1. 引入基线的必要性与目的​

策略梯度方法的核心在于通过采样完整的轨迹来估计策略的梯度。然而,由于环境的随机性和奖励信号的不确定性,累积奖励的波动性较大,导致梯度估计的方差较高。具体来说,即使在相同的状态下,由于环境的微小扰动,不同轨迹的累积奖励可能差异显著。例如,在经典的CartPole平衡任务中,即使小车和杆子的初始状态相同,由于环境的随机扰动,不同回合的累积奖励也可能不同。这种高方差的梯度估计使得参数更新的方向和幅度变得不稳定,进而导致算法在优化过程中可能出现震荡或收敛缓慢的问题。

为了实现有效的策略优化,算法需要稳定且准确的梯度估计来指导参数更新的方向和幅度。不稳定的梯度估计会使参数在更新过程中来回波动,难以朝着最优策略的方向稳步前进。这就好比在黑暗中摸索前行,如果每一步的指引方向都在大幅摆动,那么到达目的地将变得异常困难。因此,降低梯度估计的方差、提高更新的稳定性成为提升策略梯度方法性能的关键需求。

基线的引入正是为了应对上述挑战。其核心目标是降低梯度估计的方差,从而提高算法的稳定性和收敛速度。通过提供一个与状态相关且易于计算的基准值,基线对原始的累积奖励进行调整。这样可以有效减少奖励信号中的噪声成分,使梯度估计更加集中在策略本身的有效信息上,进而使参数更新更加平稳、高效地朝着提升策略性能的方向推进。

2. 数学推导​

在 REINFORCE 等策略梯度算法中,梯度估计的表达式为:

其中,是从时间步 t 开始的累积回报。这种估计方法存在一个严重问题:由于环境随机性和蒙特卡洛采样的局限性,Rt的方差可能非常大,导致梯度估计不稳定,算法收敛缓慢甚至无法收敛。

引入基线的核心目的是:在保持策略梯度无偏性的前提下,降低其方差。在数学上,可以证明对于任意与动作无关的函数 b(st​​),有:

因此,可以将策略梯度改写为:

这里的b(st​)就是基线函数,它的引入不会改变梯度的期望值,但可以显著降低方差。直观理解,基线为评估动作价值提供了一个参考点,使得算法更关注动作相对于基线的优势而非绝对价值。

3. 基线的选择标准与常见类型​

(1)选择标准

  1. 与动作无关:基线只能依赖于状态 st​,不能依赖于动作 at​,以保证梯度估计的无偏性。
  2. 方差降低效果:好的基线应该能够最大程度地减小的方差。
  3. 计算效率:基线应易于计算,不显著增加算法复杂度。

(2)常见类型

  1. 状态价值函数基线:使用状态价值函数  作为基线,这是最常见的选择。通过同时学习策略网络和价值网络 ​,可以得到更准确的基线估计。
  2. 平均回报基线:使用所有轨迹的平均回报作为全局基线,即,其中R(i)是第 i 条轨迹的总回报。这种方法简单有效,但忽略了状态的差异。
  3. 状态相关的平均回报基线:对每个状态st,使用该状态下观察到的平均回报作为基线,即。这需要维护一个“状态-回报”的统计表,适用于状态空间较小的环境。
  4. 神经网络近似的价值函数:使用神经网络 来近似状态价值函数,通过最小化均方误差来训练。这种方法灵活性强,适用于高维连续状态空间。

4. 基于基线优化的REINFORCE算法改进​

结合基线的REINFORCE算法(通常称为 REINFORCE-with-baseline)可以通过以下步骤实现:

(1)初始化:策略网络参数 θ 和价值网络参数 ϕ(如果使用神经网络近似基线)

(2)采样轨迹:使用当前策略 πθ​ 与环境交互,生成轨迹 τ=(s0​,a0​,r1​,s1​,a1​,r2​,…)。

(3)计算回报:对每个时间步 t,计算累积回报

(4)更新价值网络(如果有):

(5)更新策略网络:

(6)重复上面的步骤(2)~(6),直到收敛为止。

这种方法结合了策略梯度和价值函数的优点,既保持了策略优化的灵活性,又通过基线降低了方差,提高了学习效率。

5. 基线版的优势

与原始 REINFORCE 相比,基于基线的改进版本通常具有以下优势:

  1. 更快的收敛速度
  2. 更低的样本复杂度
  3. 更稳定的训练过程
  4. 对超参数的敏感性降低

在实际应用中,REINFORCE-with-baseline 是策略梯度方法的基础版本,本书后面讲解的 Actor-Critic等算法都是在其基础上进一步发展而来的。

总之,基线技术是策略梯度方法中的关键改进,它通过巧妙的数学设计平衡了算法的偏差和方差,为强化学习在复杂环境中的应用奠定了基础。理解基线的原理和实现,是掌握现代强化学习算法的重要一步。例如下面是一个基于基线 (Baseline) 的 REINFORCE 算法的例子,使用 OpenAI Gym 的 CartPole 环境进行测试,并包含训练过程可视化和最终策略展示功能。在原有 REINFORCE 基础上添加了状态价值函数作为基线,以降低梯度估计的方差,提高学习效率。

实例5-2:实现基于基线的 REINFORCE 算法(源码路径:codes\5\Ji.py

实例文件Ji.py的具体实现代码如下所示。

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
import gymnasium as gym
from gymnasium.wrappers import RecordVideo

# 设置随机种子确保结果可复现
torch.manual_seed(42)
np.random.seed(42)

class PolicyNetwork(nn.Module):
    def __init__(self, state_dim, action_dim, hidden_dim=128):
        super(PolicyNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, action_dim)
        self.relu = nn.ReLU()
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, x):
        x = self.relu(self.fc1(x))
        action_probs = self.softmax(self.fc2(x))
        return action_probs

class ValueNetwork(nn.Module):
    def __init__(self, state_dim, hidden_dim=128):
        super(ValueNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, 1)
        self.relu = nn.ReLU()

    def forward(self, x):
        x = self.relu(self.fc1(x))
        state_value = self.fc2(x)
        return state_value

def select_action(policy, state):
    state = torch.FloatTensor(state).unsqueeze(0)
    probs = policy(state)
    action_dist = torch.distributions.Categorical(probs)
    action = action_dist.sample()
    log_prob = action_dist.log_prob(action)
    return action.item(), log_prob

def update_policy(policy, value_net, policy_optimizer, value_optimizer, rewards, log_probs, states, gamma=0.99):
    # 计算折扣累积回报
    R = 0
    returns = []
    for r in reversed(rewards):
        R = r + gamma * R
        returns.insert(0, R)
    returns = torch.FloatTensor(returns)
    
    # 将状态转换为张量
    states = torch.FloatTensor(states)
    
    # 计算状态价值估计
    state_values = value_net(states).squeeze()
    
    # 计算优势函数 (回报 - 状态价值)
    advantages = returns - state_values.detach()
    
    # 更新价值网络
    value_loss = nn.MSELoss()(state_values, returns)
    value_optimizer.zero_grad()
    value_loss.backward()
    value_optimizer.step()
    
    # 计算策略梯度损失
    policy_loss = []
    for log_prob, advantage in zip(log_probs, advantages):
        policy_loss.append(-log_prob * advantage)  # 负号因为我们要最大化回报
    
    policy_optimizer.zero_grad()
    policy_loss = torch.cat(policy_loss).sum()
    policy_loss.backward()
    policy_optimizer.step()
    
    return policy_loss.item(), value_loss.item()

def train_reinforce_with_baseline(env, policy, value_net, policy_optimizer, value_optimizer, num_episodes=1000, gamma=0.99):
    total_rewards = []
    policy_losses = []
    value_losses = []
    
    for episode in range(num_episodes):
        state, _ = env.reset()
        done = False
        rewards = []
        log_probs = []
        states = []
        
        while not done:
            states.append(state)
            action, log_prob = select_action(policy, state)
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated
            rewards.append(reward)
            log_probs.append(log_prob)
            state = next_state
        
        episode_reward = sum(rewards)
        total_rewards.append(episode_reward)
        
        policy_loss, value_loss = update_policy(
            policy, value_net, policy_optimizer, value_optimizer, 
            rewards, log_probs, states, gamma
        )
        policy_losses.append(policy_loss)
        value_losses.append(value_loss)
        
        if (episode + 1) % 10 == 0:
            print(f"Episode {episode+1}/{num_episodes}, Reward: {episode_reward:.2f}, Policy Loss: {policy_loss:.4f}, Value Loss: {value_loss:.4f}")
    
    return total_rewards, policy_losses, value_losses

def plot_training_results(rewards, policy_losses, value_losses):
    plt.figure(figsize=(15, 5))
    
    plt.subplot(1, 3, 1)
    plt.plot(rewards)
    plt.title('Episode Rewards')
    plt.xlabel('Episode')
    plt.ylabel('Total Reward')
    
    plt.subplot(1, 3, 2)
    plt.plot(policy_losses)
    plt.title('Policy Loss')
    plt.xlabel('Episode')
    plt.ylabel('Loss')
    
    plt.subplot(1, 3, 3)
    plt.plot(value_losses)
    plt.title('Value Loss')
    plt.xlabel('Episode')
    plt.ylabel('Loss')
    
    plt.tight_layout()
    plt.show()

def evaluate_policy(env, policy, num_episodes=5, render=True):
    for episode in range(num_episodes):
        state, _ = env.reset()
        done = False
        total_reward = 0
        
        while not done:
            if render:
                env.render()
            action, _ = select_action(policy, state)
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated
            total_reward += reward
            state = next_state
        
        print(f"Evaluation Episode {episode+1}, Reward: {total_reward}")
    
    env.close()

def main():
    # 创建环境
    env = gym.make('CartPole-v1')
    state_dim = env.observation_space.shape[0]
    action_dim = env.action_space.n
    
    # 初始化策略网络和价值网络
    policy = PolicyNetwork(state_dim, action_dim)
    value_net = ValueNetwork(state_dim)
    
    # 定义优化器
    policy_optimizer = optim.Adam(policy.parameters(), lr=0.01)
    value_optimizer = optim.Adam(value_net.parameters(), lr=0.01)
    
    # 训练模型
    print("开始训练带基线的 REINFORCE 算法...")
    rewards, policy_losses, value_losses = train_reinforce_with_baseline(
        env, policy, value_net, policy_optimizer, value_optimizer, num_episodes=300
    )
    
    # 绘制训练结果
    plot_training_results(rewards, policy_losses, value_losses)
    
    # 评估训练好的策略
    print("\n评估训练好的策略...")
    eval_env = gym.make('CartPole-v1', render_mode='human')
    evaluate_policy(eval_env, policy)
    
    # 保存训练好的模型
    torch.save(policy.state_dict(), 'reinforce_baseline_cartpole_policy.pth')
    torch.save(value_net.state_dict(), 'reinforce_baseline_cartpole_value.pth')
    print("模型已保存")

if __name__ == "__main__":
    main()   

上述代码的实现流程如下所示。

(1)引入价值网络:创建了一个单独的神经网络ValueNetwork来近似状态价值函数 V (s),作为基线。

(2)计算优势函数:使用回报减去状态价值估计值作为优势函数 (Advantage),代替原始 REINFORCE 中的单纯回报值。

(3)双目标优化:同时优化策略网络和价值网络,价值网络通过最小化预测值与实际回报的均方误差进行训练。

(4)可视化增强:新增了价值网络损失的可视化,便于观察训练过程中两个网络的学习情况。

执行后会得到一组强化学习训练过程的可视化图,用于展示带基线(Baseline)的 REINFORCE 算法在训练时的关键指标变化,如图5-1所示。

图5-3  训练过程的可视化图

运行本实例将会观察到:

  1. 训练过程中回报增长更加稳定,收敛速度更快。
  2. 价值网络逐渐学会准确估计状态价值,为策略更新提供更可靠的基线。
  3. 最终策略能够更高效地控制 CartPole 平衡,获得更高的累积回报。

这种基于基线的 REINFORCE 算法是许多现代策略梯度方法 (如 Actor-Critic) 的基础,理解其原理和实现对于掌握强化学习至关重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农三叔

感谢鼓励

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

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

打赏作者

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

抵扣说明:

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

余额充值