PPO算法(附pytorch代码)

0、演变

(1) TRPO(Trust Region Policy Optimization)的原始方法

  • 目标:最大化策略性能,同时约束新旧策略的KL散度(信任区域):
    maximize  E [ π θ ( a ∣ s ) π θ old ( a ∣ s ) A t ] , s.t.  E [ KL ( π θ old ∥ π θ ) ] ≤ δ \text{maximize } \mathbb{E} \left[ \frac{\pi_\theta(a|s)}{\pi_{\theta_{\text{old}}}(a|s)} A_t \right], \quad \text{s.t. } \mathbb{E} \left[ \text{KL}(\pi_{\theta_{\text{old}}} \| \pi_\theta) \right] \leq \delta maximize E[πθold(as)πθ(as)At],s.t. E[KL(πθoldπθ)]δ
  • 实现方式:通过二阶优化(共轭梯度法+Fisher信息矩阵)求解约束问题,计算复杂且不稳定。

(2) TRPO的缺陷

  • 计算成本高:需要计算和逆Fisher矩阵,不适合大规模网络。
  • 实现复杂:约束条件需严格满足,调参困难(如KL散度阈值 (\delta) 敏感)。

PPO通过两种主要变体(Clipped Surrogate Objective 和 Adaptive KL Penalty)解决TRPO的问题:

  • 改进1:剪切替代目标(Clipped Surrogate Objective)

  • 改进2:自适应KL惩罚(Adaptive KL Penalty)

  • PPO的其他改进

    (1) 多步更新(Epoch-based Training)

    • TRPO:每批数据仅更新一次(严格满足约束)。
    • PPO:允许用同一批数据多次更新(如3~10次),提升样本效率。

    (2) 熵正则化

    • PPO在策略损失中增加熵项鼓励探索:
      L Total = L CLIP − c 1 L Value + c 2 H ( π θ ) L^{\text{Total}} = L^{\text{CLIP}} - c_1 L^{\text{Value}} + c_2 \mathcal{H}(\pi_\theta) LTotal=LCLIPc1LValue+c2H(πθ)
      TRPO通常不显式包含熵项。

一、PPO算法

(1)简介

  • PPO算法是一种强化学习中的策略梯度方法,它的全称是Proximal Policy Optimization,即近端策略优化1。PPO算法的目标是在与环境交互采样数据后,使用随机梯度上升优化一个“替代”目标函数,从而改进策略。PPO算法的特点是可以进行多次的小批量更新,而不是像标准的策略梯度方法那样每个数据样本只进行一次梯度更新12。
  • PPO算法有两种主要的变体:PPO-Penalty和PPO-Clip。PPO-Penalty类似于TRPO算法,它使用KL散度作为一个约束条件,但是将KL散度作为目标函数的一个惩罚项,而不是一个硬性约束,并且自动调整惩罚系数,使其适应数据的规模12。PPO-Clip则没有KL散度项,也没有约束条件,而是使用一种特殊的裁剪技术,在目标函数中消除了新策略远离旧策略的动机。
  • PPO同样使用了AC框架,不过相比DPG更加接近传统的PG算法,采用的是随机分布式的策略函数(Stochastic Policy),智能体(agent)每次决策时都要从策略函数输出的分布中采样,得到的样本作为最终执行的动作,因此天生具备探索环境的能力,不需要为了探索环境给决策加上扰动;PPO的重心会放到actor上,仅仅将critic当做一个预测状态好坏(在该状态获得的期望收益)的工具,策略的调整基准在于获取的收益,不是critic的导数。

(2)On-policy?

  • PPO 算法是一个 on-policy 的算法。123

  • PPO算法是一种基于策略的强化学习算法,它可以处理连续动作空间的问题。PPO算法是一种在线算法,也就是说它需要用当前的策略产生数据,并用这些数据更新策略。PPO算法不能直接使用经验回放,因为经验回放中的数据可能是由不同的策略产生的,这会导致策略梯度的偏差12。

    但是,PPO算法可以使用一种技术叫做重要性采样(importance sampling),来利用之前的数据进行多步更新23。重要性采样的思想是给每个数据加上一个权重,表示目标策略和行为策略的比例24。这样,PPO算法可以在一定程度上提高数据的利用效率,而不影响策略梯度的正确性23。

  • PPO 算法的原理是在每一步更新策略时,尽量减小代价函数,同时保证新策略和旧策略的差异不要太大。为了做到这一点,PPO 算法使用了一个特殊的目标函数,它包含了一个截断的比率因子,用来限制新策略和旧策略的比例。1
    在这里插入图片描述
    在这里插入图片描述
    参考链接点击

  • 重要性采样是一种调整数据权重的方法,它可以用于在线算法,也可以用于离线算法。PPO算法使用了重要性采样,但它并没有使用经验回放中的随机抽样和淘汰机制,而是按照时间顺序使用数据,并在一定次数后丢弃数据 。
    在这里插入图片描述

