当前位置: 首页 > news >正文

图解强化学习 |SAC

🌞欢迎来到图解强化学习的世界
🌈博客主页:卿云阁

💌欢迎关注🎉点赞👍收藏⭐️留言📝

📆首发时间:🌹2026年4月17日🌹

✉️希望可以和大家一起完成进阶之路!


目录

SAC算法网络结构

5 个神经网络

网络更新

代码实现

PolicyModel 策略网络

经验回放缓冲区ReplayBuffer

SAC算法

SAC算法网络结构

5 个神经网络

Actor —— 策略网络,输出动作分布

Critic 1 —— 第一个 Q 评估网络

Critic 2 —— 第二个 Q 评估网络

Target Critic 1 —— 稳定用的 Q 目标网络

Target Critic 2 —— 另一个稳定用的 Q 目标网络

再加 1 个可学习参数:α (alpha) —— 控制探索强度

① Actor

输入:状态 s

输出:动作 a、动作的对数概率 log π(a|s)

( 输入 next_state 对应输出 next_action 和 next_log_prob, 输入 state 对应输出 new_action 和

log_prob)

② Critic 1 / Critic 2

输入:状态 s + 动作 a

输出:一个 Q 值(评估这个动作好不好)

③ Target Critic 1 / Target Critic 2

输入:下一状态 s' + 下一动作 a'

输出:目标 Q 值(用来训练 Critic)

为什么要有 Actor(策略网络)?

因为 SAC 是 Actor-Critic 架构:

Actor 负责 “做动作”

Critic 负责 “评价动作”

如果没有 Actor,就没有策略,智能体不会动。

而且 SAC 的 Actor 是 随机策略(输出分布,不是固定动作),这是为了天然自带探索,不用像

DDPG 那样手动加噪声。

为什么要有 Critic 1 + Critic 2(两个 Q 网络)?

这是为了解决强化学习千古难题:Q 值过估计(overestimation bias)

Critic 总是倾向于把 Q 值越估越高,估高了 → Actor 学到错误行为 → 训练崩掉。

解决办法:

用两个网络,每次都取更小的那个 Q 值。

Qfinal​=min(Q1​,Q2​)这样就能显著抑制高估,训练超级稳。

为什么还要 Target Critic 1 + Target Critic 2?

主 Critic:负责实时更新、逼近目标

Target Critic:负责提供稳定、不动的标准答案

为什么要有 α(温度系数、自动学习的熵参数)?

α:自动平衡 “奖励最大化” 和 “探索随机性”

网络更新

Q 网络更新( Critic Loss)

Actor 策略更新( Policy Loss)

Alpha 自动更新( Entropy Temperature Loss)

目标网络软更新

代码部分对应公式
Q 网络更新LQ​=MSE(Q(s,a),y)
Actor 更新Lπ​=E[π⋅(αlogπ−minQ)]
Alpha 更新Lα​=α(−H(π)−Htarget​)
目标网络软更新 ϕtar​

代码实现

PolicyModel 策略网络

给定状态生成各个动作的概率

输入 state:

[0.1, 0.2, -0.1, 0.05, 0.02, 0.01, 0, 0]

经过网络 → 输出概率:

[0.05, 0.80, 0.10, 0.05]

Q网络模型

给定状态和动作样本对估计Q值

输入是 8 维向量:

state = [0.1, 0.2, -0.3, 0.05, 0.01, -0.02, 0, 0]

输出 = 每个动作的 Q 值

q_values = [ 12.3, 45.6, -5.2, 10.1 ]
经验回放缓冲区ReplayBuffer

初始化(__init__)

max_size:最多存多少条经验

buffer:用队列存储,满了自动踢掉最老的

添加experience(五元组)到缓冲区(add)

state = [0.1, 0.2, -0.3, 0.05, 0.02, -0.01, 0, 0] action = 1 reward = 2.5 next_state = [0.1, 0.18, 0.04, -0.28, 0.02, -0.01, 0, 0] done = False buffer.add(state, action, reward, next_state, done)

sample () —— 随机抽一批经验(训练用)

states, actions, rewards, next_states, dones = buffer.sample(64)

__len__返回缓冲区数据数量

print(len(buffer)) # 输出 1250,表示存了1250条经验
SAC算法

__init__

