强化学习策略梯度(Policy Gradient)太空火箭着陆示例,代码参照李宏毅HW12,新版修正
代码注释解读说明,运行于notebook
首先我们安装依赖,避免报错
1. 更新并安装系统依赖,忽略符号链接警告
!apt update -o Dir::Etc::sourcelist="/etc/apt/sources.list.d/*.list" -o Dir::Etc::sourceparts="-" -y
!apt install -y libgl1-mesa-glx libosmesa6 libglew2.2 python3-opengl xvfb -o Dpkg::Options::="--force-overwrite"
2. 安装现代版依赖,彻底避免 setup.py 报错
!pip install gymnasium[box2d] pygame pyvirtualdisplay tqdm numpy torch
3.导入我们需要的库
from pyvirtualdisplay import Display
#这个库,常用来刷新强化学习,实时更新智能体玩游戏的画面
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()%matplotlib inline
import matplotlib.pyplot as pltfrom IPython import display
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.distributions import Categorical
#表示概率分布
from tqdm.notebook import tqdm
4.固定随机种子,保证实验可复现
seed = 543 # Do not change this固定随机种子,保证实验可复现
def fix(env, seed):
env.seed(seed) #给环境本身设置种子
env.action_space.seed(seed) #给动作空间设置种子
torch.manual_seed(seed) #给pytorch CPU算子设置种子
torch.cuda.manual_seed(seed) #给pytorch 单GPU算子设置种子
torch.cuda.manual_seed_all(seed)#Pytorch 所有GPU算子设置种子
np.random.seed(seed) #给Numpy 随机数生成器设置种子
random.seed(seed) #random 模块设置种子
# torch.set_deterministic(True)
torch.backends.cudnn.benchmark = False#关闭 cudnn 自动优化:避免不同硬件上的算法选择不同
torch.backends.cudnn.deterministic = True#强制cudnn 使用确定性算法:强制cuDNN使用确定性算法,保证卷积等操作结果完全可复现
%%capture
import gymnasium as gym #强化学习的标准游乐场,提供了各种经典环境
import random
env = gym.make('LunarLander-v2') #创建月球着陆器环境
fix(env, seed) #调用fix函数,锁死随机数
观测空间,这是一个八维向量连续空间,按照顺序,(0)火箭水平坐标范围,(1)垂直坐标范围,(2)水平速度,(3)垂直速度,(4)火箭倾斜角度,(5),倾斜角速度,(6)左腿是否接触地面,(7)右腿是否接触地面
print(env.observation_space)
动作空间,是一个离散空间,四个操作,0不操作,1启动左侧引擎,2启动主引擎,3启动右引擎
print(env.action_space)
重置环境,获得初始状态,每局游戏开始时,必须调用的函数,返回8维浮点数向量,代表火箭初始状态,是对观测空间的初始化
initial_state = env.reset()
random_action = env.action_space.sample()
#动作空间随机采样个动作
print(random_action)#输出0到3的动作
#observation:执行动作后新的八维状态向量; reward:这一步获得的即时奖励; done:bool表示这一局游戏是否结束; info:调试用的额外信息
observation, reward, done, info = env.step(random_action)
接下来我们看看样例:
env.reset()
img = plt.imshow(env.render(mode='rgb_array'))
#将当前环境渲染成一个RGB图像数组
done = False
while not done:
action = env.action_space.sample() #四个动作里随机选一个,随机智能体的决策逻辑
observation, reward, done, _ = env.step(action)img.set_data(env.render(mode='rgb_array')) #更新img对象的像素数据
display.display(plt.gcf())#plt.gcf()可获得当前plt图像,display将图像显示在notebook中
display.clear_output(wait=True) #清除上一步的输出然后等待新的图像显示

