【苦しみながら理解する強化学習】Spinning Up 04-2 エクササイズ

Challengesのところを読んでるとテンションが上ってしまったw
実装の流れも良い感じにしてくれているんだろうから頑張らねば!

MLP diagonal Gaussian policy for PPOを実装せよ

前提知識

KL-constrained & KL-divergence

Kullback-Leibler divergence ( KLダイバージェンス、KL情報量 )は、2つの確率分布がどの程度似ているかを表す尺度です。

actor-critic

Q関数を求めるところと状態に応じた行動を決定する部分を分けたのがActor-Criticという強化学習方法

PPO

まずPPOとは何か。

【苦しみながら理解する強化学習】PPO
どんなもの? proximal policy optimization (PPO) policyの最適化手法。 安定して信頼性が高い。 実装コストは低く、vanilla policy...

Proximal Policy Optimizationの略。
データを使ってpolicyを改善する。
TRPOも同じことをしているが、TRPOはsecond-order method、PPOはfirst-order methodを使っている。

PPOはon-policyのアルゴリズム。
discreteとcontinuousのaction spaceどちらでも使える。

PPO Penalty

TRPOと同様にKL-constrainedを更新するが、KL-constrainedそのものを更新するのではなく、KL-divergenceの関数を更新していく。
そのためスケーリングが容易になる。

PPO Clip

以前のpolicyからどれぐらい改良されたかは考慮されない。

実装

まずmlpメソッドでBuilds a multi-layer perceptron in Tensorflow.と書いてある。

multi-layer perceptron(多層パーセプトロン)は、

だったとは…
(色々調べるとそれだけでもないらしい)

ここの引数をmlp_gaussian_policyで計算する。
恐らくその中からmlpを呼び出す。

a_phとx_phのシンボルってなんなんだ…
piはサンプルアクションだからaを入れればいいのか?

しっかり考えてから実装方法が全くわからなかったら解答をみるに限る。

解答

mlp_gaussian_policyから。

mlpの解答は以下。

まとめ

私は結局のところ何を実装していたのか…?
明らかにPPOのメインのところではなかった。
これら解決していきたい。

policyの部分の実装

名前からしてわかるが、policyの部分の実装をやっていたんだ僕たちは。
1行ずつ丁重にみていきたい。(core.pyの内容)

まぁここはよさそうだ。
状態全体と行動全体、hidden layerのアウトプットのサイズ、activation function、最終的なアウトプットとなるactivation function、そして、actionのspaceがこのメソッドに代入される。
action_spaceは今回は関係がない。(必要なのはBoxクラスのときのみ)

行動を取得する。
これは取れる行動だと思っている。

今回もう1つ作成したメソッドに入れる。
隠れ層と取れる行動を全部hidden_sizeとして入れる。

####### 脱線:mlpメソッド 開始 #######

隠れ層は初期値がmlp_actor_criticメソッドより(64, 64)。
gymのenvはHalfCheetah-v2なので、act_dimの元となる初期値a_phは>>> core.placeholders_from_spaces(env.observation_space, env.action_space)から計算される[<tf.Tensor 'Placeholder:0' shape=(?, 17) dtype=float32>, <tf.Tensor 'Placeholder_1:0' shape=(?, 6) dtype=float32>]の2つ目。

act_dim = a.shape.as_list()[-1]なので、6となる。
hidden_sizeにはlist((64, 64)) + [6]が入り[64, 64, 6]となる。
mlpメソッド内では、

となっており、[64, 64, 6][:-1]なので、[64, 64]をforする。
なので、unitサイズが64となり、最終的にreturnするのは、[64, 64, 6][-1]なので、unitサイズ6が返される。

####### 脱線:mlpメソッド 終了 #######

####### 脱線:tensor class 開始 #######

こういう感じでdimが表示されるらしい。

####### 脱線:tensor class 終了 #######

変数の予約をして、初期値を-0.5のdim(6)にしています。
この初期値は特に理由はないようです。

80行で計算したlog_stdをexponencialでstdに変換。

いよいよ、returnする変数の1つpiが出てきました。
なのでここも6要素でしょう。
17のobservationをmlpメソッドを通して6つのactionにした結果muに+ tf.random_normal(tf.shape(mu)) * stdを足しています。
これはgreedyのためだったかな?

1つ前で扱ったgaussian_likelihoodを使うのですが、これこそがpolicyを判断する確率的手法。
現在の状態から取った行動のlog probability。

####### 脱線:gaussian_likelihood 開始 #######

docによると、

sampling actions from the policy,
and computing log likelihoods of particular actions, \log \pi_{\theta}(a|s).

なので、このand以下がgaussian_likelihoodで行うこと。
という分類ができるとここで気になるのは、gaussian_likelihood以前はsample actionを取得していたのか。

####### 脱線:gaussian_likelihood 終了 #######

