【ラビットチャレンジ】深層学習 Day2 レポート

# はじめに

ラビットチャレンジの 「深層学習Day2のレポート」になります。
※後ほど更新予定です


✅　応用数学レポート　
✅　機械学習レポート 
✅　深層学習前編（day1）レポート 
⭐️ 深層学習前編（day2）レポート 
深層学習後編（day3）レポート
深層学習後編（day4）レポート

## Section1: 勾配消失問題


### 1-1. 要点


- 勾配消失問題の復習
    - 勾配消失問題
        - 誤差逆伝播が下位層に進んでいくに連れて、勾配がどんどん緩やかになっていく。そのため、勾配降下法による更新では下位層のパラメータはほとんど変わらず、訓練は最適値に収束しなくなる


- 勾配消失の解決策
    - **①活性化関数の選択**
        - ReLU関数
            - 勾配消失問題の回避とスパース化に貢献

    - **②重みの初期値設定**
        - Xavier
            - 設定する際の活性化関数 : ReLU, Sigmoid, 双曲線正接関数
            - 初期値の設定方法:重みの要素を、前の層のノード数の平方根で除算した値
        - He
            - 設定する際の活性化関数 : ReLU
            - 初期値の設定方法:重みの要素を、前の層のノード数の平方根で除算した値に対し√2を掛け合わせた値
    - ③ **バッチ正規化**
        - ミニバッチ単位で、入力値のデータの偏りを抑制する手法
        - 活性化関数に値を渡す前後に、バッチ正規化の処理を加える 

---

### 1-2. 確認テスト

【確認テスト】　連鎖律の原理を使い、dz/dxを求めよ

dz/dx = dz/dt x dt/dx
      = 2t x 1
      = 2(x + y)

---

【確認テスト】 シグモイド関数を微分した時、入力値が0の時に最大値をとる。その値として正しいものは？
- $f'(z) = f(z)・(1-f(z))=0.5・(1-0.5)=0.25 $
よって (2)が答え

---

【確認テスト】 重みの初期値に0を設定すると、どのような問題が発生するか。
- 全ての重みが均一に更新されてしまうので、パラメータのチューニングが行われなくなる

---



【確認テスト】 一般的に考えられるバッチ正規化の効果を2点あげよ
- 学習の効率があがる
- 過学習を防ぐ

【例題チャレンジ】

```py
data_x[i:i_end], data_t[i:i_end
```


### 1-3. 関連学習

※重みの初期化が必要となった理由が気になったので調査

- 「DNN向けに、特殊な重みの初期化が必要となった理由」
    - 3層MLPでは，一様分布を $ √n $　で割って正規化したものからランダムサンプリングを行い，各層の重みの値を初期化していた。これは，ヒューリスティックな重み初期化法であったが，3層MLPではこのランダム初期化で、重みの偏りのない十分な識別力を持つ隠れ層表現を学習できていた
    - しかし、深いDNNの重みを「特定の分散値の範囲の」ガウス分布で初期化すると，学習に問題が出てくることがわかりDNNでも学習を安定させることができるXavier初期化やHe重み初期化が提案されていくこととなる
 
- 参考記事
    - https://cvml-expertguide.net/terms/dl/optimization/weight-initialization/




## Section2: 学習率最適化手法

### 2-1. 要点

- 学習率の決め方とは
    - 初期の学習率設定方法の指針
        - 初期の学習率を大きく設定し、徐々に学習率を小さくしていく
        - パラメータ毎に学習率を可変させる
        - → 学習率最適化手法を利用して学習率を最適化

#### モーメンタム

- モーメンタムのメリット
    - 局所的最適解にはならず、大域的最適解となる
    - 谷間についてから最も低い位置(最適値)にいくまでの時間が早い

#### AdaGrad

- AdaGradのメリット
    - 勾配の緩やかな斜面に対して、最適値に近づける
    - 課題
        - 学習率が徐々に小さくなるので、鞍点問題を引き起こす事があった