构造函数,参数包含环境,学习率,折扣因子,更新目标网络参数,缓冲区大小

基础参数初始化

self.env = env # 强化学习环境(如LunarLander) self.gamma = gamma # 折扣因子(未来奖励的权重) self.rho = rho # 目标网络软更新系数 self.replay_buffer = ReplayBuffer(max_size=buffer_size) # 经验回放池

目标熵

self.target_entropy = -np.log2(env.action_space.n)

SAC 希望策略保持一定的随机性(探索)目标熵公式:

target_entropy=−log(动作数量)

例如:LunarLander 有 4 个动作target_entropy=−log2​(4)=−2

目的:让策略不要太贪婪,要保持探索。

设备选择(CPU/GPU)

self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

创建模型,并将模型移动到指定设备上

# 演员网络(策略) self.actor = PolicyModel(输入维度,输出维度).to(self.device) # 两个 Q 网络 self.q1 = QValueModel(输入维度,输出维度).to(self.device) self.q2 = QValueModel(输入维度,输出维度).to(self.device) # 两个目标 Q 网络 self.target_q1 = QValueModel(输入维度,输出维度).to(self.device) self.target_q2 = QValueModel(输入维度,输出维度).to(self.device)

Actor —— 策略网络,输出动作概率

Q1 —— 第一个 Q 评估网络

Q2 —— 第二个 Q 评估网络

Target Q1 —— 稳定目标

Target Q2 —— 稳定目标

for param, target_param in zip(self.q1.parameters(), self.target_q1.parameters()): target_param.data.copy_(param) for param, target_param in zip(self.q2.parameters(), self.target_q2.parameters()): target_param.data.copy_(param)

让 Target 网络一开始和主 Q 网络完全一样

self.optimizer_actor = torch.optim.Adam(self.actor.parameters(), lr=learning_rate) self.optimizer_q1 = torch.optim.Adam(self.q1.parameters(), lr=learning_rate) self.optimizer_q2 = torch.optim.Adam(self.q2.parameters(), lr=learning_rate)

3 个优化器分别更新:Actor、Q1、Q2

使用模型生成动作概率分布并采样(choose_action)

# 使用模型生成动作概率分布并采样 def choose_action(self, state): # 将状态转换为tensor输入模型 state = torch.FloatTensor(np.array([state])).to(self.device) with torch.no_grad(): action_prob = self.actor(state) # 生成分布后采样返回动作 c = torch.distributions.Categorical(action_prob) action = c.sample() return action.item()

输入状态 state(真实 8 维数字)

state = [0.1, 0.2, -0.3, 0.05, 0.02, -0.01, 0.0, 0.0]

转成 PyTorch Tensor

state = torch.FloatTensor( [[0.1, 0.2, -0.3, 0.05, 0.02, -0.01, 0.0, 0.0]] )

送入 Actor 网络 → 输出概率

action_prob = self.actor(state)
action_prob = tensor([[0.1, 0.7, 0.1, 0.1]])

动作 0:10% 动作 1:70% 动作 2:10% 动作 3:10%

采样后输出动作

1

模型更新

next_action_prob = self.actor(next_states) log_next_prob = torch.log(next_action_prob + 1e-9)

用 Actor 网络,计算【下一个状态】的所有动作概率

next_states = 下一个状态

next_states = [0.1, 0.2, -0.3, 0.05, 0.02, -0.01, 0, 0]

经过 Actor 网络输出

4 个动作的概率分布

next_action_prob = [0.1, 0.6, 0.2, 0.1]

log_next_prob = log([0.1, 0.6, 0.2, 0.1]) = [-2.30, -0.51, -1.61, -2.30]

计算目标Q值

target_q1 = self.target_q1(next_states) target_q2 = self.target_q2(next_states) target_q_min = torch.min(target_q1, target_q2) min_q_next_target = next_action_prob * (target_q_min - alpha * log_next_prob) min_q_next_target = torch.sum(min_q_next_target, dim=1, keepdim=True)
next_action_prob = tensor([[0.1, 0.6, 0.2, 0.1]]) # 动作概率 log_next_prob = tensor([[-2.30, -0.51, -1.61, -2.30]]) # log概率 alpha = 0.2 # 探索系数 target_q1 = tensor([[10, 20, 5, 8]]) # 目标Q1 四个动作 target_q2 = tensor([[11, 19, 4, 9]]) # 目标Q2 四个动作

