前回はリバーシで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学習とよりも速く収束することが示されている。
アドバンテージ関数は、以下の式で定義される。
行動価値関数Qから状態価値関数Vを減算することで、各行動aの重要度の相対的な尺度を取得している。
この定義を使用すると、DDQNのターゲット
の行動価値関数Qを、2つのモジュールに分解できる。
ここで、VとAへの分解は、一意には定まらないことに注意する必要がある。
例えば、Vに定数を足して、Aからその値を引いてもQの値は同じになるということが起きる。
この性質のため、この式を直接使用するとパフォーマンスの低下を招く。
対処として、選択した行動で、Aが0になるように、次のように式を変形する。
論文では、最大値の代わりに、平均を使用することで、学習の安定性を向上できると述べられている。
実装方法
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万イテレーション学習した結果は以下のようになった。
強さ
ランダムと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% |
直接対局でも、有意に強くなっている。