📹

【強化学習】OpenAI GymのMonitorが非推奨に。代わりはRecordVideo

2022/01/02に公開

0. はじめに

以前Qiitaで、OpenAI GymをGoogle Colab上で描画する方法を投稿しました。
(ありがたいことに、書いた中で最もLGTMされた記事で、今でも時々LGTMが増えます。)

https://qiita.com/ymd_h/items/c393797deb72e1779269

その中で紹介した3つの手法のうちの1つ(かつ実際に一番利用しそうな手法)である gym.wrappers.Monitor を利用して mp4 ビデオとして保存する手法が、非推奨になっていることに気がついたので、その対処方法を調査しました。

1. 状況

2021年9月14日にリリースのGym 0.20.0より、gym.wrappers.Monitor が非推奨になったと警告を表示するようになりました。

今は、まだ警告を表示するだけで、従来どおり gym.wrappers.Monitor を利用することができますが、将来的には削除される可能性もあるため、推奨方法を調べました。

gym.wrappers.Monitorの非推奨化については、以下のissueで提案されています。
理由としてはビデオ録画だけでなく、統計情報(?)を併せて保存したりと多機能を詰め込んだ実装になっており、そのせいで done=True じゃないと reset() を呼べなかったりと使い勝手が悪くなっているので、新しくビデオ録画だけを切り取った実装に置き換えようということでした。
https://github.com/openai/gym/issues/2297

2. 代替クラス: RecordVideo

非推奨化したgym.wrappers.Monitor代わりに、gym.wrappers.RecordVideoを使ってビデオを保存することができます。

使い方は、gym.wrappers.Monitor とほとんど同じですが、一部コンストラクタの引数や仕様が変更になっています。

RecordVideo引数 説明 対応するMonitor引数
env gym.Env ラップする環境 env (変更なし)
video_folder str ビデオ保存フォルダ directory
episode_trigger = None Optional[Callable[[int], bool]] エピソードが記録対象か判断するための関数(Noneの時には1000エピソード毎、または1000未満の立法数(n^3)エピソードで記録されます。) video_callable
step_trigger = None Optional[Callable[[int], bool]] ステップが記録対象か判断するための関数 (該当なし)
video_length = 0 int 記録するビデオの最大長(0は制限なし) (該当なし)
name_prefix = "rl-video" str 記録するビデオ名の接頭辞 (該当なし。モジュール変数 gym.wrappers.monitor.FILEPREFIX = "openaigym"で通常は固定)

(Monitorには他にも引数がありますが、機能整理したことでRecordVideoには引き継がれませんでした。)

import gym
from gym.wrappers import ReordVideo

env = RecordVideo(gym.make("CartPole-v1"), "./")

o = env.reset()
for _ in range(100):
    # step() の中で、自動的にビデオ録画されるため、 render() を明示的に呼ぶ必要はない。
    o, r, d, _ = env.step(env.action_space.sample())
    if d:
        o = env.reset()

3. Google Colabで利用する場合

前のQiita記事と基本的には同じです。

仮想X11ディスプレイであるXvfbと、それをPythonから利用するためのPyVirtualDisplayをインストールを忘れないでください。

!apt update && apt install xvfb
!pip install pyvirtualdisplay

import base64
import io
import gym
from gym.wrappers import RecordVideo
from IPython import display
from pyvirtualdisplay import Display

d = Display()
d.start()

env = RecordVideo(gym.make('CartPole-v1'),'./')

o = env.reset()

for _ in range(100):
    o, r, d, i = env.step(env.action_space.sample()) # 本当はDNNからアクションを入れる

    if d:
        env.reset()

for f in env.videos:
    video = io.open(f[0], 'r+b').read()
    encoded = base64.b64encode(video)

    display.display(display.HTML(data="""
        <video alt="test" controls>
        <source src="data:video/mp4;base64,{0}" type="video/mp4" />
        </video>
        """.format(encoded.decode('ascii'))))

4. Gym-Notebook-Wrapper (gnwrapper) を使う場合

以前作成して公開したGym-Notebook-Wrapperも更新して、v1.2.5RecordVideo に対応しました。
(Google Colabにインストールされているバージョンが古い間は、あまり影響はないかもしれませんが、) インストールされているGymのバージョンがgym<=0.19.0の時には、gym.wrappers.Monitorを、gym>=0.20.0の時には、gym.wrappers.RecordVideo を利用するように処理を分けました。
(内部構造は色々変わっていたので、ちょっと面倒でした。)

!apt update && apt install xvfb
!pip install gym-notebook-wrapper

import gnwrapper
import gym

env = gnwrapper.Monitor(gym.make('CartPole-v1'),directory="./") # Xvfbが起動される

o = env.reset()

for _ in range(100):
    o, r, d, i = env.step(env.action_space.sample()) # 本当はDNNからアクションを入れる
    if d:
        o = env.reset()

env.display() # ここで、ビデオとして保存した描画データを表示する

こちらは、両バージョンに対応するかつ互換性の観点から旧APIを維持しています。

引数 説明
env gym.Env ラップされる環境
directory str ビデオを保存するフォルダ
video_callable = None Optional[Callable[[int], bool]] エピソードが記録されるかを判断するための関数
*args / **kwargs 継承元のMonitorまたはRecordVideoに渡される。(意図的に他の機能を利用する人は元の引数を調べているだろうので、差を吸収したりはしていません。)

5. おわりに

OpenAI Gym のビデオ保存機能である gym.wrappers.Monitor が非推奨になっていたので、後継の方法を調査しました。

また、Google ColabでOpenAI Gymを利用するために公開しているライブラリのGym-Notebook-Wrapperも対応しました。

Discussion