探秘强化学习中的Soft Actor-Critic算法

探秘强化学习中的Soft Actor-Critic算法

文章引言

此篇文章是博主在钻研强化学习(RL)领域时,用于个人学习、研究及品鉴的记录,基于博主对相关领域的认知所做的学习摘录与笔记。若有不当或侵权情形,烦请指出,会即刻修正,还望包涵。文章归属于👉强化学习
专栏:

【强化学习】- 【单智能体强化学习】(13)---《Soft Actor-Critic (SAC) 算法》

Soft Actor-Critic (SAC) 算法

目录

一、Soft Actor-Critic (SAC) 算法详解

二、SAC 背景与核心思想

  1. 强化学习面临的挑战

  2. 最大熵强化学习的目标

三、SAC 算法流程

初始化:

每一回合循环:

四、公式推导

  1. Q 值更新

  2. 值函数更新

  3. 策略网络更新

  4. 目标值函数更新

[Python] Soft Actor-Critic算法实现

[Notice] 代码核心

一、Soft Actor-Critic (SAC) 算法详解

Soft Actor-Critic(SAC) 是一种前沿的强化学习算法,属于 Actor-Critic 方法的变体。它特别适用于处理连续动作空间,并通过引入最大熵(Maximum Entropy)强化学习的理念,解决了诸多传统算法存在的稳定性与探索方面的问题。

二、SAC 背景与核心思想

1. 强化学习的挑战

  • 探索与利用的平衡困境 :传统算法在初期探索新策略和后期利用已有最优策略之间难以达到平衡。
  • 不稳定性状况 :在连续动作空间中,训练常常出现发散或收敛缓慢的现象。
  • 样本效率问题 :强化学习中,数据采集成本较高,如何有效利用经验池中的数据是关键所在。

SAC 引入了如下核心思想来应对这些问题:

  1. 最大熵强化学习 :在最大化累计奖励的同时,增强策略的随机性(熵),以此鼓励探索行为。
  2. 双 Q 网络 :缓解 Q 值的过估计问题。
  3. 目标网络 :利用目标网络来稳定 Q 值的计算。

2. 最大熵强化学习的目标

传统强化学习的目标是最大化期望累计奖励:

$$J(\pi) = \mathbb{E}{\pi} \left[ \sum{t=0}^T \gamma^t r(s_t, a_t) \right]$$

而 SAC 则通过添加一个 熵项,在奖励中融入策略随机性的权重,目标变为:

$$J(\pi) = \mathbb{E}{\pi} \left[ \sum{t=0}^T \gamma^t \left( r(s_t, a_t) + \alpha \mathcal{H}(\pi(\cdot|s_t)) \right) \right]$$

其中:

  • $\mathcal{H}(\pi(\cdot|s_t)) = -\mathbb{E}_{a \sim \pi} [\log \pi(a|s_t)]$,表示策略的熵,促使策略更具随机性;
  • $\alpha$:熵系数,控制熵和奖励之间的平衡。

效果

  • 更好的探索表现 :熵的最大化使得策略更加多样化。
  • 更稳定的学习过程 :避免陷入次优策略。

三、SAC 算法流程

SAC 采用了 Actor-Critic 框架,融合策略梯度和 Q 函数更新。以下是算法的关键步骤:

