LoginSignup
1
2

More than 1 year has passed since last update.

ChainerRLのサンプルを読んでみる(examples/gym/train_dqn_gym.py編)

Last updated at Posted at 2018-01-02

ChainerRLのサンプルを読んでみる(examples/ale/train_dqn_ale.py編)の続きです。
(2017/12/31現在、ChainerRLのAPIドキュメントが簡素なので、解説してみる記事です。)

今度は、OpenAI Gym の方のサンプルを見てみます。

examples/gym/train_dqn_gym.py

例によって、Trainingの部分だけを切り出してみました。が、そんなに短くなっていないです。

train_dqn_gym_simple.py
def main(args):
    args.outdir = experiments.prepare_output_dir(
        args, args.outdir, argv=sys.argv)
    print('Output files are saved in {}'.format(args.outdir))

    def clip_action_filter(a):
        return np.clip(a, action_space.low, action_space.high)

    def make_env(for_eval):
        env = gym.make(args.env)
        if args.monitor:
            env = gym.wrappers.Monitor(env, args.outdir)
        if isinstance(env.action_space, spaces.Box):
            misc.env_modifiers.make_action_filtered(env, clip_action_filter)
        if not for_eval:
            misc.env_modifiers.make_reward_filtered(
                env, lambda x: x * args.reward_scale_factor)
        if ((args.render_eval and for_eval) or
                (args.render_train and not for_eval)):
            misc.env_modifiers.make_rendered(env)
        return env

    env = make_env(for_eval=False)
    timestep_limit = env.spec.tags.get(
        'wrapper_config.TimeLimit.max_episode_steps')
    obs_space = env.observation_space
    obs_size = obs_space.low.size
    action_space = env.action_space

    if isinstance(action_space, spaces.Box):
        action_size = action_space.low.size
        # Use NAF to apply DQN to continuous action spaces
        q_func = q_functions.FCQuadraticStateQFunction(
            obs_size, action_size,
            n_hidden_channels=args.n_hidden_channels,
            n_hidden_layers=args.n_hidden_layers,
            action_space=action_space)
        # Use the Ornstein-Uhlenbeck process for exploration
        ou_sigma = (action_space.high - action_space.low) * 0.2
        explorer = explorers.AdditiveOU(sigma=ou_sigma)
    else:
        n_actions = action_space.n
        q_func = q_functions.FCStateQFunctionWithDiscreteAction(
            obs_size, n_actions,
            n_hidden_channels=args.n_hidden_channels,
            n_hidden_layers=args.n_hidden_layers)
        # Use epsilon-greedy for exploration
        explorer = explorers.LinearDecayEpsilonGreedy(
            args.start_epsilon, args.end_epsilon, args.final_exploration_steps,
            action_space.sample)

    # Draw the computational graph and save it in the output directory.
    chainerrl.misc.draw_computational_graph(
        [q_func(np.zeros_like(obs_space.low, dtype=np.float32)[None])],
        os.path.join(args.outdir, 'model'))

    opt = optimizers.Adam()
    opt.setup(q_func)

    rbuf_capacity = 5 * 10 ** 5
    if args.episodic_replay:
        if args.minibatch_size is None:
            args.minibatch_size = 4
        if args.prioritized_replay:
            betasteps = (args.steps - args.replay_start_size) \
                // args.update_interval
            rbuf = replay_buffer.PrioritizedEpisodicReplayBuffer(
                rbuf_capacity, betasteps=betasteps)
        else:
            rbuf = replay_buffer.EpisodicReplayBuffer(rbuf_capacity)
    else:
        if args.minibatch_size is None:
            args.minibatch_size = 32
        if args.prioritized_replay:
            betasteps = (args.steps - args.replay_start_size) \
                // args.update_interval
            rbuf = replay_buffer.PrioritizedReplayBuffer(
                rbuf_capacity, betasteps=betasteps)
        else:
            rbuf = replay_buffer.ReplayBuffer(rbuf_capacity)

    def phi(obs):
        return obs.astype(np.float32)

    agent = DQN(q_func, opt, rbuf, gpu=args.gpu, gamma=args.gamma,
                explorer=explorer, replay_start_size=args.replay_start_size,
                target_update_interval=args.target_update_interval,
                update_interval=args.update_interval,
                phi=phi, minibatch_size=args.minibatch_size,
                target_update_method=args.target_update_method,
                soft_update_tau=args.soft_update_tau,
                episodic_update=args.episodic_replay, episodic_update_len=16)

    if args.load:
        agent.load(args.load)

    eval_env = make_env(for_eval=True)

    experiments.train_agent_with_evaluation(
        agent=agent, env=env, steps=args.steps,
        eval_n_runs=args.eval_n_runs, eval_interval=args.eval_interval,
        outdir=args.outdir, eval_env=eval_env,
        max_episode_len=timestep_limit)

