DDPG by gymnasium 15日目

さて最終章に近づいてまいりました。(ような気がする)

ニューラルネットワークの学習をより安定化させるために標準化Standarzation,正規化Normalizationをやっていきましょう。

まずは入力値について考えます

各特徴量ごとの学習の感度を同じくらいにそろえるために、入力値の値を相対的に加工します。

標準化

Z-scor normalization

手持ちデータを平均ゼロ、分散1の正規分布にする手法

正規化

Min-Max normalization

最小値~最大値を0~1にスケーリングする手法

 

使い分け

標準化:最大最小が決まっていない場合、外れ値が存在する場合

正規化:最大値および最小値が決まっている場合

戦闘力は標準化が良い

住宅価格も標準化が良い

弱点

弱点として、入力値はいいのですが、ネットワークを伝播していくうちに各ノードの入力値(活性化関数がかかる前)の分布は崩れていきます。

すると、勾配消失が起こりやすくなります。

重みパラメータの初期値を工夫する

そこで重みの初期値を工夫して一様分布や正規分布に従うランスを使うのですがディープラーニングではうまく動かないことがあります。

解決策として 乱数xネットワークの大きさに合わせた係数 を初期値とすると 分布が崩れにくくなるそうです。(ちょっとわかりません。)

活性化関数がシグモイド関数の場合はXavierの初期値、ReLU関数の場合はHeの初期値というものが良いそうです。

重みの要素数がnである場合、Heの初期値は1/√nとなります。

 

バッチ正規化

もっと直接的で良い方法があります。

各ノードに入力されたら、そこで正規化してしまえば良いのです。各層で毎回正規化を繰り返します。

因みになぜバッチ正規化というのかはわかりません。各レイヤーのノード群をバッチと呼んでいるのでしょうか。

とにかく各層のノードを正規化するのです。

学習の成功率を上げるとともに過学習もしにくくなるらしいです。

やりかた

標準化して線形変換するだけです。

標準化:

あるレイヤーのノード群の平均値μと分散sを求める

各ノードの値xから平均値μを引いて標準偏差σで割った値が標準化された値z

線形変換:

zにパラメータw,バイアスbを使って y=wz+bへ線形変換する。

正確に言うとこれはバッチ正規化の発展系であるレイヤーノーマライゼーションを使っています。

ActorNNクラスの改造:

重みパラメータ、バイアスパラメータの初期値

今まではランダムに初期値を決めていたところをHeの初期値に変更しました。つまり、ネットワークの各層のノード数ActorNN.各fc.weight.data.size()[0]に応じて、初期値の上下限f1,f2を決定するようにしました。nn.init.uniform_()。

各層の入力値を正規化

今までは各層への入力値は特に加工せずに活性化関数へ入れていましたが、入力値は正規化nn.LayerNorm()を適用してから活性化関数に入れるように変更しました。入力値→正規化bn→活性化関数ReLU。

CriticNNクラスの改造:

こちらも考え方は同じです。

学習結果

1000エピソード以上学習しましたが、どういうことでしょう調子いいのは300エピソードくらいのところで、そこから一気に悪化しています。

横軸:エピソード、縦軸:収益

ハーフチーターは一体何をやっているのでしょうか?

開始直後に素早く美しく前転し、ひっくり返りながらバタバタしていました。どうやら300回転超えたあたりから学習するべきデータが間違ったものしかない状況になり、回復しなかったと推測します。負のスパイラルに落ち込んでいったのでしょう。

反省

今回の条件を確認します。

agentインスタンス:

alpha=0.01,
beta=0.01,
gamma=0.99,
tau=0.01,
n_obs_space=17 ,
n_action_space=6,
n_state_action_value=1,
layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=256,

AgentDDPGクラス__init__():

alpha=0.000025,
beta=0.00025,
amma=0.99,
tau=0.001,
n_obs_space=17 ,
n_action_space=6,
n_state_action_value=1,
layer1_size=64,
layer2_size=64,
layer3_size=64,
batch_size=64,
self.actorインスタンス:
alpha=0.000025,※ここ間違った。alpha=self.alphaにするべき
n_obs_space=17,※ここ間違った。同上
n_action_space=6,※ここ間違った。同上
 layer1_size=64,※ここ間違った。同上