#### RMSProp

- RMSPropのメリット
    - 局所的最適解にはならず、大域的最適解となる
    - ハイパーパラメータの調整が必要な場合が少ない

#### Adam

- Adamとは？
    - モメンタムの過去の勾配の指数関数的減衰平均
    - RMSPropの、過去の勾配の2乗の指数関数的減衰平均

- Adamのメリットとは？
    - モメンタムおよびRMSPropのメリットを統合したアルゴリズム

---

### 2-2. 確認テスト


【確認テスト】 モメンタム・AdaGrad・RMSprop特徴を説明せよ

- モメンタム
    - 局所的最適解にはならず、大域的最適解となる
    - 谷間についてから最も低い位置にいくまでの時間が早い
- AdaGrad
    - 勾配の緩やかな斜面に対して、最適値に近づける
- RMSProp
    - ハイパーパラメータの調整が必要な場合が少ない
---


### 2-3. 関連学習

※Adamを超えた「RAdam」が気になったので調査

- RAdam
    - 画像分類のImageNetやCIFAR-10から機械翻訳のIWSLT'14やWMT'16などの幅広いタスクおよびデータセットでRAdamがAdamよりも優れた性能
    - 学習の初期段階でAdamの適応学習率の分散が大きくなりすぎるという問題があることを指摘
    - ヒューリスティックな手法で、ハイパラ調整を必要とするWarmupは上記の問題を緩和することを発見
    - 適応学習率の分散を自動的に抑えられるような機構をAdamに組み込んだものがRAdam(ハイパラ調整不要！)
        - ステップ数が4以下: 適応学習率を使わない(=ただのモーメンタム付きSGD)
        - ステップ数が4よりも大きい: Adamに補正項をかける ことで適応学習率の分散を抑える
    - 参考記事
        - https://qiita.com/omiita/items/d24568a835da6911b01e



## Section3: 過学習

### 3-1. 要点

- 過学習
    - テスト誤差と訓練誤差とで学習曲線が乖離すること
        - 特定の訓練サンプルに対して、特化して学習する
    - 原因
        - パラメータの数が多い/パラメータの値が適切でない/ノードが多い→ネットワークの自由度が高い


#### L1正則化、L2正則化

- 正則化
    - ネットワークの自由度(層数、ノード数、パラメータの値etc..)を制約すること
    - →正則化手法を利用して過学習を抑制する



- Weight decay (荷重減衰)
    - 過学習の原因
        - 重みが大きい値をとることで、過学習が発生する事がある
            - 学習させていくと、重みにばらつきが発生する。重みが大きい値は、学習において重要な値であり、重みが大きいと過学習が起こる
    - 過学習の解決策
        - 誤差に対して、正則化項を加算することで、重みを抑制する
            - 過学習が起こりそうな重みの大きさ以下で重みをコントロールし、かつ重みの大きさにばらつきを出す必要がある


#### ドロップアウト

- ドロップアウト
    - ランダムにノードを削除して学習させること
    - メリット : データ量を変化させずに、異なるモデルを学習させていると解釈できる

---
### 3-2. 確認テスト

【確認テスト】
機械学習で使われる線形モデルの正則化は、モデルの重みを制限することで可能となる。線形モデルの正則化手法の中にリッジ回帰という手法があり、その特徴を選べ。
- 解答:(a)ハイパラを大きな値に設定すると、すべての重みが限りなく0に近づく

---
【確認テスト】 L1正則化を表しているグラフはどちらか？
![スクリーンショット 2023-11-25 12.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/e5542650-8ea4-3d45-13b0-2513e8cfa469.png)


- 解答 : 右側のLasso

---

【例題チャレンジ】

- 解答 : Param
    - 2*paramであるが、係数2は正則化の係数に吸収されても買わないのでparam

