強化学習に再挑戦(二重倒立振子)

2024-05-31

流行りの機械学習でなにか動かしたいなぁとチュートリアルを動かそうとするも大抵エラーで動かない、動いたとしてもちょっと経つとライブラリの更新だか環境の変化でエラーになってしまい、その依存関係も解決できない。 ローカルで動かそうとするが環境構築でつまずく。

そんな感じで挫折を繰り返してるので、ちゃんと環境構築できるようにやってみた。

環境構築編

強化学習や機械学習のチュートリアルといえばPython、なのでその環境をある程度固定して、安心して動かせる環境を用意できるようにしたい。

Python:pyenv

Python自体のインストール、バージョン更新・変更はpyenvでできる。 詳細なインストール・使用方法は略。

  • pyenv local <バージョン番号>を実行すると.python-versionというファイルに保存され、そのフォルダ以下にいるとpythonとしたときに自動的にそのバージョンが使われる
  • その状態でpip installしてもローカルフォルダ以下ではなく、pyenvのバージョン/lib以下にインストールされる (なので他のフォルダで同じPythonバージョンを使うとバッティングする可能性があると思う)

Python仮想環境:venv

Pythonで仮想環境を作る方法が何種類かあるみたいで歴史的経緯・使用感などは自分では把握してないのだけど、venvを使うことにした。

  • 初期設定:python -m venv .venvとすると.venvというフォルダが生成される
  • 仮想環境を有効化:source .venv/bin/activate
    • 有効化すると、pip installしたものが.venv/lib以下にインストールされる

仮想環境を有効化した状態でPythonスクリプトを実行することで、想定している環境上で動作させることができる。

  • 仮想環境を無効化:deactivate

モジュールのリスト化:pip freeze

venvでローカルに作成された依存ライブラリ自体はリポジトリにコミットしないので、別のマシンで動かそうとした時に困る。 そこでインストールしたモジュールを書き出し、再利用できるようにする:

  • インストールしたモジュールの書き出し:pip freeze > requirements.txt
    • 自分でインストールしたモジュール以外にも、依存するモジュールも書き出される
  • 復元:pip install -r requirements.txt

以上で環境を固定できるようになったので、安心してスクリプトを動作させることができる(たぶん…)。

強化学習を動かしてみる

機械学習(教師あり学習)はデータを用意する必要があって、なかなか好き勝手なことをするには難しい。 なので強化学習が動かせると楽しめそう。

OpenAI Gymはdeprecated

強化学習の手始めといえばOpenAI Gymという感じでさすがに動かせるものかと思ってたんだが、少し古いコードだとenv.stepの戻り値が違うというエラーが出てしまう。 バージョン0.26.0で変更されてしまったらしい。

で対応して動かそうとしてたんだけどGym自体がすでにdepreatedになってたらしく、 Gymnasiumに引き継がれたとのこと。

  • pip install gymnasium
  • import gymnasium as gymとすれば、一応互換で使えるらしい

倒立振子を動かしてみる

Gymnasiumで強化学習の環境は用意できるけど、強化学習のコード自体はまた別に用意する必要がある。 自分で組むだけの知識も理解も足りないのでなにかサンプルを参考にしたい、と思ったらドキュメントにいろいろ紹介されていた。 その中で倒立振子:

Mujocoという物理エンジンを使った倒立振子の環境を「REINFORCE」という方法(紛らわしい)で学習するサンプルとなっている。 ソースコードが添付されているのでダウンロードして必要なモジュールをインストールすることで動かすことができる。

  • 方策ネットワーク:状態を入力、行動を正規分布の平均と標準偏差を出力とするニューラルネットワーク。共有部分として活性化関数がtanhの全結合層を2段(次元は16と32)、平均と標準偏差にそれぞれ全結合層、という構成。
  • 最適化はAdamW
  • sample_action関数内の正規分布作成時に平均値にもepsを足しているのは間違いじゃないか?

学習結果の動作を再生させる

上で公開されているコードは学習を5回行ってエピソード数に従った報酬の推移のグラフが表示されるだけで、実際にどのような動作になるのか自分にとってはイメージしづらい。 なので実際に再生・表示させたい:

  • 動作をリアルタイムに描画:gym.make("InvertedPendulum-v4")にキーワード引数render_mode='human'を追加
    • 表示内容やキー操作をいじりたかったがやり方わからず…
  • 学習結果の保存:torch.save(agent.net.state_dict(), 'ファイル名.pt')
  • 学習結果の読込:agent.net.load_state_dict(torch.load('ファイル名.pt'))
  • 行動の選択:学習時に使われるagent.sample_actionでは正規分布からサンプルするため乱数要素が含まれてしまうので、 ベストな行動(平均の値そのもの)を取り出すには、action_means[0].detach().numpy()とする

二重倒立振子を動かしてみる

読み込む環境を二重倒立振子に変えて遊んでみた。

  • 観測できる状態は11個
  • 得られる報酬には距離ペナルティと速度ペナルティが入っている。1タイムステップごとに、
    • 生存ボーナス:10
    • 距離ペナルティ: (x,yは第2ポール先端の座標)
    • 速度ペナルティ:
  • 最大タイムステップは最大1,000なので、最大の累積報酬は1万になる

ネットワーク構成をいじってみる

逆振子が二重になっている分元の構成より複雑になっているだろうはずなので、方策のネットワーク構造をあれこれいじってみる:

  • 活性化関数をtanhからReLUなど他のものに変えてみたが性能は劣化した
  • 隠れ層の段数を増やすと良くなる(3段:32-16-32にしてみた)
  • 3万〜5万エピソードくらい学習をさせると、倒立できるようになることもある(かなりムラがある)

雑多メモ