初始化

  1. 初始化两组 Q 网络 $Q_{\theta_1}, Q_{\theta_2}$,用于计算 Q 值。
  2. 初始化策略网络 $\pi_\phi$ 和值函数网络 $V_\psi$。
  3. 创建目标值函数网络 $V_{\psi'}$,并将其参数设置为 $V_{\psi}$ 的初始值。

每一回合循环

  1. 采样动作

    • 根据策略网络 $\pi_\phi$ 采样动作 $a \sim \pi(a|s)$。
    • 执行动作,将 $(s, a, r, s', \text{done})$ 记录到经验池中。
    • 更新 Q 网络

    • 使用 TD 目标更新 Q 值: $y = r + \gamma (1 - \text{done}) \cdot V_{\psi'}(s')$

    • 最小化以下损失函数: $J_Q = \mathbb{E} \left[ \left( Q_{\theta_i}(s, a) - y \right)^2 \right] \quad (i = 1, 2)$
    • 更新值函数网络

    • 值函数 $V_\psi$ 的目标是逼近以下值:
      $$y_V = \mathbb{E}{a \sim \pi} \left[ \min{i=1,2} Q_{\theta_i}(s, a) - \alpha \log \pi_\phi(a|s) \right]$$

    • 最小化值函数损失:$J_V = \mathbb{E} \left[ \left( V_\psi(s) - y_V \right)^2 \right]$

    • 更新策略网络

    • 策略网络的目标是最大化奖励和熵,最小化以下损失:
      $$J_\pi = \mathbb{E} \left[ \alpha \log \pi_\phi(a|s) - \min_{i=1,2} Q_{\theta_i}(s, a) \right]$$

  2. 更新目标值函数网络

    • 使用软更新规则: $\psi' \gets \tau \psi + (1 - \tau) \psi'$

<p>探秘强化学习中的Soft Actor-Critic算法</p>

四、公式推导

1. Q 值更新

Q 值通过 Bellman 方程更新,目标是最小化 TD 误差:$y = r + \gamma (1 - \text{done}) \cdot V_{\psi'}(s')$

损失函数为: $J_Q = \mathbb{E}{(s, a, r, s') \sim D} \left[ \left( Q{\theta_i}(s, a) - y \right)^2 \right]$

2. 值函数更新

值函数估计策略的长期价值,目标值为:

$$y_V = \mathbb{E}{a \sim \pi} \left[ \min{i=1,2} Q_{\theta_i}(s, a) - \alpha \log \pi_\phi(a|s) \right]$$

损失函数为:$J_V = \mathbb{E}{s \sim D} \left[ \left( V\psi(s) - y_V \right)^2 \right]$

3. 策略网络更新

策略网络的目标是最大化奖励和熵,等价于最小化:

$$J_\pi = \mathbb{E}{s \sim D, a \sim \pi} \left[ \alpha \log \pi\phi(a|s) - \min_{i=1,2} Q_{\theta_i}(s, a) \right]$$

4. 目标值函数更新

目标值函数使用软更新规则:$\psi' \gets \tau \psi + (1 - \tau) \psi'$

其中 $\tau \in (0, 1]$ 控制更新步长。

[Python] Soft Actor-Critic算法实现

以下是PyTorch中Soft Actor-Critic (SAC)算法的完整实现:

1.参数设置

"""《SAC, Soft Actor-Critic算法》
    时间:2024.12
    作者:不去幼儿园
"""
import torch  # 引入 PyTorch 库,用于构建和训练深度学习模型
import torch.nn as nn  # PyTorch 的神经网络模块
import torch.optim as optim  # PyTorch 的优化模块,用于更新模型参数
import numpy as np  # NumPy 库,用于高效的数值计算
import gym  # OpenAI Gym 库,用于创建和交互强化学习环境
import random  # Python 的随机模块,用于随机抽样
from collections import deque  # Python 的双端队列模块,用于构建经验回放缓冲区

# 超参数设置
GAMMA = 0.99  # 折扣因子,用于计算未来奖励
TAU = 0.005  # 软更新目标网络的参数
ALPHA = 0.2  # 熵正则化系数,鼓励探索
LR = 0.001  # 学习率,用于优化器
BATCH_SIZE = 256  # 每次训练的样本数量
MEMORY_CAPACITY = 100000  # 经验回放缓冲区的最大容量

2.策略网络

# 策略网络(用于生成随机的策略动作)
class PolicyNetwork(nn.Module):
    def __init__(self, state_dim, action_dim, max_action):
        super(PolicyNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim, 256)  # 第一层全连接层,输入状态维度
        self.fc2 = nn.Linear(256, 256)  # 第二层全连接层
        self.mean = nn.Linear(256, action_dim)  # 输出动作均值
        self.log_std = nn.Linear(256, action_dim)  # 输出动作的对数标准差
        self.max_action = max_action  # 动作的最大值,用于缩放

    def forward(self, state):
        x = torch.relu(self.fc1(state))  # 激活第一层
        x = torch.relu(self.fc2(x))  # 激活第二层
        mean = self.mean(x)  # 计算动作均值
        log_std = self.log_std(x).clamp(-20, 2)  # 将对数标准差限制在合理范围内
        std = torch.exp(log_std)  # 通过对数标准差计算标准差
        return mean, std  # 返回均值和标准差

    def sample(self, state):
        mean, std = self.forward(state)  # 获取动作分布的均值和标准差
        normal = torch.distributions.Normal(mean, std)  # 正态分布
        x_t = normal.rsample()  # 使用重参数化技巧采样
        y_t = torch.tanh(x_t)  # 使用 Tanh 将动作限制在 [-1, 1]
        action = y_t * self.max_action  # 缩放动作到最大值范围
        log_prob = normal.log_prob(x_t)  # 计算动作的对数概率
        log_prob -= torch.log(1 - y_t.pow(2) + 1e-6)  # Tanh 的修正项
        log_prob = log_prob.sum(dim=-1, keepdim=True)  # 对每个维度求和
        return action, log_prob  # 返回动作和对数概率

3.Q 网络

# Q 网络(价值函数,用于评估状态-动作对的价值)
class QNetwork(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(QNetwork, self).__init__()
        self.fc1 = nn.Linear(state_dim + action_dim, 256)  # 输入包括状态和动作
        self.fc2 = nn.Linear(256, 256)  # 第二层全连接层
        self.fc3 = nn.Linear(256, 1)  # 输出单一 Q 值

    def forward(self, state, action):
        x = torch.cat([state, action], dim=-1)  # 将状态和动作连接起来作为输入
        x = torch.relu(self.fc1(x))  # 激活第一层
        x = torch.relu(self.fc2(x))  # 激活第二层
        x = self.fc3(x)  # 输出 Q 值
        return x  # 返回 Q 值

4.经验回放缓冲区

# 经验回放缓冲区
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 = random.sample(self.buffer, batch_size)  # 随机选取 batch_size 个经验
        states, actions, rewards, next_states, dones = zip(*batch)  # 解压
        return (np.array(states), np.array(actions), np.array(rewards),
                np.array(next_states), np.array(dones))

    def __len__(self):  # 返回缓冲区当前大小
        return len(self.buffer)

5.SAC 算法

# SAC 算法智能体
class SACAgent:
    def __init__(self, state_dim, action_dim, max_action):
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 检查是否有 GPU

        # 初始化网络
        self.actor = PolicyNetwork(state_dim, action_dim, max_action).to(self.device)  # 策略网络
        self.q1 = QNetwork(state_dim, action_dim).to(self.device)  # 第一个 Q 网络
        self.q2 = QNetwork(state_dim, action_dim).to(self.device)  # 第二个 Q 网络

        # 初始化优化器
        self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=LR)  # 策略网络优化器
        self.q1_optimizer = optim.Adam(self.q1.parameters(), lr=LR)  # Q1 优化器
        self.q2_optimizer = optim.Adam(self.q2.parameters(), lr=LR)  # Q2 优化器

        # 初始化经验回放缓冲区
        self.replay_buffer = ReplayBuffer(MEMORY_CAPACITY)
        self.max_action = max_action  # 最大动作值

    def select_action(self, state):  # 根据策略选择动作
        state = torch.FloatTensor(state).to(self.device).unsqueeze(0)  # 转换状态为张量
        action, _ = self.actor.sample(state)  # 从策略中采样动作
        return action.cpu().detach().numpy()[0]  # 转换为 NumPy 格式返回

    def train(self):  # 训练智能体
        if len(self.replay_buffer) < BATCH_SIZE:  # 如果经验回放缓冲区不足,跳过训练
            return

        # 从回放缓冲区采样
        states, actions, rewards, next_states, dones = self.replay_buffer.sample(BATCH_SIZE)
        states = torch.FloatTensor(states).to(self.device)
        actions = torch.FloatTensor(actions).to(self.device)
        rewards = torch.FloatTensor(rewards).unsqueeze(1).to(self.device)
        next_states = torch.FloatTensor(next_states).to(self.device)
        dones = torch.FloatTensor(dones).unsqueeze(1).to(self.device)

        # 更新 Q 网络
        with torch.no_grad():
            next_actions, log_probs = self.actor.sample(next_states)  # 计算下一步的动作及其概率
            target_q1 = self.q1(next_states, next_actions)  # Q1 值
            target_q2 = self.q2(next_states, next_actions)  # Q2 值
            target_q = torch.min(target_q1, target_q2) - ALPHA * log_probs  # 使用最小值更新
            q_target = rewards + GAMMA * (1 - dones) * target_q  # 计算目标 Q 值

        q1_loss = ((self.q1(states, actions) - q_target) ** 2).mean()  # Q1 损失
        q2_loss = ((self.q2(states, actions) - q_target) ** 2).mean()  # Q2 损失

        self.q1_optimizer.zero_grad()  # 清空梯度
        q1_loss.backward()  # 反向传播
        self.q1_optimizer.step()  # 更新 Q1

        self.q2_optimizer.zero_grad()
        q2_loss.backward()
        self.q2_optimizer.step()

        # 更新策略网络
        new_actions, log_probs = self.actor.sample(states)
        q1_new = self.q1(states, new_actions)
        q2_new = self.q2(states, new_actions)
        q_new = torch.min(q1_new, q2_new)

        actor_loss = (ALPHA * log_probs - q_new).mean()  # 策略损失

        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()

    def update_replay_buffer(self, state, action, reward, next_state, done):
        self.replay_buffer.push(state, action, reward, next_state, done)

6.主函数循环

```python

训练循环

env = gym.make("Pendulum-v1") # 创建环境
state_dim = env.observation_space.shape[0] # 状态空间维度
action_dim = env.action_space.shape[0] # 动作空间维度
max_action = float(env.action_space.high[0]) # 最大动作值

agent = SACAgent(state_dim, action_dim, max_action) # 初始化智能体

num_episodes = 500 # 训练的总回合数
for episode in range(num_episodes):
state = env.reset() #

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/12771.html

(0)
LomuLomu
上一篇 9小时前
下一篇 7小时前

相关推荐

  • 2025最新PyCharm永久激活码及破解教程(亲测有效,支持2099年)🔥

    本教程适用于Jetbrains全家桶,包括IDEA、PyCharm、DataGrip、Goland等开发工具!💯 先给大家看看最新版PyCharm成功破解后的效果✨,有效期直接延长到2099年,简直不要太爽! 下面我就手把手教大家如何激活PyCharm,这个方法同样适用于旧版本哦~ 无论你是Windows、Mac还是Linux系统 无论你使用哪个版本 统统都…

    2025 年 5 月 19 日
    1.0K00
  • 扣子又出新功能,支持一键部署小程序,太强了!!

    大家好,我是R哥。 作为一名程序员和技术博主,我一直关注如何使用工具提升生产力,尤其是在内容创作和应用开发领域。 拿我开发一个微信小程序为例,我需要懂前端、后端、运维 等全栈技术,开发流程和技术栈复杂,我还需要购买云服务器、云数据库 等各种基础设施,资源耗费非常多。 虽然现在有如 Cursor 这样的革命性 AI 开发工具,它突破了传统开发模式的壁垒,非开发…

    2025 年 1 月 11 日
    32700
  • 履约系统:应用层、领域层、集成关系设计

    大家好,我是汤师爷~ 在这篇文章中,我们一起探讨订单履约系统的应用架构设计。 应用架构设计 我们前面讨论了系统的核心概念模型和拆单逻辑。接下来,让我们从应用架构的角度,深入了解系统的各个层次。这包括应用层、领域层,以及与其他系统的集成关系。 应用层能力 应用层定义软件的应用功能,它负责接收用户请求,协调领域层能力来执行任务,并将结果返回给用户,核心模块包括:…

    2025 年 1 月 1 日
    21400
  • 2025年最新PyCharm激活码分享 | 永久破解PyCharm专业版教程 💻

    本教程适用于JetBrains全家桶,包括IDEA、PyCharm、DataGrip、Goland等所有产品!✨ 先给大家看看最新版PyCharm成功破解到2099年的效果图,是不是很给力?😎 下面我就手把手教你如何激活PyCharm到2099年,这个方法同样适用于旧版本哦! ✅ 全平台支持:- Windows系统- Mac系统- Linux系统 所有版本通…

    2025 年 5 月 29 日
    1.1K00
  • 2024最新永久IDEA激活码「官方正版IDEA激活破解」

    2024最新永久IDEA激活码「官方正版IDEA激活破解」 本教程适用于IDEA、PyCharm、DataGrip、Goland等,支持Jetbrains全家桶! 直接进入正题,先上最新 IDEA 版本破解成功的截图,如下展示,可以看到已经成功破解到 2099 年辣,舒服! 接下来,我们来一步步看看, 来详细讲解如何激活 IDEA至 2099 年。 这个方法…

    PyCharm破解教程 2025 年 4 月 10 日
    1.2K00

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信