【深層強化学習】Actor-Criticでのマルチタスク学習の有用性

Actor-Criticを深層学習で実現する場合、主に以下の図の2通りのNN（Neural Network）構成がありえます。
これらのうち、中間層を共有した「共有型」は、単一NN内に、Actorの出力層（Policyを出力）、Criticの出力層（Valueを出力）の2つを持ちます。
この場合、**中間層を共有した単一のNNで「Policyの出力」と「Valueの出力」という複数の”タスク”を持つ**、ということになります。
この「共有型」のNNの学習は、いわゆる<strong>「マルチタスク学習」</strong>の一例となります。
※「マルチタスク学習」とは、単一NNで複数のタスクを解けるように学習することです。
![両タイプイメージ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/19440a9e-8b34-7961-cbe9-239145a99674.png)
ActorとCriticで中間層を共有し単一のNNを構成する「共有型」（**マルチタスク学習**）と
ActorとCriticで別々のNNを構成する「分離型」（**シングルタスク学習** x 2）とで、
**どちらがいい学習をするか、実験**してみました。
小難しい言い方に言い換えると、
**「マルチタスク学習はActor-Criticモデルの性能アップに有用か否か、検証**してみた」というところでしょうか。

実験の詳細と、実装全コードは、以下にあります。
フレームワークを使用せず、地道に計算グラフを書いて誤差逆伝播もnumpyだけで実装したので、読み解くのは面倒かもしれません。

https://github.com/Atsuo-Shoji/a2c_continuous_no_framework

## 実験　マルチタスク学習 vs シングルタスク学習 　どちらが良い学習をするか？ ##

### 実験の内容や条件 ###

#### 実験の目的 ####

マルチタスク学習とシングルタスク学習では、どちらが良い学習をするか、
即ち、
マルチタスク学習はActor-Criticモデルの性能アップに有用か否か、の検証

#### 実験に使用した強化学習の環境 ####

以下3環境を使用しました。
各々の環境それぞれで、マルチタスク学習とシングルタスク学習の比較をしました。
※実際は、「BipedalWalker」も加えて4環境で実験しました。が、BipedalWalkerは、マルチタスク学習とシングルタスク学習ともに、学習をほとんどしなかったため、本稿では除きます。

| 環境名 | 外観 | 状態の次元数 | 行動の次元数 | 1エピソードでの上限ステップ数 | 目的 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| Pendulum  | ![pendulum_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/86732f79-cdbd-ea7f-5ce5-307c6adbe566.png)  | 3 | 1 | 上限は無し<BR>固定で200ステップ/エピソード  | バーの直立 |
| LunarLanderContinuous  | ![LunarLanderContinuous_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/0427ba25-c570-2e66-4ed6-f1b59a37bb51.png) | 8 | 2 | 1000 | ゴール領域に着陸 |
| HopperPyBullet | ![hopper_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/ca9578f2-30ba-b99d-ded2-0925c315af85.png)  | 15 | 3 | 1000 | 遠くまで跳ねて前進 |

#### 共有型と分離型のNNの構成 ####

![両タイプNN概要図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/6ea180cc-e8c2-c852-25b5-73cad597e04f.png)

##### 共有型NN（マルチタスク学習）の構成　順伝播・誤差逆伝播 #####

![共有型NN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/0f1e8cc6-4da0-f785-5bd2-727939e39b5d.png)

:::note info
**マルチタスク学習**、即ち複数の損失がある場合の一般的な誤差逆伝播の仕方
各損失の重み付き和を全体の損失とし、それを起点に誤差逆伝播をします。
例）1つのNNに2つのLoss（L1とL2）がある場合、
L = L1 + wc x L2  （wcは係数で、ハイパーパラメーター）
のようにLossを1つ（L）にして、そのLoss（L）を誤差逆伝播の起点とします。
:::

* Actorの損失をAdvantageを介してCritic側に逆伝播させない理由
Advantageは、Actorの損失関数においては、言わば定数項のようなものです。
Advantageは、あくまでも学習時のもので、Policyの推論時には存在しないからです。
別の言い方をすると、
Policyの推論時、Actorは、K次元正規分布N(μ, var)を決定するuとvar（実際にはlog（var））を出力します。そのuとvarの出力に、Advantageも、Advantageの要素であるCritic出力値Vも、一切関与しません。

##### 分離型NN（シングルタスク学習）の構成　順伝播・誤差逆伝播 #####

![分離型NN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/81f5c4b2-21e9-6bef-f61e-0988affe81d7.png)

#### マルチタスク学習 vs シングルタスク学習　勝負の判定の仕方 ####

NNのパラメーター更新はエピソード毎　＝　1イテレーション/エピソード
1イテレーションで使用する学習データは、この1エピソードの試行結果
というやり方で学習します。

