专业教程

上一页是通过调用runner来直接运行算法。为了更好地帮助用户理解“玄策”的内部实现流程, 从而便于进一步做算法开发和实现用户自己的强化学习任务,下面以PPO算法训练MuJoCo环境任务为例, 详细介绍如何从底层地调用API实现强化学习模型训练。



步骤1:准备YAML文件,配置参数

创建 mujoco.yaml 文件,并指定相关参数,如下所示:

dl_toolbox: "torch"  # The deep learning toolbox. Choices: "torch", "mindspore", "tensorlayer"
project_name: "XuanCe_Benchmark"
logger: "tensorboard"  # Choices: tensorboard, wandb.
wandb_user_name: "your_user_name"
render: False
render_mode: 'rgb_array' # Choices: 'human', 'rgb_array'.
test_mode: False
device: "cuda:0"

agent: "PPO_Clip"  # choice: PPO_Clip, PPO_KL
env_name: "MuJoCo"
vectorize: "Dummy_Gym"
runner: "DRL"

representation_hidden_size: [256,]
actor_hidden_size: [256,]
critic_hidden_size: [256,]
activation: "LeakyReLU"

seed: 79811
parallels: 16
running_steps: 1000000
n_steps: 256
n_epoch: 16
n_minibatch: 8
learning_rate: 0.0004

use_grad_clip: True

vf_coef: 0.25
ent_coef: 0.0
target_kl: 0.001  # for PPO_KL agent
clip_range: 0.2  # for PPO_Clip agent
clip_grad_norm: 0.5
gamma: 0.99
use_gae: True
gae_lambda: 0.95
use_advnorm: True

use_obsnorm: True
use_rewnorm: True
obsnorm_range: 5
rewnorm_range: 5

test_steps: 10000
eval_interval: 5000
test_episode: 5
log_dir: "./logs/ppo/"
model_dir: "./models/ppo/"


步骤2:读取参数

该部分主要包括参数读取、环境创建、模型创建、模型训练等环节。首先创建 ppo_mujoco.py 文件,代码编写分为如下步骤:

步骤2.1 解析终端命令参数

定义如下函数 parse_args(),利用Python包 argparser 读取终端指令,获取指令参数。

import argparser

def parse_args():
    parser = argparse.ArgumentParser("Example of XuanCe.")
    parser.add_argument("--method", type=str, default="ppo")
    parser.add_argument("--env", type=str, default="mujoco")
    parser.add_argument("--env-id", type=str, default="InvertedPendulum-v4")
    parser.add_argument("--test", type=int, default=0)
    parser.add_argument("--device", type=str, default="cuda:0")
    parser.add_argument("--benchmark", type=int, default=1)
    parser.add_argument("--config", type=str, default="./ppo_mujoco_config.yaml")

    return parser.parse_args()

步骤2.2 读取参数

首先通过调用步骤2.1中的 parse_args() 函数读取终端指令参数,然后获取步骤1中的配置参数。

from xuance import get_arguments

if __name__ == "__main__":
parser = parse_args()
args = get_arguments(method=parser.method,
                     env=parser.env,
                     env_id=parser.env_id,
                     config_path=parser.config,
                     parser_args=parser)
run(args)

在该步骤中,调用了“玄策”中的 get_arguments() 函数。在该函数中,首先根据 envenv_id 变量组合,从xuance/configs/路径中查询是否有可读取的参数。 如已经有默认的参数,则全部读取。接着继续从 config.path 路径下索引步骤1中的配置文件,并读取.yaml文件中的所有参数。最后读取 parser 中的全部参数。 三次读取中,若遇到相同变量名,则以后者参数为准进行更新。最终, get_arguments() 函数将返回 args 变量,包含所有参数信息,输入 run() 函数中。



步骤3:定义run(),创建模型,运行算法

定义 run() 函数,输入为步骤2中得到的 args 变量。在函数中,实现了环境创建,实例化representation、policy、agent等模块,并实现训练。 以下是带注释的run()函数定义示例:

import os
from copy import deepcopy
import numpy as np
import torch.optim

from xuance.common import space2shape
from xuance.environment import make_envs
from xuance.torch.utils.operations import set_seed
from xuance.torch.utils import ActivationFunctions

