[最下層からの深さ学習]6学習関連スキル第1部-パラメータ更新



🌿 パラメータの更新


ニューラルネットワーク学習の目的は損失関数値をできるだけ低減するパラメータを探すことである.
→パラメータの最適値を探す
このような問題を解決することを最適化と呼ぶ

📍 かくりつけいしゃこうかほう


Stochastic Gradient Descent
  • 最適パラメータ値の手がかりを探し、パラメータの傾斜(微分)
  • を用いる
    パラメータ値は
  • パラメータの傾きにより更新され、最適値
  • に近づく.

    SGDの公式から明らかなように,SGDは傾斜方向だけに一定距離をとる簡単な方法である.
    次のようになります.
    class SGD:
    
        def __init__(self, lr=0.01):
            self.lr = lr
            
        def update(self, params, grads):
            for key in params.keys():
                params[key] -= self.lr * grads[key] 
    lrは学習率(学習率)を表し,update(params,grads)法はSGD過程で繰り返し呼び出される.
    最適化を繰り返し、パラメータを更新!

    SGDの欠点


    SGDは簡単で実現しやすいが、問題によっては効率が悪い場合がある
  • 等方向性関数
    等方向性:どの方向にも同じ性質を持つ
  • 関数は、定義の「性質」を「傾斜」と解釈します.
    等方性関数の例は次のとおりです.
    ex) f(x, y) = x^2 + y^2

    この関数の傾きは次のとおりです.

    等方関数は,各配置における傾斜方向の固有値を指すことが分かる.したがって,等方性関数に対してはSGDを用いてもよい.
  • 非等方関数
    非等方性:物理的性質は方向によって変化する
  • 非等方性関数は名前からも分かるように,各位置の傾きが指す点は1つではなく複数である.
    非等方性関数の例は次のとおりです.


    この関数は「茶わん」をx軸方向に伸ばしたように、等高線はx軸方向に伸ばした楕円の形です.
    関数の傾きは次のとおりです.

    傾斜したy軸方向が大きく、x軸方向が小さいのが特徴です.
    上の図に示すように、傾斜の大部分は最小値(0,0)を指さない.
    この状態でSGDを適用し、結果は以下の通りである.

    かなり非効率な移動は、パラメータが更新されていることを示します.
    このような結果になったのは,傾斜の方向が従来の最大値と異なることを考慮し,盲目的な傾斜よりも賢い妙策を考え出すためである.

    📍 コメント


    うんどうりょう
    物理的にはp(運動量)=m(質量)*v(速度)であり,ニューラルネットワークでは質量が1であり,運動量は速度のみを表す.
    [アニメーション](Animation)テクノロジーは、次のように数式で表されます.

    ここで,v変数は速度に相当し,物体が傾斜方向に力を受けて加速する物理法則を式で表す.
    αv項は,物体が何の力も受けないときに緩やかに低下する役割を果たす.
    つまり、物体に加わる傾斜(加速度)がなければ、物体の速度は抵抗(ex.地面の摩擦、空気の抵抗)などによって減少するため、α0.9等値に設定します.
    テーマの体現は以下の通りである.
    class Momentum:
    
        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 = {}
                for key, val in params.items():                                
                    self.v[key] = np.zeros_like(val)
                    
            for key in params.keys():
                self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
                params[key] += self.v[key]
    先ほどの非等方性関数を用いて最適化更新を行い、パスは以下の通りである.

    「ジグザグ程度」はSGDに比べて少ない.
    これは,x軸の力は小さいが同じ方向に進み続け,一定の加速の形で反映されるためである.

    また、基準を利用してLocal Minimumの問題を解決することもできます.
    既存のSGDが最大値ではなく最大値を探している場合でも、
    時には、運動量は慣性を利用して逃げることができる.

    📍 AdaGrad


    Adaptive Gradient
    ニューラルネットワーク学習では,学習率の値を正確に設定することが重要である.
    学習率が小さすぎると学習時間が長くなり、逆に大きすぎると発散し、学習が不十分になる.
    この学習率を決定する有効な方法は、学習率を低下させることです.
    →学習が進むにつれて学習率が減少する
    簡単に言えば、学習率をすべて「全体」に下げ、さらに発展させることができるのはAdaGradです.
    AdaGradは個々のパラメータに応じて学習率を調整(適応)することができる
    式は次のとおりです.

    ⊙:マトリックス内の各要素の乗算
    既存の勾配値に乗じて追加を続行するhという変数が追加されました.
    その後、パラメータの更新時に1/sqrt(h)を乗じて学習率を減少させる.
    →つまり,傾斜値が大きく,大きく更新された要素は学習率を低下させる.
    →学習率の低下はパラメータの要素によって異なる
    AdaGradでは過去の傾斜二乗を増やし続け,学習が進むにつれて更新強度が弱まる.
    このとき,無限に学習を行い,ある瞬間に更新量が0となり,更新が行われなくなるという問題が発生する可能性がある.
    RMSPropはこの問題を改善しました
    この方法は過去のすべての傾斜をバランスさせるのではなく,過去の傾斜を徐々に忘れ,新しい傾斜情報(指数移動平均値)を反映する.
    過去の傾斜の反映を指数級に下げる
    AdaGradの実装は次のとおりです.
    class AdaGrad:
    
        """AdaGrad"""
    
        def __init__(self, lr=0.01):
            self.lr = lr
            self.h = None
            
        def update(self, params, grads):
            if self.h is None:
                self.h = {}
                for key, val in params.items():
                    self.h[key] = np.zeros_like(val)
                
            for key in params.keys():
                self.h[key] += grads[key] * grads[key]
                params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
    最後の行に1-7という小さな値を加えて、hが0のときにdivision byzeroエラーが発生しないようにします.

    AdaGradを使用して、上記の非等方性関数を最適化します.更新パスは次のとおりです.

    y軸方向の傾斜が大きく、最初は移動が大きかったが、その大きな移動によりhが急激に増大し、更新度合いが大幅に減少した.

    📍 Adam


    Adaptive Moment Estimation
  • MomentumとAdaGradハイブリッド技術
  • で用いる係数と学習率の係数は
  • を用いる.
  • の学習率を低減し、速度を計算し、学習の更新強度
  • を適応的に調整する.
  • スーパーパラメータ「偏り補正」が
  • に行われました.
    現在最も多く使用されているオプティマイザです.
    Adamクラスは次のように実装されています.
    class Adam:
    
        def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
            self.lr = lr
            self.beta1 = beta1
            self.beta2 = beta2
            self.iter = 0
            self.m = None
            self.v = None
            
        def update(self, params, grads):
            if self.m is None:
                self.m, self.v = {}, {}
                for key, val in params.items():
                    self.m[key] = np.zeros_like(val)
                    self.v[key] = np.zeros_like(val)
            
            self.iter += 1
            lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)         
            
            for key in params.keys():
                #self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
                #self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
                self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
                self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
                
                params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
                
                #unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
                #unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
                #params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
    Hyperパラメータは全部で3つあります
    学習率-α, 主パラメータの係数-β1、二次基準係数-β2
    論文では、デフォルト設定は次のとおりです.β1は0.9で、β2は0.999で、この値は多くの場合良い結果を得ることができます.
    Adamは、次の最適化された更新パスを提供します.

    💬 どの更新方法を使用しますか?


    すべての問題において常に優れた手法は存在しない.😢
    また、当然ですが、学習率などのスーパーパラメータをどのように設定しても結果は変わります!
    現在多くの研究でSGDが使われており、アニメーションやAdaGradも試してみる価値がある.
    最近アダムをよく使うそうです.
    コメントブログ
    https://beoks.tistory.com/30
    https://beoks.tistory.com/29?category=789329
    https://wordbe.tistory.com/entry/MLDL-パフォーマンス-向上-重要
    https://sacko.tistory.com/42