游乐游手机版
首页/AI热点日报/热点详情

一篇文章彻底掌握PPO强化学习算法

类型:热点整理2026-06-26
1 引言在强化学习的众多流派中,策略梯度方法一度是直接优化策略的主力军。但传统的REINFORCE算法有一个硬伤——方差高、收敛慢,就像一个新手司机,方向盘打得太猛,容易跑偏。后来Schulman等人提出了近端策略优化算法(PPO),核心思路是在更新策略时加一个“安全带”:既保证更新幅度别太大,又

1. 引言

在强化学习的众多流派中,策略梯度方法一度是直接优化策略的主力军。但传统的REINFORCE算法有一个硬伤——方差高、收敛慢,就像一个新手司机,方向盘打得太猛,容易跑偏。后来Schulman等人提出了近端策略优化算法(PPO),核心思路是在更新策略时加一个“安全带”:既保证更新幅度别太大,又让计算过程足够简洁。结果大家也看到了,这个算法几乎成了现代强化学习任务的标准配置。

2. 算法原理

PPO的核心思想其实很直观:别让新旧策略之间差距太大,否则很容易学崩。具体来说,它通过一个精心设计的目标函数来驱动更新,公式长这样:

其中:

  • 表示新旧策略的概率比,就是新策略相对旧策略的“变化幅度”。
  • 是优势函数(Advantage Function)的估计,它告诉我们当前动作比平均水平好多少。
  • 是一个超参数,用来控制截断的窗口大小——可以理解为允许策略变化的“安全阈值”。

2.1 优势函数估计

优势函数 通常用广义优势估计(GAE)来计算,公式如下:

这里的TD残差 定义为:

是折扣因子, 是平衡偏差和方差的参数。简单说,GAE相当于在单步TD和蒙特卡洛估计之间做插值, 越小越偏向单步,越大越像完整回报。

2.2 策略更新

PPO的策略更新就是最大化 。由于引入了 操作,当 超出 范围时,损失函数对 不再敏感——这就像一个限速器,确保每次更新步子不会迈得太大。

2.3 价值网络更新

除了策略网络,PPO还维护一个价值网络来估计状态值函数 ,损失函数如下:

其中 是对真实价值的估计,常用TD目标:

2.4 总损失函数

把策略损失、价值损失以及一个熵正则项(鼓励探索)揉在一起,就得到了PPO的总损失:

其中:

  • 和 是权重系数,用来调节各部分在总损失中的比重。
  • 是策略的熵,熵越大说明策略越随机,有助于在训练初期多探索。

3. 案例分析

理论讲完了,来点实际的。我们在经典的CartPole-v1环境上跑了一个PPO实验。这个任务很简单:控制小车左右移动,让上面的杆子保持直立不倒。

3.1 代码实现

下面给出PPO算法在CartPole-v1上的核心代码(完整实现参考后续说明):

