五分钟入门强化学习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)  # 软更新目标价值网络
相关推荐
不会就选b2 小时前
算法日常・每日刷题--<贪心>14
算法
Evand J2 小时前
【MATLAB例程|图像滤波降噪13】局部噪声方差自适应滤波(LNR)图像降噪与效果展示。附完整例程的下载链接
图像处理·计算机视觉·matlab·滤波·代码·降噪
小和尚同志2 小时前
小黑插图 Skill:从 11.7k star 的 Codex 专属,到 Claude Code 能用的平替
人工智能·aigc
高洁012 小时前
孪生不止在工厂:能源、医疗与农业
人工智能·深度学习·transformer·知识图谱·tornado
外域速览3 小时前
智谱50亿美元押注AI自训练
大数据·人工智能
人工智能培训3 小时前
孪生不止在工厂:能源、医疗与农业
大数据·人工智能
米小虾3 小时前
让模型说"我不知道",比让它答对更难:放弃文本生成能换来什么
人工智能
新新学长搞科研3 小时前
【SPIE出版】2026年人工智能、新材料与新能源国际学术会议(AINMNE 2026)
人工智能·新能源·新材料
野生技术架构师3 小时前
2026 Java 面试全套总结,八股 + 场景 + AI 相关面试考点
java·人工智能·面试
QYRdata3 小时前
隐私合规技术迎来拐点:数据主体请求自动化年复合增长率14.0%(2026-2032)
大数据·服务发现