人工生命のベンチマーク「Evolution Gym 」について、設定と利用のしかたです。[※1]
- ※1
- 環境構築は次で検証していますが、手順は、Linux + Docker / Windows + WSL2 + Docker (CE) にも流用できるはずです:
- ・
- Apple silicon + XQuartz + Docker
関連
- ◯
- コンテナを導入する:Docker
- ◯
- 高機能のウィンドウシステム:X Window System (XQuartz) , Docker, OpenGL, Tcl/Tk, Xvfb, x11vnc
検証
- ・
- PC:MacBook Air M1 2020
- ・
- OS(ホスト):macOS 11.31
- ・
- OS(ゲスト):Ubuntu 20.04 LTS
- ・
- ウィンドウシステム:X Window (XQuartz 2.8.5)
- ・
- コンテナ:Docker Desktop 4.13.1
背景
ある目的のための最適なカタチって、どんなものでしょう?
ヒトをふくめ、生物には進化過程や生命維持によるさまざまな制約がありますよねーーたとえば、車輪があれば平坦な地形を高速に移動できるのに、そのような構造を獲得した動物はほぼいなかったり。[※1]
でも機械なら、より多様なカタチを現実に作ることができます。さらに仮想現実の機械なら、より柔軟に・簡単に試すことができます。[※2]
このアプリ「Evolution Gym 」は、いろいろな目的のための最適なカタチやうごき、そしてそれらを獲得するためのやり方を、仮想現実の機械=ソフトウェアの人工生命を使って探るものです(構成と動作、そしてそれらを最適化する手法のベンチマークにすることを目標にしています)。
- ・
- Evolution Gym
- ※1
- 車輪のように回転するパーツだと、そこに栄養を送ったり神経を延ばしたりするのが難しいためですが、それでも一部の小さな生物は、回転する構造を獲得していたりします(グールドの「ニワトリの歯」〜「車輪なき王国」でも語られる大腸菌やシロアリの腸内細菌が有名ですが、ほかにもいるようですね)ーーさらに歯車を持つ生物もいたり(2013年に発見されたようです):
- ・
- https://gendai.media/articles/-/83552
- ※2
- じっさいクルマも、レーシングカーから建設機械まで、膨大な数のカタチとうごきについて、実用性とコストのトレードオフが極限まで試されてきました。
概要
アプリは、MIT人工知能研究所の研究者が提供しています。なおベースには、強化学習のベンチマーク(OpenAI Gym)を採用しています。[※1]
- ・
- https://arxiv.org/abs/2201.09863
- ◯
- 要素:カタチとうごき 〜 構成と動作(デザインとコントロール)
このアプリでは、4種類のブロック(「ボクセル」)から、人工生命(「ロボット」)を作ります:
- ・
- 黒(■)……固定ブロック:剛体/Rigid Voxel
- ・
- 灰(■)……固定ブロック:軟体/Soft Voxel
- ・
- 青(■)……可動ブロック:垂直方向に伸び縮み/Vertical Actuator
- ・
- 橙(■)……可動ブロック:水平方向に伸び縮み/Horizontal Actuator
これでできるものはいわゆるソフトロボット(アクチュエータ=バネ)になりますが、汎用の構造を探るベースにもなるものです。[※2]
- ◯
- 要素:やり方 〜 手法(アルゴリズム)
アプリのサンプルでは、2種類の手法(アルゴリズム)を最適化に使っています:[※3]
- ・
- カタチ/構成に対して……進化計算(GA, BO, CPPN-NEAT )
- ・
- うごき/動作に対して……強化学習(PPO )
これらのアルゴリズムは、まずいろいろなカタチを進化計算で試し(外側のループ)、それらのカタチに対して、いろいろなうごきを強化学習で試す(内側のループ)、という分担になっています。[※4]
- ◯
- 要素:しごと 〜 課題(タスク)
ロボットは環境(「ワールド」)におかれ、そこでさまざまなしごとをこなします(歩く、登る、道具をつかう、形状をかえる……)。
これらのしごとに対して、最適なカタチ・うごきを、うまいやり方で探ることになります。
- ◯
- 戦略
進化計算+強化学習の場合は、まずワールドで、いくつかのボクセルをランダムに組み合わせます。これでいくつかの個体のロボットを作り、うまくしごとをこなしたものを、次の世代に残しますーーこのサイクル(進化・学習)を繰り返すことで、ロボットをそれぞれのしごとに最適化していきます。
とはいえこのような戦略でも、達成が難しいしごとがあるようですねーーより高度なアルゴリズムが望まれるので、その検証の場としてもこのアプリを使ってほしいとのこと。
- ※1
- OpenAI GymはすでにGymnasium に引き継がれていますが、このアプリは古い方を使っていますーー新しい方に修正したいなら、次が参考になるかもしれません:
- ・
- 強化学習のためのシンプルな環境:Farama Gymnasium, OpenAI Gym, Docker, X Window System, Pyglet, pygame, tkinter
- ※2
- なら車輪などを実現するモータや歯車、リンクやカムやはどうかというと、これは複数のソフトロボットの協調で実現できるはずです(協調作業はこのアプリのサンプルにはまだありませんし、論文でも明確には言及されていませんがーーこれら小さなソフトロボットたちは、より大きな機構を構成するベースになりうるものです)。なお自分自身が回転するという課題も、ベンチーマークのひとつにあったりします:
- ※3
- ベイズ最適化(BO)は進化計算ではないのですが、より大きなくくりのブラックボックス最適化のひとつです。
- ※4
- 動作の制御(コントラーラ)にも進化計算を使った試みや、べつの強化学習を使った試みもあります(いずれも岡瑞起氏/斉藤拓己氏による検証ですーー進化計算の方が優れている課題もあるみたいですね):
- ・
- https://note.com/mizuki_oka/n/n7536b0bdff1f
- ・
- https://note.com/mizuki_oka/n/nece36941088c
設置:共通
設置のしかたは、次に書かれています:
- ・
- https://github.com/EvolutionGym/evogym
ここでは、コンテナを使って設置しますーーなおフォルダ構成は、次を前提にしています:
- ・
- ${HOME}/sys/vir/vir130 …… コンテナの設定フォルダ
- ・
- ${HOME}/app_pkg/vir130 …… ライブラリ群のフォルダ
ローカルでパッケージを取得します:
$ cd ${HOME}/app_pkg/vir130 $ git clone --depth 1 --recurse-submodules https://github.com/EvolutionGym/evogym.git
コンテナの初期状態を設定し:
- ・
- ${HOME}/sys/vir/vir130/Dockerfile
FROM ubuntu:20.04 RUN apt-get -y update RUN apt-get -y upgrade RUN apt-get -y install python3 RUN apt-get -y install pip RUN ln -s /usr/bin/python3.8 /usr/bin/python
コンテナを作り:
$ docker build --no-cache -t ubuntu:vir130 ${HOME}/sys/vir/vir130
起動します(また適宜、コンテナの状態をイメージに反映させます):
# 起動する: $ docker run -it --rm -v ${HOME}/app/opt:/opt -v ${HOME}/app_pkg:/pkg --name cnt130 ubuntu:vir130 # 反映する: $ docker commit cnt130 ubuntu:vir130
コンテナ上で、各種ライブラリを設置します:
$ apt install xorg-dev Geographic area: 6 Time zone: 79 $ apt install libglu1-mesa-dev $ apt install libglew-dev # 拡張機能:GLEW $ apt install mesa-utils # 動作検証のため:OpenGL $ apt install git $ apt install cmake $ cd /pkg/vir130/evogym $ pip install -r requirements.txt $ python setup.py install # バージョンを指定し入れ替え:1.23 $ pip install numpy== ... $ pip uninstall -y numpy $ pip install numpy==1.23.5
ローカルでX サーバを起動し:
$ xhost +
コンテナで次を実行しますーーロボットが歩く画面が表示されたら、設置は完了です:
$ export DISPLAY=host.docker.internal:0.0 $ export LIBGL_ALWAYS_INDIRECT=1 $ glxinfo | grep -i render ... OpenGL renderer string: Apple M1 $ glxgears # 動作検証:OpenGL ... $ cd /pkg/vir130/evogym/examples $ python gym_test.py
設置:共通:デザインツール
ワールドやロボットを手描きで作ることができる、デザインツールが用意されています。
- ・
- https://github.com/EvolutionGym/evogym-design-tool
ローカルでパッケージを取得します:
$ cd ${HOME}/app_pkg/vir130 $ git clone --depth 1 https://github.com/EvolutionGym/evogym-design-tool.git
コンテナ上で、必要なライブラリを設置します:
$ apt install python3-tk
次のコマンドで、編集画面を表示します:
$ export DISPLAY=host.docker.internal:0.0 $ cd /pkg/vir130/evogym-design-tool/src $ python main.py
このツールで作ったワールドやロボットのファイル(JSON形式)は、次に置かれます:
- ・
- evogym-design-tool/src/exported
これらのファイルをチュートリアルやサンプルで使うときは、次に置きます:
- ・
- evogym/tutorials/world_data
- ・
- evogym/examples/world_data
設置:個別:macOS
以下、macOS 特有の問題への対応です:
- ◯
- 対処:アニメーションの表示
アプリはアニメーションをX Windowで表示しますが、macOS のXQuartz では表示前に失敗するようです。[※1]
このようなときは(ローカルのX サーバが使えないときは)、コンテナでX サーバを起動させ、その映像をローカルのVNC アプリに送る、というやり方もあります:
コンテナは(ポートを開けるために)次のように起動し:
$ docker run -it --rm -p 5900:5900 -v ${HOME}/app/opt:/opt -v ${HOME}/app_pkg:/pkg --name cnt130 ubuntu:vir130
コンテナ上で、次のライブラリ群を設置します:
$ apt install xvfb $ apt install x11vnc
アニメーションを表示するときは、次のサーバ群を起動させておき:
$ export n=1 $ export DISPLAY=:${n} $ Xvfb :${n} -screen 0 1200x600x24 & $ x11vnc -display :${n} -forever -passwd ${password} &
ローカルでは、次にアクセスしておきます:[※2]
vnc://localhost:5900/
試したいコマンドを実行すると、結果がローカルの画面に表示されます。
- ◯
- 対処:デザインツール
デザインツールの方は(tkinter を使っているので)XQuartz で表示できますーーただサブウィンドウへの操作が起きると(警告ウィンドウへの操作など)、ウィンドウ全体がクラッシュしてしまいます。[※3]
とりあえずUIのスクリプトを修正し、エディット/セーブ/ロード時にサブウィンドウを表示させないことで対処していますがーー
- ・
- evogym-design-tool/src/gui.py
def update_gs_click(...): ... if new_width < self.old_gs_width: ... else: # この間をコメントアウト def load_click(...): ... if len(self.objects.items()) > 0: ... else: # この間をコメントアウト def save_click(...): ... if os.path.exists(save_path): ... else: # この間をコメントアウト
- ◯
- 対処
確実に利用するなら、ホストサーバをべつに用意し(Ubuntu, X Window, xrdp)、ローカルからリモートデスクトップ(RDP )で操作するのが無難でしょうけど……とはいえこういうアプリはローカルで動かしたい、というのもありますしね……
- ※1
- Segmentation faultで失敗しますーーOpenGLは有効になっているので(glxgearsは表示される)、XQuartz のOpenGLが対応できないGLEWの機能を使っているのかもしれません。次の事例では、ソフウェアレンダリングを強制するため、Mesaのソースからのコンパイルで対応できたようですが:
- ・
- https://discourse.vtk.org/t/using-vtk-from-within-a-docker-container-on-os-x-intel-arm/9375
- ※2
- macOS のデスクトップのメニューから、VNC アプリの画面を開きます:
- ・
- 移動 > サーバへ接続
- ※3
- 次のイシューにあるやり方を試してみましたが、結果は同じでした(クラッシュ)。ただこの対処なら、たしかにTK()の前にglfw.init() が実行されているので、うまくいきそうなんですけどね……:
- ・
- https://github.com/EvolutionGym/evogym-design-tool/issues/4
利用:進化・学習のチュートリアル
アプリには、ロボットを進化・学習させるための雛形が用意されています:
- ・
- https://github.com/EvolutionGym/evogym/tree/main/tutorials
この雛形に、試したいアルゴリズムを組み込んでいくことになりますーー基本的な使い方(観察のしかた/報酬の与え方)は、次に解説されています:
- ・
- https://evolutiongym.github.io/tutorials/new-env.html
次のコマンドで、この雛形を実行できます:
$ cd /pkg/vir130/evogym/tutorials $ python visualize_simple_env.py
- ◯
- 作ったロボットのふるまいを見てみる
ロボットを自分で作り、そのふるまいをとりあえず見てみたいなら、このチュートリアルの雛形が使えます。
- ※
- このロボットのデザインは、前田高志氏のドット絵サイトで提供されている「右を向いたペンギン」「左を向いたペンギン」を参考にしています:
- ・
- https://dotown.maeda-design-room.net/
最初に、次のスクリプトの次の箇所をコメントアウトしておきます(ランダムに生成されるロボットを、ワールドに加えないようにします):
- ・
- evogym/tutorials/envs/simple_env.py
#self.world.add_from_array('robot', body, 1, 1, connections=connections)
- ◯
- 作ったロボットのふるまいを見てみる[その1]
いちばんかんたんなのは、ワールドに直接ロボットを加えるやり方ですーーワールドのファイルをデザインツールで編集します(このとき、加えたロボットのオブジェクトは「robot 」という名前にします):
- ・
- evogym/tutorials/world_data/simple_walker_env.json
- ◯
- 作ったロボットのふるまいを見てみる[その2]
ロボットだけのファイルを作り、それを読み込んでワールドに加えることもできますーースクリプトには、次のような記述を加えます(このときワールドにあるモノとぶつかりそうなら、なにもないところに動かしておく必要があります)。[※1]
- ・
- evogym/tutorials/envs/simple_env.py
from evogym import EvoWorld, WorldObject robot = WorldObject.from_json('/pkg/vir130/evogym/tutorials/world_data/my_robot_001.json') # ロボットのファイルから読み込む robot.set_pos(1,1) # てきとうなところに動かす self.world.add_object(robot) # ワールドにロボットを加える
- ◯
- 作ったロボットのふるまいを見てみる[その3]
ロボットはテキストファイルだけで作ることもできますーーボクセルには、種類ごとに番号が割り当ててあります:
- ・
- evogym/evogym/utils.py
VOXEL_TYPES = { 'EMPTY': 0, 'RIGID': 1, 'SOFT': 2, 'H_ACT': 3, 'V_ACT': 4, 'FIXED': 5, }
この番号を、テキストファイルの上でロボットのカタチに並べるだけです:[※2]
0 0 4 4 4 4 0 0 0 0 4 4 1 4 0 0 0 0 4 4 4 4 3 0 0 0 4 4 4 4 0 4 4 4 4 4 4 4 4 4 4 0 4 2 2 4 0 0 0 0 4 2 2 4 0 0 0 3 3 0 0 3 3 0
スクリプトには、次のような記述を加えます(この場合、スクリプトからロボットに名前をつける必要があります):
- ・
- evogym/tutorials/envs/simple_env.py
import numpy as np from evogym import EvoWorld, WorldObject body = np.loadtxt('/pkg/vir130/evogym/tutorials/world_data/my_robot_001.txt', dtype='int') # ロボットのテキストファイルから読み込む robot = WorldObject.from_array('robot', body) # 名前をつける robot.set_pos(1,1) # てきとうなところに動かす self.world.add_object(robot) # ワールドにロボットを加える
- ◯
- 作ったロボットのふるまいを見てみる[その4]
次のようなスクリプトを書けば、挙動の大きさ(アクチュエータであるバネの強さ)でロボットのふるまいがどう変わるか、確かめておくことができます:
- ・
- a.py
import sys import numpy as np from gym import spaces from evogym import EvoWorld, WorldObject from evogym.envs import EvoGymBase a_low = sys.argv[1] a_high = sys.argv[2] a_path = sys.argv[3] g_name = 'robot' class Try(EvoGymBase): def __init__(self): self.world = EvoWorld() if a_path.endswith('.json'): robot = WorldObject.from_json(a_path) if a_path.endswith('.txt'): robot = WorldObject.from_array(g_name, np.loadtxt(a_path, dtype='int')) self.world.add_object(robot) EvoGymBase.__init__(self,self.world) num_actuators = self.get_actuator_indices(g_name).size self.action_space = spaces.Box(low=float(a_low), high=float(a_high), shape=(num_actuators,), dtype=np.float) self.default_viewer.track_objects(g_name) # ロボットにカメラを合わせる env = Try() while True: env.step({g_name: env.action_space.sample()}) env.render()
次のように使います(数字はバネの制限の最小値と最大値、ファイル形式はJSON/TXTどちらでも可):
$ python a.py 0.5 1.5 /pkg/vir130/evogym/tutorials/world_data/my_robot_001.json $ python a.py 0.5 1.5 /pkg/vir130/evogym/tutorials/world_data/my_robot_001.txt
- ※1
- 原点は左下になります。
- ※2
- 次が参考になります(この形式のロボット群が置いてあります):
- ・
- https://github.com/sttkm/ComparisonOnEvogym/tree/main/robot_files
利用:進化・学習のためのリファレンス
以下、スクリプトを組むときのリファレンスです:
- ・
- https://evolutiongym.github.io/documentation/evogym/base.html
次のクラス群が提供されています:
- ・
- envs.EvoGymBase …… 進化・学習のための環境(ApenAIのgym.Env を継承しています)
- ・
- EvoWorld …… ワールドの参照と操作
- ・
- WorldObject …… ワールドに投入するオブジェクトの参照と操作(ロボットなど)
- ・
- EvoSim …… ロボットのふるまいの参照と操作(アクチュエータなど)
- ・
- EvoViewer …… ワールドのカメラの参照と操作
利用:進化・学習のサンプル
いくつかのアルゴリズム(進化計算や強化学習)が実装されたサンプルもあります:
- ・
- https://github.com/EvolutionGym/evogym/tree/main/examples
次のようなコマンドで、ロボットを進化・学習させることができます:
$ cd /pkg/vir130/evogym/examples # 強化学習(PPO )で、自分で作った/生成されたロボットに、うごきを学習させる: $ python run_group_ppo.py --algo ppo --use-gae --lr 2.5e-4 --clip-param 0.1 --value-loss-coef 0.5 --num-processes 4 --num-steps 128 --num-mini-batch 4 --log-interval 100 --use-linear-lr-decay --entropy-coef 0.01 --eval-interval 50 # 進化計算(GA)と強化学習(PPO )で、ロボットのカタチとうごきを進化・学習させる: $ python run_ga.py --env-name "Walker-v0" --algo ppo --use-gae --lr 2.5e-4 --clip-param 0.1 --value-loss-coef 0.5 --num-processes 4 --num-steps 128 --num-mini-batch 4 --log-interval 100 --use-linear-lr-decay --entropy-coef 0.01 --eval-interval 50
ここで指定したスクリプト(run_group_ppo.py, run_ga.py, ...)は、たんに引数つきで必要な関数を呼び出しているだけですーーなので、これらのファイルの引数を編集することで、いろいろなパターンを試すことができます。それぞれの引数の意味は、サンプルのページで解説されています:
- ・
- https://github.com/EvolutionGym/evogym/tree/main/examples
またコマンドラインの引数は、すべて強化学習(PPO )のものです。これらの意味は、次の元サイトを参照とありますが……:[※1]
- ・
- https://github.com/ikostrikov/pytorch-a2c-ppo-acktr-gail
進化・学習の結果は、次のようなコマンドで確認できます(指定した個体や世代のアニメーションが表示されます):
$ python visualize.py --env-name "Walker-v0" Enter experiment name: test_ga Enter generation number: 3 Enter robot rank: 3 Enter num iters: 1000
全個体・全世代のアニメーションを、ファイルに生成することもできます(GIF 形式):[※2]
$ python make_gifs.py
生成されたアニメーションのファイルは、次のフォルダ下の試みごとのフォルダ群に保存されます:[※3][※4]
- ・
- evogym/examples/saved_data/all_media/...
- ※1
- このサイトの記述だけだとあまり参考にならないと思うので、OpenAIのStable Baselinesのガイドを挙げておきます:
- ・
- https://stable-baselines.readthedocs.io/en/master/modules/ppo1.html
- ※2
- このコマンドも、(アニメーションをリアルタイムに表示こそしないものの、実行時には)描画できるデバイスが指定されている必要があります。
- ※3
- これは「歩く」というしごとを、進化計算(GA)+強化学習(PPO )で試みたもののひとつですーー上から第0世代〜第3世代のロボット群ですが、歩かないものは淘汰され、歩くものからはそれなりにカタチとうごきを引き継ぎ、最後は速く歩けるものが残っていますね(この試みでは、第2と第3世代の右端のロボットが、このしごと(「歩く」)にもっとも適応した(=最適化された)ロボットです)。
- ※4
- 次の試みとも結果が微妙に違っているのが面白かったりーーなおこちらは、人工生命研究の歴史から「Evolution Gym 」について解説した、岡瑞起氏よる記事です:
- ・
- https://note.com/mizuki_oka/n/n1719412f641b