五分钟入门强化学习DDPG

深度确定性策略梯度DDPG(deep deterministic policy gradient,DDPG)

代码实现:

python 复制代码
import torch
import torch.nn.functional as F
import numpy as np

class PolicyNet(torch.nn.Module):
    ''' 策略网络 (Actor):负责根据状态,直接输出一个确定的连续物理动作值 '''
    def __init__(self, state_dim, hidden_dim, action_dim, action_bound):
        super(PolicyNet, self).__init__()  # 继承并初始化 PyTorch 的 nn.Module
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)  # 第一层全连接,输入状态特征
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)  # 第二层全连接,直接输出动作
        # action_bound 是环境物理限制的最大动作值(例如方向盘最多只能打 30 度,这里就是 30)
        self.action_bound = action_bound  

    def forward(self, x):
        x = F.relu(self.fc1(x))  # 经过隐藏层,使用 ReLU 激活函数增加非线性
        # 极其关键:这里使用 tanh 激活函数!
        # tanh 会把神经网络的输出死死限制在 [-1, 1] 之间。
        # 然后乘以 action_bound (比如 30),最终输出的动作就被完美限制在了 [-30, 30] 物理范围内!
        return torch.tanh(self.fc2(x)) * self.action_bound


class QValueNet(torch.nn.Module):
    ''' 价值网络 (Critic):负责给 (状态, 动作) 对打分,评价这个动作在当前状态下有多好 '''
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(QValueNet, self).__init__()
        # 与 DQN 不同!这里的输入不仅有状态,还有动作!所以输入维度是 state_dim + action_dim
        self.fc1 = torch.nn.Linear(state_dim + action_dim, hidden_dim) 
        self.fc2 = torch.nn.Linear(hidden_dim, hidden_dim)  # 第二个隐藏层
        self.fc_out = torch.nn.Linear(hidden_dim, 1)  # 最终输出一个标量:Q值(即这个动作的预计得分)

    def forward(self, x, a):
        # 核心操作:在维度 1 (列维度) 上,将 状态向量 x 和 动作向量 a 强行拼接在一起
        # 相当于把 "当前处境" 和 "你的决定" 揉成一团,送给 Critic 审判
        cat = torch.cat([x, a], dim=1) 
        x = F.relu(self.fc1(cat))  # 经过第一层并激活
        x = F.relu(self.fc2(x))  # 经过第二层并激活
        return self.fc_out(x)  # 输出 Q 值(实数,不加激活函数)


