跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

TD3 算法详解:双延迟深度确定性策略梯度

TD3 算法针对 DDPG 的 Q 值过估计问题,通过双 Critic 网络、延迟更新和目标策略平滑三项改进提升稳定性。本文解析其数学原理并提供 PyTorch 实现代码,适用于连续动作空间强化学习任务。

赛博朋克发布于 2026/3/29更新于 2026/10/2487 浏览
TD3 算法详解:双延迟深度确定性策略梯度

TD3 算法详解:双延迟深度确定性策略梯度

双延迟深度确定性策略梯度算法(Twin Delayed Deep Deterministic Policy Gradient, TD3)是强化学习中专为解决连续动作空间问题设计的一种算法。它由 Fujimoto 等人在 2018 年提出,旨在改进深度确定性策略梯度(DDPG)算法中存在的训练不稳定和 Q 值过估计问题。

一、TD3 的背景与动机

1. DDPG 的局限性

DDPG 结合了策略(Actor)和价值函数(Critic),在连续动作空间中表现优异,但在实际应用中暴露出以下关键问题:

  • Q 值过估计问题:Critic 网络在训练时容易高估 Q 值,导致 Actor 基于错误的价值评估进行优化,策略更新不稳定。
  • 策略噪声敏感:确定性策略直接输出动作,容易对极端动作过拟合,陷入局部最优。
  • 训练震荡:Actor 和 Critic 同时更新可能导致相互干扰,造成训练过程震荡甚至失败。

2. TD3 的理论基础

TD3 基于 Actor-Critic 架构和确定性策略梯度(DPG),并借鉴了双 Q 学习(Double Q-Learning)的思想。其核心在于通过三项创新机制来缓解上述问题。

二、TD3 的核心思想

TD3 在 DDPG 的基础上提出了三项关键改进:

1. 双 Critic 网络(Twin Critics)

动机:解决 Q 值系统性高估偏差。 方法:使用两个独立的 Critic 网络计算 Q 值,取两者的最小值作为目标 Q 值。 $$ y = r + \gamma \min \big( Q_{\theta_1'}(s', \pi_{\phi'}(s')), Q_{\theta_2'}(s', \pi_{\phi'}(s')) \big) $$ 效果:有效减少了过估计偏差,使策略学习更加保守和稳健。

2. 延迟更新(Delayed Policy Updates)

动机:避免 Actor 在不稳定的 Q 值估计上进行优化。 方法:降低 Actor 和目标网络的更新频率。通常每更新 Critic $n$ 次(如 $n=2$),才更新一次 Actor。 效果:提高了策略更新的稳定性,确保 Critic 有足够时间收敛。

3. 目标策略平滑(Target Policy Smoothing)

