Stable BaselinesとOpenAI Gymで強化学習の環境構築


強化学習のライブラリstable baselinesを使ってpythonで強化学習を行うためのメモ。

環境:Ubuntu 20.04

目次

1. 必要要件まとめ
2. インストール前の注意
3. インストール手順
4. 使い方  
  4-1. OpenAI Gym
  4-2. Stabe Baselines 基本編
  4-3. Stabe Baselines 環境の並列化

必要要件まとめ

Python 3.7
pip 20.1.1
gym
TensorFlow 1.14.0
stable-baselines 2.10.1

強化学習を行うライブラリはたくさんあるが、ここではstable-baselinesを使用する。
理由は、KerasRLやChainerRLと比較して
・ 対応している強化学習アルゴリズムが多い
・ バグがない
・ 公式Documentが充実している
からだ。

インストール前の注意

stable-baselinesは中でtensorflowを使用するが、tensorflow2には対応していないため、tensorflow1をインストールする必要がある。
最新のpip3==21.x.xではtensorflow1がインストールできないため、pip3==20.x.xを使用しなければならない。
そのためにPython3.8ではなく、Python3.7にしなければならないということだ。

ちなみにUbuntu20.04ではシステムのPythonがPython3.8になってしまっているのでpyenvやcondaなどを使用してPythonのバージョンを変更しなければならない。
またほかのプログラムでtensorflow2を使用する場合もpythonの仮想環境を作成してバージョンを分ける必要がある。

参考サイト
Pyenvを使用してUbuntuにPythonをインストールする
venv: Python 仮想環境管理

インストール手順

Python3.7が入れられればインストールは簡単だ。

$ sudo apt install python3-pip
$ pip3 install gym tensorflow==1.14.0 stable-baselines

これでインストールは完了だ。
これらをインストールする際に、自動でnumpyなど必要なライブラリもインストールされる。

使い方

OpenAI Gym

import gym

#環境を作成
env = gym.make('CartPole-v0')
max_step = 200

#環境を初期状態に戻す(返り値は状態)
obs = env.reset()

#シミュレーション開始
for id_step in range(max_step):
    #環境を1ステップ進める(引数は環境で用意されたサンプルアクション)
    obs, reward, done, info = env.step(env.action_space.sample())
    #描画
    env.render()
    #終了
    if done:
        break

OpenAI gymの詳しい使い方はOpenAI Gym 入門を参照。
公式ドキュメント(英語)

Stable Baselines 基本編

stable-baselinesはopenAIが開発したライブラリであるため、gym形式の環境にしか強化学習を行えない。
以下はCartPole環境に対しDQNで学習を行った例だ。

#!/usr/bin/env python3                                                                      
 import os
 import gym
 from stable_baselines.bench import Monitor
 from stable_baselines.deepq.policies import MlpPolicy
 from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv
 from stable_baselines.common.cmd_util import make_vec_env
 from stable_baselines.common.evaluation import evaluate_policy
 from stable_baselines import DQN

 def main():
     #ハイパーパラメータ設定
     time_steps = 25000

     #ログ保存先
     log_dir = './logs/'
     os.makedirs(log_dir, exist_ok=True)
     model_name = 'cartpoleDQN'

     #環境作成
     env = gym.make('CartPole-v0')

     #強化学習モデルの作成
     model = DQN(MlpPolicy, env, verbose=1, tensorboard_log=log_dir)

     #学習
     model.learn(total_timesteps=time_steps)

     #モデル評価
     eval_env = gym.make('CartPole-v0')
     mean_reward, std_reward = evaluate_policy(model, eval_env, n_eval_episodes=10)
     print('Mean reward: {} +/- {}'.format(mean_reward, std_reward))

     #モデルの保存
     model.save(model_name)


 if __name__ == '__main__':
     main()

実行すると警告が大量に出るがこれはstable-baselines内でtensorflow1の古いコードを用いていることが原因なので無視してよい。最終的に以下のようなコードが出れば成功だ。