总结表格

情况 A > 0 A > 0 A>0(鼓励动作) A < 0 A < 0 A<0(减少动作)
r > 1 + ϵ r > 1+\epsilon r>1+ϵmin选裁剪后的 ( 1 + ϵ ) ⋅ A (1+\epsilon) \cdot A (1+ϵ)Amin选未裁剪的 r ⋅ A r \cdot A rA
r < 1 − ϵ r < 1-\epsilon r<1ϵmin选未裁剪的 r ⋅ A r \cdot A rAmin选裁剪后的 ( 1 − ϵ ) ⋅ A (1-\epsilon) \cdot A (1ϵ)A
1 − ϵ ≤ r ≤ 1 + ϵ 1-\epsilon \leq r \leq 1+\epsilon 1ϵr1+ϵ两者相等,随便选两者相等,随便选

1. 详解一下min和clip操作:
A>0很好理解,表示当前动作比平均水平好,告诉策略网络当前的策略学的还不错,可以更新,但不要更新太大,即只clip很大的ratio。

好好解释一下A<0时的情况:

  • r > 1 + ϵ r > 1+\epsilon r>1+ϵ,min选择了未裁剪的 r ⋅ A r \cdot A rA

    • PPO的目标是通过梯度上升优化这个损失函数(实际上是最大化 min ⁡ ( r ⋅ A , clip ( r ) ⋅ A ) \min(r \cdot A, \text{clip}(r) \cdot A) min(rA,clip(r)A))。当 A < 0 A < 0 A<0 时, r ⋅ A r \cdot A rA 是个负值,选了更负的未裁剪值(比如-100而不是-10),意味着:
    • 信号更强:告诉优化器“这个动作真的很烂,新策略还大幅增加它的概率,太离谱了,必须狠狠纠正!”更大的负值会让梯度更强力地推动新策略减少这个动作的概率。
    • 惩罚过度自信 r > 1 + ϵ r > 1+\epsilon r>1+ϵ 表示新策略过于自信地增加了烂动作的概率,选未裁剪的 r ⋅ A r \cdot A rA 是在“以牙还牙”——你增加得越多,惩罚越狠,确保下次不敢乱来。如果选裁剪后的 ( 1 + ϵ ) ⋅ A (1+\epsilon) \cdot A (1+ϵ)A,惩罚力度不够(负得少),优化器可能不会充分意识到问题的严重性,新策略下次还是可能偏向这个烂动作。
  • r < 1 + ϵ r < 1+\epsilon r<1+ϵ,min选择了裁剪的 r ⋅ A r \cdot A rA

    • A < 0 A < 0 A<0 时,损失是负的,选更负的裁剪值(比如-10而不是-2),即避免了信号太弱,意味着:
    • 避免过于乐观:如果选未裁剪的 r ⋅ A r \cdot A rA(负得少,比如-2),因为 r r r 很小,惩罚信号会变得太弱。优化器可能误以为“这个动作没那么糟”,下次更新时不够坚定地减少它的概率。
    • 保持一致性:裁剪后的 ( 1 − ϵ ) ⋅ A (1-\epsilon) \cdot A (1ϵ)A 提供了一个稳定的惩罚基准(基于 1 − ϵ 1-\epsilon 1ϵ),不会因为 r r r 变得极小而让信号完全消失。这保证了算法对烂动作的态度始终是“必须减少”,而不是“减少一点就够了”。

