Actor-Critic 算法
📅 发表于 2025/08/31
🔄 更新于 2025/08/31
👁️ -- 次访问
📝 0 字
⏳ 0 分钟
rl-theory
#Actor
#Critic
#Q Actor-Critic
#A2C
#A3C
#Advantage
#Actor loss
#Critic Loss
#GAE
#λ-return
#单步TD
#MC估计
核心思想
学习一个策略
,得到尽可能高的回报和环境交互采样
估计当前策略的价值
,即评估演员的好坏优点
高方差问题
策略梯度
和采样
策略的价值估计
,带来了更稳定的估计核心思想
Critic
:Q函数MC策略梯度
使用累计回报核心思想
优势函数=Q-V
作为权重,动作是Q,基线是V 同一状态下的基线
, 而非所有状态的均值TD误差近似优势函数
无需估计Q
r的方差相比G的方差小很多
策略梯度
loss
actor_loss
= -(log_probs * advantages.detach()).mean()
TD error 均方误差
,即使二者更加接近即可 critic_loss
= advantages.pow(2).mean()
优点
技巧1:估计V和Actor2个网络
技巧2:探索机制
输出设置约束
,使分布的熵不要太小,希望不同动作的采样概率平均一些
技巧3:优势函数值域固定到[-1,1]
Actor、Critic 定义
# 分开定义
class Critic(nn.Module):
''' 估计状态价值,输出标量
'''
def __init__(self,state_dim):
self.fc1 = nn.Linear(state_dim, 256)
self.fc2 = nn.Linear(256, 256)
self.fc3 = nn.Linear(256, 1)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
value = self.fc3(x)
return value
class Actor(nn.Module):
''' 采样动作,输出logits_p,动作概率
'''
def __init__(self, state_dim, action_dim):
self.fc1 = nn.Linear(state_dim, 256)
self.fc2 = nn.Linear(256, 256)
self.fc3 = nn.Linear(256, action_dim)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
logits_p = F.softmax(self.fc3(x), dim=1)
return logits_p
# 合在一起定义
class ActorCritic(nn.Module):
''' 输入状态,输出动作概率和状态价值
'''
def __init__(self, state_dim, action_dim):
self.fc1 = nn.Linear(state_dim, 256)
self.fc2 = nn.Linear(256, 256)
self.action_layer = nn.Linear(256, action_dim)
self.value_layer = nn.Linear(256, 1)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
logits_p = F.softmax(self.action_layer(x), dim=1)
value = self.value_layer(x)
return logits_p, value
Agent:采样动作、计算优势函数、计算loss、策略更新
from torch.distributions import Categorical
class Agent:
def __init__(self):
self.model = ActorCritic(state_dim, action_dim)
def sample_action(self,state):
''' 动作采样
'''
state = torch.tensor(state, device=self.device, dtype=torch.float32)
# 策略网络输出动作概率分布
logits_p, value = self.model(state)
dist = Categorical(logits_p)
# 依概率采样一个分布
action = dist.sample()
return action
def _compute_returns(self, rewards, dones):
''' 计算回报,做归一化
'''
returns = []
discounted_sum = 0
for reward, done in zip(reversed(rewards), reversed(dones)):
if done:
discounted_sum = 0
# 折扣回报
discounted_sum = reward + (self.gamma * discounted_sum)
returns.insert(0, discounted_sum)
# 回报归一化
returns = torch.tensor(returns, device=self.device, dtype=torch.float32).unsqueeze(dim=1)
returns = (returns - returns.mean()) / (returns.std() + 1e-5) # 1e-5 to avoid division by zero
return returns
def compute_advantage(self):
''' 计算优势
'''
# 从经验池中采样数据:动作概率、状态、回报、结束
logits_p, states, rewards, dones = self.memory.sample()
# 根据rewards 计算回报
returns = self._compute_returns(rewards, dones)
states = torch.tensor(states, device=self.device, dtype=torch.float32)
# 当前模型去估计状态价值
logits_p, values = self.model(states)
# 实际回报 - 状态价值 作为优势
advantages = returns - values
return advantages
def compute_loss(self):
'''计算损失函数
'''
# 采样数据
logits_p, states, rewards, dones = self.memory.sample()
returns = self._compute_returns(rewards, dones)
states = torch.tensor(states, device=self.device, dtype=torch.float32)
# 估计价值V、计算策略logits_p
logits_p, values = self.model(states)
# 计算advantages
advantages = returns - values
dist = Categorical(logits_p)
# 计算log_prob
log_probs = dist.log_prob(actions)
# 注意这里策略损失反向传播时不需要优化优势函数,因此需要将其 detach 掉
actor_loss = -(log_probs * advantages.detach()).mean()
# critic loss
critic_loss = advantages.pow(2).mean()
return actor_loss, critic_loss
背景
核心思想
1个全局网络
+多个worker并行探索训练
同策略算法
,虽然看起来像异策略 优点
平行探索
,保持训练稳定性背景
λ-return
移动加权平均
,平衡了TD和MC方法
,也平衡了偏差和方差
广义优势估计核心思想
总结
TD误差
广义优势估计
单步TD
MC估计
优点