class DDPG:
    ''' DDPG 算法核心大脑 '''
    def __init__(self, state_dim, hidden_dim, action_dim, action_bound, sigma, actor_lr, critic_lr, tau, gamma, device):
        # 1. 实例化主策略网络 (Actor)
        self.actor = PolicyNet(state_dim, hidden_dim, action_dim, action_bound).to(device)
        # 2. 实例化主价值网络 (Critic)
        self.critic = QValueNet(state_dim, hidden_dim, action_dim).to(device)
        # 3. 实例化目标策略网络 (Target Actor,充当提供稳定动作的"靶子")
        self.target_actor = PolicyNet(state_dim, hidden_dim, action_dim, action_bound).to(device)
        # 4. 实例化目标价值网络 (Target Critic,充当提供稳定分数的"靶子")
        self.target_critic = QValueNet(state_dim, hidden_dim, action_dim).to(device)
        
        # 初始化时,将主网络的参数"硬拷贝"给目标网络,保证起跑线一致
        self.target_critic.load_state_dict(self.critic.state_dict())
        self.target_actor.load_state_dict(self.actor.state_dict())
        
        # 定义主 Actor 的优化器
        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)
        # 定义主 Critic 的优化器
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)
        
        self.gamma = gamma  # 折扣因子
        self.sigma = sigma  # 探索噪声的标准差。因为 DDPG 输出的是确定性动作,如果不加噪声,它永远不会尝试新动作
        self.tau = tau  # 软更新 (Soft Update) 的平滑系数,通常极小(如 0.005)
        self.action_dim = action_dim  # 动作维度大小
        self.device = device  # 计算设备

    def take_action(self, state):
        ''' 根据当前状态做出动作决定 (带探索) '''
        state = torch.tensor([state], dtype=torch.float).to(self.device)  # 转换格式并升维
        # 让 Actor 网络直接算出一个动作值。由于不需要求导,直接 .item() 取出其中的 Python 浮点数
        action = self.actor(state).item()
        
        # 【DDPG 探索机制】:给原本确定的动作,加上一个服从正态分布(高斯分布)的随机噪声!
        # np.random.randn 生成标准正态分布随机数,乘以 sigma 控制噪音大小
        action = action + self.sigma * np.random.randn(self.action_dim)
        return action

    def soft_update(self, net, target_net):
        ''' 目标网络软更新魔法:每次只把主网络的参数向目标网络渗透一点点,防止目标值剧烈震荡 '''
        # 遍历目标网络和主网络的所有参数矩阵
        for param_target, param in zip(target_net.parameters(), net.parameters()):
            # 软更新公式:目标参数 = (1 - tau) * 目标旧参数 + tau * 主网络新参数
            # .copy_() 是 PyTorch 的就地修改操作,直接覆盖内存里的数值
            param_target.data.copy_(param_target.data * (1.0 - self.tau) + param.data * self.tau)

    def update(self, transition_dict):
        ''' 核心训练逻辑 '''
        # 从经验池提取数据,并转换为适合放入神经网络的 Tensor 格式,全扔到指定设备上
        states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions'], dtype=torch.float).view(-1, 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)

        # ---------------- 1. 更新 Critic (评论家) ----------------
        # 计算未来的目标 Q 值:让 Target Actor 预测下一步该做什么,然后让 Target Critic 给这个未来动作打分
        next_q_values = self.target_critic(next_states, self.target_actor(next_states))
        
        # 算出当前状态下的 目标绝对真理 (TD Target)。游戏结束时 (dones=1),未来期望强制归零
        q_targets = rewards + self.gamma * next_q_values * (1 - dones)
        
        # 让主 Critic 对当前状态和刚做过的动作打分:self.critic(states, actions)
        # Critic 的损失函数:主 Critic 的预测值 与 目标真理值 之间的均方误差 (MSE)
        critic_loss = torch.mean(F.mse_loss(self.critic(states, actions), q_targets))
        
        self.critic_optimizer.zero_grad()  # 清空 Critic 优化器梯度
        critic_loss.backward()  # 反向传播,算出 Critic 每个参数的梯度
        self.critic_optimizer.step()  # 修改 Critic 参数

        # ---------------- 2. 更新 Actor (演员) ----------------
        # 【DDPG 最精妙的一行代码】:如何指导 Actor 更新参数?
        # 让最新的 Actor 重新想一下在当前 states 下该做什么动作:self.actor(states)
        # 把这个刚想出来的动作,送给最新的 Critic 去打分:self.critic(states, self.actor(states))
        # Actor 的目标是让自己的动作拿到【最高的分数】。
        # 但 PyTorch 优化器默认是求 Loss 的【最小值】。所以在前面加个负号 -torch.mean()。
        # 这样,要想 Loss 越小,就逼着 Critic 打出的分数越来越大!这就是确定性策略梯度定理的核心实现。
        actor_loss = -torch.mean(self.critic(states, self.actor(states)))
        
        self.actor_optimizer.zero_grad()  # 清空 Actor 优化器梯度
        actor_loss.backward()  # 反向传播,算出 Actor 每个参数该怎么动,才能让 Critic 打出更高的分
        self.actor_optimizer.step()  # 修改 Actor 参数

        # ---------------- 3. 更新目标网络 (靶子) ----------------
        # 每次训练完,不仅更新主网络,还要让两个目标网络吸收一点主网络的聪明才智(软更新)
        self.soft_update(self.actor, self.target_actor)  # 软更新目标策略网络
        self.soft_update(self.critic, self.target_critic)  # 软更新目标价值网络
相关推荐
潜创微科技1 小时前
2026年办公KVM切换器方案服务商选型参考:技术能力与服务体验双维度评估
大数据
万岳科技系统开发1 小时前
互联网医院小程序搭建怎么做?从0开始建设完整平台
大数据·小程序
jeffer_liu1 小时前
Spring AI 生产级实战:记忆管理
java·人工智能·后端·spring·语言模型
土星云SaturnCloud1 小时前
基于边缘计算的商场智慧运营架构设计与AI落地实践
服务器·人工智能·ai·边缘计算
vivo互联网技术1 小时前
ICLR 2026 | LiveMoments 用参考图引导的扩散模型提升重选封面帧画质
人工智能·算法·aigc技术探索
Wonderful U1 小时前
Python+Django实战|个人博客内容管理系统:搭建轻量化、高自由度的个人动态博客CMS系统
人工智能·python·django
懂AI的老郑1 小时前
词元:AI理解语言的秘密钥匙
人工智能