target_q_min = 取两个 Q 的最小值

target_q_min = [[10, 19, 4, 8]]

计算熵探索项 alpha * log_next_prob

min_q_next_target = next_action_prob * (target_q_min - torch.exp(self.log_alpha) * log_next_prob)
alpha * log_next_prob = 0.2 * [-2.30, -0.51, -1.61, -2.30] = [-0.46, -0.10, -0.32, -0.46]

target_q_min - alpha * log_next_prob

[10, 19, 4, 8] - [-0.46, -0.10, -0.32, -0.46] = [10+0.46, 19+0.10, 4+0.32, 8+0.46] = [10.46, 19.10, 4.32, 8.46]

乘以动作概率

next_action_prob * (...) = [0.1, 0.6, 0.2, 0.1] * [10.46, 19.10, 4.32, 8.46] = [ 0.1 * 10.46 = 1.046 0.6 * 19.10 = 11.46 0.2 * 4.32 = 0.864 0.1 * 8.46 = 0.846 ]
[1.046, 11.46, 0.864, 0.846]
sum = 1.046 + 11.46 + 0.864 + 0.846 = 14.216

最终结果

min_q_next_target = tensor([[14.216]])

计算下一状态的 “期望 Q 值”

双 Q 取最小 + 熵探索奖励 + 动作概率加权

计算TD目标

td_target = rewards + (1 - dones) * self.gamma * min_q_next_target

TD 目标 = 当前奖励 + 折扣因子 × 下一状态的期望 Q 值

rewards = 5.0 # 当前得到的奖励 dones = 0 # 回合没结束(0=False) gamma = 0.99 # 折扣因子 min_q_next_target = 14.216 # 上一步算出来的下一状态Q值
td_target = 19.07

计算Q网络的loss

# 计算Q网络的loss q1 = self.q1(states) q2 = self.q2(states) q1_loss = F.mse_loss(q1.gather(1, actions), td_target.detach()).mean() q2_loss = F.mse_loss(q2.gather(1, actions), td_target.detach()).mean()

q1 = tensor([[12, 15, 8, 10]]) # 4个动作的Q值
q2 = tensor([[13, 14, 7, 11]])

q1.gather(1, 1) → 15

(15 - 19.07)² = ( -4.07 )² = 16.56

actor网络更新

##### actor网络更新 ##### # 计算当前状态的动作概率 action_prob = self.actor(states) log_prob = torch.log(action_prob + 1e-9) # 计算Q值和actor网络的loss q1 = self.q1(states) q2 = self.q2(states) inside_term = torch.exp(self.log_alpha) * log_prob - torch.min(q1, q2) actor_loss = torch.sum(action_prob * inside_term, dim = 1, keepdim = True).mean()

已知(假设)

action_prob = tensor([[0.1, 0.6, 0.2, 0.1]]) # 动作概率 log_prob = tensor([[-2.3, -0.51, -1.6, -2.3]]) # log概率 alpha = 0.2 # 探索系数 q1 = tensor([[12, 20, 5, 9]]) q2 = tensor([[11, 19, 4, 10]])

首先看这行代码

inside_term = torch.exp(self.log_alpha) * log_prob - torch.min(q1, q2)
min_q = torch.min(q1, q2) = min([12,20,5,9], [11,19,4,10]) = tensor([[11, 19, 4, 9]])
alpha * log_prob = 0.2 * [-2.3, -0.51, -1.6, -2.3] = [-0.46, -0.102, -0.32, -0.46] inside_term = [-0.46, -0.102, -0.32, -0.46] - [11, 19, 4, 9] = [-11.46, -19.102, -4.32, -9.46]
action_prob * inside_term = [0.1, 0.6, 0.2, 0.1] * [-11.46, -19.102, -4.32, -9.46] = [ -1.146, -11.461, -0.864, -0.946 ]
sum = -1.146 -11.461 -0.864 -0.946 = -14.417
actor_loss = -14.417

alpha参数更新

##### alpha参数更新 ##### # 计算alpha值的loss inside_term = -torch.sum(action_prob * log_prob, dim = 1, keepdim = True) - self.target_entropy alpha_loss = (torch.exp(self.log_alpha) * inside_term.detach()).mean()