2. 另外需要注意的点:

  • PPO 算法只使用当前策略产生的经验来更新网络,而不使用历史策略产生的经验。这意味着 PPO 算法需要在每次更新策略后丢弃之前收集的经验,因为它们不再适用于新的策略。这样做的好处是 PPO 算法可以保证策略和值函数之间的一致性,也就是说,值函数可以很好地估计当前策略的性能。2

  • 虽然 PPO 算法可以使用经验回放缓冲区来存储和重用历史经验,但这并不改变它是一个 on-policy 的算法。因为 PPO 算法在使用缓冲区中的数据时,仍然需要计算新策略和旧策略的比例,并且使用截断的比率因子来限制更新幅度。这样做相当于对 off-policy 的数据进行了校正,使得它们更接近 on-policy 的数据。4

  • 如果 PPO 算法使用经验回放,那么它需要对 off-policy 的数据进行一些校正,以减小偏差和方差。一种常用的校正方法是使用重要性采样权重 (ISW),它可以衡量数据的分布和当前策略的分布之间的差异,并对损失函数或者优势函数进行加权。12

  • 另一种校正方法是使用优先级经验回放 (PER),它可以根据数据的价值或者优势来给数据分配优先级,并按照优先级来采样数据。这样可以使得更有价值或者更有优势的数据被更频繁地采样,从而提高学习效率。2

(3)GAE (Generalized Advantage Estimation)

广义优势估计(Generalized Advantage Estimation,简称GAE)是强化学习中用于估算策略梯度的一种方法,特别适用于异策性策略优化算法,如PPO(Proximal Policy Optimization)。它通过结合多步回报来改进优势函数的估计,从而在减少方差的同时尽量保持偏差不变。

GAE的核心思想在于平衡偏差和方差之间的关系。通常情况下,单步的优势估计具有较高的方差但较低的偏差,而使用蒙特卡洛方法计算的多步回报虽然偏差较大但是方差较小。GAE通过引入一个调节参数 γ \gamma γ(折扣因子)和 λ \lambda λ来权衡这两者,得到一个更稳定的估计值。

GAE的定义为:
A t = ∑ l = 0 ∞ ( γ λ ) l δ t + l A_t = \sum_{l=0}^{\infty} (\gamma \lambda)^l \delta_{t+l} At=l=0(γλ)lδt+l
其中,

  • δ t = r t + γ V ( s t + 1 ) − V ( s t ) \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t) δt=rt+γV(st+1)V(st) 称为TD(Temporal Difference)误差,
  • r t r_t rt 是时间步 t t t时的奖励,
  • γ \gamma γ 是折扣因子,决定了未来奖励的重要性,
  • V ( s ) V(s) V(s) 是状态价值函数,代表从状态 s s s开始按照当前策略行动能获得的期望回报,
  • λ \lambda λ 是GAE的一个超参数,用于控制估计中的偏差和方差之间的权衡。当 λ = 0 \lambda = 0 λ=0时,GAE退化为一步TD误差;当 λ = 1 \lambda = 1 λ=1时,GAE相当于累积了所有的TD误差,接近于蒙特卡洛方法。

该公式可改写为递推形式:
A t = δ t + γ λ A t + 1 A_t = \delta_t + \gamma \lambda A_{t+1} At=δt+γλAt+1
即当前时刻的优势函数由当前TD误差和下一时刻的优势函数加权组成。
代码实现步骤

  1. 逆序遍历TD误差
    代码从最后一个时间步开始向前处理(td_delta[::-1]),因为计算 A t A_t At 需要依赖后续的 A t + 1 A_{t+1} At+1

  2. 递归计算优势函数
    初始化 advantage 为0。对于每个逆序的TD误差 delta
    advantage = δ + γ λ ⋅ advantage \text{advantage} = \delta + \gamma \lambda \cdot \text{advantage} advantage=δ+γλadvantage
    这恰好对应递推公式 A t = δ t + γ λ A t + 1 A_t = \delta_t + \gamma \lambda A_{t+1} At=δt+γλAt+1

  3. 反转结果列表
    逆序计算完成后,反转列表使优势函数与原始时间步对齐。
    具体实现:

# 定义折扣因子和平滑因子
gamma = 0.99
lambda = 0.95

# 初始化优势函数估计和回报估计为空列表
advantages = []
returns = []

# 初始化时间差分误差为0
delta = 0

# 从最后一个时间步开始反向遍历经验
for state, action, reward, next_state, done in reversed(experiences):

    # 如果是终止状态,那么下一个状态的值为0,否则用值函数网络预测
    if done:
        next_value = 0
    else:
        next_value = value_network.predict(next_state)

    # 计算当前状态的值
    value = value_network.predict(state)

    # 计算时间差分误差
    delta = reward + gamma * next_value - value

    # 计算优势函数估计,使用时间差分误差和上一个时间步的优势函数估计
    advantage = delta + gamma * lambda * advantage

    # 计算回报估计,使用奖励和下一个状态的值
    return = reward + gamma * next_value

    # 将优势函数估计和回报估计插入到列表的开头
    advantages.insert(0, advantage)
    returns.insert(0, return)

