gym-super-mario-brosで途中から再開する方法
日曜日の勉強会の仲間が「マリオAIチャレンジ」をやっているのを見ていて、楽しそうだったので最近触っています。
まだ学習をさせるところまでは行けていなくて、マリオに行動を配列で指示したり、gifなどの画像に出力させるところをいじくっているところです。
ところで、途中まで進んだマリオを、そこから再開できる方法があるか考えていました。それができれば計算が早くなるという想定です。最初はdeepcopyでできるのではないかと考えたのですが、やってみるとうまくいきませんでした。おそらくNES emulator (=nes_env.pyの_LIBオブジェクト)がpythonのdeepcopyでは適切にコピーされないのだと思います。
不慣れなPythonなのですが、コードをがんばって追いかけると、nes_env.pyの_backupメソッドとresetメソッドを呼べば良さそうなことまでわかりました。backupメソッドを呼ぶとその箇所が保存されて、その後のresetメソッドではそこに戻ることができるようです。 しかし、resetメソッドはSuperMarioBrosEnvから呼べるのですが、backupメソッドは外部に公開されていないメソッドなので、使えないようなのです。困った。
いろいろ調べたところ、envオブジェクトはWrapperクラスのサブクラスの複数回の委譲を繰り返してSuperMarioBrosEnv(そしてNESEnv)に到達するようで、このWrapperクラスにbackupメソッドを追加すれば良さそうだということに気がつきました。
Pythonのクラスにメソッドを後から追加する方法を調べて、手探りで書いてみました。
# Wrapperクラスにbackupメソッドを追加 def backup(self): if hasattr(self.env, "_backup"): self.env._backup() # gym_super_mario_bros.smb_env.SuperMarioBrosEnv#backup else: self.env.backup() # 委譲 Wrapper.backup = backup
(↑2023/01/08 22:55修正)
backupメソッドを持つオブジェクトが見つかるまで委譲先を探して、backupメソッドを持つオブジェクト(SuperMarioBrosEnvクラスのオブジェクト)が見つかったらそれを実行します。 やってみると、うまくいくようです。やった!
ちなみに、Wrapperクラスは委譲の仕組みが入っているので、resetメソッドは何もしなくても委譲されてSuperMarioBrosEnvクラスのresetメソッドが呼ばれます。SuperMarioBrosEnvクラスの_backupメソッドが委譲されていなかったのは、 _
で始まるメソッド名だからです。
(2023/01/08 22:55追記)
Google Colaboratoryも、最小限のコードでの再現環境として作っておきました。 colab.research.google.com
わかりにくいかもしれませんが、最初はクリボーにぶつかってやられているけど、ちょっと戻ってジャンプで回避しています。