layer2_size=64,※ここ間違った。同上
layer3_size=64,※ここ間違った。同上
batch_size=64※ここ間違った。同上

なので actorとしては

ActorNN:He適用, alpha=0.000025, Adam

batch64,obs17→17 fc1 64→64 bn1 64→64 ReLU 64→64 fc2 64→64 bn2 64→64ReLU64→64fc3 6 →Tanh6→ action6

※layer3_sizeは不使用

という状況でした。一部間違っていました。直します。

修整後↓

self.actor = ActorNN(device=self.device, alpha=self.alpha, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                            layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

ちなみにActorNNの初期化メソドは

ActorNNクラス__init__():
alpha=0.001
n_obs_space=17,
n_action_space=6
layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=64

です。

target_actorインスタンスも下記のように間違っているので

self.target_actor = ActorNN(device=self.device, alpha=0.000025, n_obs_space=17, n_action_space=6,
                                    layer1_size=64, layer2_size=64, layer3_size=64, batch_size=64)

直します。↓修整後

self.target_actor = ActorNN(device=self.device, alpha=self.alpha, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

 

次に、target_criticインスタンスを見ていきます。

beta=0.000025,※ここも間違い。self.betaにするべき
n_obs_space=17,※同上
n_action_space=6,※同上
 layer1_size=64,※同上
layer2_size=64,※同上
layer3_size=64,※同上
batch_size=64)※同上
これも間違っているので修正。修整後はこちら↓
self.target_critic = CriticNN(device=self.device, beta=self.beta, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

同じくtarget_criticインスタンスもself.なんちゃらにしていないので修正が必要です。修整後↓

self.critic = CriticNN(device=self.device, beta=self.beta, n_obs_space=self.n_obs_space, n_action_space=self.n_action_space,
                                    layer1_size=self.layer1_size, layer2_size=self.layer2_size, layer3_size=self.layer3_size, batch_size=self.batch_size)

なので target_criticとしては

CriticNN:He適用, beta=0.000025, Adam

batch64,obs17+act6→input23  fc1 64→64 bn1 64→64 ReLU 64→64 fc2 64→64 bn2 64→64fc3 1→活性化関数なし→Q_value 1

※layer3_sizeは不使用

ちなみに
CriticNNクラスの__init__()
beta=0.001,
n_obs_space=17,
n_action_space=6,
 layer1_size=256,
layer2_size=256,
layer3_size=256,
batch_size=64):
です。

現在までのまとめ

学習率とノード数を間違って入れていましたが、これが悪いとは現時点では言えません。

可能性としてはバッファメモリが10000で1エピソード2000ステップなので5回連続でダメな収益結果の場合、「ひっくり返ってお終い」、のようなダメな経験しか持ってない状態になります。

これが、二度とまともな状態に学習が継続できない要因ではないでしょうか。

(メモ:ステップを400とすると1エピソードがなぜか5倍の2000ステップまで実行されます。仕様ですかね。)

修正したコード

ひとまず、間違っていた部分を直したので張っておきます。

agent = AgentDDPG(device=device, alpha=0.01, beta=0.01, gamma=0.99, tau=0.01,
                  n_obs_space=17 , n_action_space=6, n_state_action_value=1,
                  layer1_size=256, layer2_size=256, layer3_size=256, batch_size=256, mode=EVAL_TRAIN_MODE) # cuda追加
がネットワーク生成までこのパラメータが適用されるように修正しました。
これの結果:学習が進みません。チーターの様子を見ると開始直後に静止してお終いです。ぐーたらになってしまいました。

ノード数が多すぎる?256

学習率が小さすぎる?0.01

パラメータの初期値 Heがうまくいってない?/1√256は小さすぎる?

レイヤー正規化が上手くいってない?

次回へ続きます。