見出し画像

ML-AgentsにおけるPPOとSACの学習結果の比較

たねむらです。
僕は普段、Unity上で使える強化学習パッケージであるML-Agentsを使って色々な実験をしています。

今回は、ML-Agentsで利用可能な「PPO」と「SAC」の二種類の強化学習アルゴリズムを使って、以下の画像のように、水の入ったシリンダーにロボットを投入し、溺れずに水面近くで泳ぎ続けるようなタスクを学習させてみました。本記事では、この実験での学習設定や学習結果についてまとめます。

水の入ったシリンダーに投入されるロボット

※ 本記事は強化学習の基礎知識やML-Agentsについてある程度知っている方を対象としています。ML-Agentsについては、@npakaさんのUnity ML-Agents入門にとても分かりやすくまとめられています。

報酬設定

エージェントに与える報酬は、以下の二つにしました。

  • 体の中心と水面の中心との距離に応じたプラスの報酬(0 ~ 0.04)

  • 頭の角度に応じたマイナスの報酬(0 ~ -0.01)

一つ目は、溺れずに泳ぐという目的を達成するために、体の中心が水面に近いほど報酬を与えるというシンプルなものです。水面の中心から離れることを許容すると、シリンダーの壁に手をぶつけてその衝撃で沈んでしまったりしていたので、水面の中心に近いほど良いという条件にしました。

二つ目は、真上方向に対する頭の角度によってマイナスの報酬を与えるというものです。マイナスの報酬を与えることで、ある程度異常は角度が大きくならなくなることを期待して設定しました。もしこの報酬を設定しなかった場合は以下のように、頭が完全に水に浸かってしまいます。

また、学習の効率化のために、シリンダーの底まで溺れてしまった場合はそのエピソードを終了し、ロボットの位置を初期位置(水面の少し上)に戻してやり直すようにします。

学習アルゴリズムによる差が分かりやすくなるように、これらの報酬設定やリセット条件はPPOとSACの両方とも揃えます。

学習設定

ここでの学習設定は、mlagents-learnの実行時に指定するconfigファイルに記載する内容のことを指します。以下に、それぞれのconfigファイルの内容を記載します。

PPO

behaviors:
  SwimAgent:
    trainer_type: ppo

    max_steps: 30000000
    time_horizon: 100
    summary_freq: 10000
    keep_checkpoints: 7
    checkpoint_interval: 5000000

    hyperparameters:
      batch_size: 2048
      buffer_size: 20480
      learning_rate: 0.0003
      learning_rate_schedule: linear
      beta: 0.005
      epsilon: 0.2
      lambd: 0.95
      num_epoch: 3

    network_settings:
      normalize: true
      hidden_units: 128
      num_layers: 3

    reward_signals:
      extrinsic:
        gamma: 0.99
        strength: 1.0

SAC

behaviors:
  SwimAgent:
    trainer_type: sac

    max_steps: 30000000
    time_horizon: 1000
    summary_freq: 10000
    keep_checkpoints: 7
    checkpoint_interval: 5000000
    threaded: true

    hyperparameters:
      batch_size: 512
      buffer_size: 2000000
      learning_rate: 0.00003
      learning_rate_schedule: constant
      buffer_init_steps: 0
      tau: 0.005
      steps_per_update: 30.0
      save_replay_buffer: false
      init_entcoef: 0.01

    network_settings:
      normalize: True
      hidden_units: 128
      num_layers: 3

    reward_signals:
      extrinsic:
        gamma: 0.995
        strength: 1.0

どちらのアルゴリズムでも、以下の画像のように同時に10体のエージェントを学習させます。そのため、SACでは3ステップごとにポリシー(および報酬信号)の更新が行われることになります。

10体のエージェントで同時に学習する様子

学習結果

学習結果を比較してみましょう。

学習中に獲得した報酬の推移

PPOとSACの学習中の獲得報酬の推移は以下のようになりました。今回の報酬設定では、1エピソードに獲得できる最大の報酬が40となります。

各学習アルゴリズムによる獲得報酬の推移(水色がPPO、紺色がSAC)

どちらのアルゴリズムでも、初めはランダムな行動からスタートしているので、報酬はほとんどもらえていません。
PPOの場合、300万ステップほどで獲得報酬が増加し、1000万ステップの時点で獲得報酬をほぼ最大化することができています。
一方で、SACの場合は800万ステップほどで獲得報酬が増加しますが、獲得報酬の最大値はPPOよりも少し少ないようです。また、学習後期になっても報酬に若干のばらつきがあることが分かります。

この結果から、SACよりもPPOの方がより早く、より多くの報酬を安定して貰うことができていることが分かります(アルゴリズム的には、SACの方がより効率的であって欲しいのですが)。

学習中の平均エピソード長の推移の比較

今回のタスクでは、ロボットがシリンダーの底についた時点でエピソードを打ち切ります。そのため、エピソード長はロボットがどれだけシリンダーの底まで溺れずに耐えられたかをあらわします。
以下は、PPOとSACの学習中の平均エピソード長の推移の比較です。

各学習アルゴリズムによる平均エピソード長の推移(水色がPPO、紺色がSAC)

獲得報酬の量はエピソード長に関係するので、獲得報酬の推移と同じような推移になっています。PPOは300万ステップ以降は平均エピソード長が1000に張り付いており、溺れることが全くなくなっていることが分かります。一方で、SACは学習後期でも平均エピソード長が1000に張り付いておらず、たまに溺れていることが分かります。

動きの比較

それぞれの動きを確認してみましょう。
(noteの仕様上動画を直接埋め込めないのでツイートを埋め込んでいます)

PPO

PPOで学習した場合は、以下のような動きになりました。

一つ目の報酬が、「体の中心と水面の中心との距離」に応じた報酬なので、体の中心を水面に持っていこうとした結果、足を主に使ったこのような泳ぎ方になったのかなと思います。どちらかというと、泳いでるというよりは走っているような動きになっています。

SAC

SACで学習した場合は、以下のような動きになりました。途中でスローになります。

PPOと違い、体の中心を水面まで持っていくことはできていませんが、泳ぎの動作としてはすごく自然な動きになっているのではないでしょうか。こちらは、手と足のどちらも使って体の位置を保とうとしているように見えます。

まとめ

結果を比較すると、PPOの方がより効率的かつ安定して学習が進むということが分かりました。一方で、SACの方は手足を使った動きを学習しており、視覚的に自然な動きになっていました。

PPOは、間近の経験のみを使って学習する「On-Policy」という枠組みに分類されるアルゴリズムです。そのため、ある程度報酬を獲得できる方針がわかってくると、その方針を極めていくような学習になると考えられます。今回、バタ足で泳ぐことに特化したのも、こうした学習方法によるものではないでしょうか。
SACは、ある程度過去の経験も使って学習する「Off-Policy」という枠組みに分類されるアルゴリズムです。そのため、初期にいろいろな動きを試したときの経験も忘れずに覚えておくことで、全身を使った動きになったのではないかと考えられます。

今回のタスクでは、SACの方がより自然な動きを学習することができましたが、想定される最大の報酬を得られてはおらず、たまに溺れることがあるなど学習も安定していないため、タスクや報酬設定によってはPPOの方が優れたポリシーを学習できるのではないかと思います。また、SACの方がハイパーパラメータの設定に敏感なので、特別の理由がない限りはPPOを使うのがいいんじゃないかなと思います。

この記事が気に入ったらサポートをしてみませんか?