E資格用語の備忘録
シラバス2024 3.深層学習 (2)深層モデルのための最適化 i.基本的なアルゴリズム モメンタム
Pathological Curvature
関数の形状が渓谷のようになっており、勾配降下法などの最適化アルゴリズムに影響を与える問題。
この形状で勾配降下法は、一方向に進むスピードが速く、他の方向に進むスピードが遅い場合、オーバーシュート(一度の更新が大きすぎること)を引き起こし、振動が発生し、最小値に到達するのが遅くなる。
解決策として、モメンタムやNesterov Accelerated Gradientなどの手法が提案されている。
import numpy as np
# 最適化手法 : SGD : 確率的勾配降下法
class SGD:
# 学習率を0.01で初期化
def __init__(self, lr=0.01):
self.lr = lr
# パラメータと勾配を受け取り、パラメータの更新
def update(self, params, grads):
# パラメータの各キーを順に取得、.keys()は辞書のすべてのキーを返すメソッド
for key in params.keys():
# 各パラメータを各勾配で更新
params[key] -= self.lr * grads[key]
Momentum
最適化アルゴリズムの一つで、パラメータの更新に前回の更新方向を考慮に入れた手法。
通常の勾配降下法(SGD)に比べ最適化の過程での振動が抑制され、よりスムーズな収束が可能になる。
import numpy as np
# 最適化手法 : モーメンタム
class Momentum:
# 学習率を0.01、モーメンタム項を0.9で初期化
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None # ベロシティ用のインスタンス変数の初期化
# パラメータの更新、パラメータと勾配を受け取る
def update(self, params, grads):
# 初回ベロシティが無い状態
if self.v is None:
# 空の辞書を代入
self.v = {}
# 各パラメータのキーと値を順に取得、.items()は辞書のすべてのキーと値のペアを返すメソッド
for key, val in params.items():
# valと同じ形状のゼロ配列を生成
self.v[key] = np.zeros_like(val)
# パラメータの各キーを順に取得、.keys()は辞書のすべてのキーを返すメソッド
for key in params.keys():
# ベロシティを各勾配で更新
self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
# 各パラメータを各勾配で更新
params[key] += self.v[key]
Nesterov Accelerated Gradient
ネステロフのモーメンタムやNAGとも言う。
最適化アルゴリズムの一つで、モメンタムを改良したもので、モメンタムが次の位置を予測し、その予測位置での勾配を計算する。
モメンタムSGDが引き起こすオーバーシュ
ートを抑制し、最適化の速度を向上させる。
実装においては理論上の更新式に等価な式を使用する。
import numpy as np
# 最適化手法 : ネステロフのモーメンタム : NAG
class Nesterov:
# 学習率を0.01、モーメンタム項を0.9で初期化
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None # ベロシティ用のインスタンス変数の初期化
# パラメータの更新、パラメータと勾配を受け取る
def update(self, params, grads):
# 初回ベロシティが無い状態
if self.v is None:
# 空の辞書を代入
self.v = {}
# 各パラメータのキーと値を順に取得、.items()は辞書のすべてのキーと値のペアを返すメソッド
for key, val in params.items():
# valと同じ形状のゼロ配列を生成
self.v[key] = np.zeros_like(val)
# パラメータの各キーを順に取得、.keys()は辞書のすべてのキーを返すメソッド
for key in params.keys():
# 各パラメータを各勾配で更新
params[key] += self.momentum * self.momentum * self.v[key]
params[key] -= (1 + self.momentum) * self.lr * grads[key]
# params[key] = params[key] + self.momentum * self.momentum * self.v[key] - (1 + self.momentum) * self.lr * grads[key]
# 次回のベロシティを各勾配で更新
self.v[key] *= self.momentum
self.v[key] -= self.lr * grads[key]
# self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
# ネステロフのモーメンタムでは、「先読み」を行い、現在のベロシティ方向に少し「先読み」した位置での勾配を計算し、その情報を用いてパラメータを更新する。
# 上記の実装では、次回のベロシティを今回の最後に更新する。初回の更新については、ネステロフのモーメンタムも通常のモーメンタムも同じ。