DDPG by gymnasium 10日目

未解決の課題・疑問点

  1. model.train()とmodel.eval()の使い方が分からない。
  2. 計算の高速化(GPUの利用)
  3. 適切なエピソード数
  4. 適切なメモリバッファ数
  5. ネットワークの入力値?パラメータ?の正規化。
  6. 保存したパラメータを読み出すのはactorとtarget_actorまたcriticとtarget_criticで共通で良いのだろうか。

model.train()とmodel.eval()の使い方

ニューラルネットワークの訓練モードと評価モードを切り替えるメソドのようです。

例えばNNモデルがactorの場合

インスタンス生成:actor = ActorNN(引数) してから

actor.train()で訓練モードに設定すると、バッチ正規化やドロップアウトなどの要素が有効になります。あくまで自分でバッチ正規化、ドロップアウトを設定していた場合です。

逆に、actor.eval()にするとバッチ正規化、ドロップアウトを設定していたとしても無効化されます。

ここで重要なのはactor.eval()であっても勾配は計算するし、パラメータ更新も行われるということです。評価モードということなので、推論だけするのかと勘違いしてしまいますが違います。

証拠としてスクリプトを置いておきますので実行してみてください。ちゃんと勾配計算して損失関数の値も減少していきます。

サンプルスクリプト

パラメータ更新による損失関数の減少グラフ

推論するときはOUActionNoise()を止めよう

バッチ正規化やドロップアウトで訓練した場合、actor.eval()で無効化が必要なのはわかりました。しかし行動ノイズは依然として有効なので、こちらも止めましょう。

ou_noise = OUActionNoise(mu=np.zeros(1), sigma=0)

のように sigmaを0に設定することで更新を停止するギミックが必要になります。

メインスクリプト

 

 

AgentDDPGクラス

これでOUActionNoise()は無効化できました。

ハーフチーターがプルプルしなくなりました。
しかし、各ニューラルネットワークのパラメータ更新は止まっているわけではありません。

パラメータ更新を止める

書きかけです。おそらく、EVAL_TRAIN_MODE = ‘eval_mode’ でないときだけパラメータ更新メソドである、optim.step()を行うようにすればよいと思います。
if EVAL_TRAIN_MODE != ‘eval_mode’:
    self.optimizer.step()

現在のスクリプト全体

 

DDPG by gymnasium 9日目

前回まででうまいこと学習が進むようになりましたので、今回はパラメータの保存と読出をやってみましょう。

例えば、エピソードを100回繰り返しある程度ハーフチーターが前に進む方策を得たらパラメータをいったん保存します。

プログラムを止めて次回動かすときは保存したパラメータを読み込んで、学習済みの状態から動かすことができます。

これで突然プログラムが途中で止まってしまっても被害を最小限にできますね。

パラメータ保存

メインスクリプトでエピソードの終わり、次のエピソードが始まる直前に下記コードを入れます。

パラメータ読込

ActorNN(n.Module)クラスの__init__()内に入れて、actor, target_actorのインスタンス生成と同時にパラメータを引き継いでもらうようにします。

CriticNN(nn.Module)クラスも同様に。

これで、動きます。

学習のノウハウ

学習のコツを編み出しました。

学習初期はエージェントの動きが小さくなかなか前進しません。

最初はステップ数を10~100程度に小さくして、スタートダッシュだけを覚えさせました。

そこでいったん止めて、ステップ数を200、400と増やしていくと安定して走り続けるハーフチーターが得られます。

計算の高速化(3Dモデルの表示をオフにする)

env = gym.make(“HalfCheetah-v4”, render_mode= ‘human’)
の中のrender_modeを’depth_array’に変更すればOKです。

学習の進行状況のリアルタイム可視化

エピソード数とそのリワードだけを表示しています。
print(‘episode, total_reward : ‘, episode , total_reward)
パラメータの保存を10エピソード毎にやっています。
if episode % 10 == 0:
print(‘==== params were saved. ====’)
↓↓↓出力

解決できた課題

  1. パラメータのセーブとロード
  2. 途中で止まった時に続行可能にしたい
  3. 計算の高速化(print文の無効化)
  4. 計算の高速化(3Dモデルの表示をオフにする)
  5. 学習の進行状況のリアルタイム可視化
  6. 適切なステップ数
  7. 適切なニューラルネットワーク構造

未解決の課題・疑問点

  1. 計算の高速化(GPUの利用)
  2. 適切なエピソード数
  3. 適切なメモリバッファ数
  4. model.train()とmodel.eval()の使い方が分からない。
  5. ネットワークの入力値?パラメータ?の正規化。
  6. 保存したパラメータを読み出すのはactorとtarget_actorまたcriticとtarget_criticで共通で良いのだろうか。

これまでのスクリプト

 

DDPG by gymnasium 8日目

chatGPTより提案されたニューラルネットワーク構造を導入してみます。

actorNNは隠れ層ノードを64から256に増やしました。

criticNNも隠れ層ノードを64から256に増やしました。

actorNNの活性化関数はrelu, relu,tanhで出力のまま変わらず。

criticNnの活性化関数はrelu,reluで最終層は活性化関数なしで出力。こちらも変更なしです。

基本構造は悪くなかったようです。

変わらず actor_lossesが上昇傾向にあります。

次は、ステップ数を10から50に増やしてみます。

前のめりを覚えたようで、たまにひっくり返ります。

しかし、actor_losses, critic_lossesは上昇傾向で変わらす。しかし、なんか前に行こうと頑張っているようには見えます。符号が逆になってないだろうか?

ここで行動にノイズを入れて環境の探索性を上げることで学習が良い方向に進むかやってみます。

DDPGにおけるOUActionNoiseクラスは、行動に対してオルナシュウ-ウーレンベック(Ornstein-Uhlenbeck)過程に基づくノイズを生成するために使用されるクラスです。このノイズは、環境の探索性を増加させるためにアクションに追加されます。

このクラスのインスタンス化時に、平均値(mu)、標準偏差(sigma)、タイムステップの幅(dt)、回帰係数(theta)、初期値(x0)を指定します。__call__メソッドは、ノイズを生成して返します。

DDPGの学習時には、Actorネットワークから生成されたアクションにOUActionNoiseクラスを適用してノイズを追加し、環境への探索性を高めます。これにより、探索と収束のトレードオフを実現し、より良いポリシーの探索を促進することができます。

44.OUActionNOoiseクラスを作成する

AgentDDPGクラスに追加

choose_actionメソド内でactionにノイズを入れる。

actorにしろcriticにしろ、常にtargetが動いているのでlossが小さくなるわけではないのかなと思い始めました。

前にぴょんぴょん跳ねるような動作が生まれてきました。ノイズのおかげでしょうか。

EPISODES = 1000 # episodes
STEPS = 100    # steps
ではどうでしょうか。
30000ステップを超えたあたりから、ハーフチーターは開始1秒で前進側にすっ飛んでいく挙動が得られました。
しかし、安定してすっ飛んでいくわけではなく、ちょっともたついてから前傾姿勢で進む場合と混ざり合っています。それでも、開始直後に後退する動作はなくなりましたので確実に成長しています。
ニューラルネットワークのパラメータ更新はうまくいっているようです。
リプレイバッファのサイズはまだ1000だけにしていますがもっと多いほうがいいのでしょうか。多すぎると古い情報がなかなか更新されないので学習が遅くなってしまう気がします。
バッチサイズ64に対してベストなバッファサイズはどのように考えればよいでしょうか。課題です。
次回は、ニューラルネットワークモデルのパラメータ保存と読み出しについて考えていきましょう。