def run(args):
    agent_name = args.agent  # 获取智能体名称
    set_seed(args.seed)  # 设置随机种子

    # prepare directories for results
    args.model_dir = os.path.join(os.getcwd(), args.model_dir, args.env_id)  # 模型存储/读取路径
    args.log_dir = os.path.join(args.log_dir, args.env_id)  # 日志文件存储路径

    # build environments
    envs = make_envs(args)  # 创建强化学习环境
    args.observation_space = envs.observation_space  # 获取观测空间
    args.action_space = envs.action_space  # 获取动作空间
    n_envs = envs.num_envs  # 获取并行环境个数

    # prepare representation
    from xuance.torch.representations import Basic_MLP  # 导入表征器类
    representation = Basic_MLP(input_shape=space2shape(args.observation_space),
                            hidden_sizes=args.representation_hidden_size,
                            normalize=None,
                            initialize=torch.nn.init.orthogonal_,
                            activation=ActivationFunctions[args.activation],
                            device=args.device)  # 创建MLP表征器

    # prepare policy
    from xuance.torch.policies import Gaussian_AC_Policy  # 导入策略类
    policy = Gaussian_AC_Policy(action_space=args.action_space,
                                representation=representation,
                                actor_hidden_size=args.actor_hidden_size,
                                critic_hidden_size=args.critic_hidden_size,
                                normalize=None,
                                initialize=torch.nn.init.orthogonal_,
                                activation=ActivationFunctions[args.activation],
                                device=args.device)  # 创建服从高斯分布的随机策略

    # prepare agent
    from xuance.torch.agents import PPOCLIP_Agent, get_total_iters  # 导入智能体类
    optimizer = torch.optim.Adam(policy.parameters(), args.learning_rate, eps=1e-5)  # 创建优化器
    lr_scheduler = torch.optim.lr_scheduler.LinearLR(optimizer, start_factor=1.0, end_factor=0.0,
                                                    total_iters=get_total_iters(agent_name, args))  # 创建学习率衰减器
    agent = PPOCLIP_Agent(config=args,
                          envs=envs,
                          policy=policy,
                          optimizer=optimizer,
                          scheduler=lr_scheduler,
                          device=args.device)  # 创建PPO智能体

    # start running
    envs.reset()  # 环境初始化
    if args.benchmark:  # run benchmark
        def env_fn():  # 创建测试环境,用于每个阶段训练结束后,随机初始化测试环境并进行测试
            args_test = deepcopy(args)  # 拷贝原有参数
            args_test.parallels = args_test.test_episode  # 更改并行环境数量为测试回合数
            return make_envs(args_test)  # 返回实例化测试环境

        train_steps = args.running_steps // n_envs  # 获取智能体总的运行步数
        eval_interval = args.eval_interval // n_envs  # 确定每轮训练步数
        test_episode = args.test_episode  # 获取测试回合数
        num_epoch = int(train_steps / eval_interval)  # 确定训练轮数

        test_scores = agent.test(env_fn, test_episode)  # 第0步测试,得到测试结果
        best_scores_info = {"mean": np.mean(test_scores),  # 平均累积回合奖励
                            "std": np.std(test_scores),  # 累积回合奖励方差
                            "step": agent.current_step}  # 当前步数
        for i_epoch in range(num_epoch):  # 开始轮回训练
            print("Epoch: %d/%d:" % (i_epoch, num_epoch))  # 打印第i_epoch轮训练的基本信息
            agent.train(eval_interval)  # 训练eval_interval步
            test_scores = agent.test(env_fn, test_episode)  # 测试test_episode个回合

            if np.mean(test_scores) > best_scores_info["mean"]:  # 若当前测试结果为历史最高,则保存模型
                best_scores_info = {"mean": np.mean(test_scores),
                                    "std": np.std(test_scores),
                                    "step": agent.current_step}
                # save best model
                agent.save_model(model_name="best_model.pth")
        # end benchmarking
        print("Best Model Score: %.2f, std=%.2f" % (best_scores_info["mean"], best_scores_info["std"]))  # 结束benchmark训练,打印最终结果
    else:
        if not args.test:  # train the model without testing
            n_train_steps = args.running_steps // n_envs  # 确定总的运行步数
            agent.train(n_train_steps)  # 直接训练模型
            agent.save_model("final_train_model.pth")  # 保存最终训练结果
            print("Finish training!")  # 结束训练
        else:  # test a trained model
            def env_fn():
                args_test = deepcopy(args)
                args_test.parallels = 1
                return make_envs(args_test)

            agent.render = True
            agent.load_model(agent.model_dir_load, args.seed)  # 加载模型文件
            scores = agent.test(env_fn, args.test_episode)  # 测试模型
            print(f"Mean Score: {np.mean(scores)}, Std: {np.std(scores)}")
            print("Finish testing.")  # 结束测试

    # the end.
    envs.close()  # 关闭环境
    agent.finish()  # 结束实验

完成以上三个步骤后,可在终端运行 ppo_mujoco.py Python文件,训练模型:

$ python ppo_mujoco.py --method ppo --env mujoco --env-id Ant-v4

该实例的完整代码见如下链接:

https://github.com/agi-brain/xuance/examples/ppo/ppo_mujoco.py