PPOのハイパーパラメータメモ #2a: バッチサイズ（離散行動空間）編

# はじめに
PPOのハイパーパラメータを色々弄るとどうなるか実験のメモ．今回は離散行動空間の環境でPPOを学習させるときのバッチサイズについて．

[Best Practices when training with PPO](https://github.com/EmbersArc/PPO/blob/master/best-practices-ppo.md)という記事があり，バッチサイズについて次のようなことが書かれている．
>batch_size corresponds to how many experiences are used for each gradient descent update. This should always be a fraction of the buffer_size. If you are using a continuous action space, this value should be large (in 1000s). If you are using a discrete action space, this value should be smaller (in 10s).
> Typical Range (Continuous): 512 - 5120
> Typical Range (Discrete): 32 - 512

要するに：

- バッチサイズ(batch_size)は勾配降下の各更新にどれだけのサンプルを使うかに対応する
- バッチサイズの倍数がバッファサイズ(buffer_size)でなければならない
- 行動空間が離散の時は小さめ，連続の時は大きめのほうがよい

今回は離散行動空間を持つMountainCar-v0でこれを（雑に）検証してみる．


#実験
update_interval(=バッファサイズ)を2048に固定し，バッチサイズを1,8,16,32,64,128,256,512,1024,2048と変えて実験してみる．離散行動空間での「典型例」の範囲は32〜512らしいが，果たして本当か．

実験にはChainerRLを使う．[こちら](https://github.com/chainer/chainerrl/blob/master/examples/mujoco/train_ppo_gym.py)のソースコードをお借りした．
argumentで変更するのは--batchsize（と，保存場所を実験ごとに変更するので--outdir）である．

```python

    parser.add_argument('--gpu', type=int, default=0)
    parser.add_argument('--env', type=str, default='MountainCar-v0')
    parser.add_argument('--arch', type=str, default='FFSoftmax',
                        choices=('FFSoftmax', 'FFMellowmax',
                                 'FFGaussian'))
    parser.add_argument('--bound-mean', action='store_true')
    parser.add_argument('--seed', type=int, default=0,
                        help='Random seed [0, 2 ** 32)')
    parser.add_argument('--outdir', type=str, default='results',
                        help='Directory path to save output files.'
                             ' If it does not exist, it will be created.')
    parser.add_argument('--steps', type=int, default=10 ** 6)
    parser.add_argument('--eval-interval', type=int, default=10000)
    parser.add_argument('--eval-n-runs', type=int, default=10)
    parser.add_argument('--reward-scale-factor', type=float, default=1e-2)
    parser.add_argument('--standardize-advantages', action='store_true')
    parser.add_argument('--render', action='store_true', default=False)
    parser.add_argument('--lr', type=float, default=3e-4)
    parser.add_argument('--weight-decay', type=float, default=0.0)
    parser.add_argument('--demo', action='store_true', default=False)
    parser.add_argument('--load', type=str, default='')
    parser.add_argument('--logger-level', type=int, default=logging.INFO)
    parser.add_argument('--monitor', action='store_true')

    parser.add_argument('--update-interval', type=int, default=2048)
    parser.add_argument('--batchsize', type=int, default=64)
    parser.add_argument('--epochs', type=int, default=10)
    parser.add_argument('--entropy-coef', type=float, default=0.0)

```
最適化手法には，[以前の結果](https://qiita.com/deepirl_learner/items/d269f87a40295c40ca48)を踏まえSMORMS3（引数なし）を使う．


## 実験環境

- CPU : Intel Core i7-8700CPU @ 3.20Hz x 12
- メモリ : 32GB
- グラボ: GeForce RTX2080Ti 11GB

#結果
#学習曲線
100区間の移動平均をとったもの．（2048は最後まで性能が向上しなかった）
![reward.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459984/029dfc2d-d2e3-c518-f2ae-278c248f5548.png)



# bestモデルの性能
bestモデルで10000回走らせたときの累積報酬の箱ひげ図．ちなみに，確認できた報酬の最大値は-83，最小値は-200である．
![reward_eval.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459984/fda3d0bf-9574-4f20-d37e-ef412ef49481.png)


## 学習時間
右は拡大したもの．当たり前の話ではあるが，バッチサイズを小さくすると時間が増える．

![time.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459984/1e80a083-3f7d-cbdf-9593-4b04e6a15c32.png)

# 結論
学習時間と性能のバランス的に，**32**あたりが良さそう．

> Typical Range (Discrete): **32** - 512

先人の教えは正しかった．気が向いたら続きます．