策略梯度算法

只要神经网络输出的是每个动作的执行概率,通过梯度上升/下降进行参数更新,那就是策略梯度算法

策略梯度定理(Policy Gradient Theorem) ,也通常被称为 REINFORCE 算法的核心公式。

公式拆解:数学符号与代码的映射

  1. (求期望):

    • 数学含义: 按照当前策略 \pi_\theta 去环境中玩游戏,采样出很多条轨迹 \tau,然后求平均。

    • 代码实现: 在代码中,这体现为我们让智能体在环境中跑完一整局游戏,收集到一个批次(Batch)的数据,最后在算 Loss 时调用 torch.mean() 取平均。

  2. G_t (累积回报):

    • 数学含义: 从时间步 t 开始,直到游戏结束,未来所有奖励的总和(带衰减)。

    • 代码实现: 用一个从后往前的 for 循环,倒序累加真实的 reward。

  3. \log \pi_\theta(a_t|s_t) (对数概率):

    • 数学含义: 在状态 s_t 下,网络决定采取动作 a_t 的概率,再取自然对数。

    • 代码实现: torch.log( policy_net(state) )。

  4. \nabla_\theta (求梯度):

    • 数学含义: 对参数 \theta 求偏导,指示参数更新的方向。

    • 代码实现: 代码里根本不需要你手写微积分,只需要一句极其简单的 loss.backward(),PyTorch 就会自动顺着计算图把这部分算出来。

二、 代码实现(REINFORCE 更新逻辑)

python 复制代码
import torch
import torch.nn.functional as F
​
# 假设这是一局游戏打完后,我们收集到的完整轨迹数据 (Trajectory)
# 长度为 T (例如 T=4)
reward_list = [1.0, 1.0, 1.0, -10.0]  # 每一步环境给的真实瞬时奖励 r_t
state_list = [...]                    # 每一步的环境状态 s_t
action_list = [0, 1, 0, 1]            # 每一步智能体实际做出的动作 a_t
gamma = 0.99                          # 折扣因子
​
def update_policy(policy_net, optimizer):
    # ---------------------------------------------------------
    # 1. 计算公式右侧的 G_t (累积折扣回报)
    # 技巧:由于计算 t 时刻的回报需要用到 t+1 时刻的回报,所以必须"倒序计算"
    # ---------------------------------------------------------
    G_t_list = []
    G = 0  # 游戏结束后的未来回报是 0
    for r in reversed(reward_list):
        G = r + gamma * G           # G_t = r_t + gamma * G_{t+1}
        G_t_list.insert(0, G)       # 把算好的 G 塞回列表头部,恢复正序排列
    
    # 将 G_t 转换为 PyTorch 张量,准备参与神经网络的计算
    G_t_tensor = torch.tensor(G_t_list, dtype=torch.float32)
​
    # ---------------------------------------------------------
    # 2. 计算公式中间的 \log \pi_\theta(a_t|s_t)
    # ---------------------------------------------------------
    states_tensor = torch.tensor(state_list, dtype=torch.float32)
    actions_tensor = torch.tensor(action_list).view(-1, 1) # 重塑为列向量方便提取
    
    # 网络前向传播,算出这批状态下,所有动作的概率分布
    # 假设输出:[[0.8, 0.2], [0.3, 0.7], ...]
    all_action_probs = policy_net(states_tensor)
    
    # 核心:用 gather 提取出"实际执行的那个动作"对应的概率
    action_probs = all_action_probs.gather(1, actions_tensor).squeeze()
    
    # 取对数,完美对应公式里的 \log \pi_\theta
    log_probs = torch.log(action_probs)
​
    # ---------------------------------------------------------
    # 3. 缝合公式,计算最终的 Loss,并触发 \nabla_\theta
    # ---------------------------------------------------------
    # 公式:\sum [ \log \pi_\theta * G_t ]
    # 注意两个细节:
    # 第一:PyTorch 的优化器默认是"梯度下降(求极小值)",但策略梯度是要"最大化期望收益 J(\theta)"!
    #       所以我们必须在公式前面强行加一个负号 (-),把最大化问题变成最小化问题。
    # 第二:公式里的期望 \mathbb{E} 在代码中体现为对整个 Batch 的数据求平均 torch.mean()
    loss = -torch.mean(log_probs * G_t_tensor)
​
    # 清空优化器上一步残留的旧梯度
    optimizer.zero_grad()
    
    # 魔法发生的地方:这一步等价于公式最外层和最里层的 \nabla_\theta!
    # PyTorch 会自动顺着 log_probs 追溯到 policy_net 里的每一个权重参数,算出更新方向
    loss.backward()
    
    # 执行参数更新:\theta \leftarrow \theta + \alpha \nabla_\theta J(\theta)
    optimizer.step()
相关推荐
小郎君6581 小时前
MPTT树复选框的半选状态:一棵百万节点树从递归到计数器,再到区间覆盖
算法
酌沧1 小时前
MemT让记忆成为模型内生能力
jvm·人工智能·算法
Shimeri2 小时前
越卷越像:我用遗传算法做了个"内卷"实验
算法·three.js·数据可视化·遗传算法·进化算法
Evand J2 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang2 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节
爱读书的小胖3 小时前
LeetCode 55 跳跃游戏 C 语言解题分析
c语言·数据结构·算法·leetcode·1024程序员节
CCblandy3 小时前
前缀缓存命中率不靠引擎靠排列:本机仿真 3 轮,易变字段放开头 98.0%→0.0%
人工智能·python·算法·自然语言处理·性能优化
上班摸鱼下班游戏3 小时前
Unity 仿生智能算法 从蚁群算法 开始
算法·unity·游戏引擎
pipixia200333 小时前
C++ 面试高频:namespace 的 6 个知识点,从 using 到匿名命名空间
c语言·学习·算法·嵌入式·命名空间·c++面试·匿名命名空间
Navigator_Z4 小时前
LeetCode //C - 1300. Sum of Mutated Array Closest to Target
c语言·算法·leetcode