上から見ていきます

chainerrl.experiments.prepare_output_dir

train_dqn_gym_simple.py
    args.outdir = experiments.prepare_output_dir(
        args, args.outdir, argv=sys.argv)
    print('Output files are saved in {}'.format(args.outdir))

chainerrl.experiments.prepare_output_dirはログ等の出力用のディレクトリをつくるための関数です。公式ドキュメントにはありませんが、ソース中にドキュメントがありました。
(この出力が、Chainerの"result/log"と同じなら、ChainerUIが使えるんですけどね。。)

prepare_output_dir.py
    """Prepare a directory for outputting training results.

    An output directory, which ends with the current datetime string,
    is created. Then the following information is saved into the directory:

        args.txt: command line arguments
        command.txt: command itself
        environ.txt: environmental variables

    Additionally, if the current directory is under git control, the following
    information is saved:

        git-head.txt: result of `git rev-parse HEAD`
        git-status.txt: result of `git status`
        git-log.txt: result of `git log`
        git-diff.txt: result of `git diff`

    Args:
        args (dict or argparse.Namespace): Arguments to save
        user_specified_dir (str or None): If str is specified, the output
            directory is created under that path. If not specified, it is
            created as a new temporary directory instead.
        argv (list or None): The list of command line arguments passed to a
            script. If not specified, sys.argv is used instead.
        time_format (str): Format used to represent the current datetime. The
        default format is the basic format of ISO 8601.
    Returns:
        Path of the output directory created by this function (str).
    """

chianerrl.misc.env_modifiers.make_action_filtered

train_dqn_gym_simple.py
    def make_env(for_eval):
        env = gym.make(args.env)
        if args.monitor:
            env = gym.wrappers.Monitor(env, args.outdir)
        if isinstance(env.action_space, spaces.Box):
            misc.env_modifiers.make_action_filtered(env, clip_action_filter)
        if not for_eval:
            misc.env_modifiers.make_reward_filtered(
                env, lambda x: x * args.reward_scale_factor)
        if ((args.render_eval and for_eval) or
                (args.render_train and not for_eval)):
            misc.env_modifiers.make_rendered(env)
        return env

    env = make_env(for_eval=False)

gymは、OpenAI Gymのパッケージで、ChainerRLのAPIではありません。gym.makeについてはこちらを参照してください。gym.wrappers.Monitorについてはドキュメントが無いのですが、ログのようなもののようです。この辺りを見れば分かると思います

chianerrl.misc.env_modifiers.make_action_filteredは、その名の通りActionをフィルターするもので、入力されたアクションに対して、アクションフィルターの関数(上記コードの場合Lambda)を適用します。

chianerrl.misc.env_modifiers.make_rendered

先ほどのコードにもう一つ、chainerrl.misc.env_modifiers.make_renderedというAPIがありました。
これは以下のような実装です。

env_modifiers.py
def make_rendered(env, *render_args, **render_kwargs):
    base_step = env.step
    base_close = env.close

    def step(action):
        ret = base_step(action)
        env.render(*render_args, **render_kwargs)
        return ret

    def close():
        env.render(*render_args, close=True, **render_kwargs)
        base_close()

    env.step = step
    env.close = close

Open AI Gym の stepcloseを書き換えているようです。stepについては、ここに記述があります。
ようは、stepが呼ばれる度にrender をしよう、closeが呼ばれたら一度renderしてウインドウをCloseしよう、という書き換えをしているようです。