####### 脱線:行動のサンプルとはなんだったのか? 開始 #######

Q値を参考に。

Q値は「報酬」ではなく「価値」であることに注意してください。つまり、Q値とは短期的な報酬ではなく、長期的な意味での価値を値として持っている関数です。

式1を式2に置き換えることができる。

そこで、期待値をとるのではなく、実際に行動を実施して次の時点の状態を確認しながら、少しずつQ値を更新していきます。実際に行動した結果のサンプルで期待値の代用としよう、というわけです。

とあるように、行動のサンプルでした。
ここでいう行動のサンプルは、piだったんですね。

####### 脱線:行動のサンプルとはなんだったのか? 終了 #######

行動サンプルのlog probability。

####### 脱線:なぜlog probabilityなのか? 開始 #######

Gradient methods generally work better optimizing logp(x) than p(x) because the gradient of logp(x) is generally more well-scaled.

便利だからってことなんでしょう。

####### 脱線:なぜlog probabilityなのか? 終了 #######

お返しします。

ppoメソッドでどうやって使われるか

本家実装コードのppo.pyでは変数名actor_criticとして利用されているので、ここで使われている。
まずはplace_holderで初期化されているだけ。
そして、all_phsに格納されているので、これはグラフのplace_holderとしてまとめたということだろう。

ac_kwargsにはaction_spaceだけ入っている。

そして、いよいよsess.runで使われるところ!

feed_dict={x_ph: o.reshape(1,-1)}でx_phの上書きをする。
o.reshapの初期値はgymから持ってきています。

その後、stepで更新していくという感じでした。

ppoメソッド

clipというのが数式に入っているが、どうやって実装するんだろうか?
ここでppoメソッドをみていきたい。

ログと乱数はちょっと飛ばして、

ここは普通に、状態と行動のshapeをとってきてる。

policyへaction_spaceを渡す準備。

183, 184はplace_holderにしているだけです。

散々みてきたactor_critic周り。

計算結果の保管用クラスの初期化。

scope名がpivそれぞれの変数の数をログへ。

####### 脱線:core.count_vars 開始 #######

core.count_varsは以下の通りとなっている。

get_varsでは、

  • trainable_variablesはtrainable=Trueの変数を全て返す。
  • そして、scopeを判断して該当するものだけ返す。

その返ってきた変数に対して、np.prodは次元関係なく乗じて変数をカウントする。

####### 脱線:core.count_vars 終了 #######

adv_ph, ret_ph, logp_old_phはbufに登録されたものをbuf.getで取得するようになっている。1つ言えることは全てplaceholderである。

ratioは、現在の状態から取った行動のlog probabilityの更新前後の差分。のexponential。
min_advのところでtf.whereというのははじめてみたんですが、1つ目のattrに入ったもの(配列など)が、Trueならば第二引数、Falseならば第三引数の要素を返すようです。
つまり、min_advではadvantageのplaceholderが0より大きかった場合(1+clip_ratio)*adv_phを、小さかった場合(1-clip_ratio)*adv_phとなる。
clip_ratioはハイパーパラメータで初期値は0.2になっている。
以下の式となる。

実装式

ここでなぜPPOでこのような式があるかというと、TRPOなどではpolicyをアップデートする時にアップデート幅が大きくなってしまうため、PPOでは以下の式のようにアップデート幅を制限している。

ppo policy calc

ここでいうL functionが実装された式となる。
L functionのmin内の1つ目のattrは実際のアップデート幅、2つ目のattrは実装式なので、実際のアップデート幅が大きすぎるとclipしている式から計算された結果がアップデート幅として使われるようになっている。

minimum内は上記の通り。
reduce_meanで配列の平均を計算して、-としている。
policyのloss functionとして使うための準備をしている。

報酬のloss function。

####### 脱線:tf.squeeze 開始 #######

vはcore.pyで以下のようになっている。

squeeze配下のように動作する。

####### 脱線:tf.squeeze 終了 #######

KL-divergenceのサンプル、entropyのサンプルは平均から計算している。
tf.logical_orは、tensorに対するor構文です。
なので、ratio > (1+clip_ratio)かratio < (1-clip_ratio)のどちらかがTrueの場合はTrue。
tf.cast(clipped, tf.float32)ではboolをtf.float32に変換しているので、1か0になる。
それの平均をclipfracとしている。

上記で求めていたpi_lossv_lossをの最適化をする。

####### 脱線:MpiAdamOptimizerクラス 開始 #######

mpi_tf.pyにて。

なので、単純にAdamOptimizerで初期化しているだけ。

####### 脱線:MpiAdamOptimizerクラス 終了 #######

さて、ようやくsessionの開始ですぞ。

その他

gym

gymいい!
Roboticsというのが新しくあるし。
これいいのでは!
これやろう。

mojoco

必然的にmujocoも知らなくてはいけない気がする。


mujocoも一度読む。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です