# 将优势函数估计和回报估计转换为张量
advantages = torch.tensor(advantages)
returns = torch.tensor(returns)

注意一下几点:

  1. 为什么要从最后一个时间步开始反向遍历
    答:为了利用之前计算的优势函数估计和回报估计来加速计算。如果从第一个时间步开始正向遍历,那么每个时间步都需要计算一个无限级数的和,这样会很慢。而如果从最后一个时间步开始反向遍历,那么每个时间步只需要用一个时间差分误差和上一个时间步的优势函数估计来计算当前的优势函数估计,这样会很快。同理,回报估计也可以用奖励和下一个状态的值来递推计算,而不需要用一个无限级数的和。
  2. 代码中returns有什么作用?
    答:returns 是用来存储每个时间步的回报估计的,回报估计是指从当前状态开始,按照当前策略采取动作所能获得的未来折扣奖励之和的期望。回报估计可以用来更新值函数网络,使其更接近真实的状态值。回报估计也可以用来计算优势函数,如果没有值函数网络的话。

二、损失函数

在这里插入图片描述

在这里插入图片描述

三、代码

'''
@Author  :Yan JP
@Created on Date:2023/4/19 17:31 
'''
# https://blog.csdn.net/dgvv4/article/details/129496576?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-3-129496576-blog-117329002.235%5Ev29%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-3-129496576-blog-117329002.235%5Ev29%5Epc_relevant_default_base3&utm_relevant_index=6
# 代码用于离散环境的模型
import numpy as np
import torch
from torch import nn
from torch.nn import functional as F


# ----------------------------------- #
# 构建策略网络--actor
# ----------------------------------- #

class PolicyNet(nn.Module):
    def __init__(self, n_states, n_hiddens, n_actions):
        super(PolicyNet, self).__init__()
        self.fc1 = nn.Linear(n_states, n_hiddens)
        self.fc2 = nn.Linear(n_hiddens, n_actions)

    def forward(self, x):
        x = self.fc1(x)  # [b,n_states]-->[b,n_hiddens]
        x = F.relu(x)
        x = self.fc2(x)  # [b, n_actions]
        x = F.softmax(x, dim=1)  # [b, n_actions]  计算每个动作的概率
        return x


# ----------------------------------- #
# 构建价值网络--critic
# ----------------------------------- #

class ValueNet(nn.Module):
    def __init__(self, n_states, n_hiddens):
        super(ValueNet, self).__init__()
        self.fc1 = nn.Linear(n_states, n_hiddens)
        self.fc2 = nn.Linear(n_hiddens, 1)

    def forward(self, x):
        x = self.fc1(x)  # [b,n_states]-->[b,n_hiddens]
        x = F.relu(x)
        x = self.fc2(x)  # [b,n_hiddens]-->[b,1]  评价当前的状态价值state_value
        return x


# ----------------------------------- #
# 构建模型
# ----------------------------------- #