OpenAI Gym 関連

以下は、ChainerRLではなく、OpenAI GymのAPI達です。

train_dqn_gym_simple.py
    timestep_limit = env.spec.tags.get(
        'wrapper_config.TimeLimit.max_episode_steps')
    obs_space = env.observation_space
    obs_size = obs_space.low.size
    action_space = env.action_space

env.spec.tags.getは、envの値を取るAPIの用です。タグのリスト等は見つからないのですが。。'wrapper_config.TimeLimit.max_episode_steps'で、1エピソード中のステップ数の最大値を取得しています。
env.observation_spaceは、環境を観測するときのオブジェクトを返し、env.action_spaceは、行動のオブジェクトを返します。これらは、Box(4,)Discrete(2)が返ってきます。こちらの'Spaces'に詳しくかかれてます

chainerrl.q_functions

Open AI Gymの方もドキュメントの記述はあまりないのですが、、Box()はN次元の空間を持ち、Discrete()は1次元のようです。
次のコードでは、どちらのタイプかによって、Q関数の構造を変えています。

train_dqn_gym_simple.py
    if isinstance(action_space, spaces.Box):
        action_size = action_space.low.size
        # Use NAF to apply DQN to continuous action spaces
        q_func = q_functions.FCQuadraticStateQFunction(
            obs_size, action_size,
            n_hidden_channels=args.n_hidden_channels,
            n_hidden_layers=args.n_hidden_layers,
            action_space=action_space)
        # Use the Ornstein-Uhlenbeck process for exploration
        ou_sigma = (action_space.high - action_space.low) * 0.2
        explorer = explorers.AdditiveOU(sigma=ou_sigma)
    else:
        n_actions = action_space.n
        q_func = q_functions.FCStateQFunctionWithDiscreteAction(
            obs_size, n_actions,
            n_hidden_channels=args.n_hidden_channels,
            n_hidden_layers=args.n_hidden_layers)
        # Use epsilon-greedy for exploration
        explorer = explorers.LinearDecayEpsilonGreedy(
            args.start_epsilon, args.end_epsilon, args.final_exploration_steps,
            action_space.sample)

chainerrl.q_functions.FCQuadraticStateQFunctionは、コード中にドキュメントがありました。
その名の通り、Fully-connected で、2次元(Quadratic)のStateを持つ、Q関数のようです。

chainerrl/q_functions/state_q_functions.py
    """Fully-connected state-input continuous Q-function.

    Args:
        n_input_channels: number of input channels
        n_dim_action: number of dimensions of action space
        n_hidden_channels: number of hidden channels
        n_hidden_layers: number of hidden layers
        action_space: action_space
        scale_mu (bool): scale mu by applying tanh if True
    """

もう一方のchainerrl.q_functions.FCStateQFunctionWithDiscreteActionもその名の通り、Discrete Action(離散的な行動)のためのQ関数のようです。

    """Fully-connected state-input Q-function with discrete actions.

    Args:
        n_dim_obs: number of dimensions of observation space
        n_dim_action: number of dimensions of action space
        n_hidden_channels: number of hidden channels
        n_hidden_layers: number of hidden layers
    """

chainerrl.explorers

上のコードには、explorers.AdditiveOUexplorers.LinearDecayEpsilonGreedyも使われていました。

action_space が、Box の場合に使われているexplorers.AdditiveOUのコード中のドキュメントは以下の通りです。

chainerrl/explorers/additive_ou.py
    """Additive Ornstein-Uhlenbeck process.

    Used in https://arxiv.org/abs/1509.02971 for exploration.

    Args:
        mu (float): Mean of the OU process
        theta (float): Friction to pull towards the mean
        sigma (float or ndarray): Scale of noise
        start_with_mu (bool): Start the process without noise
    """

https://arxiv.org/abs/1509.02971 を見てみると、"Continuous control with deep reinforcement learning" という論文である事が分かります。
よく分かっていないのですが、action_spaceBoxなので、行動空間がN次元で連続である、という事でいいんでしょうか。ちなみに、"Ornstein-Uhlenbeck process"は、平均回帰過程です。

