mao9o9の技術メモ

個人的に興味のある分野についての技術メモです。

学習率の変化による損失と精度への影響 (2)

はじめに

前回の記事で、勾配と学習率の関係において一度最小値に達した後、再び勾配は大きくなったため、勾配と学習率の関係に着目して何故このような減少が生じるのか調べた。

SGDアルゴリズムの違いによる影響

ます、SGDアルゴリズムの違いが原因であるのか調べた。

  • モーメンタムなし

w_t = w_{t-1} - \gamma \nabla_w f(w_{t-1})
f:id:mao9o9:20220309211301p:plain:w500
図1 モーメンタムなしの場合の勾配
  • モーメンタムあり

v_t = \mu v_{t-1} + \nabla_wf(w_{t-1}) \\
w_t = w_{t-1} - \gamma v_t
f:id:mao9o9:20220309211526p:plain:w500
図2 モーメンタムありの場合の勾配
  • モーメンタム + nestrov

v_t = \mu v_{t-1} + \nabla_wf(w_{t-1}) \\
w_t = w_{t-1} - \gamma (\nabla_wf(w_{t-1}) + \mu v_t)
f:id:mao9o9:20220309211621p:plain:w500
図3 nestrovを用いた場合の勾配

図1、図2、図3にepochと勾配の関係を示す。図2、図3においては、 v_tを勾配とした。いずれのSGDの手法においてもepochと勾配の関係に違いはなかった。

損失関数による影響

CIFAR100のような多クラス分類では、モデル出力値にLogSoftmax関数を適用した後、NLLLossにより推定値を求めている。これはCross Entropy Lossとほぼ同じである。

NLLLoss

NLLLossを次式に示す。


\begin{align}\begin{split}
l_n &= -w_{y_n} x_{n, y_n}\\
NLLLoss &= \sum^N_{n=1} \frac{1}{\sum^N_{n=1}w_{y_n}} l_n
\end{split}\end{align}

今回の話では、 xはモデルの出力値、 yはラベルのインデックスである。また、 Nはバッチサイズ、 wは重みであり1である。NLLLossは x微分しても定数であり勾配に影響しない。

LogSoftmax

LogSoftmaxを次式に示す。


\begin{align}
LogSoftmax(x_i) = log\left(\frac{exp(x_i)}{\sum_j exp(x_j)}\right)
\end{align}

 xはモデルの出力値である。図4にこの関数への入力と出力の関係を示す。

f:id:mao9o9:20220309211911p:plain
図4 LogSoftmaxの入出力の関係

図のようにLogSoftmaxは負の値であり、線形である。この関数の微分形を次式に示す。


