[深層強化学習] Dueling Networkの実装とcartpoleにおける性能比較

# はじめに

### DuelingDQNとは
![スクリーンショット 2021-09-12 15.17.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/b82b9ac6-eeea-a104-f349-26dd6b935b08.png)

通常のDQNは上図のようにSequencialなNetworkを通じて行動価値関数を予測していきます。それに対し、DuelingNetworkでは下図のように途中で状態価値関数を求めるV(s)と行動価値関数からV(s)を引いたものを求めるAdvantageに分岐します。その後、それらを足し合わせることで最終的な行動価値関数を導出します。

本記事ではシンプルなDQNとDuelingDQNをPytorchを用いて実装し、性能比較していきます。

### Github

### 参考
- [【深層強化学習】Dueling Network 実装・解説](https://qiita.com/omuram/items/c03a2b2266c958564a9a)




# 実装
### シンプルなNetworkの実装
ここではネットワークのみの実装を行います。

```
class DQN(nn.Module):

    def __init__(self, num_states, num_actions):
        super(DQN, self).__init__()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(num_states, 32),
            nn.ReLU(),
            nn.Linear(32, 32),
            nn.ReLU(),
            nn.Linear(32, num_actions)
        )

    def forward(self, x):
        x = x.to(device)
        logits = self.linear_relu_stack(x)
        return logits
```
全結合層を3つ使用し、活性化関数にはReLU関数を使用しています。

### Dueling Networkの実装

```
class DuelingDQN(nn.Module):
    def __init__(self, num_states, num_actions):
        super(DuelingDQN, self).__init__()
        self.num_states = num_states
        self.num_actions = num_actions

        self.fc1 = nn.Linear(self.num_states, 32)
        self.relu = nn.ReLU()
        self.fcV1 = nn.Linear(32, 32)
        self.fcA1 = nn.Linear(32, 32)
        self.fcV2 = nn.Linear(32, 1)
        self.fcA2 = nn.Linear(32, self.num_actions)

    def forward(self, x):
        x = self.relu(self.fc1(x))

        V = self.fcV2(self.fcV1(x))
        A = self.fcA2(self.fcA1(x))

        averageA = A.mean(1).unsqueeze(1)
        return V.expand(-1, self.num_actions) + (A - averageA.expand(-1, self.num_actions))
```
DuelingDQNでは最初に全結合層を行い、その後AdvantageとVに分岐され、最終的にVとAから出力を算出します。

# 結果
DQNとDuelingDQNでの実行結果を示します。今回は下記のハイパーパラメータを利用し3000エピソード実行しました。

| Parameter | Value | Description |
|:-----------|------------:|:------------:|
| episode       | 3000        | エピソード数         |
| capacity       | 10000        | experience replayの容量         |
| batch_size       | 32        | バッチサイズ         |
| gamma     | 0.97      | 時間割引率       |
| target_update_iter       | 20        | target networkの更新間隔         |
| eps_start         | 0.9          | ε-greedyのパラメータ           |
| eps_end       | 0.5       | ε-greedyのパラメータ       |
| eps_decay    | 200     | ε-greedyのパラメータ    |

### DQN
![スクリーンショット 2021-09-12 14.46.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/5fdcbe89-fd5a-344e-a50c-ee6c10b06739.png)

### Dueling DQN
![スクリーンショット 2021-09-12 14.51.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/e908cc67-671b-6440-970c-ede6e07d07f3.png)

シンプルなDQNを使用した場合は2000エピソード超えたあたりから成績が向上していますが、Dueling DQNを使用した場合は1200エピソードあたりから向上しており、学習効率の向上が見て取れます。

また、Cartpoleは簡単な問題であるのと報酬を200で切っているため、性能についてははっきりわかりませんが、3000エピソードまでで比べるとDuelingDQNの方が成績は良い傾向が見れます。


### DuelingDQNの学習結果
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/64043a46-bead-4878-a0fd-618de6c2a727.gif)

エピソード数が少ないだけあって、だいぶ安定していないですね。

#### [追記]
このあとエピソード6000回行った結果です。
100回の試行すべてで200stepに到達していますが、若干遊びを覚えているような？感じがします。
200stepくらいならこのくらいの雑さでも大丈夫ということを学習したのかもしれません、、、。
(単純に学習が足りてないだけの可能性が高いです)

![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/e86b5657-790b-9e59-49b4-5823683774f4.gif)


![スクリーンショット 2021-09-12 16.04.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251551/40c26597-ce48-8a1f-1f1f-848d6d4b791a.png)

# まとめ
今回はDueling DQNの実装を行い、通常のDQNとCartpoleでの性能実験を行いました。
深層学習は他にも多くの手法があるため、それらも実装していきたいと思います。 (直近はRainbowの実装を目指していきます。)