---
【例題チャレンジ】
- 解答 : sign(param)
    - L1ノルムは、|param|なのでその勾配が誤差の勾配に加えられる


---
【例題チャレンジ】
- 解答:image[top:bottom, left:right,:]


### 3-3. 関連学習

気になる記事を見つけたので
- Transformerをベースとした事前学習モデルのfine-tuningの過学習を抑制するTips
    - Multi Sample Dropout
        - 通常、この廃棄するニューロンのサンプル(論文ではdropout sample)は1イテレーションごとにサンプリングされますが、Multi Sample Dropoutでは1イテレーションで複数のdropout sampleを作成。各サンプルに対して損失を計算して、その平均を最終的なそのイテレーションの損失とする
    - R-Drop
        - １回のイテレーションで学習データを2回流すと、Dropoutパターンが異なる出力が2つ得られる。この2 つの出力分布間の KL-divergence を学習と同時に最小化することで正則化する手法 
- 参考記事
    - https://www.ai-shift.co.jp/techblog/2170


### 3-4. 実装演習

```py 
import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer


(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)
optimizer = optimizer.SGD(learning_rate=0.01)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)
        
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        

lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()
```
![スクリーンショット 2023-11-26 23.44.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/54052118-c03d-9e84-37ed-62832d47951f.png)

- training setは精度がほぼ1.0であるが、test setは0.8いかないぐらいの値である
- 目的は汎用的なモデルを作るところなのでこれからは何らかのアプローチをしていくべき

---

```py 
from common import optimizer

(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]


network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)


iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.01

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10
hidden_layer_num = network.hidden_layer_num

# 正則化強度設定 ======================================
weight_decay_lambda = 0.1
# =================================================

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0
    
    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * network.params['W' + str(idx)]
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += 0.5 * weight_decay_lambda * np.sqrt(np.sum(network.params['W' + str(idx)] ** 2))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        
        
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)
        
        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()

```
- weight_decayのパラメータを加えた学習である
- 最適な値はわからないので optuna等で探すのも一つの手である

---

```py 
class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask

from common import optimizer
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

print("データ読み込み完了")

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

# ドロップアウト設定 ======================================
use_dropout = True
dropout_ratio = 0.4
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda, use_dropout = use_dropout, dropout_ratio = dropout_ratio)
optimizer = optimizer.SGD(learning_rate=0.01)
# optimizer = optimizer.Momentum(learning_rate=0.01, momentum=0.9)
# optimizer = optimizer.AdaGrad(learning_rate=0.01)
# optimizer = optimizer.Adam()

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    d_batch = d_train[batch_mask]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

    loss = network.loss(x_batch, d_batch)
    train_loss_list.append(loss)    
    
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        
        
lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()
```

- Dropoutを含んだモデルでの学習である
- このパラメータの最適なものは色々試してみないとわからない
- 学習曲線は値によってかなり変わってくることが色々試してわかった

---

## Section4: 畳み込みニューラルネットワークの概念

### 4-1. 要点

#### 畳み込み層

- 畳み込み層
    - 画像の場合、縦、横、チャネルの3次元のデータをそのまま学習し、次に伝えることができる
    - 結論 : 3次元の空間情報も学習できるような層が畳み込み層


- パディング(Padding)
    - 入力画像の周囲にピクセルを囲む手法
    - 通常の畳み込みをしていると出力は若干サイズダウンするのでそこの穴埋めをするようなイメージ
    - 画像サイズが変わらないようにできたり、端の特徴量も捉えることが出来る
    - ※参考資料
        - https://zawapython.hatenablog.com/entry/2018/10/03/210211

- ストライド(Stride)
    - 畳み込みにおいて使われるフィルタが画像を移動する間隔のこと
    - 基本的にはストライドは1が多いが、かなり大きな画像の時は1以上を設定したりもする
- チャンネル
    - 各ピクセルが持つ情報の数
    - グレースケール画像:1, カラー画像:3 (RGB)
