Python实现强化学习PPO算法详解(第5部分)

这篇是PPO算法代码分析的最后一篇,如果要去跑代码,就将我这几篇挨着复制到运行软件中即可,要注意好缩进!

五、定义环境

import gym
import os
import numpy as np


def all_seed(env, seed=2):
    ''' 万能的seed函数
    '''
    if seed == 0:
        return
    env.seed(seed)  # env config
    np.random.seed(seed)
    random.seed(seed)
    torch.manual_seed(seed)  # config for CPU
    torch.cuda.manual_seed(seed)  # config for GPU
    os.environ['PYTHONISTA'] = str(seed)  # config for python scripts
    # config for cudnn
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.enabled = False


def env_agent_config(cfg):
    env = gym.make(cfg.env_name)  # 创建环境
    all_seed(env, seed=cfg.seed)
    n_states = env.observation_space.shape[0]
    n_actions = env.action_space.n
    print(f"状态空间维度:{n_states},动作空间维度:{n_actions}")
    # 更新n_states和n_actions到cfg参数中
    setattr(cfg, 'n_states', n_states)
    setattr(cfg, 'n_actions', n_actions)
    agent = Agent(cfg)
    return env, agent

Gym是环境,os是操作系统相关内容,numpy是数值操作相关。

env.seed(seed):设置环境的随机种子为指定的 seed 值。

之后设置其他随机种子:设置环境的、numpy 库、Python 内置的随机数生成器random的、PyTorch 在CPU上的、PyTorch 在GPU上随机种子。os.environ['PYTHONHASHSEED'] = str(seed):设置 Python 脚本的哈希种子。确保在不同的运行环境中相同的输入数据生成相同的哈希值。

针对 cudnn 进行一些配置,torch.backends.cudnn.deterministic = True: 这一行代码告诉 cuDNN 库在执行卷积等操作时使用确定性算法,即每次运行时相同输入会产生相同输出。这有助于确保在相同条件下运行时的结果一致性,尤其是在使用随机数种子控制的情况下。torch.backends.cudnn.benchmark = False: 将此参数设置为 False 可以禁用 cuDNN 的自动寻找最适合当前配置的卷积算法功能。关闭 benchmark 模式可能会增加一些计算时间,但有助于确保结果的可重复性。torch.backends.cudnn.enabled = False: 这行代码可以显式地禁用 cuDNN 加速。在某些情况下,可能需要禁用 cuDNN 加速以避免一些特定问题,或者用于调试目的。

之后定义一个新的函数,先创建环境env,之后设置所有需要的随机种子。获取环境的状态空间维度和动作空间维度(获取方式不同,获取状态是连续空间,所以需要用shape[0],对于动作的离散空间,用n就行了)。接着使用 setattr 函数将 n_states 和 n_actions 更新到配置参数 cfg 中(setattr 的作用是将 n_states 和 n_actions 这两个变量的值分别设置到对象 cfg 中)。最后创建智能体对象 agent,并返回环境和智能体对象。

注意:

(1)"种子"通常指的是一个用来初始化随机数生成器的数值。随机数生成器根据种子生成一个伪随机数序列。如果使用相同的种子,那么生成的随机数序列将是确定性的,也就是说每次生成的随机数都是一样的。(简单来说,我们对很多东西进行初始化时,需要给一个初始值,但是每次随机的初始值都不一样的话,结果就很难复现出来,所以我们加入一个种子值,这里设定的是seed=1,所以每次选择的随机数序列也就确定了,随机就变成了伪随机。这种机制确保了实验的可重现性,使得在同样的条件下,每次运行得到的结果都是相同的,有助于调试和验证算法的正确性。)

六、设置参数

import matplotlib.pyplot as plt
import seaborn as sns


