用PyTorch实现SAC:最大熵Actor-Critic
Soft Actor-Critic (SAC) 是近几年在连续控制任务里用得比较多的离线策略算法。它把策略熵直接放进了目标函数,让智能体在最大化奖励的同时保持一定的随机性,探索能力比很多传统方法强。这篇文章写一下它的核心原理,再给出一个完整的 PyTorch 实现。
从标准RL到最大熵目标
传统强化学习的目标是最大化累计折扣奖励:
$$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 用了 Actor-Critic 那一套,但 Critic 部分有两个 Q 网络($Q_{\theta_1}, Q_{\theta_2}$),训练时取两者中的较小值作为目标,这主要是为了压住 Q 值过估计。另外它还用目标网络做软更新,让价值估计平滑一些。
Q 网络的目标值按 Bellman 方程算:
$$y = r + \gamma (1 - \text{done}) \cdot V_{\psi'}(s')$$
损失就是 MSE:
$$J_Q = \mathbb{E}{(s, a, r, s') \sim D} \left[ \left( Q{\theta_i}(s, a) - y \right)^2 \right]$$
策略网络这边输出的是均值和标准差,通过重参数化采样得到动作,再用 $\tanh$ 把动作压到 $[-1, 1]$ 再乘上 max_action。对数概率的计算要带上 $\tanh$ 的修正项,这是 SAC 论文里的标准操作。策略更新的目标是最小化:
$$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]$$
直觉上就是让动作的 Q 值尽可能高,同时不要变得太确定(熵尽量大)。
代码实现
下面是一个干净的 PyTorch 实现,环境用的是 Pendulum-v1。代码分成了参数配置、网络定义、回放缓冲区和训练循环几个部分,可以直接跑。
参数配置
"""SAC 实现 - 2024.12"""
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import gym
import random
from collections import deque
GAMMA = 0.99
TAU = 0.005 # 目标网络软更新系数
ALPHA =
LR =
BATCH_SIZE =
MEMORY_CAPACITY =