class PPO:
    '''PPO算法'''
    def __init__(self, state_dim, hidden_dim, action_dim, actor_lr, critic_lr, gamma,
                 lmbda, epsilon, epochs, device):
        self.action_dim = action_dim
        self.actor_critic = ActorCritic(state_dim, hidden_dim, action_dim).to(device)
        self.actor_optimizer = optim.Adam(self.actor_critic.actor_parameters(), lr=actor_lr)
        self.critic_optimizer = optim.Adam(self.actor_critic.critic_parameters(), lr=critic_lr)
        self.gamma = gamma  # 折扣因子
        self.lmbda = lmbda  # GAE参数
        self.epsilon = epsilon  # PPO截断范围
        self.epochs = epochs  # PPO的更新次数
        self.device = device
    
    def take_action(self, state):
        '''根据策略网络选择动作'''
        state = torch.tensor([state], dtype=torch.float).to(self.device)
        with torch.no_grad():
            action_probs, _ = self.actor_critic(state)
        dist = torch.distributions.Categorical(action_probs)
        action = dist.sample()
        return action.item()
    
    def update(self, transition_dict):
        '''更新策略网络和价值网络'''
        states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).view(-1).to(self.device)
        rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).view(-1, 1).to(self.device)
        next_states = torch.tensor(transition_dict['next_states'], dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'], dtype=torch.float).view(-1, 1).to(self.device)
        
        # 计算TD误差和优势函数
        _, state_values = self.actor_critic(states)
        _, next_state_values = self.actor_critic(next_states)
        td_target = rewards + self.gamma * next_state_values * (1 - dones)
        delta = td_target - state_values
        delta = delta.detach().cpu().numpy()
        
        # Generalized Advantage Estimation (GAE)
        advantage_list = []
        advantage = 0.0
        for delta_t in delta[::-1]:
            advantage = self.gamma * self.lmbda * advantage + delta_t[0]
            advantage_list.append([advantage])
        advantage_list.reverse()
        advantages = torch.tensor(advantage_list, dtype=torch.float).to(self.device)
        
        # 计算旧策略的log概率
        with torch.no_grad():
            action_probs_old, _ = self.actor_critic(states)
            dist_old = torch.distributions.Categorical(action_probs_old)
            log_probs_old = dist_old.log_prob(actions)
        
        # 更新策略网络和价值网络
        for _ in range(self.epochs):
            action_probs, state_values = self.actor_critic(states)
            dist = torch.distributions.Categorical(action_probs)
            log_probs = dist.log_prob(actions)
            ratio = torch.exp(log_probs - log_probs_old)
            surr1 = ratio * advantages.squeeze()
            surr2 = torch.clamp(ratio, 1 - self.epsilon, 1 + self.epsilon) * advantages.squeeze()
            actor_loss = -torch.mean(torch.min(surr1, surr2))
            critic_loss = F.mse_loss(state_values, td_target.detach())
            
            # 更新策略网络
            self.actor_optimizer.zero_grad()
            actor_loss.backward()
            self.actor_optimizer.step()
            
            # 更新价值网络
            self.critic_optimizer.zero_grad()
            critic_loss.backward()
            self.critic_optimizer.step()

3.2 结果分析

Iteration 1: 100%|██████████| 30/30 [00:00<00:00, 66.19it/s, Episode=30/300, A verage Return=10.00]
Iteration 2: 100%|██████████| 30/30 [00:00<00:00, 36.67it/s, Episode=60/300, A verage Return=162.90]
Iteration 3: 100%|██████████| 30/30 [00:01<00:00, 24.94it/s, Episode=90/300, A verage Return=278.70]
Iteration 4: 100%|██████████| 30/30 [00:01<00:00, 19.59it/s, Episode=120/300, A verage Return=287.80]
Iteration 5: 100%|██████████| 30/30 [00:01<00:00, 17.57it/s, Episode=150/300, A verage Return=240.70]
Iteration 6: 100%|██████████| 30/30 [00:01<00:00, 21.10it/s, Episode=180/300, A verage Return=354.60]
Iteration 7: 100%|██████████| 30/30 [00:02<00:00, 12.90it/s, Episode=210/300, A verage Return=450.50]
Iteration 8: 100%|██████████| 30/30 [00:02<00:00, 11.59it/s, Episode=240/300, A verage Return=500.00]
Iteration 9: 100%|██████████| 30/30 [00:02<00:00, 11.52it/s, Episode=270/300, A verage Return=475.50]
Iteration 10: 100%|██████████| 30/30 [00:02<00:00, 11.31it/s, Episode=300/300, A verage Return=500.00]

从训练输出可以看到,智能体的平均回报从一开始的10,到第8个迭代就已经达到了500(环境满分),之后稳定在满分水平。说明PPO在大约200个回合左右就学会了“怎么让杆子不倒下”。

学习曲线也印证了这一点:经过约200个回合,智能体表现达到了环境最高分。这验证了PPO在处理连续状态和离散动作问题上确实有一手——既高效又稳定。

4. 总结

PPO通过一个简洁的概率比截断机制,配合GAE优势估计,实现了既安全又快速的策略更新。在CartPole这样的经典测试环境中,它快速收敛到最优策略,表现稳定。正因为它简单又有效,PPO已经成为当前强化学习领域最受欢迎的算法之一,从机器人控制到游戏AI,到处都有它的身影。


来源:https://www.53ai.com/news/finetuning/2024101780267.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。