class PPO:
    def __init__(self, n_states, n_hiddens, n_actions,
                 actor_lr, critic_lr, lmbda, epochs, eps, gamma, device):
        # 实例化策略网络
        self.actor = PolicyNet(n_states, n_hiddens, n_actions).to(device)
        # 实例化价值网络
        self.critic = ValueNet(n_states, n_hiddens).to(device)
        # 策略网络的优化器
        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)
        # 价值网络的优化器
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)

        self.gamma = gamma  # 折扣因子
        self.lmbda = lmbda  # GAE优势函数的缩放系数
        self.epochs = epochs  # 一条序列的数据用来训练轮数
        self.eps = eps  # PPO中截断范围的参数
        self.device = device

    # 动作选择
    def take_action(self, state):
        # 维度变换 [n_state]-->tensor[1,n_states]
        state = torch.tensor(state[np.newaxis, :]).to(self.device)
        # 当前状态下,每个动作的概率分布 [1,n_states]
        probs = self.actor(state)
        # 创建以probs为标准的概率分布
        action_list = torch.distributions.Categorical(probs)
        # 依据其概率随机挑选一个动作
        action = action_list.sample().item()
        return action

    # 训练
    def learn(self, transition_dict):
        # 提取数据集
        states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).to(self.device).view(-1, 1)
        rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).to(self.device).view(-1, 1)
        next_states = torch.tensor(transition_dict['next_states'], dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'], dtype=torch.float).to(self.device).view(-1, 1)

        # 目标,下一个状态的state_value  [b,1]
        next_q_target = self.critic(next_states)
        # 目标,当前状态的state_value  [b,1]
        td_target = rewards + self.gamma * next_q_target * (1 - dones)
        # 预测,当前状态的state_value  [b,1]
        td_value = self.critic(states)
        # 目标值和预测值state_value之差  [b,1]
        td_delta = td_target - td_value

        # 时序差分值 tensor-->numpy  [b,1]
        td_delta = td_delta.cpu().detach().numpy()
        advantage = 0  # 优势函数初始化
        advantage_list = []

        # 计算优势函数
        for delta in td_delta[::-1]:  # 逆序时序差分值 axis=1轴上倒着取 [], [], []
            # 优势函数GAE的公式 :计算优势函数估计,使用时间差分误差和上一个时间步的优势函数估计
            advantage = self.gamma * self.lmbda * advantage + delta
            advantage_list.append(advantage)
        # 正序
        advantage_list.reverse()
        # numpy --> tensor [b,1]
        advantage = torch.tensor(advantage_list, dtype=torch.float).to(self.device)

        # 策略网络给出每个动作的概率,根据action得到当前时刻下该动作的概率
        old_log_probs = torch.log(self.actor(states).gather(1, actions)).detach()

        # 一组数据训练 epochs 轮
        for _ in range(self.epochs):
            # 每一轮更新一次策略网络预测的状态
            log_probs = torch.log(self.actor(states).gather(1, actions))
            # 新旧策略之间的比例
            ratio = torch.exp(log_probs - old_log_probs)
            # 近端策略优化裁剪目标函数公式的左侧项
            surr1 = ratio * advantage
            # 公式的右侧项,ratio小于1-eps就输出1-eps,大于1+eps就输出1+eps
            surr2 = torch.clamp(ratio, 1 - self.eps, 1 + self.eps) * advantage

            # 策略网络的损失函数
            actor_loss = torch.mean(-torch.min(surr1, surr2))
            # 价值网络的损失函数,当前时刻的state_value - 下一时刻的state_value
            critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))

            # 梯度清0
            self.actor_optimizer.zero_grad()
            self.critic_optimizer.zero_grad()
            # 反向传播
            actor_loss.backward()
            critic_loss.backward()
            # 梯度更新
            self.actor_optimizer.step()
            self.critic_optimizer.step()

import matplotlib.pyplot as plt
import gym
import torch
if __name__ == '__main__':


    device = torch.device('cuda') if torch.cuda.is_available() \
        else torch.device('cpu')

    # ----------------------------------------- #
    # 参数设置
    # ----------------------------------------- #

    num_episodes = 300  # 总迭代次数
    gamma = 0.9  # 折扣因子
    actor_lr = 1e-3  # 策略网络的学习率
    critic_lr = 1e-2  # 价值网络的学习率
    n_hiddens = 16  # 隐含层神经元个数
    env_name = 'CartPole-v0'
    return_list = []  # 保存每个回合的return

    # ----------------------------------------- #
    # 环境加载
    # ----------------------------------------- #

    env = gym.make(env_name)
    n_states = env.observation_space.shape[0]  # 状态数 4
    n_actions = env.action_space.n  # 动作数 2

    # ----------------------------------------- #
    # 模型构建
    # ----------------------------------------- #

    agent = PPO(n_states=n_states,  # 状态数
                n_hiddens=n_hiddens,  # 隐含层数
                n_actions=n_actions,  # 动作数
                actor_lr=actor_lr,  # 策略网络学习率
                critic_lr=critic_lr,  # 价值网络学习率
                lmbda=0.95,  # 优势函数的缩放因子
                epochs=10,  # 一组序列训练的轮次
                eps=0.2,  # PPO中截断范围的参数
                gamma=gamma,  # 折扣因子
                device=device
                )

    # ----------------------------------------- #
    # 训练--回合更新 on_policy
    # ----------------------------------------- #

    for i in range(num_episodes):

        state = env.reset()  # 环境重置
        done = False  # 任务完成的标记
        episode_return = 0  # 累计每回合的reward

        # 构造数据集,保存每个回合的状态数据
        transition_dict = {
            'states': [],
            'actions': [],
            'next_states': [],
            'rewards': [],
            'dones': [],
        }

        while not done:
            action = agent.take_action(state)  # 动作选择
            next_state, reward, done, _ = env.step(action)  # 环境更新
            # 保存每个时刻的状态\动作\...
            transition_dict['states'].append(state)
            transition_dict['actions'].append(action)
            transition_dict['next_states'].append(next_state)
            transition_dict['rewards'].append(reward)
            transition_dict['dones'].append(done)
            # 更新状态
            state = next_state
            # 累计回合奖励
            episode_return += reward

        # 保存每个回合的return
        return_list.append(episode_return)
        # 模型训练
        agent.learn(transition_dict)

        # 打印回合信息
        print(f'iter:{i}, return:{np.mean(return_list[-10:])}')

    # -------------------------------------- #
    # 绘图
    # -------------------------------------- #

    plt.plot(return_list)
    plt.title('return')
    plt.show()