--------------------------------------
| % time spent exploring  | 77       |
| episodes                | 100      |
| mean 100 episode reward | 23.6     |
| steps                   | 2332     |
--------------------------------------
--------------------------------------
| % time spent exploring  | 2        |
| episodes                | 200      |
| mean 100 episode reward | 90.2     |
| steps                   | 11349    |
--------------------------------------
Mean reward: 115.0 +/- 8.160882305241266

コードの細部について解説していく。

model = DQN(MlpPolicy, env, verbose=1, tensorboard_log=log_dir)

第一引数のMlpPolicyはDQNで用いるニューラルネットワークの定義だ。MlpPolicyはstable-baselinesで定義されている最もシンプルなニューラルネットワークである。詳細はこちら

第二引数のenvはこのモデルが対象とする環境だ。これによりMlpPolicyの入力層や出力層のノード数を決定する。

第三引数のverboseはコンソールに学習経過を出力するタイプだ。値は0, 1, 2の3つであり、大きいほど詳細に出力される。

第四引数のtensorboard_logは学習経過をtensorboardで観察するための出力ファイルだ。
学習後や学習中でもターミナルをもう一つ開き、

$ tensorboard --logdir=./logs/

とコマンドを実行し、表示されたURLをクリックするとtensorboardを開くことができる。

model.learn(total_timesteps=time_steps)

ここでtotal_timestepsは学習で実行される総ステップ数であり、総エピソード数ではない。つまり
total_timesteps = (1エピソードのステップ数)×(総エピソード数)
となる。

Stable Baselines 環境の並列化

ここ数年で出てきたA2CやPPOなどの強化学習手法は環境を並列化して学習を行う。
ここでは環境を並列化する方法を解説する。

1. make_vec_env

単一プロセスで複数の環境を実行するもの。最もよく使われるもの。
環境の並列数だけ設定すればよく、あとはいい感じに自動で設定してくれる。

2. SubprocVecEnv

環境の数だけプロセスを用意し、1環境1プロセスで強化学習を行っていく。通常はプロセス間通信の遅延により、make_vec_envよりも時間がかかる。しかし、1ステップの処理が重い環境ではmake_vec_envよりも高速となる場合がある。
(ちなみに自分の場合は、カスタムgym環境を作って強化学習を行った際はSubprocVecEnvはmake_vec_envの10倍高速になった。)

サンプルコード
#!/usr/bin/env python3
import os 
import gym
from stable_baselines.bench import Monitor
from stable_baselines.common.policies import MlpPolicy
from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv
from stable_baselines.common.cmd_util import make_vec_env
from stable_baselines.common.evaluation import evaluate_policy
from stable_baselines import PPO2

def main():
    #ハイパーパラメータ
    n_envs = 16
    time_steps = int(1e+5)
    multiproc = False

    #ログ保存先
    log_dir = './logs/'
    os.makedirs(log_dir, exist_ok=True)
    model_name = 'ppo2'

    #並列環境作成
    if multiproc == False:
        env = make_vec_env('CartPole-v0',
                n_envs=n_envs,
                monitor_dir=log_dir)
    else:
        env = SubprocVecEnv([lambda: Monitor(gym.make('CartPole-v0'),
                filename=log_dir+'{}.monitor.csv'.format(i), allow_early_resets=False) for i in range(n_envs)],
                start_method='spawn')

    #強化学習モデルの作成
    model = PPO2(MlpPolicy, env, verbose=1)

    #学習
    model.learn(total_timesteps=time_steps)

    #モデルの保存
    model.save(model_name)

SubprocVecEnvで各環境に対しログ保存ファイルを作ろうとしたがうまくいかず。。。
tensorboard用のログファイルは作成できるのですが各エピソードの記録を記したcsvファイルの作成がうまくいかず...
解決策をご存じの方がいたらコメントお願いします。