適格度トレース(方策)

 \displaystyle
\newcommand{\bs}{\boldsymbol}

 前回は、価値関数について「前方観測と後方観測は1エピソードでの更新量の和が一致するのではないか」ということを追った。

 Sutton, Barto『強化学習(第2版)』の第13章 方策勾配法 p.296では、アクタークリティックに対しても同じように適格度トレースを適用できると記述がある。

 これについて考える。

 そもそもここでは状態価値関数を用いたベースライン付きREINFORCEアルゴリズムから発展した形でアクタークリティックを考えている。なので、まず前提となる更新式が

 \displaystyle
\begin{align}
\bs{\theta} _ {t + 1} := \bs{\theta} _ t + \alpha \left( G _ {t:t+1} - \hat{v}(S _ t, \bs{w}) \right) \nabla \ln \pi(A _ t | S _ t, \bs{\theta})
\end{align}

となっている。これの  G _ {t:t+1} \lambda 収益に置き換えるということになるので

 \displaystyle
\begin{align}
G _ t ^ \lambda = (1 - \lambda) \sum _ {n=1} ^ \infty \lambda ^ {n-1} G _ {t:t+n}
\end{align}

を使って

 \displaystyle
\begin{align}
\bs{\theta} _ {t + 1} := \bs{\theta} _ t + \alpha \left( G _ t ^ \lambda - \hat{v}(S _ t, \bs{w}) \right) \nabla \ln \pi(A _ t | S _ t, \bs{\theta})
\end{align}

である。

ここで、前回みたように価値関数について更新量が

 \displaystyle
\begin{align}
\Delta ^ {F} _ t := \alpha \left\lbrack G _ t ^ \lambda - \hat{v} (S _ t, \bs{w} _ t) \right\rbrack \nabla \hat{v}(S _ t, \bs{w} _ t)
\end{align}

であったことを考えると、  \nabla \hat{v}(S _ t, \bs{w} _ t) \nabla \ln \pi(A _ t | S _ t, \bs{\theta}) に変わっただけなので、この前の議論がそのまま適用できる。なので適格度トレースがほぼ同じ形で適用できるのだろう。

つまり、適格度トレースが適用できるのは  \lambda 収益を使って、かつ現在の価値関数との差分を考える場合のActor-Criticであるのではないか。一般の方策勾配法でやれるということではないのかもしれない。(もしかすると一般の場合でもなにか上手い変形ができるのかもしれないが)

(やや脇道だがわかっていないこととして、「現在の価値関数との差分を考える場合のActor-Critic」というのは、行動価値関数を「報酬 + 次ステップの状態価値」で代用したAdvantage Actor-Critic、という言い方をして良いのだろうか)

とりあえず  \lambda 収益を使うAdvantage Actor-Critic形式はかなり良いことが多い気がするので、結構これでいいんじゃね感がある。


価値関数と方策関数について、途中までニューラルネットワークを共有している場合も、単純に適格度トレースベクトルに足すだけで良さそう。共通部分のパラメータが  \bs{\phi} としたら

 \displaystyle
\begin{align}
z _ 0 &:= \bs{0} \\
z _ t &:= \gamma \lambda z _ {t - 1} + \nabla _ {\bs{\phi}} \hat{v}(S _ t, \bs{\phi}) + \nabla _ {\bs{\phi}} \ln \pi(A _ t | S _ t, \bs{\phi})
\end{align}

を使って

 \displaystyle
\begin{align}
\Delta ^ B _ t := \alpha \delta _ t \bs{z} _ t
\end{align}

とするのではないか。


この共有部分がさらに違う自己教師あり学習などに使われているとすると、その勾配は適格度トレースとは全然関係ない形で勾配を求めてこれらに足すという形になると考える。

PyTorchでの実装を前提として、イメージ以下のような感じでは。実際にやってみたわけではないので想像。

    optimizer.zero_grad()
    loss_p_and_v.backward()

    # トレースベクトルの更新
    with torch.no_grad():
        for et, param in zip(eligibility_traces, model.parameters()):
            et.mul_(lambda_ * gamma).add_(param.grad)

    # 自己教師あり学習の損失
    optimizer.zero_grad()
    loss_ss.backward()

    # 適格度トレースの方から損失を足す
    for param, et in zip(model.parameters(), eligibility_traces):
        param.grad.add_(learning_rate * delta * et)

    optimizer.step()

ここでoptimizerとしてAdamみたいなものを使ったときにおかしなことが起こらないのかどうかはよくわからない。