代码解析:

  1. log_probs = torch.log(self.actor(states).gather(1, actions))
    • self.actor(states) 是一个策略网络,它接受一批状态作为输入,输出每个状态下每个动作的概率分布,假设有 N 个状态,M 个动作,那么输出的形状是 (N, M)。
    • .gather(1, actions) 是一个张量操作,它根据 actions 中的索引从第一个维度上选取元素,actions 是一个形状为 (N, 1) 的张量,表示每个状态下实际采取的动作的索引,那么输出的形状也是 (N, 1),表示每个状态下实际采取的动作的概率。
    • torch.log() 是一个张量操作,它对输入的每个元素取对数,输出的形状和输入相同,表示每个状态下实际采取的动作的对数概率。
      因此,这行代码最终得到一个形状为 (N, 1) 的张量,表示每个时间步的动作的对数概率。
  2. critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
    注意这里需要detach一下!!!!!!!!!
  3. PPO有价值网络critic,可以用目标网络吗?
    • PPO 有一个价值网络,用来估计状态值,并用于计算优势函数或者回报估计。理论上,PPO 也可以使用目标网络来生成目标状态值,从而稳定价值网络的训练。但是,PPO 是一个 on-policy 的算法,它只使用当前策略产生的经验来更新网络,而不使用历史策略产生的经验。这意味着 PPO 需要在每次更新策略后丢弃之前收集的经验,因为它们不再适用于新的策略。这样做的好处是 PPO 可以保证策略和值函数之间的一致性,也就是说,值函数可以很好地估计当前策略的性能。

    • 如果 PPO 使用目标网络,那么目标网络的参数会滞后于价值网络的参数,这可能导致目标状态值和价值网络输出之间的不一致性,也就是说,目标状态值可能不能很好地估计当前策略的性能。这样做的代价是 PPO 可能降低学习效率,因为它不能及时反映环境和策略的变化。

    • 因此,PPO 通常不使用目标网络,而是直接使用价值网络来生成目标状态值。这样做的好处是 PPO 可以提高学习效率,因为它可以及时反映环境和策略的变化。

不同损失函数的写法(联合损失优化):

