Actor-Critic算法的基础认识
Actor-Critic:演员 - 评论家算法
价值算法:只估价值,不能直接优化动作策略,连续动作不好处理
纯策略梯度:更新方差大、收敛慢,要等回合结束才能更新
AC 算法:
Actor 直接优化行动策略,适配各类动作空间。
Critic 实时评估动作好坏,降低更新波动、加速收敛。
边交互边更新,不用等待回合完结,学习效率更高
Actor-Critic算法的网络结构
Actor(策略网络)
目标:在状态 s 下,输出动作 a,让未来总奖励最大化。
输入:状态 s
输出:
离散动作:动作概率分布
连续动作:确定性动作或动作分布参数
学习方式:根据 Critic 的评价,调整策略参数,让 “高分动作” 出现的概率越来越高。
Critic(价值网络)
目标:学会给 Actor 的动作打分,判断 “这个动作在当前状态下好不好”。
输入:状态 s(或状态 + 动作 (s,a))
输出:
状态价值 (V(s)):在状态 s 下,未来能拿到多少平均奖励;
动作价值 (Q(s,a)):在状态 s 做动作 a,未来能拿到多少平均奖励。
学习方式:用 TD 误差(时序差分误差)更新价值估计,让打分越来越准。
Actor-Critic算法的网络更新
Actor的更新:
目的:让 Actor 根据 Critic 的评价(TD 误差)调整动作策略,让好动作被更多选择,坏动作被更
少选择,持续优化决策。
(1)获取当前交互得到的状态 s、动作 a、奖励 r、下一状态 s'、是否结束 done。
(2)将状态 s 输入 Critic 网络,得到当前状态价值 V (s);将下一状态 s' 输入 Critic 网络,得到下
一状态价值 V (s')。
(3)Critic 计算 TD 误差:
δ = r + γ * V (s') - V (s)
(δ 代表动作的好坏:正 = 好,负 = 差)
(4)将状态 s 输入 Actor 网络,得到策略对动作 a 的输出概率 / 得分。
(5)依据策略梯度定理更新在线 Actor 网络参数:
Actor 根据 Critic 算出的 TD 误差来更新:动作好就加强,动作差就减弱。
TD Target(时序差分目标)
优势函数(Advantage)
Actor 损失(策略梯度)
Critic的更新
目的:精准预估状态价值,缩小评估偏差,为 Actor 优化提供可靠评判依据(1)获取单步交互数
据:当前状态s、即时奖励r、下一状态s'、终止标记done
(2)把s、s'分别输入 Critic 网络,得到估值(V(s))与(V(s'))
(3)计算时序差分误差
(4)以误差为损失依据,反向传播更新 Critic 网络参数,不断修正估值结果
Critic 损失(价值回归)
手算AC算法
网络的输入输出
input_dim = 6 (Acrobot 的 6 维观测空间:两个角余弦、两个角正弦、两个角速度)
output_dim = 3 (Acrobot 的 3 个离散动作:-1扭矩, 0扭矩, +1扭矩)
假设输入X是:X = [1.0, 0.0, 1.0, 0.0, 0.0, 0.0]
actor_output: [0.07, 0.93, 0.00] (给环境采样用的动作概率)
动作 0(向左猛踢,-1扭矩):0.07 (7%)
动作 1(腰部放松,0扭矩):0.93 (93%)
动作 2(向右猛踢,+1扭矩):0.00 (0%)
critic_output: -98.5 状态价值
选择动作
假设此时 Acrobot 的状态 state 如下(对应 [cos1, sin1, cos2, sin2, v1, v2])
state = [0.0, 1.0, 1.0, 0.0, 0.5, -0.2]
action_prob:tensor([[0.02, 0.85, 0.13]])
action = 1
(大概率会返回 1,但有极小可能返回 0 或 2(是一个采样过程)
所以这段代码的作用是根据当前动作,输入到Actor网络中选择出一个动作。
模型更新
假设我们的 buffer 如下:
经验1:(s1, a1=2, r1=-1.0, ns1, d1=0) 经验2:(s2, a2=0, r2=-1.0, ns2, d2=0) states = [ [1,0,1,0,0,0], # s1 [0,1,0,1,0.1,0.1] # s2 ] actions = [2, 0] rewards = [-1.0, -1.0] dones = [0, 0] gamma = 0.99 (默认,AC常用)
网络前向输出
Actor 输出(动作概率):
action_prob = [ [0.07, 0.93, 0.00], # s1 对应动作概率 [0.02, 0.85, 0.13] # s2 对应动作概率 ]
Critic 输出(当前状态价值)
state_value = [ [-98.5], # V(s1) [-95.0] # V(s2) ]
Critic 输出(下一状态价值)
next_state_value = [ [-97.0], # V(s1') [-93.0] # V(s2') ]
手工计算
经验 1:
td_target1 = -1.0 + 0.99 * 1.0 * (-97.0) = -1.0 - 96.03 = -97.03
经验 2:
td_target2 = -1.0 + 0.99 * 1.0 * (-93.0) = -1.0 - 92.07 = -93.07
最终:
td_target = [-97.03, -93.07]
计算 TD Delta(误差)
经验 1:
td_delta1 = -97.03 - (-98.5) = 1.47
经验 2:
td_delta2 = -93.07 - (-95.0) = 1.93
最终:
td_delta = [1.47, 1.93]
正数 → 动作比预期好 → Actor 要加强这个动作
计算 log_prob(动作对数概率)
log_prob2 = log(0.02) ≈ -3.91
log_prob = log( 选中动作的概率 )
经验 1:动作 a=2,概率 = 0.00
log_prob1 = log(0.00) → 负无穷(这里我们换成 0.01 方便计算) log(0.01) ≈ -4.6
经验 2:动作 a=0,概率 = 0.02
log_prob2 = log(0.02) ≈ -3.91
最终:
log_prob = [-4.6, -3.91]
计算 Actor Loss
经验 1: -4.6 * 1.47 = -6.76 经验 2: -3.91 * 1.93 = -7.55 平均: mean = (-6.76 -7.55) / 2 = -7.155 加负号: actor_loss = -(-7.155) = 7.155
计算 Critic Loss
经验 1: (-98.5 + 97.03)² = (-1.47)² = 2.16 经验 2: (-95.0 + 93.07)² = (-1.93)² = 3.72 平均: critic_loss = (2.16 + 3.72) / 2 = 2.94 第六步:总 Loss loss = actor_loss + 0.5 * critic_loss = 7.155 + 0.5*2.94 = 7.155 + 1.47 = 8.625
td_target = [-97.03, -93.07] td_delta = [1.47, 1.93] log_prob = [-4.6, -3.91] actor_loss = 7.155 critic_loss = 2.94 total_loss = 8.625
Critic 让自己的估值更准
Actor 因为 td_delta 为正,加强了刚才选择的动作
AC 只需要一步数据就能更新,不需要等回合结束
AC算法是单步更新,在线更新
AC算法核心更新仅依赖单步交互数据s,a,r,s',满足即时计算TD误差、完成参数更新的数学条件,
天生支持单步更新;传统策略梯度需整回合累计回报才能计算,无法单步更新。代码采用回合批量
更新,只是为提升训练稳定性与运算效率。
核心点
Actor 损失为什么是:actor_loss = - (log_prob * td_delta).mean()
Actor 的任务:让 “好动作” 概率变高,让 “坏动作” 概率变低。
对数转换后,乘积求导转为加减求导,梯度计算大幅简化。最大化动作概率等价于最大化对数概率,配合负号就能转为深度学习通用的最小损失优化范式。
log_prob = torch.log( 策略网络输出的动作概率 )
你选了这个动作,就取这个动作的概率,再取对数
例子:概率 = 0.07 → log (0.07) ≈ -2.66
作用:用来告诉网络 “我刚才选了哪个动作”。
td_delta = 实际收益 - 网络预估收益
td_delta > 0:这个动作比想象中好!
td_delta < 0:这个动作比想象中差!
作用:评价刚才动作的好坏
为什么要把 log_prob 和 td_delta 乘起来?
loss_part = log_prob * td_delta
本质含义:
✅ 如果动作好(td_delta 正)
log (prob) 是负数
正数 × 负数 = 负数
❌ 如果动作差(td_delta 负)
log (prob) 是负数
负数 × 负数 = 正数
为什么前面要加一个 负号?
actor_loss = - loss_part
好动作 → 算出来是负数 → 加负号 → 变成正数 → 网络会减小它 → 让概率上升
坏动作 → 算出来是正数 → 加负号 → 变成负数 → 网络会增大它 → 让概率下降
神经网络永远做一件事:想尽办法 让 损失(loss) 变 小!
不管你怎么设计公式,网络只会:loss 越小越好 → 拼命减小 loss
好动作的损失是正数
actor_loss = - ( -0.2 ) = +0.2
现在网络要做什么?
网络想让 loss 变小!
loss 现在是 0.2
网络想把它变成 0.1、0.05、0…
怎么才能让 loss 变小?
必须让 log_prob 变大(从 -0.1 → -0.05 → -0.01)
log_prob 变大 → 概率变大!