- ※全結合で画像を学習した際の課題
    - デメリット
        - 画像の場合、縦、横、チャンネルの3次元データだが、1次元データとして処理される→RGBの各チャンネル間の関係性が学習に反映されないということ

#### プーリング層

- プーリング処理
    - 画像の代表値を抜き出す処理
    - MAX Pooling, Average Pooling とかある
    - 着目領域における代表値の並進移動に対してロバスト性を有することを期待してプーリング処理が行われる

---

### 4-2. 確認テスト

【確認テスト】 サイズ6x6の入力画像を、サイズ2x2のフィルタで畳み込みした時の出力画像のサイズは？なお、StrideとPaddingは1とする
- 解答 : 7*7 (公式より)

---

### 4-3. 関連学習

- Global Average Poolingが全結合層の代わりにCNN終盤層に用いる利点
    - 全結合層を，グローバル平均プーリング層に差し替えると，CNN全体のパラメータが削減される．これにより，大規模なCNNが持つ「パラメータが多すぎて過学習しやすい」というリスクを減らすことに繋がる
    - また、全結合層を除外してその代わりにGAP層を用いることで、CNN全体が「畳み込み操作のみ」になりクラス確率出力と各特徴チャンネルがしっかり紐付くという利点がある
    - 参考記事
        - https://cvml-expertguide.net/terms/dl/layers/pooling-layer/global-average-pooling/



## Section5: 最新のCNN

### 5-1. 要点

- 論文
    - ImageNet Classification with Deep Convolutional Neural Networks
    - URL : https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf
    - 2012

- AlexNet
    - 2012年以前は画像から特徴量を人間が定義してそれを元にモデルを構築するアプローチが主流だったが、AlexNetの登場により人間が定義しなくてもモデルが特徴を抽出して画像認識をしてくれる巨大なNNアーキ
    - activation funcで今までは、tanhとかsigmoidを使用していたがReLUを導入
    - Sigmoidを使うとダメな理由は、微分の最大値が0.25となり、中間層にSigmoidを使いすぎると勾配消失してしまう
    - AlexNetでは一部の全結合層で0.5の確率で出力が0になるドロップアウトを適用
    - 参考
        - https://toukei-lab.com/alexnet#google_vignette
        - https://dx-consultant-fast-evolving.com/alexnet/


        



![スクリーンショット 2023-11-25 13.20.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223559/699c7eb2-32a2-f296-ded5-b0e2f40b4c5f.png)

#### 5-2. 関連学習

- 最近のモデルと比較すると簡単な構造をしている。だけど細かい工夫が多く見られ改めて面白いニューラルネットワークだと感じました。特に "重複付き最大値プーリング"は面白い部分。ストライドの感覚をフィルタサイズ未満にし、周囲のプーリング領域とあえて重なるようにしています。こうすることによって過剰適合しにくくなるとか

- ※参考記事
    - https://developers.agirobots.com/jp/deep-learning-2020-alexnet/


## 6. [フレームワーク演習] 正則化/最適化

### 6-1. 要点
- 過学習を抑制する方法
    - パラメータ正則化
        - L1正則化 (Lasso回帰)
        - L2正則化 (Ridge回帰)
        - Elastic Net
            - L1とL2を組み合わせたもの
    - 正則化レイヤー
        - Dropout
    - 正規化レイヤー
        - Batch正規化
        - Layer正規化
        - Instance正規化

- 過学習が起きる理由
    - 訓練サンプルにだけ適合した学習をした結果
    - 例
        - パラメータの数が多すぎる
        - パラメータの値が偏っている
        - ノードが多すぎる
        - 学習データが不足している
        - 学習データが偏っている




# 終わりに

AlexNetが "最新CNN" という分類になっていたが違和感があった(なぜ最新でなのか？)。久しぶりAlexNetの論文も軽く読んでみて懐かしかった。