24/11/12 算法笔记<强化学习> Policy Gradient策略梯度
gradient的核心就是每次更新前要重新收集,每个阶段的actor是不一样的.
策略梯度算法的核心思想:
-
策略表示:首先,策略梯度方法需要一个策略,该策略能够根据当前的状态选择一个动作。这个策略通常由一个参数化的函数表示,例如一个神经网络。
-
梯度上升:算法通过梯度上升方法来优化策略的参数,使得期望回报最大化。梯度上升的方向是梯度的反方向,因为我们要最大化回报。
-
采样:在策略梯度方法中,智能体(Agent)会与环境交互,根据当前策略采样一系列的状态-动作对。
-
计算梯度:对于采样得到的数据,算法会计算策略参数相对于期望回报的梯度。这个梯度可以通过蒙特卡洛方法或者时间差分学习(Temporal Difference Learning, TD)来估计。
-
参数更新:最后,算法根据计算出的梯度来更新策略的参数。
策略梯度算法的关键步骤:
-
初始化:选择一个初始策略参数(通常是随机的),并初始化一些必要的变量。
-
交互:智能体根据当前策略与环境交互,收集状态-动作-奖励的样本。
-
计算回报:对于每个采样的样本,计算其回报。在策略梯度中,这通常是通过折扣回报来完成的。
-
估计梯度:对于每个样本,估计策略参数相对于该样本回报的梯度。
-
更新策略:使用梯度上升方法更新策略参数。
-
重复:重复步骤2-5,直到策略收敛或达到一定的迭代次数。
θ是Policy Gradient中模型的参数,给定一组模型参数它去玩n次游戏,一整把游戏的所有state、action和reward我们统称为τ 。τ 作为训练资料被收集起来训练一次模型,参数被update之后,再次去玩游戏,收集数据,之后更新模型。直至模型收敛为止。
如果先不考虑R ( τ^n ) 将每一个state都输入到network里面,整个训练过程就可以被视作是分类问题。这个式子,在 的情况下,采取 会得到高分,所以我们希望出现这个情况的概率越大越好。
而乘上R ( τ^n ) 的话,其实就是将得到的Reward当作是一个系数,放大得到高分Reward的τ 的概率。所以其实整个Policy Gradient并不是模型端有什么新的改进,甚至可以用之前常见的分类网络来做这个事情,它是一个强化学习的训练思路。
我们来看一下它的代码
import torch
import torch.nn as nn
import torch.optim as optim #优化器
import torch.distributions as dist #分布模块
import numpy as np
# 定义策略网络
class PolicyNet(nn.Module):
def __init__(self, state_dim, hidden_dim, action_dim):
super(PolicyNet, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, action_dim)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return torch.softmax(x, dim=-1)
# 定义策略梯度算法
class PolicyGradient:
def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, device):
self.policy_net = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=learning_rate)
self.gamma = gamma
self.device = device
def take_action(self, state):
state = torch.tensor([state], dtype=torch.float).to(self.device)
probs = self.policy_net(state)
action_dist = dist.Categorical(probs)
action = action_dist.sample()
return action.item()
def update(self, transition_dict):
states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
actions = torch.tensor(transition_dict['actions'], dtype=torch.int).to(self.device)
rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).to(self.device)
# 计算折扣回报
G = 0
returns = []
for r in rewards[::-1]:
G = r + self.gamma * G
returns.insert(0, G)
returns = torch.tensor(returns).to(self.device)
# 梯度上升
self.optimizer.zero_grad()
for i in range(len(states)):
state = states[i]
action = actions[i]
log_prob = torch.log(self.policy_net(state)[action])
loss = -log_prob * returns[i]
loss.backward()
self.optimizer.step()
# 主函数
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
state_dim = 4 # 状态空间维度
hidden_dim = 128 # 隐藏层维度
action_dim = 2 # 动作空间维度
learning_rate = 0.01 # 学习率
gamma = 0.99 # 折扣因子
num_episodes = 1000 # 训练回合数
agent = PolicyGradient(state_dim, hidden_dim, action_dim, learning_rate, gamma, device)
for episode in range(num_episodes):
state = env.reset()
done = False
rewards = []
states = []
actions = []
while not done:
action = agent.take_action(state)
next_state, reward, done, _ = env.step(action)
rewards.append(reward)
states.append(state)
actions.append(action)
state = next_state
# 将回合数据打包成字典
transition_dict = {
'states': states,
'actions': actions,
'rewards': rewards
}
# 更新策略
agent.update(transition_dict)
if __name__ == "__main__":
main()
我们来分析每段代码
1.定义策略网络
class PolicyNet(nn.Module):
def __init__(self, state_dim, hidden_dim, action_dim):
super(PolicyNet, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, action_dim)
def forward(self, x):
x = torch.relu(self.fc1(x)) #这行代码将输入数据 x 通过第一个全连接层 fc1,然后应用ReLU激活函数。ReLU函数可以增加非线性,并且有助于解决梯度消失问题。
x = self.fc2(x) #这行代码将经过ReLU激活的输出作为第二个全连接层 fc2 的输入。
return torch.softmax(x, dim=-1)
层这样分布的好处包括:
- 简单性:这个网络结构非常简单,易于实现和理解。
- 灵活性:通过调整
hidden_dim
的大小,可以灵活地控制网络的容量。 - 非线性:ReLU激活函数引入非线性,使得网络可以学习复杂的状态-动作映射关系。
- 概率输出:softmax输出使得网络可以直接预测每个动作的概率,这对于强化学习中的决策过程非常有用。
- 适用于多种任务:这种结构不仅适用于强化学习,也可以用于其他需要预测概率分布的任务。
2.定义策略梯度算法类
class PolicyGradient:
def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, device):
#策略网络
self.policy_net = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=learning_rate)
self.gamma = gamma
self.device = device
这个类初始化策略梯度算法,包括策略网络、优化器、折扣因子和设备(CPU或GPU)。优化器使用Adam算法,这是一种常用的梯度下降优化器。
什么叫策略网络?
策略网络(Policy Network)是强化学习(Reinforcement Learning, RL)中的一个概念,它是一种用于决策的神经网络,其目的是学习一个策略(policy),这个策略能够根据当前的环境状态(state)来选择一个最优的动作(action)。在强化学习中,策略网络通常被用来近似或学习一个策略函数,这个函数将状态映射到动作的概率分布。
3.策略网络采样动作
def take_action(self, state):
state = torch.tensor([state], dtype=torch.float).to(self.device)
probs = self.policy_net(state)
action_dist = dist.Categorical(probs)
action = action_dist.sample()
return action.item()
这个方法根据当前策略网络和给定的状态,采样一个动作。它首先将状态转换为张量,然后通过策略网络获取动作的概率分布,接着使用这个分布来采样一个动作。
4.更新策略网络
def update(self, transition_dict):
states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
actions = torch.tensor(transition_dict['actions'], dtype=torch.int).to(self.device)
rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).to(self.device)
# 计算折扣回报
G = 0
returns = []
for r in rewards[::-1]:
G = r + self.gamma * G
returns.insert(0, G)
returns = torch.tensor(returns).to(self.device)
# 梯度上升
self.optimizer.zero_grad()
for i in range(len(states)):
state = states[i]
action = actions[i]
log_prob = torch.log(self.policy_net(state)[action])
loss = -log_prob * returns[i]
loss.backward()
self.optimizer.step()
这个方法用于更新策略网络。它首先将转换数据转换为张量,然后计算折扣回报。接着,它使用梯度上升来更新策略网络的参数。对于每个样本,它计算策略网络输出的对数概率和回报的乘积,然后进行反向传播。
5.主函数
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
state_dim = 4 # 状态空间维度
hidden_dim = 128 # 隐藏层维度
action_dim = 2 # 动作空间维度
learning_rate = 0.01 # 学习率
gamma = 0.99 # 折扣因子
num_episodes = 1000 # 训练回合数
agent = PolicyGradient(state_dim, hidden_dim, action_dim, learning_rate, gamma, device)
for episode in range(num_episodes):
state = env.reset()
done = False
rewards = []
states = []
actions = []
while not done:
action = agent.take_action(state)
next_state, reward, done, _ = env.step(action)
rewards.append(reward)
states.append(state)
actions.append(action)
state = next_state
# 将回合数据打包成字典
transition_dict = {
'states': states,
'actions': actions,
'rewards': rewards
}
# 更新策略
agent.update(transition_dict)
if __name__ == "__main__":
main()
主函数设置了一些超参数,包括状态空间维度、隐藏层维度、动作空间维度、学习率、折扣因子和训练回合数。然后,它创建了一个策略梯度算法的实例,并开始训练过程。在每个回合中,智能体与环境交互,收集状态、动作和奖励的数据,然后使用这些数据来更新策略网络。
缺点:
-
高方差: 策略梯度方法通常具有高方差,这意味着学习过程可能会非常不稳定,导致策略性能的大幅波动。
-
探索与利用的权衡: 虽然策略梯度方法内置了探索机制,但在实践中,找到合适的探索与利用平衡可能很困难,特别是在早期训练阶段。
-
计算成本: 策略梯度方法可能需要大量的采样来估计梯度,这可能导致高计算成本,尤其是在复杂的环境中。
-
对策略表示的敏感性: 策略梯度方法的性能可能对策略表示的选择非常敏感,包括网络架构、激活函数和参数初始化等。
-
难以处理稀疏奖励: 在稀疏奖励的环境中,策略梯度方法可能会遇到挑战,因为智能体可能需要很长时间才能学会将动作与奖励联系起来。
-
策略崩溃: 在某些情况下,策略梯度方法可能会遇到策略崩溃的问题,即策略在更新过程中突然变得非常糟糕,这通常是由于梯度估计的不准确或策略更新步长过大。
-
难以调试和分析: 由于策略梯度方法的复杂性,调试和分析学习过程可能非常困难,特别是当策略表现不佳时。