class Config:
    def __init__(self) -> None:
        self.env_name = "CartPole-v1"  # 环境名字
        self.new_step_api = True  # 是否用gym的新api
        self.algo_name = "PPO"  # 算法名字
        self.mode = "train"  # train or test
        self.seed = 2  # 随机种子
        self.device = "cuda"  # device to use
        self.train_eps = 200  # 训练的回合数
        self.test_eps = 20  # 测试的回合数
        self.max_steps = 200  # 每个回合的最大步数
        self.eval_eps = 5  # 评估的回合数
        self.eval_per_episode = 10  # 评估的频率

        self.gamma = 0.99  # 折扣因子
        self.k_epochs = 4  # 更新策略网络的次数
        self.actor_lr = 0.0003  # actor网络的学习率
        self.critic_lr = 0.0003  # critic网络的学习率
        self.eps_clip = 0.2  # epsilon-clip
        self.entropy_coefficient = 0.01  # entropy的系数
        self.update_freq = 100  # 更新频率
        self.actor_hidden_dim = 256  # actor网络的隐藏层维度
        self.critic_hidden_dim = 256  # critic网络的隐藏层维度


def smooth(data, weight=0.9):
    '''用于平滑曲线,类似于Tensorboard中的smooth曲线
    '''
    last = data[0]
    smoothed = []
    for point in data:
        smoothed_val = last * weight + (1 - weight) * point  # 计算平滑值
        smoothed.append(smoothed_val)
        last = smoothed_val
    return smoothed


def plot_rewards(rewards, cfg, tag='train'):
    ''' 画图
    '''
    sns.set()
    plt.figure()  # 创建一个图形实例,方便同时多画几个图
    plt.title(f"{tag}ing curve on {cfg.device} of {cfg.algo_name} for {cfg.env_name}")
    plt.xlabel('episodes')
    plt.plot(rewards, label='rewards')
    plt.plot(smooth(rewards), label='smoothed')
    plt.legend()
    plt.show()  # 显示绘制的图形

用config类创造实例,然后进行初始化,设置好参数。

定义smooth函数,对数据进行平滑处理。首先将数据的第一个点作为初始值,然后创建一个空列表用于存放平滑后的数值,并遍历原始数据中的每个数据点,之后使用指数加权移动平均计算平滑值,将平滑值添加到平滑列表中,最后更新last为当前的平滑值,以便下一次循环使用,循环结束后就返回平滑后的数据。

plot_rewards 函数用于绘制奖励曲线图,其中包括了原始奖励曲线和经过平滑处理后的奖励曲线。函数内部使用了 seaborn 库设置样式,并调用了 plt 对象来创建图形、设置标题、标签,并绘制曲线。

注意:

(1)self.new_step_api = False # 是否用gym的新api,对于这句而言,self.new_step_api 可能是用来控制是否使用 Gym 最新版本中引入的新的环境交互方法(新的 API)。不使用可能是为了兼容之前的代码或特定的需求。

(2)import matplotlib.pyplot as plt和import seaborn as sns是给画图设置样式,并让其可视化的。

七、开始训练

# 获取参数
cfg = Config()
# 训练
env, agent = env_agent_config(cfg)
best_agent, res_dic = train(cfg, env, agent)

plot_rewards(res_dic['rewards'], cfg, tag="train")
# 测试
res_dic = test(cfg, env, best_agent)
plot_rewards(res_dic['rewards'], cfg, tag="test")  # 画出结果

创建一个 Config 类的实例,用于存储参数配置,

根据参数配置创建环境和智能体。

使用训练函数 train 对代理进行训练,并返回最佳智能体和训练结果奖励。调用 plot_rewards 函数绘制训练奖励曲线。

使用测试函数 test 对最佳智能体进行测试。再次调用 plot_rewards 函数绘制测试奖励曲线。

当然,代码最好再整体看一下,理解一下各部分之间的关系

(以上代码分析均为个人理解,如果有误,欢迎指正讨论!)

作者:沈念辰

物联沃分享整理
物联沃-IOTWORD物联网 » Python实现强化学习PPO算法详解(第5部分)

发表回复