**判定ポイントは以下2点です。**
* **報酬の大小
この「1エピソードの試行」で稼得した報酬の大小（全エピソード合計やエピソード平均）**
* **成長のスピード
大きな報酬を稼得できるレベルに成長するまでに要したエピソード数**

---
### 実験結果 ###

#### Pendulum での実験結果 ####

マルチタスク学習とシングルタスク学習を、各々10万エピソード費やして行いました。

##### 学習の効果　モデル学習前と後でのAgentの挙動比較 #####

| ＜モデル学習前＞<BR>未学習モデルでPlay<br>バーは一向に立たない | | ＜モデル学習後＞<BR>学習済モデル（分離型NN）でPlay<br>バーは途中から直立を維持 |
|:-:|:-:|:-:|
| ![Pendulum_sep_biginner.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/1a2c7413-5c05-5030-9693-5da845ac4574.gif) | <font color="red">**➡**</font> | ![Pendulum_sep_202010010221.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/736a9324-e938-64ed-1170-e0f1adfbf6e7.gif) |

##### 学習時の稼得報酬/エピソードの推移（10万エピソード） #####

（マルチタスク学習・共有型NN）学習時の稼得報酬/エピソードの推移
![pendulum_std_G_202010122142_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/4f973415-537a-19da-0cfb-881c4f9b76c7.png)
※ ↑ ↓ 縦軸の目盛りの違いに注意

（シングルタスク学習・分離型NN）学習時の稼得報酬/エピソードの推移
![Pendulum_sep_std_G_202010121602_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/65d94160-4707-8b85-6cf2-d871a0701204.png)

|マルチタスク学習／<br>シングルタスク学習|稼得報酬<br>合計|稼得報酬/エピソード<br>平均値|稼得報酬/エピソード<br>最大値|
|:-:|:-:|:-:|:-:|
|マルチタスク学習<br>共有型NN|-5310万|‐531|0|
|シングルタスク学習<br>分離型NN|-7366万|-737|0|

報酬の大小：シングルタスク学習より、**マルチタスク学習の方が顕著に大きい。**
成長のスピード：シングルタスク学習より、**マルチタスク学習の方が顕著に早い。**

##### Pendulum　マルチタスク学習 vs シングルタスク学習　判定 #####

|マルチタスク学習<br>共有型NN|シングルタスク学習<br>分離型NN|
|:-:|:-:|
|<font color="red">**勝ち**</font>|<font color="blue">負け</font>|

**結論：Pendulumでは、マルチタスク学習はActor-Criticモデルの性能アップに有用**

---
#### LunarLanderContinuous での実験結果 ####

マルチタスク学習とシングルタスク学習を、各々1万エピソードを費やして行いました。

##### 学習の効果　モデル学習前と後でのAgentの挙動比較 #####

|＜モデル学習前＞<BR>未学習モデルでPlay<br>枠外に飛去またはゴール外に落下| |＜モデル学習後＞<BR>学習済モデル（分離型NN）でPlay<br>ゴール内に水平を保って着陸|
|:-:|:-:|:-:|
|![LunarLanderc_beginner.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/86fa20ea-817f-3174-75a5-26fd56667bea.gif)| <font color="red">**➡**</font> |![LunarLanderc_step_202010022143_2_138s.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/213ab587-ea5b-eb85-a090-23a9d1ba8c3a.gif)|

##### 学習時の稼得報酬/エピソードの推移（1万エピソード） #####

（マルチタスク学習・共有型NN）学習時の稼得報酬/エピソードの推移
![LunarLanderc_std_G_202010121108_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/02c370e9-3a0e-2b1e-dbd5-e215927899ff.png)
※ ↑ ↓ 縦軸の目盛りの違いに注意

（シングルタスク学習・分離型NN）学習時の稼得報酬/エピソードの推移
![LunarLanderc_sep_std_G_202010130007_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/6927b71e-58ff-4675-0f62-224056fa3c9e.png)

|マルチタスク学習／<br>シングルタスク学習|稼得報酬<br>合計|稼得報酬/エピソード<br>平均値|稼得報酬/エピソード<br>最大値|
|:-:|:-:|:-:|:-:|
|マルチタスク学習<br>共有型NN|80万|80|289|
|シングルタスク学習<br>分離型NN|94万|94|282|

報酬の大小：合計では、マルチタスク学習に対して**シングルタスク学習の方が大きい。**
成長のスピード：どちらが早いか判定不能。どちらも（Pendulumのように）いきなり成長したりしていないため。つまり**顕著な違いは見当たらない。**

##### LunarLanderContinuous　マルチタスク学習 vs シングルタスク学習　判定 #####

|マルチタスク学習<br>共有型NN|シングルタスク学習<br>分離型NN|
|:-:|:-:|
|<font color="blue">惜敗</font>|<font color="red">辛勝</font>|