损失项优化目标符号原因
策略损失(surr1/surr2)策略改进,最大化替代目标-优化器最小化损失,因此需要对最大化目标取负号。
价值函数损失(MSE)价值函数拟合,最小化预测误差+优化器直接最小化MSE,无需调整符号。
熵正则项(entropy)探索性,最大化策略熵-优化器最小化损失,因此需要对最大化目标取负号。

 # Optimize policy for K epochs:
 for _ in range(self.K_epochs):
     # Evaluating old actions and values :
     logprobs, state_values, dist_entropy = self.policy.evaluate(old_states, old_actions)

     # Finding the ratio (pi_theta / pi_theta__old):
     ratios = torch.exp(logprobs - old_logprobs.detach())

     # Finding Surrogate Loss:
     advantages = rewards - state_values.detach()
     surr1 = ratios * advantages
     surr2 = torch.clamp(ratios, 1-self.eps_clip, 1+self.eps_clip) * advantages
     loss = -torch.min(surr1, surr2) + 0.5*self.MseLoss(state_values, rewards) - 0.01*dist_entropy # MSE的导数会产生因子2,与0.5相乘后抵消

     # take gradient step
     self.optimizer.zero_grad()
     loss.mean().backward()
     self.optimizer.step()

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一。它主要用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。强化学习的特点在于没有监督数据,只有奖励信号。 强化学习的常见模型是标准的马尔可夫决策过程(Markov Decision Process, MDP)。按给定条件,强化学习可分为基于模式的强化学习(model-based RL)和无模式强化学习(model-free RL),以及主动强化学习(active RL)和被动强化学习(passive RL)。强化学习的变体包括逆向强化学习、阶层强化学习和部分可观测系统的强化学习。求解强化学习问题所使用的算法可分为策略搜索算法和值函数(value function)算法两类。 强化学习理论受到行为主义心理学启发,侧重在线学习并试图在探索-利用(exploration-exploitation)间保持平衡。不同于监督学习和非监督学习,强化学习不要求预先给定任何数据,而是通过接收环境对动作的奖励(反馈)获得学习信息并更新模型参数。强化学习问题在信息论、博弈论、自动控制等领域有得到讨论,被用于解释有限理性条件下的平衡态、设计推荐系统和机器人交互系统。一些复杂的强化学习算法在一定程度上具备解决复杂问题的通用智能,可以在围棋和电子游戏中达到人类水平。 强化学习在工程领域的应用也相当广泛。例如,Facebook提出了开源强化学习平台Horizon,该平台利用强化学习来优化大规模生产系统。在医疗保健领域,RL系统能够为患者提供治疗策略,该系统能够利用以往的经验找到最优的策略,而无需生物系统的数学模型等先验信息,这使得基于RL的系统具有更广泛的适用性。 总的来说,强化学习是一种通过智能体与环境交互,以最大化累积奖励为目标的学习过程。它在许多领域都展现出了强大的应用潜力。
下面是一个使用PyTorch实现的PPO算法代码示例: ```python import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F class ActorCritic(nn.Module): def __init__(self, state_dim, action_dim): super(ActorCritic, self).__init__() self.fc1 = nn.Linear(state_dim, 128) self.fc2 = nn.Linear(128, 64) self.actor = nn.Linear(64, action_dim) self.critic = nn.Linear(64, 1) def forward(self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) action_probs = F.softmax(self.actor(x), dim=-1) state_value = self.critic(x) return action_probs, state_value class PPO: def __init__(self, state_dim, action_dim, lr_actor=0.0003, lr_critic=0.001, gamma=0.99, clip_ratio=0.2): self.actor_critic = ActorCritic(state_dim, action_dim) self.optimizer_actor = optim.Adam(self.actor_critic.actor.parameters(), lr=lr_actor) self.optimizer_critic = optim.Adam(self.actor_critic.critic.parameters(), lr=lr_critic) self.gamma = gamma self.clip_ratio = clip_ratio def compute_returns(self, rewards, masks, values): returns = torch.zeros_like(rewards) discounted_return = 0 for i in reversed(range(len(rewards))): discounted_return = rewards[i] + self.gamma * discounted_return * masks[i] returns[i] = discounted_return returns = (returns - returns.mean()) / (returns.std() + 1e-8) return returns def compute_advantage(self, rewards, masks, values): returns = self.compute_returns(rewards, masks, values).detach() advantages = returns - values return advantages def update(self, states, actions, old_log_probs, rewards, masks): action_probs, values = self.actor_critic(states) returns = self.compute_returns(rewards, masks, values) advantages = self.compute_advantage(rewards, masks, values) ratio = torch.exp(action_probs.log_prob(actions) - old_log_probs) surrogate1 = ratio * advantages surrogate2 = torch.clamp(ratio, 1 - self.clip_ratio, 1 + self.clip_ratio) * advantages actor_loss = -torch.min(surrogate1, surrogate2).mean() critic_loss = F.mse_loss(returns, values) self.optimizer_actor.zero_grad() actor_loss.backward() self.optimizer_actor.step() self.optimizer_critic.zero_grad() critic_loss.backward() self.optimizer_critic.step() ``` 这里的代码实现了一个简单的ActorCritic模型作为PPO算法的基础。在`PPO`类中,`update`方法用于更新模型参数,`compute_returns`方法计算回报值,`compute_advantage`方法计算优势值。PPO算法的核心在于使用两个surrogate loss来进行策略优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值