接下来我们开始定义模型:
class PolicyGradientNetwork(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(8, 16)
self.fc2 = nn.Linear(16, 16)
self.fc3 = nn.Linear(16, 4)def forward(self, state):
hid = torch.tanh(self.fc1(state))
hid = torch.tanh(self.fc1(hid))
return F.softmax(self.fc3(hid), dim=-1)
class PolicyGradientAgent():
def __init__(self, network):
self.network = network
self.optimizer = optim.SGD(self.network.parameters(), lr=0.001)
def forward(self, state):
return self.network(state)def sample(self, state):
action_prob = self.network(torch.FloatTensor(state))#将状态输出网络中,得到四个动作的决策概率
action_dist = Categorical(action_prob)#把概率向量包装成了一个分布,方便按概率采样
action = action_dist.sample()#根据概率分布随机选动作,概率越高越可能被选中
log_prob = action_dist.log_prob(action)#已经抽好的动作,原来的的概率取对数。这个对数概率值,目的是为了参与损失计算,对数概率的数值稳定性更强
return action.item(), log_prob
def learn(self, log_probs, rewards):
loss = (-log_probs * rewards).sum()self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
network = PolicyGradientNetwork()
agent = PolicyGradientAgent(network)
开始训练,大体上与深度学习训练步骤相同
agent.network.train()
EPISODE_PER_BATCH = 5# 每收集五局游戏的数据,更新一次智能体
NUM_BATCH = 500# 总共更新500次,500x5=2500局游戏
avg_total_rewards, avg_final_rewards = [], []
#记录每一批(5)局的平均奖励,total是单批总奖励平均值,单批最后一步奖励平均值(降落或坠毁)
prg_bar = tqdm(range(NUM_BATCH))
#进度条显示
for batch in prg_bar:log_probs, rewards = [], [] #存储每一步的对数概率与奖励
total_rewards, final_rewards = [], [] #总奖励和最后的奖励# collect trajectory
for episode in range(EPISODE_PER_BATCH):
state = env.reset()
total_reward, total_step = 0, 0
seq_rewards = []
while True:action, log_prob = agent.sample(state)
next_state, reward, done, _ = env.step(action)log_probs.append(log_prob)
# seq_rewards.append(reward)
state = next_state
total_reward += reward
total_step += 1
rewards.append(reward) # change here
if done:
final_rewards.append(reward)
total_rewards.append(total_reward)
breakprint(f"rewards looks like ", np.shape(rewards))
avg_total_reward = sum(total_rewards) / len(total_rewards)
avg_final_reward = sum(final_rewards) / len(final_rewards)avg_total_rewards.append(avg_total_reward)
avg_final_rewards.append(avg_final_reward)
prg_bar.set_description(f"Total: {avg_total_reward: 4.1f}, Final: {avg_final_reward: 4.1f}")
rewards = (rewards - np.mean(rewards)) / (np.std(rewards) + 1e-9)
# 奖励归一化,均值为0,方差唯一,能让训练更稳定
agent.learn(torch.stack(log_probs), torch.from_numpy(rewards))
#将对数概率张量堆叠成一个二维张量<-->[tensor(...),tensor(...)...]-->tensor([.........])
print("logs prob looks like ", torch.stack(log_probs).size())
print("torch.from_numpy(rewards) looks like ", torch.from_numpy(rewards).size())
看一下总奖励
plt.plot(avg_total_rewards)
plt.title("Total Rewards")
plt.show()

看一下最后一次奖励,意味着着陆的身位
plt.plot(avg_final_rewards)
plt.title("Final Rewards")
plt.show()

最后我们开始测试
fix(env, seed)
agent.network.eval()
NUM_OF_TEST = 5
test_total_reward = []
action_list = []
for i in range(NUM_OF_TEST):
actions = []
state = env.reset()img = plt.imshow(env.render(mode='rgb_array'))
total_reward = 0
done = False
while not done:
action, _ = agent.sample(state)
actions.append(action)
state, reward, done, _ = env.step(action)total_reward += reward
img.set_data(env.render(mode='rgb_array'))
display.display(plt.gcf())
display.clear_output(wait=True)
print(total_reward)
test_total_reward.append(total_reward)action_list.append(actions)

更多推荐

所有评论(0)