Actor-Critic架构
一句话概述
Actor-Critic架构是强化学习中最主流的框架——Actor(演员)负责决策:根据当前状态选择动作;Critic(评论家)负责评价:估计当前状态或动作的价值。Critic的评估结果用于指导Actor的更新,大幅降低了策略梯度的方差。Actor-Critic结合了基于策略和基于价值方法的优点,是现代RL算法(A2C、PPO、SAC、TD3)的标准架构。
💡 核心要点:①Actor输出动作概率分布(策略),Critic输出价值估计(V(s)或Q(s,a));②Critic用TD学习更新价值估计,Actor用Critic提供的优势函数更新策略;③Actor-Critic将REINFORCE的蒙特卡洛回报G_t替换为TD误差或优势函数,实现在线学习;④Actor和Critic通常共享底层特征提取网络,减少参数量并加速训练。
教学与演示
一、Actor-Critic的核心思想——分工合作
是什么(定义):Actor-Critic由两个网络组成:Actor π_θ(a|s)(策略网络)和Critic V_φ(s)或Q_φ(s,a)(价值网络)。Actor根据Critic的反馈来改进策略,Critic根据环境奖励来改进价值估计。两者相互促进:更好的Critic→更好的Actor更新→更好的策略→更好的数据→更好的Critic。
大白话 Actor-Critic = 演员 + 导演。演员(Actor)在台上表演,导演(Critic)在台下评论。演员根据导演的反馈调整表演,导演根据实际效果调整评价标准。两者配合,比单独一个人(纯策略)或一个评分系统(纯价值)更高效。
为什么(原理):Actor-Critic解决了REINFORCE的两大痛点:(1) 方差大——用Critic的低方差估计替代高方差G_t;(2) 不能在线学习——Critic的TD误差让每步都能更新。Critic的TD误差δ_t = r + γV(s') - V(s)天然是优势函数A(s,a)的无偏估计,用它来更新Actor既高效又低方差。
怎么做(实现):
import numpy as np
n_states = 9; n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
def step(state, action):
row, col = state // 3, state % 3
dr, dc = actions[action]
nr = max(0, min(2, row + dr))
nc = max(0, min(2, col + dc))
next_state = nr * 3 + nc
if next_state == 8: r = 10.0
elif next_state == 7: r = -5.0
else: r = -0.1
return next_state, r, next_state in [7, 8]
class ActorCritic:
def __init__(self, n_states, n_actions):
# Actor参数
self.theta = np.zeros((n_states, n_actions))
# Critic参数
self.V = np.zeros(n_states)
def act(self, state):
logits = self.theta[state]; exp_l = np.exp(logits - np.max(logits))
probs = exp_l / exp_l.sum()
return np.random.choice(n_actions, p=probs), probs
def update(self, state, action, reward, next_state, done, probs, alpha_a=0.01, alpha_c=0.1, gamma=0.9):
# Critic: 计算TD误差并更新V
td_target = reward + gamma * self.V[next_state] * (1 - done)
td_error = td_target - self.V[state]
self.V[state] += alpha_c * td_error # Critic更新
# Actor: 用TD误差作为优势函数更新策略
grad = -probs.copy(); grad[action] += 1
self.theta[state] += alpha_a * td_error * grad # Actor更新
def train_episode(self):
state = 0; total = 0
while state not in [7, 8]:
action, probs = self.act(state)
next_state, reward, done = step(state, action)
self.update(state, action, reward, next_state, done, probs)
total += reward; state = next_state
return total
np.random.seed(42)
ac = ActorCritic(n_states, n_actions)
rewards = [ac.train_episode() for _ in range(300)]
print("=== Actor-Critic训练结果 ===")
print(f"最后10ep平均奖励: {np.mean(rewards[-10:]):.2f}")
action_names = ["上", "下", "左", "右"]
print(f"\nActor学到的策略:")
for i in range(3):
row = [];
for j in range(3):
s = i*3+j
if s==8: row.append("目标")
elif s==7: row.append("陷阱")
else: row.append(action_names[np.argmax(ac.theta[s])])
print(" "+" ".join(f"{a:>4}" for a in row))
print(f"\nCritic学到的V值:")
for i in range(3):
print(" "+" ".join(f"{ac.V[i*3+j]:>6.2f}" for j in range(3)))什么用(应用):Actor-Critic是A2C、A3C、PPO、SAC、TD3等现代RL算法的基础架构。在机器人控制、游戏AI、自动驾驶等领域,几乎所有state-of-the-art方法都基于Actor-Critic。
哪些坑(缺点):需要同时训练两个网络,超参数更多;Actor和Critic的学习率需要协调(Critic通常学习更快);两个网络的目标可能冲突。
二、TD误差作为优势函数——为什么有效
是什么(定义):在Actor-Critic中,TD误差δ_t = r + γV(s') - V(s)是优势函数A(s,a)的无偏估计。因为E[δ_t] = E[Q(s,a) - V(s)] = A(s,a)。用TD误差替代蒙特卡洛回报G_t,既降低了方差,又实现了在线学习。
大白话 TD误差就是"惊喜"——实际奖励+预期未来价值,减去当前预期,看看"事情比想象的好还是坏"。如果比想象的好(δ>0),说明这个动作不错,值得多做;如果比想象的差(δ<0),说明这个动作不行,应该少做。
怎么做(实现):
import numpy as np
# ==================== TD误差 = 优势函数估计 ====================
# 模拟一个简单场景
V_s = 5.0 # 当前估计V(s)=5
V_s_next = 8.0 # 下一状态V(s')=8
reward = 1.0 # 即时奖励
gamma = 0.9
td_error = reward + gamma * V_s_next - V_s
print("=== TD误差 = 优势函数 ===")
print(f"V(s)={V_s}, r={reward}, V(s')={V_s_next}")
print(f"TD误差 = {reward} + {gamma}×{V_s_next} - {V_s} = {td_error:.1f}")
print(f"TD误差>0 → 实际比预期好 → 动作值得多做")
print(f"TD误差<0 → 实际比预期差 → 动作应该少做")
print(f"\n对比REINFORCE的G_t:")
print(f" G_t = 整条轨迹的累积奖励(方差大,需要等episode结束)")
print(f" TD误差 = 一步的'惊喜'(方差小,每步都能更新)")什么用(应用):TD误差是Actor-Critic的核心学习信号。在A2C/A3C中,TD误差直接用于更新Actor和Critic;在PPO中,GAE(广义优势估计)将TD误差扩展到多步,平衡偏差和方差。
哪些坑(缺点):TD误差是有偏的(因为V(s)和V(s')的估计不准确);单步TD误差可能忽略长期依赖(需要用n步回报或GAE)。
三、Actor和Critic的网络设计
是什么(定义):在实际实现中,Actor和Critic通常共享底层特征提取网络,然后各自有一个输出头。共享网络减少参数量、加速训练,但也可能引入梯度冲突。在离散动作空间中,Actor输出动作概率分布(softmax),Critic输出V(s)标量;在连续动作空间中,Actor输出高斯分布参数(μ, σ),Critic输出V(s)标量。
怎么做(实现):
import numpy as np
# ==================== Actor-Critic网络结构 ====================
class SharedActorCritic:
"""共享底层的Actor-Critic网络(概念)"""
def __init__(self, state_dim, n_actions, hidden_dim=64):
# 共享特征层
self.W_shared = np.random.randn(state_dim, hidden_dim) * 0.1
self.b_shared = np.zeros(hidden_dim)
# Actor头
self.W_actor = np.random.randn(hidden_dim, n_actions) * 0.1
self.b_actor = np.zeros(n_actions)
# Critic头
self.W_critic = np.random.randn(hidden_dim, 1) * 0.1
self.b_critic = np.zeros(1)
def forward(self, state):
feat = np.maximum(0, state @ self.W_shared + self.b_shared)
# Actor输出
logits = feat @ self.W_actor + self.b_actor
probs = np.exp(logits - np.max(logits))
probs /= probs.sum()
# Critic输出
V = (feat @ self.W_critic + self.b_critic)[0]
return probs, V
print("=== Actor-Critic网络结构 ===")
print("共享结构: 输入 → 共享特征层 → [Actor头, Critic头]")
print(" Actor头: 输出动作概率分布(softmax)")
print(" Critic头: 输出状态价值V(s)(标量)")
print()
print("优势:")
print(" 1. 减少参数量:共享底层特征")
print(" 2. 加速训练:特征表示被两个任务共同优化")
print(" 3. 稳定性:共享特征提供了隐式的正则化")
print()
print("挑战:")
print(" 1. 梯度冲突:Actor和Critic的优化目标可能冲突")
print(" 2. 学习率协调:需要平衡两个头的学习速度")什么用(应用):共享网络结构是A2C、A3C、PPO的标准做法。在OpenAI的PPO实现中,Actor和Critic共享CNN特征提取层,然后分叉为策略头和值函数头。
哪些坑(缺点):共享网络可能导致梯度冲突;Actor和Critic需要不同的学习率(通常Critic更快);如果输入特征差异大,分离网络可能更好。
四、Actor-Critic vs REINFORCE vs Q-learning
是什么(定义):Actor-Critic位于REINFORCE和Q-learning之间——REINFORCE用G_t(高方差、无偏),Q-learning用TD+max(低方差、有偏),Actor-Critic用TD误差(低方差、有偏但更灵活)。
怎么做(实现):
print("=" * 60)
print("REINFORCE vs Actor-Critic vs Q-learning")
print("=" * 60)
print()
print("REINFORCE:")
print(" 更新信号: G_t (蒙特卡洛回报)")
print(" 偏差: 无偏")
print(" 方差: 高")
print(" 在线: 否(需要完整轨迹)")
print(" 动作空间: 连续+离散")
print()
print("Actor-Critic:")
print(" 更新信号: TD误差 / 优势函数")
print(" 偏差: 有偏(Critic估计不准)")
print(" 方差: 低")
print(" 在线: 是(每步更新)")
print(" 动作空间: 连续+离散")
print()
print("Q-learning:")
print(" 更新信号: TD误差(with max)")
print(" 偏差: 有偏(max操作)")
print(" 方差: 低")
print(" 在线: 是(每步更新)")
print(" 动作空间: 仅离散")
print()
print("Actor-Critic = REINFORCE的灵活性 + Q-learning的稳定性")什么用(应用):理解三者的关系有助于选择正确的算法。Actor-Critic是当前最通用的选择,覆盖了大多数RL应用场景。
哪些坑(缺点):Actor-Critic虽然通用,但调参比Q-learning更复杂(需要协调两个学习率);在某些简单离散任务中,Q-learning可能更简单高效。
五、实战:Actor-Critic完整实现
是什么(定义):实现一个完整的Actor-Critic智能体,包含共享底层、分离的Actor和Critic头、以及在线训练循环。
怎么做(实现):
import numpy as np
n_states = 9; n_actions = 4
actions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
def step(state, action):
row, col = state // 3, state % 3
dr, dc = actions[action]
nr = max(0, min(2, row + dr))
nc = max(0, min(2, col + dc))
next_state = nr * 3 + nc
if next_state == 8: r = 10.0
elif next_state == 7: r = -5.0
else: r = -0.1
return next_state, r, next_state in [7, 8]
class FullActorCritic:
def __init__(self, hidden=32):
# 共享特征层
self.Ws = np.random.randn(n_states, hidden) * 0.1; self.bs = np.zeros(hidden)
# Actor
self.Wa = np.random.randn(hidden, n_actions) * 0.1; self.ba = np.zeros(n_actions)
# Critic
self.Wc = np.random.randn(hidden, 1) * 0.1; self.bc = np.zeros(1)
def forward(self, state_vec):
h = np.maximum(0, state_vec @ self.Ws + self.bs)
logits = h @ self.Wa + self.ba
probs = np.exp(logits - np.max(logits)); probs /= probs.sum()
V = (h @ self.Wc + self.bc)[0]
return probs, V, h
def train_episode(self, alpha_a=0.01, alpha_c=0.1, gamma=0.9):
state = 0; total = 0
while state not in [7, 8]:
sv = np.zeros(n_states); sv[state] = 1.0
probs, V, h = self.forward(sv)
action = np.random.choice(n_actions, p=probs)
next_state, reward, done = step(state, action)
nsv = np.zeros(n_states); nsv[next_state] = 1.0
_, V_next, _ = self.forward(nsv)
td = reward + gamma * V_next * (1-done) - V
# Critic更新(简化)
self.Wc += alpha_c * td * h.reshape(-1,1)
self.bc += alpha_c * td
# Actor更新
grad = -probs.copy(); grad[action] += 1
self.Wa += alpha_a * td * h.reshape(-1,1) @ grad.reshape(1,-1)
self.ba += alpha_a * td * grad
total += reward; state = next_state
return total
np.random.seed(42)
ac = FullActorCritic()
rewards = [ac.train_episode() for _ in range(300)]
print(f"完整Actor-Critic: 最后10ep平均={np.mean(rewards[-10:]):.2f}")什么用(应用):这个实现是理解A2C、PPO等现代算法的基础。从这里出发,添加n步回报→A2C,添加裁剪机制→PPO,添加异步训练→A3C。
哪些坑(缺点):简化实现没有使用优化器(如Adam);没有使用经验回放(On-Policy);梯度更新是简化的,实际中需要自动微分。
概念关系图谱
| 概念 | 上位概念 | 核心思想 | 关键公式/方法 | 特点 |
|---|---|---|---|---|
| Actor | 策略网络 | 选择动作 | π_θ(a | s) |
| Critic | 价值网络 | 评估价值 | V_φ(s) | 评价者 |
| TD误差 | 学习信号 | 实际vs预期的差距 | δ=r+γV(s')-V(s) | 驱动更新 |
| 共享网络 | 架构设计 | Actor和Critic共享底层 | 共享特征提取层 | 减少参数 |
| 在线学习 | 学习范式 | 每步都能更新 | TD学习 | 高效率 |
重点答疑
Q1: Actor-Critic和REINFORCE的核心区别?
REINFORCE用蒙特卡洛回报G_t(需要完整轨迹,方差大),Actor-Critic用TD误差/优势函数(可以每步更新,方差低)。Actor-Critic = REINFORCE - 蒙特卡洛 + Critic。
解答:REINFORCE是"事后总结",Actor-Critic是"边做边改"。TD误差让每步都能学习,Critic让学习信号更稳定。
Q2: Actor和Critic的学习率应该如何设置?
通常Critic的学习率大于Actor(如Critic=0.001, Actor=0.0001)。因为Critic需要快速适应变化的策略,而Actor需要保守更新(策略变化太大会导致不稳定)。在PPO中,通常使用相同的Adam优化器但不同的学习率。
解答:Critic学得快,Actor学得慢。Critic是"评论家",需要快速跟上演员的变化;Actor是"演员",改得太快容易"演砸"。
Q3: Actor-Critic中,Critic可以不学V(s)而学Q(s,a)吗?
可以。Critic学Q(s,a)的变体(如DDPG、TD3)适用于连续动作空间。学Q(s,a)的Critic可以直接指导Actor选择更好的动作。但学Q(s,a)需要处理argmax问题(在连续空间中困难),而DDPG通过让Actor直接近似argmax来解决。
解答:学V(s)更简单,学Q(s,a)信息更丰富。SAC学Q(s,a),PPO学V(s),各有应用场景。
Q4: 为什么Actor-Critic是On-Policy的?
因为Actor的更新需要当前策略π_θ的得分函数∇log π_θ(a|s),而得分函数依赖于当前策略。如果使用旧策略的数据,梯度方向会偏差。所以Actor-Critic通常需要On-Policy数据。SAC通过使用Off-Policy的Critic(学Q)和On-Policy的Actor来实现部分Off-Policy。
解答:Actor更新需要"当前策略"的信息,旧数据中的动作概率分布和新策略不同,梯度方向会偏差。这是PPO需要重新收集数据的原因。
章节单词汇总
| 英文 | 音标 | 中文 |
|---|---|---|
| Actor-Critic | /ˈæktər ˈkrɪtɪk/ | 演员-评论家 |
| shared network | /ʃerd ˈnetwɜːrk/ | 共享网络 |
| TD error | /tiː diː ˈerər/ | TD误差 |
| online learning | /ˈɒnlaɪn ˈlɜːrnɪŋ/ | 在线学习 |
| head | /hed/ | 头(网络输出分支) |
| backbone | /ˈbækboʊn/ | 骨干网络 |
面试练习
Q1 [单选] Actor-Critic中,Actor和Critic分别负责什么?
- undefined
解答:Actor(演员)负责决策选择动作,Critic(评论家)负责评估当前状态或动作的价值。
Q2 [单选] Actor-Critic相比REINFORCE的主要优势是什么?
- undefined
解答:Actor-Critic用Critic的低方差估计替代高方差G_t,且可以每步更新(在线学习)。
Q3 [多选] 以下哪些算法属于Actor-Critic架构?
- undefined
解答:A2C、PPO、SAC都是Actor-Critic架构。DQN是基于价值的方法,没有显式的Actor。
Q4 [单选] 在Actor-Critic中,TD误差δ_t = r + γV(s') - V(s)是什么的估计?
- undefined
解答:E[δ_t] = E[Q(s,a) - V(s)] = A(s,a),TD误差是优势函数的无偏估计。
Q5 [多选] 以下关于Actor-Critic的说法,哪些是正确的?
- undefined
解答:A正确,共享网络是标准做法。B正确,Critic使用TD学习。C错误,A2C/PPO是On-Policy的。D正确,Actor用TD误差乘以得分函数更新。