一方、explorers.LinearDecayEpsilonGreedyは、$\epsilon$を低減させる、普通のEpsilon-greedyです。ChainerRLのサンプルを読んでみる(examples/ale/train_dqn_ale.py編)にも出てきました。

chainerrl/explorers/epsilon_greedy.py
    """Epsilon-greedy with linearyly decayed epsilon

    Args:
      start_epsilon: max value of epsilon
      end_epsilon: min value of epsilon
      decay_steps: how many steps it takes for epsilon to decay
      random_action_func: function with no argument that returns action
      logger: logger used
    """

chainerrl.misc.draw_computational_graph

次にあるchainerrl.misc.draw_computational_graphはq関数のモデルをPNGファイルにして保存しています。

train_dqn_gym_simple.py
    # Draw the computational graph and save it in the output directory.
    chainerrl.misc.draw_computational_graph(
        [q_func(np.zeros_like(obs_space.low, dtype=np.float32)[None])],
        os.path.join(args.outdir, 'model'))

chainerrl.replay_buffer

次は、Replay bufferの生成をしています。
大きく分けて、Episodic Replayかどうか、と、Prioritizedかどうかで、以下の4つのAPIがあります。

  • chainerrl.replay_buffer.PrioritizedEpisodicReplayBuffer
  • chainerrl.replay_buffer.EpisodicReplayBuffer
  • chainerrl.replay_buffer.PrioritizedReplayBuffer
  • chainerrl.replay_buffer.ReplayBuffer
train_dqn_gym_simple.py
   if args.episodic_replay:
        if args.minibatch_size is None:
            args.minibatch_size = 4
        if args.prioritized_replay:
            betasteps = (args.steps - args.replay_start_size) \
                // args.update_interval
            rbuf = replay_buffer.PrioritizedEpisodicReplayBuffer(
                rbuf_capacity, betasteps=betasteps)
        else:
            rbuf = replay_buffer.EpisodicReplayBuffer(rbuf_capacity)
    else:
        if args.minibatch_size is None:
            args.minibatch_size = 32
        if args.prioritized_replay:
            betasteps = (args.steps - args.replay_start_size) \
                // args.update_interval
            rbuf = replay_buffer.PrioritizedReplayBuffer(
                rbuf_capacity, betasteps=betasteps)
        else:
            rbuf = replay_buffer.ReplayBuffer(rbuf_capacity)

PrioritizedReplayBufferは、以下の論文のようです。

Prioritized Experience Replay
https://arxiv.org/abs/1511.05952

エピソード記憶については、どの論文、という事ではないようですが、エピソード毎にReplayBufferを保存している手法のようです。

chainerrl.agents.DQN

DQNのAgentです。こちらにはドキュメントがあります。

train_dqn_gym_simple.py
    agent = DQN(q_func, opt, rbuf, gpu=args.gpu, gamma=args.gamma,
                explorer=explorer, replay_start_size=args.replay_start_size,
                target_update_interval=args.target_update_interval,
                update_interval=args.update_interval,
                phi=phi, minibatch_size=args.minibatch_size,
                target_update_method=args.target_update_method,
                soft_update_tau=args.soft_update_tau,
                episodic_update=args.episodic_replay, episodic_update_len=16)

chainerrl.experiments.train_agent_with_evaluation

最後、chainer.experiments.train_agent_with_evaluationで学習と評価をします。
これは、Webにドキュメントがあります。
http://chainerrl.readthedocs.io/en/latest/experiments.html#training-and-evaluation

ChainerRLのサンプルを読んでみる(examples/ale/train_dqn_ale.py編)にも出てきました。

train_dqn_gym_simple.py
    eval_env = make_env(for_eval=True)

    experiments.train_agent_with_evaluation(
        agent=agent, env=env, steps=args.steps,
        eval_n_runs=args.eval_n_runs, eval_interval=args.eval_interval,
        outdir=args.outdir, eval_env=eval_env,
        max_episode_len=timestep_limit)
1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2