TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

リバーシ(オセロ)で深層強化学習 その6(Dueling Network)

前回リバーシでDDQNを試したが、今回は同じくDQNの発展形であるDueling Networkを試す。

Dueling Network

[1511.06581] Dueling Network Architectures for Deep Reinforcement Learning

DQNでは行動価値を行動価値関数Qで推定するが、Dueling Networkでは、行動価値関数Qを状態価値関数Vとアドバンテージ関数Aに分解する。
DQNの以前の研究で、状態価値関数とアドバンテージ関数の2つに分解することで、Q学習とよりも速く収束することが示されている。

アドバンテージ関数は、以下の式で定義される。
\displaystyle
A^{\pi}(s,a) = Q^{\pi}(s,a) - V^{\pi}(s)
行動価値関数Qから状態価値関数Vを減算することで、各行動aの重要度の相対的な尺度を取得している。

この定義を使用すると、DDQNのターゲット
\displaystyle
y_i^{DDQN} =  r + \gamma Q(s',argmax_{a'} Q(s',a';\theta_i)   ;\theta^{-})
の行動価値関数Qを、2つのモジュールに分解できる。
\displaystyle
Q(s,a;\theta,\alpha,\beta) =  {V}(s;\theta,\beta) + {A}(s,a;\theta,\alpha)


ここで、VとAへの分解は、一意には定まらないことに注意する必要がある。
例えば、Vに定数を足して、Aからその値を引いてもQの値は同じになるということが起きる。
この性質のため、この式を直接使用するとパフォーマンスの低下を招く。


対処として、選択した行動で、Aが0になるように、次のように式を変形する。
\displaystyle
Q(s,a;\theta,\alpha,\beta) =  {V}(s;\theta,\beta)~+
\left({A}(s,a;\theta,\alpha) - \max_{a' \in |\mathcal{A}|}  {A}(s, a' ;\theta,\alpha) \right)


論文では、最大値の代わりに、平均を使用することで、学習の安定性を向上できると述べられている。
\displaystyle
Q(s,a;\theta,\alpha,\beta) =  {V}(s;\theta,\beta)~+
\left({A}(s,a;\theta,\alpha) - \frac{1}{|\mathcal{A}|} \sum_{a'} {A}(s, a' ;\theta,\alpha) \right)

実装方法

Dueling Networkは、DQNのネットワークの出力を、一旦VとAに分けた後、上記式の結果を出力することで実現する。
ネットワークの変更のみで実装でき、DQNの学習部分はそのまま適用できる。

論文では、学習部分はDDQNを使用しているので、DDQNを使用する。

実装

前回までのDQNのネットワークを以下のように変更した。
fcl2を、fcl2_advとfcl2_vに分けている。
forwardの最後で、fcl2_advとfcl2_vから出力のQを計算している。

class DQN(nn.Module):

    def __init__(self):
        super(DQN, self).__init__()
        self.conv1 = nn.Conv2d(2, k, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(k)
        # 省略
        self.conv10 = nn.Conv2d(k, k, kernel_size=3, padding=1)
        self.bn10 = nn.BatchNorm2d(k)
        self.fcl1 = nn.Linear(k * 64, fcl_units)
        self.fcl2_adv = nn.Linear(fcl_units, 65)
        self.fcl2_v = nn.Linear(fcl_units, 1)

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        # 省略
        x = F.relu(self.bn10(self.conv10(x)))
        x = F.relu(self.fcl1(x.view(-1, k * 64)))

        adv = self.fcl2_adv(x)
        val = self.fcl2_v(x).expand(-1, 65)

        out = val + adv - adv.mean(1, keepdim=True).expand(-1, 65)

        return out.tanh()

実行結果

1万イテレーション学習した結果は以下のようになった。

損失

f:id:TadaoYamaoka:20191218225242p:plain
2000イテレーションまでは安定していないが、それ以降は、DQNやDDQNよりも安定して損失が低下している。

強さ

ランダムと1000局対局した結果は以下の通り。

結果 勝率 信頼区間95%
DQN 833勝152敗15分 84.57% 86.7~82.2%
DDQN 847勝131敗22分 86.61% 88.6~84.3%
Dueling 935勝58敗7分 94.16% 95.5~92.5%

DQNとDuelingの勝率に有意差があるか検定するための統計量zは、-6.92となった。
z<−1.96であるため、DQNとDuelingの勝率は等しいという帰無仮説は棄却され、Duelingの方が有意に強いと言える。


DQNと直接対局した結果は、

先手 後手 結果 勝率(信頼区間95%)
Dueling DQN 498勝462敗40分 55.0~48.7%
DQN Dueling 412勝562敗26分 45.42~39.20%
計(DDQN-DQN) 1060勝874敗66 57.0~52.6%

直接対局でも、有意に強くなっている。

まとめ

Dueling Networkでリバーシの学習を行った。
DQNと比較して、同じイテレーション数で有意に強くなることが確認できた。
Dueling Networkは、リバーシに有効な手法と言えそうだ。

論文では、Prioritized Replayと組み合わせることでさらに効果があると述べられているので、次は、Prioritized Replayを試してみたい。

(続く)