alpha 是控制 “探索强度” 的旋钮,这段代码让 alpha 自动变大 / 变小,不用人工调参!

alpha 大 → 策略更随机、更爱探索

alpha 小 → 策略更稳定、更爱 exploit

action_prob = tensor([[0.1, 0.6, 0.2, 0.1]]) # 动作概率 log_prob = tensor([[-2.3, -0.51, -1.6, -2.3]]) # log概率 target_entropy = -2.0 # 目标熵:-log2(4) = -2 alpha = 0.2 # 当前探索系数

计算当前策略的熵 H (π)

-torch.sum(action_prob * log_prob, dim=1, keepdim=True)
action_prob * log_prob = 0.1*-2.3 = -0.23 0.6*-0.51 = -0.306 0.2*-1.6 = -0.32 0.1*-2.3 = -0.23 sum = -0.23 -0.306 -0.32 -0.23 = -1.086 取负: -entropy = 1.086

计算 inside_term

inside_term = 当前熵 - 目标熵
inside_term = 1.086 - (-2.0) = 1.086 + 2 = 3.086

alpha_loss

alpha_loss = alpha * inside_term

alpha_loss = 0.2 * 3.086 = 0.617

http://www.jsqmd.com/news/658536/

相关文章:

  • MySQL数据库磁盘写满后如何紧急处理_清理日志与扩容空间
  • 低成本蓝牙串口方案实测:大夏龙雀BT-36/37模块选型、AT指令配置与手机PC互联
  • 石家庄能力考哪家日语机构更专业?
  • AppleRa1n:iOS 15-16激活锁绕过解决方案深度解析
  • 手把手教你用Docker搞定COCO数据集预处理(含Python2.7、CoreNLP、Doc2Vec完整配置)
  • 5分钟快速掌握SketchUp STL插件:设计师的终极3D打印转换指南
  • 告别Keil:在Windows上构建VSCode+GCC+OpenOCD一体化ARM开发环境
  • Harness Engineering 实战四:Java 项目的 Harness 层写在哪?附完整Demo
  • 消防主机组网通信质量有担忧?巧用光纤环网冗余方案,实现超远距离、高可靠CAN通讯
  • 长代码生成为何频频崩溃?揭秘LLM在1000+行函数中的5个隐性失效点
  • 别只做标题党了!我用扣子AI智能体,把公众号爆款标题的9种套路都做成了自动化模板
  • g4f提供的模型调用:python JavaScript和curl
  • 2026年质量好的陕西消防器材/西安消防器材优质厂家推荐榜 - 品牌宣传支持者
  • UE4材质性能优化笔记:一张贴图搞定树叶的粗糙度、透光和AO(附节点详解)
  • 【SITS2026实战白皮书】:大厂AI编程工具落地路径、踩坑清单与ROI量化报告(仅内部流出3份)
  • 避开这些坑:Syncthing局域网单向同步的完整配置流程与防火墙设置详解
  • python changes
  • 2026年3月揭晓:含电气AI软件系统的能源管理系统EMS有哪些,高低压配电柜安装,电气AI软件系统供应商口碑推荐 - 品牌推荐师
  • 伺服系统三环增益调优:从理论公式到实践步骤
  • ESP32-S3 智能农业监测与自动灌溉系统:从硬件选型到云端部署全解析
  • 小白从零开始学渗透:8 个核心步骤直接上手
  • Sunshine游戏串流终极指南:15分钟打造你的跨设备游戏天堂
  • 新能源汽车电池包液冷流道流动与传热的数值优化
  • Excel公式美化神器:3分钟让复杂公式变清晰,工作效率提升300%
  • 全网最通俗:什么是网络安全,为何人人都要重视
  • 大模型API网关缓存预热失效真相,3个被忽视的上下文依赖因子正在 silently 拖垮你的P99延迟
  • C# OnnxRuntime 部署 DDColor
  • C++14的[[deprecated]]属性怎么用?手把手教你优雅地标记过时代码(附自定义警告信息)
  • 基于Kotti-py312这个项目,帮我写一个AI 交流网站。先帮我规划一下!我的诉求是能实现AI资源的互助,大家互相帮着找点子,一起落地实践!
  • SITS2026 AI配置生成器深度拆解:从YAML Schema解析到动态策略注入的7步工业级落地流程