\begin{align}
\frac{\partial}{\partial x}log\left(\frac{exp(x_i)}{\sum_j exp(x_j)}\right) &= \frac{\frac{\partial}{\partial x}Softmax(x_i)}{Softmax(x_i)} \\
\frac{\partial}{\partial x}Softmax(x_i) &= \frac{exp(x_i)\sum_j exp(x_j) - exp(x_i)\frac{\partial}{\partial x}\sum_j exp(x_j)}{\left(\sum_j exp(x_j) \right)^2} \\
&= \frac{exp(x_i)\sum_j exp(x_j) - (exp(x_i)^2}{\left(\sum_j exp(x_j)\right)^2} \\
&= \frac{exp(x_i)}{\sum_j exp(x_j)}\left(1 - \frac{exp(x_i)}{\sum_j exp(x_j)}\right) \\
\therefore \frac{\partial}{\partial x}log\left(\frac{exp(x_i)}{\sum_j exp(x_j)}\right) &= 1 - \frac{exp(x_i)}{\sum_j exp(x_j)}
\end{align}

図5に微分式への入力と出力の関係を示す。

f:id:mao9o9:20220309212215p:plain
図5 LogSoftmaxの微分形への入出力

図に示すように微分形の出力はほぼ一定である。従って、学習率と勾配の関係に影響しないと考えられる。

ついでに、順伝播と逆伝播の関係を調べた。これらは式(4)のように表され、LogSoftmaxの順伝播と逆伝播は図6のようになる。


\begin{align}\begin{split}
forward:\ \ \  y &= f(x) \\
backward:\ \ \ x &= \frac{1}{\dot f(x)}y
\end{split}\end{align}
f:id:mao9o9:20220309212320p:plain
図6 順伝播と逆伝播

 f(x)はLogSoftmax関数である。

結論

参考[1]によると、高次元のニューラルネットワークのパラメータを最適化する時、局所解は事実上存在せず、全次元のうち数次元において極値をある鞍点が存在する。この鞍点からどのように脱出するかを突き止めるまで、鞍点への接近と脱出の状態が繰り返される。

というのが上記の実験で見られた勾配の振動であると思われる。

大域解ではない局所解である確率は損失関数が大域解に近づくほど高くなるため、学習がある程度進めば局所解である部分で止めても問題はないらしい。

参考

[1] ディープラーニングのサマースクールで学んだ26のこと

SGDについて

はじめに

SGD(Stochastic gradient decession)について、設定した目的関数に対してどのようにパラメータを更新しているのかシミュレーションを行い調べた。

実装環境

ついでにPytorchでのSGDの挙動を調べるためPytorch版とnumpy版を作成して、ステップの更新を調べた。

github.com

SGD

SGDは目的関数 f(w)を最小化する手法。 f(w)導関数を求め、入力の勾配に応じて更新する度合を設定する。

モーメントを使用しない時、


w_t = w_{t-1} - \gamma \nabla_w f(w_{t-1})

momentumを使用するとき、


v_t = \mu v_{t-1} + \nabla_wf(w_{t-1}) \\
w_t = w_{t-1} - \gamma v_t

momentumを使用かつnestrovを適用する時、


v_t = \mu v_{t-1} + \nabla_wf(w_{t-1}) \\
w_t = w_{t-1} - \gamma (\nabla_wf(w_{t-1}) + \mu v_t)


結果

  • パラメータ設定

SGD:momentum = 0.9, nesterov = True

初期点: (x, y) = (20, 0)

学習率: lr = -0.0002 * iteration + 0.01

  • 目的関数: f(x,y) = x^{2} + y^{2}
f:id:mao9o9:20220219100122p:plain
図1  f(x, y) = x^2 + y^2

図1に示す目的関数は (x, y) = (0, 0)で極小値を持つ。

f:id:mao9o9:20220219100345p:plain
図2 パラメータの軌跡

目的関数の xy平面上におけるSGDのパラメータの軌跡を図2に示す。勾配が0となる点に向けて移動するため、 xの負の方向にパラメータが更新されている。

f:id:mao9o9:20220219100431p:plainf:id:mao9o9:20220219100444p:plain
図3 iterationと勾配および損失の関係

次に、iterationと勾配、および損失の関係を図3に示す。iterationが15の時、勾配は0となり損失は最小値をとった。その後の勾配は負であるため、目的関数の最小点を超えて坂道を駆け上っているような状態であると思われる。

  • 目的関数: f(x, y) = x^{2} - y^{2}
f:id:mao9o9:20220219100556p:plain
図4  f(x, y) = x^2 - y^2

図4に示すように (x, y) = (0, 0)で鞍点を持つ関数である。

f:id:mao9o9:20220219100649p:plain
図5 パラメータの軌跡

目的関数の xy平面上におけるSGDのパラメータの軌跡を図5に示す。勾配が0となる点に向けて移動するため、 xの負の方向にパラメータが更新されている。また、今回のような初期値の取り方では鞍点から抜け出せず、モデルに最適な最小値をとれていない。

f:id:mao9o9:20220219100731p:plainf:id:mao9o9:20220219100740p:plain
図6 iterationと勾配および損失の関係

次に、iterationと勾配、および損失の関係を図6に示す。鞍点にはまっているにも関わらず損失および勾配は図3と同様な傾向を示している。

従って、損失が0に近づいたとしても目的関数の最小値であるとは限らない。つまり、iterationにおける損失をみただけでは、目的関数が最小となる値を求められているかわからない。

  • PytorchとnumpyのSGDの比較

パラメータの更新式はPytorchのReferenceを参考に構成したため、異なる点は勾配の計算方法であると思われる。Pytorchでは中心差分で計算してるのかな?うまく再現できなかったため不明である。

参考

Pytorch – 確率的勾配降下法 (SGD)、Momentum について解説

6.1.2:SGD【ゼロつく1のノート(実装)】

学習率の変化による損失と精度への影響 (1)

はじめに

前回の記事において、epochの途中で学習率を変更すると精度が上昇する現象が確認できたため、エポックごとに学習率を変化させた時の損失と精度を調べてみた。

https://github.com/9mao9/changing-learning-rate

実装環境

バージョン情報

  • torch 1.10.0+cu111
  • torchvision 0.11.1+cu111
  • numpy 1.19.5
  • sklearn 1.0.2

訓練データはCIFAR-100を利用した。

ネットワークモデルはWideResNetを用いた。

実験条件

  • 入力サイズ:[3, 32, 32]
  • バッチサイズ:128
  • 初期学習率:0.1
  • weight decay:5e-4
  • optimizer: SGD (momentum: 0.9, nestrov=True)
  • transform: RandAugmentation (magnitude: 10)
  • Drop out: 0.3

学習率の更新

epoch数が[0, 60, 120]の時、学習率 lrは[0.1, 0.02, 0.04]となる点に対して近似を行い、関数を作成した。学習率の上限は0.1、下限は0.004とし、下限に達するまでepochを増加させた。従って、(i), (ii), (iii)は100 epoch、(iv)は120 epoch、(v)は147 epochまで行った。

(1) 一定:(i)  lr = 0.02, (ii)  lr = 0.004

(2) 線形:(iii)  lr = -0.0013x + 0.1, (iv)  lr = -0.0008x + 0.1

(3) 非線形:(v)  lr = 0.1e^{-0.0268x}, (vi)  lr = -0.000006x^{2} + 0.0002x + 0.1

epoch数を xとおいた。各条件を図1に示す。

f:id:mao9o9:20220209001540p:plain:w500
図1 epochに対する学習率の変化

結果

f:id:mao9o9:20220209001619p:plainf:id:mao9o9:20220209001635p:plain
図1 左:(i)、右:(ii)のepochと損失の関係

(i)と(ii)のepochと損失の関係を図2に示す。学習率が高い時の方が早く損失が小さくなり学習が進んでいるように見える。また、精度も早くに高くなっている。


f:id:mao9o9:20220209002056p:plainf:id:mao9o9:20220209002105p:plain
図3 左:(iii)、右:(iv)のepochと損失の関係

(iii)と(iv)のepochと損失の関係を図3に示す。(iii)では学習率の下限0.004となる74 epochで損失および精度が収束している。(iv)では50 epochで一度収束し、学習率が小さくなるにつれて再度損失が小さくなった。


f:id:mao9o9:20220209002233p:plainf:id:mao9o9:20220209002242p:plain
図4 左:(v)、右:(vi)のepochと損失の関係

(v)と(vi)のepochと損失の関係を図4に示す。(v)のように学習の始めで大きく減少し緩やかになる学習率の設定だと損失の変化も緩やかになった。対して、始めは緩やかで徐々に大きく減少していく学習率だと損失がより小さくなった。


f:id:mao9o9:20220209002354p:plainf:id:mao9o9:20220209002402p:plain
図5 左:一定の学習率、右:線形・非線形な学習率の勾配との関係

学習率が一定の時のepochと損失勾配の関係、線形、非線形の時の学習率と損失勾配の関係を図5に示す。(i)では60 epochで勾配は収束した。(ii)は勾配の振動が大きく最小値に収束していないため、まだ学習の途中であると考えられる。(iii) - (vi)では一度最小値に達した後、再び勾配は大きくなっていった。

損失勾配は式(1)により導出したため、厳密には損失の勾配とは異なる。

 \displaystyle
g_t = \frac{w_{n-1} - w_n}{\eta} \ \ \ \ \ \ (n=1, 2, \cdots)

 g_tは損失勾配、 wは損失コスト、 \etaは学習率である。

結論

本実験より、一般的に言われている「学習率は大きいほど学習が早く進む」ことを確認できた。また、今回用いたネットワークに限定されるかもしれないが、学習率を動的に変化させる方が損失が小さく精度は高くなる傾向にあることがわかった。その際、(iv)または(vi)のような徐々に学習率が小さくなる方法がよさげであった。

勾配と学習率の関係において、一度最小値に達した後、再び勾配は大きなったことについて、勾配の振動については(2)の記事を設けて調査する。

WideResNetを試してみた

はじめに

ディープラーニングについて学習する上で、WideResNetを実装して試行毎の損失、正解率をグラフ化してみた。

github.com

WideResNetについて

従来ではResNetの層の深さに対するアプローチが行われていたが、層の幅を広くすることで、浅い層でも高い精度を実現した手法[1]。モデルの作成において[2][3][4]を参考にした。

f:id:mao9o9:20220203220343p:plain:w400
図1 WideResNetのネットワークの構成 [1]より引用

実装環境

バージョン情報

  • torch 1.10.0+cu111
  • torchvision 0.11.1+cu111
  • numpy 1.19.5
  • sklearn 1.0.2

訓練データはCIFAR-100を利用した。

実験条件

  • 入力サイズ:[3, 32, 32]
  • バッチサイズ:128
  • 学習率:0.1 (60 epochからは0.2倍)
  • Network width:2, 10
  • weight decay:5e-4 (Network width: 10の時 1e-4)
  • optimizer: SGD (momentum: 0.9, nesterov)
  • transform: RandAugmentation (magnitude: 10)
  • Drop out: 0.3

Network widthが10の時、初期学習率が0.1だと損失が発散してしまったため、0.01とした。ネットワークの深さは28層とし、100 epochで確かめた。

結果

f:id:mao9o9:20220203220426p:plain:w500
図2 wrn-28-2の訓練と精度

f:id:mao9o9:20220203220452p:plain:w500
図3 wrn-28-10の訓練と精度

精度は式(1)に示す正解率である。

 \displaystyle
\rm{Accuracy} = \frac{\rm{TP + NF}}{\rm{TP + NP + TF + NF}} \ \ \ \ (1)

wrn-28-2では実行時間は1時間ほどで、60 epochで学習率を0.2倍した際に精度は上昇した。50層のResNetと比較すると、50 epochまでであるため、1/4の時間でほぼ同様の精度となった。

wrn-28-10では3時間ほどであったが、精度はwrn-28-2よりも高くなった。また、wrn-28-10では30 epochからTrainよりもValの損失が大きくなり、過学習の傾向が見られた。

結論

ネットワークが広くなるほど(チャンネル数が大きくなるほど)実行時間は増加するが、精度は高くなり、ネットワークの深さだけでなく広さに対しても実行時間と精度はトレードオフの関係にあることが確認できた。

学習が進むほど損失は小さくなるため、損失に応じた学習率の設定が精度向上に有効? → 次回

参考

[1] S. Zagoruyko, et.al., "Wide Residual Networks", 2016.

[2] WideResNet作成時に引っかかった点

[3] 論文の勉強3 WideResNet

[4] Pytorch - Wide ResNet の仕組みと実装について解説

conv層のバイアスあり・なしの比較

はじめに

[1]を読むとバッチノルム層がある場合、畳み込み層のバイアスは必要ないっぽい?

なので、前回のプログラムを修正してResnet-RSのみバイアスあり・なしで実行してみました。

あと、細々したところも修正...


Githubに載せたコードも修正しました。

github.com

github.com

Batch Norm [2]

各層への入力はその前の入力に依存しているため、その入力のばらつきに偏りがある場合、層が深くなると小さな誤差が積み重なっていき目的関数が最小に達しないという問題がある。これを、入力のばらつきを標準化して同じスケールの整えることで、問題解決を図る手法。

(解釈がおかしければご指摘いただければ幸いです。)

実装環境

前回と同様

実験条件

畳み込み層のバイアス有り、無しの二通り。

他、前回と同様。

結果

ネットワークの深さは50層とし、50 epochで確かめた。

f:id:mao9o9:20220128215538p:plain:w600
図1 Resnet-RSの損失と精度

実線はバイアスあり、点線はバイアスなしである。精度は式(1)に示す評価データにおける正解率である。

 \displaystyle
\rm{Accuracy} = \frac{\rm{TP + NF}}{\rm{TP + NP + TF + NF}} \ \ \ \ (1)


畳み込み層のバイアスの有無で違いは見られなかった。結局バッチノルム層で標準化されるから畳み込み層のバイアスの有無は関係ないのかな。

参考

[1] Why does the resnet model given by pytorch omit biases from the convolutional layer?

[2] S. loffe, et.al, "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift", 2015

Resnetを試してみた

はじめに

ディープラーニングについて学習する上で、Resnet、Resnet-RSを実装して試行毎の損失、正解率を比較してみた。

ColaboratoryでのコードをGithubに載せました。

Resnet: https://github.com/9mao9/Resnet-RS_CIFAR100

Resnet-RS: https://github.com/9mao9/Resnet-RS_CIFAR100

Resnetについて

Resnet [1]

図1に示す残差ブロックの導入により、層を深くすると生じる勾配消失を防ぎ、従来のモデルよりも高い精度を実現した手法。モデルの作成において[2][3]を参考にした。

f:id:mao9o9:20220122004756p:plain:w500
図1 残差ブロック [1]より引用

Resnet-RS [4]

Resnetのネットワーク構造をResnet-D [5]に変更し、SEブロック [6]を追加したモデル。モデルの作成において[7][8]を参考にした。

実装環境

バージョン情報

  • torch 1.10.0+cu111
  • torchvision 0.11.1+cu111
  • numpy 1.19.5
  • sklearn 1.0.2

訓練データはCIFAR-100を利用した。

実験条件

両者とも入力サイズ、バッチサイズなどをResnet-RS [4]に準拠。

  • 入力サイズ:[3, 160, 160]
  • バッチサイズ:128
  • 学習率:0.0078125 (Resnetは0.001)
  • weight decay:4e-5
  • optimizer: SGD (momentum: 0.9)
  • transform: RandAugmentation (magnitude: 10)
  • Drop out: 0.25 (Resnet-rsのみ)

Resnetの学習率を0.0078125とすると学習が進まなかったため0.001とした。また、ResnetのFC層の前にドロップアウトを追加しても学習が進まなかったため、除外した。

結果

ネットワークの深さは50層とし、論文の通り350 epochも行うと時間がかかりすぎるため、50 epochで確かめた。

f:id:mao9o9:20220122005510p:plain:w500
図2 Resnetの損失と精度
f:id:mao9o9:20220122005610p:plain:w500
図3 Resnet-RSの損失と精度

精度は式(1)に示す正解率である。ResnetよりもResnet-RSの方が学習が進み、精度も高くなっている傾向がある。SEブロックおよびドロップアウトの影響が大きいと考えられる。

 \displaystyle
\rm{Accuracy} = \frac{\rm{TP + NF}}{\rm{TP + NP + TF + NF}} \ \ \ \ (1)


50 epochで精度が収束してるように見えるけど試行回数を増やすとあがるのかな。

参考

[1] K. He, et.al., "Deep Residual Learning for Image Recognition", 2015.

[2] Residual Network(ResNet)の理解とチューニングのベストプラクティス

[3] ResNet50をpytorchで実装

[4] I. Bello, et.al, "Revisiting ResNets: Improved Training and Scaling Strategies", 2021

[5] T. He, et.al, "Bag of Tricks for Image Classification with Convolutional Neural Networks", 2018

[6] J. Hu, et.al, "Squeeze-and-Excitation Networks", 2017)

[7] 帰ってきたResNet!最新の画像認識モデル「ResNet-RS」を解説!

[8] SOTA性能を得るためのResNetsの学習とスケーリング戦略!