**結論：LunarLanderContinuousでは、マルチタスク学習はActor-Criticモデルの性能アップに有用とは言えない**

---
#### HopperPyBullet での実験結果 ####

マルチタスク学習とシングルタスク学習を、各々5万エピソードを費やして行いました。

##### 学習の効果　モデル学習前と後でのAgentの挙動比較 #####

| ＜モデル学習前＞<BR>未学習モデルでPlay<br>すぐ転倒し前に進めない | | ＜モデル学習後＞<BR>学習済モデル（共有型NN）でPlay<br>完璧な歩様で上限1000ステップまで前進 |
|:-:|:-:|:-:|
| ![HopperPyBullet_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/e619ed93-9ff4-809f-e6c0-7100ef3583cb.gif) | <font color="red">**➡**</font> | ![Hopper_std_G_202010122350_1000_1579.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/28806a25-9d53-2871-b9d9-cc2882e04c88.gif) |

※HopperPyBulletの画像が白いのはご了承下さい。Google Colaboratory上で3Dのpybullet-gymの動きを色つきでキャプチャーする簡便な方法がありませんでした。
↓本来のHopperPyBulletの外観。脳内イメージ補完してください。
![hopper_60.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/4ebc068d-792c-346a-1a91-81c8db82794b.png)

##### 学習時の稼得報酬/エピソードの推移（5万エピソード） #####

（マルチタスク学習・共有型NN）学習時の稼得報酬/エピソードの推移
![Hopper_std_G_202010122350_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/72691616-debb-4ccd-2efa-202a67af747d.png)
※ ↑ ↓ 縦軸の目盛りの違いに注意

（シングルタスク学習・分離型NN）学習時の稼得報酬/エピソードの推移
![Hopper_sep_std_G_202010122356_score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/73c1a4d7-41bf-3f5d-2839-58792e3f2294.png)

|マルチタスク学習／<br>シングルタスク学習|稼得報酬<br>合計|稼得報酬/エピソード<br>平均値|稼得報酬/エピソード<br>最大値|
|:-:|:-:|:-:|:-:|
|マルチタスク学習<br>共有型NN|3619万|724|1689|
|シングルタスク学習<br>分離型NN|2263万|453|1276|

報酬の大小：シングルタスク学習より、**マルチタスク学習の方が顕著に大きい。**
成長のスピード：シングルタスク学習より、**マルチタスク学習の方が顕著に早い。**

##### HopperPyBullet　マルチタスク学習 vs シングルタスク学習　判定 #####

|マルチタスク学習<br>共有型NN|シングルタスク学習<br>分離型NN|
|:-:|:-:|
|<font color="red">**圧勝**</font>|<font color="blue">惨敗</font>|

**結論：HopperPyBulletでは、マルチタスク学習はActor-Criticモデルの性能アップに有用**

---
#### 3環境まとめ ####

##### マルチタスク学習 vs シングルタスク学習　判定 #####

|環境|外観|マルチタスク学習<br>共有型NN|シングルタスク学習<br>分離型NN|
|:-:|:-:|:-:|:-:|
|Pendulum| ![pendulum_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/86732f79-cdbd-ea7f-5ce5-307c6adbe566.png)  |<font color="red">**勝ち**</font>|<font color="blue">負け</font>|
|LunarLanderContinuous| ![LunarLanderContinuous_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/0427ba25-c570-2e66-4ed6-f1b59a37bb51.png) |<font color="blue">惜敗</font>|<font color="red">辛勝</font>|
|HopperPyBullet| ![hopper_mini.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/ca9578f2-30ba-b99d-ded2-0925c315af85.png)  |<font color="red">**圧勝**</font>|<font color="blue">惨敗</font>|

#### 総括　マルチタスク学習 vs シングルタスク学習 　どちらが良い学習をするか？ ####

**この実験においては（！）、
マルチタスク学習の方が「概ね」シングルタスク学習より良い学習をした**、即ち、
**マルチタスク学習は「概ね」Actor-Criticモデルの性能アップに有用であった**、
と言えるでしょう。
しかし、強化学習の環境は無数にあり、本実験では使用したのはそのうち3つのみです（しかもマルチタスク学習がうまくいったのはそのうち2つのみ）。他の環境ではまた**違った結果が得られるかも**しれません。
ですが、少なくとも、
<font color="red"><strong>Actor-Criticを実装する場合、中間層を共有してマルチタスク学習させることを検討する価値はある、</strong></font>
ということは言えるでしょう。

## 実験結果の考察 ##

#### マルチタスク学習（共有型NN）が概ねうまくいった理由の推測 ####

マルチタスク学習がうまくいく条件として、以下が挙げられています。

##### マルチタスク学習がうまくいく条件 #####
＝＝＝以下引用、ただし本稿に関係がある部分のみ＝＝＝