动机:防止策略对特定状态下的极端动作过拟合。 方法:在计算目标值时,对目标动作加入裁剪后的高斯噪声。 $$ a' = \pi_{\phi'}(s') + \text{clip}(\epsilon, -c, c), \quad \epsilon \sim \mathcal{N}(0, \sigma) $$ 效果:增强了算法对噪声和目标值波动的鲁棒性。

三、数学细节解析

1. Critic 损失函数

TD3 使用均方误差最小化 Critic 的损失: $$ L(\theta) = \mathbb{E} \big[ (Q_{\theta}(s, a) - y)^2 \big] $$ 其中目标值 $y$ 采用双 Critic 的最小值计算。

2. Actor 策略梯度

Actor 通过最大化 Critic 的输出优化策略: $$ \nabla_\phi J(\phi) = \mathbb{E}{s \sim \rho^\pi} \big[ \nabla_a Q{\theta_1}(s, a) \big|{a=\pi\phi(s)} \nabla_\phi \pi_\phi(s) \big] $$

3. 软更新机制

目标网络参数通过软更新保持同步: $$ \theta_i' \leftarrow \tau \theta_i + (1 - \tau) \theta_i' $$ $$ \phi' \leftarrow \tau \phi + (1 - \tau) \phi' $$

四、PyTorch 实现

以下是基于 PyTorch 的完整 TD3 实现示例。代码结构清晰,包含网络定义、经验回放缓冲区和训练循环。

1. 环境配置与参数

import argparse
import gym
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from collections import deque

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

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--env_name', default="Pendulum-v0")
    parser.add_argument('--learning_rate', default=3e-4, type=float)
    parser.add_argument('--gamma', default=0.99, type=float)
    parser.add_argument('--tau', default=0.005, type=float)
    parser.add_argument('--batch_size', default=100, type=int)
    parser.add_argument('--policy_noise', default=0.2, type=float)
    parser.add_argument('--noise_clip', default=0.5, type=float)
    parser.add_argument('--policy_delay', default=2, type=int)
    return parser.parse_args()

2. 网络定义

class Actor(nn.Module):
    def __init__(self, state_dim, action_dim, max_action):
        super(Actor, self).__init__()
        self.fc1 = nn.Linear(state_dim, 400)
        self.fc2 = nn.Linear(400, 300)
        self.fc3 = nn.Linear(300, action_dim)
        self.max_action = max_action

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        return self.max_action * torch.tanh(self.fc3(x))

class Critic(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(Critic, self).__init__()
        self.fc1 = nn.Linear(state_dim + action_dim, 400)
        self.fc2 = nn.Linear(400, 300)
        self.fc3 = nn.Linear(300, 1)

    def forward(self, x, u):
        xu = torch.cat([x, u], 1)
        x = torch.relu(self.fc1(xu))
        x = torch.relu(self.fc2(x))
        return self.fc3(x)

3. 经验回放缓冲区

class ReplayBuffer:
    def __init__(self, capacity):
        self.buffer = deque(maxlen=capacity)

    def push(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size):
        batch = np.random.choice(len(self.buffer), batch_size, replace=False)
        states, actions, rewards, next_states, dones = [], [], [], [], []
        for idx in batch:
            s, a, r, ns, d = self.buffer[idx]
            states.append(np.array(s, copy=False))
            actions.append(np.array(a, copy=False))
            rewards.append(np.array(r, copy=False))
            next_states.append(np.array(ns, copy=False))
            dones.append(np.array(d, copy=False))
        return np.array(states), np.array(actions), np.array(rewards), \
               np.array(next_states), np.array(dones)

4. 训练逻辑

class TD3Agent:
    def __init__(self, state_dim, action_dim, max_action, args):
        self.actor = Actor(state_dim, action_dim, max_action).to(device)
        self.actor_target = Actor(state_dim, action_dim, max_action).to(device)
        self.critic_1 = Critic(state_dim, action_dim).to(device)
        self.critic_2 = Critic(state_dim, action_dim).to(device)
        self.critic_1_target = Critic(state_dim, action_dim).to(device)
        self.critic_2_target = Critic(state_dim, action_dim).to(device)

        self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=args.learning_rate)
        self.critic_1_optimizer = optim.Adam(self.critic_1.parameters(), lr=args.learning_rate)
        self.critic_2_optimizer = optim.Adam(self.critic_2.parameters(), lr=args.learning_rate)

        self.memory = ReplayBuffer(args.batch_size * 10)
        self.policy_delay = args.policy_delay
        self.tau = args.tau
        self.gamma = args.gamma

        # 初始化目标网络
        self.actor_target.load_state_dict(self.actor.state_dict())
        self.critic_1_target.load_state_dict(self.critic_1.state_dict())
        self.critic_2_target.load_state_dict(self.critic_2.state_dict())

    def select_action(self, state):
        state = torch.FloatTensor(state.reshape(1, -1)).to(device)
        return self.actor(state).cpu().data.numpy().flatten()

    def update(self, batch_size):
        if len(self.memory.buffer) < batch_size:
            return

        states, actions, rewards, next_states, dones = self.memory.sample(batch_size)
        states = torch.FloatTensor(states).to(device)
        actions = torch.FloatTensor(actions).to(device)
        rewards = torch.FloatTensor(rewards).reshape(-1, 1).to(device)
        next_states = torch.FloatTensor(next_states).to(device)
        dones = torch.FloatTensor(dones).reshape(-1, 1).to(device)

        # 计算目标动作并添加噪声
        noise = torch.randn_like(actions) * args.policy_noise
        noise = noise.clamp(-args.noise_clip, args.noise_clip)
        next_actions = (self.actor_target(next_states) + noise).clamp(-1, 1) * self.actor.max_action

        # 计算目标 Q 值(双 Critic 最小值)
        target_Q1 = self.critic_1_target(next_states, next_actions)
        target_Q2 = self.critic_2_target(next_states, next_actions)
        target_Q = torch.min(target_Q1, target_Q2)
        target_Q = rewards + ((1 - dones) * self.gamma * target_Q).detach()

        # 更新 Critic
        current_Q1 = self.critic_1(states, actions)
        loss_Q1 = nn.MSELoss()(current_Q1, target_Q)
        self.critic_1_optimizer.zero_grad()
        loss_Q1.backward()
        self.critic_1_optimizer.step()

        current_Q2 = self.critic_2(states, actions)
        loss_Q2 = nn.MSELoss()(current_Q2, target_Q)
        self.critic_2_optimizer.zero_grad()
        loss_Q2.backward()
        self.critic_2_optimizer.step()

        # 延迟更新 Actor
        if self.update_count % self.policy_delay == 0:
            actor_loss = -self.critic_1(states, self.actor(states)).mean()
            self.actor_optimizer.zero_grad()
            actor_loss.backward()
            self.actor_optimizer.step()

            # 软更新目标网络
            for param, target_param in zip(self.actor.parameters(), self.actor_target.parameters()):
                target_param.data.copy_(self.tau * param.data + (1 - self.tau) * target_param.data)
            for param, target_param in zip(self.critic_1.parameters(), self.critic_1_target.parameters()):
                target_param.data.copy_(self.tau * param.data + (1 - self.tau) * target_param.data)
            for param, target_param in zip(self.critic_2.parameters(), self.critic_2_target.parameters()):
                target_param.data.copy_(self.tau * param.data + (1 - self.tau) * target_param.data)

        self.update_count += 1

五、总结

TD3 通过双 Critic 网络、延迟更新和目标策略平滑三项改进,显著提升了连续控制任务中的训练稳定性和性能。相比 DDPG,它更有效地解决了 Q 值过估计问题,成为后续许多强化学习算法的重要基石。在实际应用中,建议根据具体任务调整超参数,特别是噪声幅度和更新延迟次数。

参考文献:Addressing Function Approximation Error in Actor-Critic Methods (Fujimoto et al., 2018)

目录

  1. TD3 算法详解:双延迟深度确定性策略梯度
  2. 一、TD3 的背景与动机
  3. 1. DDPG 的局限性
  4. 2. TD3 的理论基础
  5. 二、TD3 的核心思想
  6. 1. 双 Critic 网络(Twin Critics)
  7. 2. 延迟更新(Delayed Policy Updates)
  8. 3. 目标策略平滑(Target Policy Smoothing)
  9. 三、数学细节解析
  10. 1. Critic 损失函数
  11. 2. Actor 策略梯度
  12. 3. 软更新机制
  13. 四、PyTorch 实现
  14. 1. 环境配置与参数
  15. 2. 网络定义
  16. 3. 经验回放缓冲区
  17. 4. 训练逻辑
  18. 五、总结

更多推荐文章

查看全部
  • Linux 线程概念与 pthread 接口入门
  • 网络安全基础与黑客技术入门指南
  • Python 爬虫入门与数据分析基础指南
  • Openclaw 开源仿生机械爪原理与应用解析
  • Hunyuan-MT-7B-WEBUI 图形化界面详解
  • ES6 新特性实战:进制表示、Symbol 与类继承
  • Hunyuan-MT-7B 助力国际 NGO 在华项目多语本地化
  • 基于 YOLO 与 LLM 的 Web 目标检测与智能分析系统
  • 命令行下的 MCP 桥接工具:MCPHost 实践与模型兼容性测试
  • 程序员学历重要性探讨及 Python 技术成长路线
  • AI 可得性时代:人类能力结构的重构与未来应对
  • 前端数据可视化工具对比与选型指南
  • 大模型时代程序员如何提升效率:AIGC 辅助编程实践与展望
  • AI 双重突破:FARS 全自动科研系统与 AIGC 电影《团圆令》技术解析
  • 2026 年高校 AIGC 检测政策汇总与应对指南
  • 动态网站爬虫实战:SpiderFlow 可视化编排与自定义函数
  • 前端面试核心知识点整理(含 JavaScript、Vue、React 等)
  • Trae 集成 Figma MCP 实现前端代码自动生成
  • Java 开发者转型 AI 大模型应用开发指南
  • 腾讯云部署 OpenClaw 对接飞书实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online