Pythonで文字認識ニューラルネットワーク構築【Coursera Machine Learning】(3.ミニバッチ勾配降下法＆おまけ)

#はじめに
この記事は前回の続きですが、おまけという位置づけです。まだご覧になってない方はぜひ一読ください
[Pythonで文字認識ニューラルネットワーク構築【Coursera Machine Learning】(1.概要)](https://qiita.com/trashboxacc/items/ec3fa62afe6999f6bdf0)
[Pythonで文字認識ニューラルネットワーク構築【Coursera Machine Learning】(2.実践)](https://qiita.com/trashboxacc/items/1033a333a00902071b1a)


>　ちょっと前に出た機械学習のゼミで[Courseraの教材](https://www.coursera.org/learn/machine-learning)を扱っていたので復習がてらアウトプットしていこうと思います。Week5の課題であるex4は、もともとはOctaveで書かれているので自己流Pythonで記述します。当然処理にかかる時間は長〜くなるので実用目的ではなく勉強目的の人に向けて書いてますのであしからず。__PyTorchやTensorFlowなどの機械学習ライブラリは使いません。__

ということで今回はタイトルの通り、ミニバッチ勾配降下法の実装をしていきます。がんばりましょう。

ソースコードを見たい方はこちら
[Mkamono/Letter_identify/blob/stochastic_gradient_descent/letter_identify.py](https://github.com/Mkamono/Letter_identify/blob/stochastic_gradient_descent/letter_identify.py)

おまけのコード
[Mkamono/Letter_identify/blob/stochastic_gradient_decent_moderate/letter_identify.py](https://github.com/Mkamono/Letter_identify/blob/stochastic_gradient_decent_moderate/letter_identify.py)

世界一わかりやすいCourseraのスライドはこちら
[Coursera-Machine-Learning-Stanford/ex4.pdf](https://github.com/atinesh-s/Coursera-Machine-Learning-Stanford/blob/master/Week%205/Programming%20Assignment/machine-learning-ex4/ex4.pdf)

#環境

- windows 10 home
- VScode 1.6.0
- Python 3.8.11

#概要
ミニバッチ勾配降下法はすべてのデータを使って学習する勾配降下法、一つのデータごとに学習していく確率的勾配降下法の中間に当たる学習方法です。数〜数百程度のデータごとに学習していく方法です。

__ミニバッチ勾配降下法について自分は理解が足りていません。__自覚できるほどに無知なので、正しい結果を出せているとは思っていませんが、あくまでコンセプトの理解の助けになればと思います。

batch_size(一回の学習に使うデータの数)は10くらいが良いかと思います。
パラメータを収束させるためにeta(学習率)は徐々に減少させる必要があります。減少方法には色々ありますが、[Wikipedia](https://ja.wikipedia.org/wiki/%E7%A2%BA%E7%8E%87%E7%9A%84%E5%8B%BE%E9%85%8D%E9%99%8D%E4%B8%8B%E6%B3%95)に書いてあった__確率的近似法__(学習率を繰り返し回数で割る方法)を使います。

・簡単な例

|学習回数|1|2|3|4|5|
|-|-|-|-|-|-|
|eta(初期値100)|100|50|33.3...|25|20|
といった感じに減衰していきます。
#変更点
基本的には前回書いたコードを流用します。なので今回は変更点のみの記事となります。
##batch_size, etaの初期化
(eta_init, batch_size) = (100, 10)
これを記述するだけです。

##Backpropagationへのeta, batch_sizeの実装

```python
def Backpropagation(x,y,theta,lam, training_set_number, eta_init, batch_size):
```
とりあえず引数にそれぞれ追加します。

```python
    while True:
        try:
            p = np.random.permutation(training_x.shape[0])#シャッフル
            training_x = training_x[p]
            training_y = training_y[p]

            for batch in range(int((num_data_list)/batch_size)):
                DELTA_1 = []
                DELTA_2 = [] #初期化
                eta = eta_init/((batch+1) + (iter * int((num_data_list)/batch_size)))
                for M in range(batch_size):
                    x_m = training_x[batch*batch_size+M-1]
                    y_m = training_y[batch*batch_size+M-1][:, np.newaxis]
```
この辺が変更点のほとんどです。
シャッフルをしないと結果が偏ってくるのでやりましょう。そしてバッチ単位で繰り返すようにfor文を書きます。
etaの値を実装するのは割と面倒ですが基本はイテレーションで割っているだけなので、式の上流から分解して理解していきましょう。
トレーニングセットの定義はバッチごとにしっかりと。

##Refresh_thetaへのetaの実装
これは簡単に。コードを見れば一瞬で理解できます。

```python
def Refresh_theta(theta,D1,D2, eta):
    theta[0] = theta[0] - eta*D1
    theta[1] = theta[1] - eta*D2
    return
```
学習率という名の通りの使い方をしていますね。更新幅を操るためにD行列にetaをかけています。

#おまけ
etaだけじゃなくbatch_sizeも可変できるのでは？と思ったので実装してみました。
コンセプトとしては、確率的勾配降下法から勾配降下法へのスムーズな移行です。
そして説明は雑です。

コードは基本的に先程のミニバッチ勾配降下法のコードを流用します。

```python
    while True:
        try:
            if eta > 1:
                p = np.random.permutation(training_x.shape[0])#シャッフル
                training_x = training_x[p]
                training_y = training_y[p]

                batch_size = int((num_data_list**(1/30))**(iter-1)) + 1*(iter-1)
                eta = (num_data_list)/batch_size
                print("batch_size = ", batch_size, "    eta = ", eta)
            elif eta < 1:
                eta = 1
                batch_size = num_data_list
```
ここしか変更点はありません。
何をしているのかというと、ループ毎にbatch_sizeにある数をかけ続けて、ちょうど30回かけたときにデータセットの総数になるようにしています。式的に言うと
batch_size(初期値１)×{(データセット数)<sup>(1/30)</sup>}<sup>(繰り返し回数)</sup>
という更新をしています。

それに合わせてetaは(num_data_list)/batch_sizeにしています。これはつまり
eta×batch_size = データの総数
となるような調整をしています。これはミニバッチによる一回の更新で勾配降下法一回分の効果があるようにするためです。

batch_size = 1
のとき、これは確率的勾配降下法となります。
batch_size = データの総数
となるとき、etaは1となり、いつもの勾配降下法となります。
そしてbatch_sizeの影響は指数関数的であるので、指数関数的に増加するようにしました。
結論から言うと、かなり有用でした。コードはGithubにあげてあるので試してみてください。

#終わりに
今回はおまけ編でしたが、結構コードを書いているのは楽しかったです。
前回、前々回の記事はもっと丁寧に書いているので、よかったら見てください。

[Pythonで文字認識ニューラルネットワーク構築【Coursera Machine Learning】(1.概要)](https://qiita.com/trashboxacc/items/ec3fa62afe6999f6bdf0)
[Pythonで文字認識ニューラルネットワーク構築【Coursera Machine Learning】(2.実践)](https://qiita.com/trashboxacc/items/1033a333a00902071b1a)