> 1. 関連するタスク
マルチタスク学習は、タスク間に共通の特徴や構造が存在する場合に効果的です。例えば、画像分類と物体検出のような、画像の特徴を共有するタスクは、マルチタスク学習で一緒に学習させると効果的です。
> 2. 適切なネットワーク構造
一般的に、タスク間で共有される部分とタスク固有の部分に対応するネットワーク構造を持つモデルが必要です。共有レイヤーで複数のタスクの共通特徴を抽出し、タスク固有のレイヤーで各タスクの特定の特徴を処理します。
> 3. 適切な損失関数
マルチタスク学習では、各タスクの損失関数を組み合わせて全体の損失関数を設定する必要があります。この際、各タスクの重要性を適切に設定することが重要です。一方で、タスク間の競合を避けるために、各タスクの損失の重みを動的に調整するアプローチもあります。

＝＝＝引用終わり＝＝＝

マルチタスク学習がうまくいったPendulumとHopperPyBulletについては、上記全てを、少なくともある程度は満たしていた、ということなのでしょう。

##### 「1. 関連するタスク」「2. 適切なネットワーク構造」について #####

「マルチタスク学習は、**タスク間に共通の特徴**や構造が存在する場合に効果的」
「**共有レイヤーで複数のタスクの共通特徴を抽出**し、タスク固有のレイヤーで各タスクの特定の特徴を処理」
と書いてあります。

`時刻tにおける状態State：s(t)`　とおくと、
NN入力値s(t)に対するActorのPolicy出力は、そのs(t)に対する最適Policyであり、
NN入力値s(t)に対するCriticのValue出力は、そのs(t)の「価値」（強化学習の教科書的表現をすると、s(t)で取りえる全Actionの見積報酬和の期待値）です。
つまり、**ActorのPolicy出力タスクとCriticのValue出力タスクは、「状態s(t)とは何ぞや？その特徴は？」を共有している、そしてそれは、共有されている中間層の出力そのもの**、と言えるのではないでしょうか。

**共に共通の情報「NN入力値のStateの特徴」を必要とする異なるタスクを課されているActorとCriticで中間層を共有しているからこそ、その中間層は「NN入力値のStateの特徴」をとらえきる能力を獲得できる**のであって、それは分離型NNでは成しえないことです。
本実験でマルチタスク学習がうまくいったPendulumとHopperPyBulletについては、**共有された中間層は、「NN入力値のStateの特徴」をとらえきれるくらいの表現力を持てる構造をしている**、ということでしょう。
それにより、たった1層しかないActorとCriticの各々の出力層で、それなりに妥当なPolicyとValueを出力できるのでしょう。
![結果考察_共有型_2_75.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613026/127edba4-20ed-7c1b-114d-d58b99d47925.png)

**学習の安定性という重要な視点**もあると思います。
マルチタスク学習の共有型NNは中間層を共有しており、「NNほぼ1個」。
シングルタスク学習 **x 2**の分離型NNは文字通り「NN2個」。
**共有型NNの方が学習が安定**しやすいのだと思います。

##### 「3. 適切な損失関数」について ##### 

本実験での共有型マルチタスク学習の損失関数は、
`L_actor：Actorの損失関数値`　　`L_critic：Criticの損失関数値`　　として、
`L = L_actor + wc * L_critic`
です。この左辺Lを誤差逆伝播の起点とします。
wcは重み係数で、L_actorとL_criticの”相対的な重視の度合い”です。
**本実験では、このwcを一律「1」にしています。Actorの損失とCriticの損失のどちらかをより重視する、という理由が無い**からです。
ただし、本来、このwcは、学習時のL_actorとL_criticのスケール（数値規模）の大小によっても、適した数値が決まるようです（例えば、ある学習局面で、L_actor=1、L_critic=0.01のようにスケールが乖離している場合を想像するとわかる）。
PendulumもHopperPyBulletも、lossの遷移グラフ（本稿には掲載してません）を見ると、L_actorとL_criticのスケールは大体同じで、**wc=1であることに問題はなかった**のでしょう。

#### LunarLanderContinuousだけマルチタスク学習（共有型NN）がうまくいかなかった理由の推測 ####

上記「マルチタスク学習がうまくいく条件」のうち、
「2. 適切なネットワーク構造」をまず疑いました。
共有されている中間層の表現力が不足しており、LunarLanderContinuousの「NN入力値のStateの本質」を、共有されている中間層でとらえきれていない可能性があります。
これが理由かどうかは、実際に対策実装をして結果を見ないことにはわからないです。

「3. 適切な損失関数」も疑いました。
しかし、lossの遷移グラフ（本稿には掲載してません）を見ると、L_actorとL_criticのスケール（数値規模）は、むしろPendulumとHopperPyBulletよりも同一で、wc=1であることに問題があるように思えませんでした。