[PYTHON] Liste der Gradientenabstiegsmethoden (2020)

Zielperson

Ich habe die stochastische Gradientenabstiegsmethode zusammengefasst, die eine Optimierungsmethode für tiefes Lernen ist. Basierend auf den in hier zusammengefassten Formeln und Papieren bestimmt Pytorch jedoch die Anfangswerte und Konstanten, die nicht in den Papieren enthalten waren. , Keras, Chainer usw. werden nur zusammengefasst. Formeln usw. werden implementierungsähnlich zusammengefasst. Schauen Sie also bitte nach, ob Sie sie selbst implementieren möchten.

Bitte zögern Sie nicht, auf Fehler hinzuweisen. Wenn Sie Informationen zu neuen Optimierungsmethoden haben, teilen Sie uns bitte die von Ihnen erstellten Optimierungsmethoden mit!

Inhaltsverzeichnis

Einführung

Zunächst werde ich als Vorwissen den Unterschied zwischen der Methode des steilsten Abstiegs und der Methode des stochastischen Gradientenabstiegs erläutern. Das ist keine große Sache.

Was ist die steilste Abstiegsmethode?

Die Methode mit dem steilsten Abstieg ist eine Methode zum Abstieg in die Richtung, die die Verlustfunktion am meisten reduziert (die Richtung, in der der Gradient maximal ist) **. Es wird auch als Gradientenabstiegsmethode bezeichnet. Als Verfahren,

  1. Geben Sie alle Eingabedaten in das neuronale Netzwerk ein und lassen Sie es berechnen
  2. Berechnen Sie den Fehler mit der ** Verlustfunktion ** wie ** Quadratfehler ** oder ** Kreuzentropiefehler **
  3. Berechnen Sie das partielle Differential der Verlustfunktion für jeden Parameter unter Verwendung des ** Kettengesetzes **
  4. ** Parameter nach bestimmten Regeln aktualisieren **
  5. Wiederholen Sie die Schritte 1 bis 4, bis die ** Änderung ** des Verlustfunktionswerts klein genug ist.

Die Kettenregel wird in [hier] kurz erwähnt (https://qiita.com/kuroitu/items/221e8c477ffdd0774b6b#%E9%80%A3%E9%8E%96%E5%BE%8B).

** Eine bestimmte Regel ** ist die Essenz der Gradientenabstiegsmethode. Die Leistung wird durch diesen Teil bestimmt. Leider gibt es (zumindest vorerst) nicht für alle Probleme die beste Methode für den Gradientenabstieg. Die Parameteraktualisierungsformel gilt für $ t $ zu einem bestimmten Zeitpunkt.

w_{t+1} = w_t - \eta \nabla_{w_t} \mathcal{L}(w_t)

Es kann ausgedrückt werden als Wenn für jedes Parameterelement ausgedrückt

w_{t+1}^{(i)} = w_t^{(i)} - \eta \nabla_{w_{t}^{(i)}} \mathcal{L}(w_t)

Und so weiter (natürlich sind die Parameterdimensionen normalerweise mehrdimensional). Zur Erklärung des Symbols

ist. Die Parameteraktualisierung gemäß dieser Aktualisierungsformel wird wiederholt, bis sich die Verlustfunktion kaum ändert. Lernen gilt als abgeschlossen, wenn sich wenig ändert. Beachten Sie, dass dies nicht "bis der Wert der Verlustfunktion klein genug ist" ** ist. Zum Beispiel ist es möglich, dass das Lernen aufgrund von Faktoren wie unzureichenden Parametern nicht konvergiert, so dass ** "bis das Ausmaß der Änderung ausreichend klein ist" ** Lernen durchgeführt wird.

Was ist die stochastische Gradientenabstiegsmethode?

Bevor wir die stochastische Gradientenabstiegsmethode diskutieren, wollen wir zunächst die Nachteile der steilsten Abstiegsmethode vorstellen.

Der Nachteil der Methode mit dem steilsten Abstieg besteht darin, dass ** die Parameter unter Verwendung aller Eingabedaten ** aktualisiert werden. Das liegt daran, dass Sie mit dieser Methode ** nicht mehr herauskommen können, wenn Sie den Mindestwert ** erreichen. Details finden Sie unter hier. Ich werde es also weglassen, aber kurz gesagt, wenn Sie lernen, alle Daten gleichzeitig zu verwenden, werden Sie leicht in eine solche Situation geraten. Übrigens, wenn die Parameterdimension 2 oder mehr wird, erscheint ein problematischerer Punkt namens ** Sattelpunkt **. Der Sattelpunkt ist "ein Punkt, der ein Maximalwert für einen Parameter, aber ein Minimalwert für einen anderen Parameter ist", was sehr problematisch ist. Dies liegt daran, dass bekanntlich der Sattelpunkt exponentiell ansteigt, wenn die Parameterdimension mehrdimensional wird. Dies wird als ** Dimensional Curse ** bezeichnet. Da dieser Sattelpunkt jedoch einen Gradienten von $ 0 $ aufweist, wird das Lernen nicht fortgesetzt. Darüber hinaus ist es mit zunehmender Parameterdimension viel wahrscheinlicher, dass sie als die minimalen und maximalen Punkte angezeigt wird, sodass sie sich nicht von der Situation unterscheidet, in der es überall "Fallstricke" gibt. Darüber hinaus ist die Parameterdimension im Grunde die Gesamtzahl der Gewichte $ w $ und Bias $ b $, sodass sie schnell zu einer lächerlichen Zahl wird. </ font>

Leises Gespräch ...

In jedem Fall sind die Lernregeln sehr heikle und sehr komplizierte Probleme. Kommen wir zurück zum Thema. Der Nachteil der Methode mit dem steilsten Abstieg besteht darin, dass alle Eingabedaten verwendet werden. Es scheint also, dass dies gelöst werden kann, indem dies einzeln durchgeführt wird. Daher wird das Verfahren zum Aktualisieren von Parametern unter Verwendung von Eingabedaten nacheinander als ** stochastisches Gradientenabstiegsverfahren ** bezeichnet. Außerdem wird "eine Lernmethode, die Parameter für jede Eingabedaten nacheinander aktualisiert" auf diese Weise als "Online-Lernen" bezeichnet. Da die Parameter für jede Eingabedaten nacheinander aktualisiert werden, scheint es, dass ein genaueres Modell erstellt werden kann. Übrigens ist es wahrscheinlich, dass Zufallszahlen verwendet werden, um Daten einzeln aus allen Eingabedaten zu extrahieren.

Obwohl es sich um eine solche probabilistische Gradientenabstiegsmethode handelt, hat sie den Nachteil, dass sie nicht parallelisiert werden kann **. Es ist eine Verschwendung, nicht vom Fortschritt der Parallelisierungsverarbeitung wie GPUs in der heutigen Zeit profitieren zu können. Als Ergebnis von wurde eine Methode namens ** Mini-Batch-Lernen ** entwickelt. Die Idee ist einfach: Aktualisieren Sie die Parameter mit einer bestimmten Anzahl von Eingabedaten gleichzeitig (wahrscheinlich $ 16 $ oder $ 32 $). Wenn dies der Fall ist, kann es parallelisiert werden. </ font>

Die Verwendung von Mini-Batch-Lernen hat jedoch den Nachteil, dass ** "in scharfen Tälern vibriert" **. Es scheint, dass der einzige Weg, dies zu überwinden, darin besteht, es den Lernregeln selbst zu überlassen.

SGD Erstens ist die grundlegendste Gradientenabstiegsmethode. SGD: Der stochastische Gradientenabstieg wird im Japanischen als probabilistische Gradientenabstiegsmethode übersetzt. Mit anderen Worten, es ist die Lernregel, die ich zuvor erwähnt habe. Als mathematischer Ausdruck ist es dasselbe wie die Methode mit dem steilsten Abstieg.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  \Delta w_t &= - \eta g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

In der -Implementierung wird $ g_t $ nach dem Kettengesetz als fließender Fehler (Gradient) berechnet (TensorFlow sollte durch automatische Differenzierung erfolgen?). Sie müssen sich also keine Sorgen um $ g_t $ machen. $ \ Delta w_t $ ist die Anzahl der Aktualisierungen in diesem Zeitschritt. Multiplizieren Sie einfach den Gradienten $ g_t $ mit der Lernrate $ \ eta $. Der Grund dafür, dass es wie $ \ Delta w_t = - \ eta g_t $ negativ wird, besteht darin, es in die letzte Formel einzufügen. Es gibt keine weitere Bedeutung. Wenn Sie also $ w_ {t + 1} = w_t- \ Delta w_t $ setzen, können Sie $ \ Delta w_t = \ eta g_t $ verwenden. </ font>

Der Wert des Hyperparameters ist

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}

ist.

Implementierungsbeispiel

optimizers.py


class SGD(Optimizer):
    def __init__(self, eta=1e-2, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        dw = -self.eta*grad_w
        db = -self.eta*grad_b
        return dw, db

MomentumSGD Ein einfaches SGD hat eine langsame Konvergenz und ist anfällig für Probleme wie Vibrationen und Sattelpunkte. Daher muss es verbessert werden. Eine Möglichkeit, dies zu tun, besteht darin, ** Momentum ** zu berücksichtigen. Moment ist kurz Trägheit. Vibrationen werden unter Verwendung der Informationen des vorherigen Gradienten unterdrückt.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  \Delta w_t &= \mu \Delta w_{t-1} - (1 - \mu) \eta g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Es gibt einige Schwankungen in der Notation, abhängig davon, wo $ \ eta $ multipliziert wird und ob $ 1- \ mu $ mit dem zweiten Term auf der rechten Seite der zweiten Gleichung multipliziert wird. Nur ein kurzer Blick

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  \Delta w_t &= \mu \Delta w_{t-1} - \eta g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Oder

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  \Delta w_t &= \mu \Delta w_{t-1} - g_t \\
  w_{t+1} &= w_t + \eta \Delta w_t
\end{align}

Da war auch. In diesem Artikel wird es durch die erste Formel implementiert. Die Schwankung der Notation hier kann durch entsprechende Änderung der Hyperparameter $ \ mu $ und $ \ eta $ in einen äquivalenten Ausdruck umgewandelt werden. Machen Sie sich also keine Sorgen. </ font>

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}
\mu mu 0.9
\Delta w_0 dw, db 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class MSGD(Optimizer):
    def __init__(self, eta=1e-2, mu=0.9, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.mu = mu

        #Halten Sie den Wert des vorherigen Schritts
        self.dw = 0
        self.db = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
        db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
        
        #Das Zuweisen in der Ansicht anstelle der Kopie erfolgt, weil diese Werte verwendet werden können
        #Dies liegt daran, dass es nicht geändert wird.
        self.dw = dw
        self.db = db
        
        return dw, db

NAG NAG: Nesterovs Accelerated Gradient-Methode ist eine Methode, die MSGD modifiziert, um die Beschleunigung zur Konvergenz zu beschleunigen. Durch Durchführen der Gradientenberechnung unter Verwendung des vorherigen ** Aktualisierungsbetrags ** ist es möglich, das Ziel in einem Zeitschritt grob zu schätzen.

** Vielleicht [diese Formel](https://qiita.com/ZoneTsuyoshi/items/8ef6fa1e154d176e25b8#%E3%83%8D%E3%82%B9%E3%83%86%E3%83%AD%E3 % 83% 95% E3% 81% AE% E5% 8A% A0% E9% 80% 9F% E6% B3% 95nag-1983) Falsch. ** ** **

Wahrscheinlich die folgende Formel.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t+\mu \Delta w_{t-1}) \\
  \Delta w_t &= \mu \Delta w_{t-1} - (1-\mu)\eta g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Außerdem [hier](https://qiita.com/omiita/items/1735c1d048fe5f611f80#54-%E3%83%A2%E3%83%BC%E3%83%A1%E3%83%B3%E3% 82% BF% E3% 83% A0% E3% 81% AE% E6% 94% B9% E8% 89% AF% E7% 89% 88-nag)

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t - \mu \Delta w_{t-1}) \\
  \Delta w_t &= \mu \Delta w_{t-1} + (1-\mu)g_t \\
  w_t &= w_{t-1} - \eta \Delta w_t
\end{align}

(Die Notation entspricht der dieses Artikels). Wir machen das Gleiche und haben beide implementiert und ausprobiert, aber sie haben gleich funktioniert, also sollte es kein Problem sein.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}
\mu mu 0.9
\Delta w_0 dw, db 0

Es wird gesagt.

Implementierungsbeispiel

optimizers.py


class NAG(Optimizer):
    def __init__(self, eta=1e-2, mu=0.9, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.mu = mu
        
        #Enthält den Wert des vorherigen Schritts
        self.dw = 0
        self.db = 0
    
    
    def update(self, grad_w, grad_b, w=0, b=0, df=None, *args, **kwds):
        grad_w = df(w + self.mu*self.dw)
        grad_b = df(b + self.mu*self.db)
        
        dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
        db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
        
        #Das Zuweisen in der Ansicht anstelle des Kopierens liegt daran, dass diese Werte verwendet werden können
        #Dies liegt daran, dass es nicht geändert wird.
        self.dw = dw
        self.db = db
        
        return dw, db

Es scheint schwierig zu sein, es beim tiefen Lernen zu verwenden, da es notwendig ist, den Gradienten, der in der Kettenregel geflossen ist, erneut zu fließen ... Ich frage mich, wie es geht ...

AdaGrad Da MSGD keine Informationen zur Konvergenzrichtung verwendet hat, besteht das Problem, dass die Konvergenz in Richtung eines sanften Gradienten langsam ist. AdaGrad ist ein Pionier bei der Anpassung der Lernrate für jede Dimension, um dieses Problem zu lösen.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L}(w_t) \\
  \Delta w_t &= - \cfrac{\eta}{\displaystyle\sqrt{\sum_{\tau=1}^{t}{g_{\tau}^2}}} g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

In Bezug auf die Implementierung die Summe der Quadrate der zweiten Gleichung

\begin{align}
  gw_t = gw_{t-1} + g_t^2
\end{align}

Es spart Speicher und Berechnung, indem es so gehalten wird.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-3=10^{-3}
gw_0 gw, gb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class AdaGrad(Optimizer):
    def __init__(self, eta=1e-3, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta

        #Halten Sie den Wert des vorherigen Schritts
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.gw += grad_w*grad_w
        self.gb += grad_b*grad_b
        
        dw = -self.eta*grad_w/np.sqrt(self.gw)
        db = -self.eta*grad_b/np.sqrt(self.gb)

        return dw, db

RMSprop In AdaGrad gibt es einen steilen Hang und dann einen sanften Hang, und selbst wenn es danach ein anderes Tal gibt, steigt es nicht viel ins Tal ab. Der Grund ist, dass sich das Quadrat des Gradienten weiter ansammelt und die Lernrate weiter abnimmt. RMSprop hat dies verbessert. Indem wir den Einfluss des Gradienten mit dem Koeffizienten $ \ rho $ "vergessen", lösen wir das Problem, dass die Lernrate weiter abnimmt.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  v_t &= \rho v_{t-1} + (1-\rho)g_t^2 \\
  \Delta w_t &= - \cfrac{\eta}{\sqrt{v_t + \varepsilon}}g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Bei der Implementierung die zweite Formel

\begin{align}
  v_t &= \rho v_{t-1} + (1-\rho)g_t^2 \\
\Leftrightarrow
  v_t &= (v_{t-1} - v_{t-1}) + \rho v_{t-1} + (1-\rho)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} - (1-\rho)v_{t-1} + (1-\rho)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\rho)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}
\rho rho 0.99
\varepsilon eps 1e-8=10^{-8}
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class RMSprop(Optimizer):
    def __init__(self, eta=1e-2, rho=0.99, eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.rho = rho
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -self.eta*grad_w/np.sqrt(self.vw+self.eps)
        db = -self.eta*grad_b/np.sqrt(self.vb+self.eps)
        
        return dw, db

AdaDelta RMSprop ist tatsächlich physikalisch falsch. Einfach ausgedrückt, die Abmessungen beider Seiten stimmen nicht überein. Ich möchte sagen, dass es nicht wirklich wichtig ist, weil ich kein physisches Problem löse ... aber es gibt ein paar Probleme damit. Schließlich muss der Hyperparameter $ \ eta $ für jedes Problem angepasst werden. Zum Beispiel

\begin{align}
  f(x) &= x^2 \\
  g(x) &= 10x^2
\end{align}

Ziehen Sie in Betracht, jede dieser beiden Funktionen anzupassen. Wenn zu diesem Zeitpunkt die Lernregeln verwendet werden, deren Dimensionen nicht übereinstimmen, muss die Lernrate für jede Funktion angepasst werden. Mit anderen Worten, Sie müssen die Hyperparameter anpassen. Dies ist eine ärgerliche und schwierige Aufgabe, und ich kann mir nicht vorstellen, wie viel Zeit verschwendet würde, wenn ich dies für eine groß angelegte Studie tun würde, deren einmaliges Lernen mehrere Tage dauern würde. Die Nichtübereinstimmung der Dimensionen ist also ein ziemliches Problem, und es ist dieses AdaDelta, das es löst.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  v_t &= \rho v_{t-1} + (1-\rho) g_t^2 \\
  \Delta w_t &= - \cfrac{\sqrt{u_{t-1} + \varepsilon}}{\sqrt{v_t + \varepsilon}} g_t \\
  u_t &= \rho u_{t-1} + (1-\rho) (\Delta w_t)^2 \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Formeln 2 und 4 zur Montage

\begin{align}
  v_t &= \rho v_{t-1} + (1-\rho)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\rho)(g_t^2 - v_{t-1}) \\
  u_t &= \rho u_{t-1} + (1-\rho) (\Delta w_t)^2 \\
\Leftrightarrow
  u_t &= u_{t-1} + (1-\rho) \left\{ ( \Delta w_t)^2 - u_{t-1} \right\}
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\rho rho 0.95
\varepsilon eps 1e-6=10^{-6}
v_0 vw, vb 0
u_0 uw, ub 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class AdaDelta(Optimizer):
    def __init__(self, rho=0.95, eps=1e-6, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.rho = rho
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.vw = 0
        self.vb = 0
        self.uw = 0
        self.ub = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -grad_w*np.sqrt(self.uw+self.eps)/np.sqrt(self.vw+self.eps)
        db = -grad_b*np.sqrt(self.ub+self.eps)/np.sqrt(self.vb+self.eps)
        
        self.uw += (1-self.rho)*(dw**2 - self.uw)
        self.ub += (1-self.rho)*(db**2 - self.ub)
        
        return dw, db

Adam Unnötig zu sagen, es ist der berühmte Algorithmus Adam. Seitdem wurden verschiedene Verbesserungen vorgenommen, aber es scheint immer noch aktiv zu sein. Seine Stärke ist schließlich seine hohe Vielseitigkeit. Es gibt jedoch keinen Unterschied in der verbesserten Version von RMSprop, sodass auch das Problem besteht, dass die Dimensionen nicht tatsächlich übereinstimmen. Trotzdem wird im Vergleich zu anderen Lernregeln angenommen, dass das Dimensionsproblem aufgrund der Eigenschaft des ** Vergessens von Gradienteninformationen ** gelindert wird.

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  m_t &= \beta_1 m_{t-1} + (1- \beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1- \beta_2) g_t^2 \\
  \alpha_t &= \alpha_0 \cfrac{\sqrt{1-\beta_2^t}}{1- \beta_1^2} \\
  \Delta w_t &= - \alpha_t \cfrac{m_t}{\sqrt{v_t + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Die obige Formel verwendet den verbesserten Code im Text des Originalartikels. Wenn es eine Formel ist, wie sie ist

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  m_t &= \beta_1 m_{t-1} + (1- \beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1- \beta_2) g_t^2 \\
  \hat{m}_t &= \cfrac{m_t}{1-\beta_1^t} \\
  \hat{v}_t &= \cfrac{v_t}{1-\beta_2^t} \\
  \Delta w_t &= - \alpha_0 \cfrac{\hat{m}_t}{\sqrt{\hat{v}_t + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

nicht wahr.

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \beta_1 m_{t-1} + (1-\beta_1)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\beta_1)(g_t - m_{t-1}) \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\beta_2)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\alpha_0 alpha 1e-3=10^{-3}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
\varepsilon eps 1e-8=10^{-8}
m_0 mw, mb 0
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class Adam(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        alpha_t = self.alpha*np.sqrt(1-self.beta2**t)/(1-self.beta1**t)
        
        dw = -alpha_t*self.mw/(np.sqrt(self.vw+self.eps))
        db = -alpha_t*self.mb/(np.sqrt(self.vb+self.eps))
        
        return dw, db

RSMpropGraves Dies ist eine verbesserte Version von RMSprop, die von Graves entwickelt wurde. Es scheint, dass es im Bereich der handschriftlichen Zeichenerkennung verwendet wird.

\begin{align}
  g_t &= \nabla_{w_t} (w_t) \\
  m_t &= \rho m_{t-1} + (1-\rho) g_t \\
  v_t &= \rho v_{t-1} + (1-\rho) g_t^2 \\
  \Delta w_t &= - \eta \cfrac{g_t}{\sqrt{v_t - m_t^2 + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \rho m_{t-1} + (1-\rho)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\rho)(g_t - m_{t-1}) \\
  v_t &= \rho v_{t-1} + (1-\rho)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\rho)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-4=10^{-4}
\rho rho 0.95
\varepsilon eps 1e-4=10^{-4}
m_0 mw, mb 0
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class RMSpropGraves(Optimizer):
    def __init__(self, eta=1e-4, rho=0.95, eps=1e-4, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.rho = rho
        self.eps = eps

        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self,grad_w, grad_b, *args, **kwds):
        self.mw += (1-self.rho)*(grad_w - self.mw)
        self.mb += (1-self.rho)*(grad_b - self.mb)
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -self.eta*grad_w/np.sqrt(self.vw - self.mw**2 + self.eps)
        db = -self.eta*grad_b/np.sqrt(self.vb - self.mb**2 + self.eps)
        
        return dw, db

SMORMS3 Dies ist auch eine Verbesserung von RMSprop. Es scheint, dass das Problem der Dimension durch die Krümmung und die LeCun-Methode überwunden wurde. Ich bin nicht sicher, was die LeCun-Methode ist, aber es scheint, dass LeCun eine Technik für die Backpropagation-Methode befürwortet und ein Modell namens LeNet ankündigt.

\begin{align}
  g_t &= \nabla_{w_t}(w_t) \\
  s_t &= 1 + (1-\zeta_{t-1} s_{t-1}) \\
  \rho_t &= \cfrac{1}{s_t + 1} \\
  m_t &= \rho_t m_{t-1} + (1-\rho_t) g_t \\
  v_t &= \rho_t v_{t-1} + (1-\rho_t) g_t^2 \\
  \zeta_t &= \cfrac{m_t^2}{v_t + \varepsilon} \\
  \Delta w_t &= \cfrac{\textrm{min}\{ \eta, \zeta_t \}}{\sqrt{v_t + \varepsilon}} g_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Formeln 4 und 5 zur Montage

\begin{align}
  m_t &= \rho_t m_{t-1} + (1-\rho_t)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\rho_t)(g_t - m_{t-1}) \\
  v_t &= \rho_t v_{t-1} + (1-\rho_t)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\rho_t)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-3=10^{-3}
\varepsilon eps 1e-8=10^{-8}
\zeta_0 zetaw, zetab 0
s_t sw, sb 0
m_0 mw, mb 0
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class SMORMS3(Optimizer):
    def __init__(self, eta=1e-3, eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.zetaw = 0
        self.zetab = 0
        self.sw = 0
        self.sb = 0
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.sw = 1 + (1 - self.zetaw*self.sw)
        self.sb = 1 + (1 - self.zetab*self.sb)
        rhow = 1/(1+self.sw)
        rhob = 1/(1+self.sb)
        
        self.mw += (1-rhow)*(grad_w - self.mw)
        self.mb += (1-rhob)*(grad_b - self.mb)
        self.vw += (1-rhow)*(grad_w**2 - self.vw)
        self.vb += (1-rhob)*(grad_b**2 - self.vb)
        
        self.zetaw = self.mw**2 / (self.vw + self.eps)
        self.zetaw = self.mb**2 / (self.vb + self.eps)
        
        dw = -grad_w*(np.minimum(self.eta, self.zetaw)
                      /np.sqrt(self.vw + self.eps))
        db = -grad_b*(np.minimum(self.eta, self.zetab)
                      /np.sqrt(self.vb + self.eps))
        
        return dw, db

AdaMax Es ist eine dimensionslose Version von Adam.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \beta_1 m_{t-1} + (1-\beta_1) g_t \\
  u_t &= \textrm{max}\left\{ \beta_2 u_{t-1}, |g_t| \right\} \\
  \Delta w_t &= - \cfrac{\alpha}{1-\beta_1^t}\cfrac{m_t}{u_t} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Die zweite Formel für die Montage

\begin{align}
  m_t &= \rho_t m_{t-1} + (1-\rho_t)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\rho_t)(g_t - m_{t-1}) \end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\alpha alpha 2e-3=2 \times 10^{-3}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
m_0 mw, mb 0
u_0 uw, ub 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class AdaMax(Optimizer):
    def __init__(self, alpha=2e-3, beta1=0.9, beta2=0.999,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.uw = 0
        self.ub = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.uw = np.maximum(self.beta2*self.uw, np.abs(grad_w))
        self.ub = np.maximum(self.beta2*self.ub, np.abs(grad_b))
        
        alpha_t = self.alpha/(1 - self.beta1**t)
        
        dw = -alpha_t*self.mw/self.uw
        db = -alpha_t*self.mb/self.ub
        
        return dw, db
Nadam Es ist eine Kombination von NAG zu Adam.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \mu m_{t-1} + (1-\mu) g_t \\
  v_t &= \nu v_{t-1} + (1-\nu) g_t^2 \\
  \hat{m}_t &= \cfrac{\mu}{1 - \mu^{t+1}} m_t + \cfrac{1-\mu}{1-\mu^t} g_t \\
  \hat{v}_t &= \cfrac{\nu}{1-\nu^t} v_t \\
  \Delta w_t &= - \alpha \cfrac{\hat{m}_t}{\sqrt{\hat{v}_t + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Tatsächlich sind $ \ alpha $ und $ \ mu $ zeitschrittabhängige Parameter, wenn true </ font>

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \mu_t m_{t-1} + (1-\mu_t) g_t \\
  v_t &= \nu v_{t-1} + (1-\nu) g_t^2 \\
  \hat{m}_t &= \cfrac{\mu_t}{1 - \displaystyle\prod_{\tau=1}^{t+1}{\mu_{\tau}}} m_t + \cfrac{1-\mu_t}{1-\displaystyle\prod_{\tau=1}^{t}{\mu_{\tau}}} g_t \\
  \hat{v}_t &= \cfrac{\nu}{1-\nu^t} v_t \\
  \Delta w_t &= - \alpha_t \cfrac{\hat{m}_t}{\sqrt{\hat{v}_t + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Es wird durch die Formel ausgedrückt. Es scheint jedoch, dass es in den meisten Fällen behoben ist, so dass ich es vorerst behoben habe.

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \mu m_{t-1} + (1-\mu)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\mu)(g_t - m_{t-1}) \\
  v_t &= \nu v_{t-1} + (1-\nu)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\nu)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\alpha alpha 2e-3=2 \times 10^{-3}
\mu mu 0.975
\nu nu 0.999
\varepsilon eps 1e-8=10^{-8}
m_0 mw, mb 0
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class Nadam(Optimizer):
    def __init__(self, alpha=2e-3, mu=0.975, nu=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.mu = mu
        self.nu = nu
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.mu)*(grad_w - self.mw)
        self.mb += (1-self.mu)*(grad_b - self.mb)
        self.vw += (1-self.nu)*(grad_w**2 - self.vw)
        self.vb += (1-self.nu)*(grad_b**2 - self.vb)
        
        mhatw = (self.mu*self.mw/(1-self.mu**(t+1)) 
                 + (1-self.mu)*grad_w/(1-self.mu**t))
        mhatb = (self.mu*self.mb/(1-self.mu**(t+1)) 
                 + (1-self.mu)*grad_b/(1-self.mu**t))
        vhatw = self.nu*self.vw/(1-self.nu**t)
        vhatb = self.nu*self.vb/(1-self.nu**t)
        
        dw = -self.alpha*mhatw/np.sqrt(vhatw + self.eps)
        db = -self.alpha*mhatb/np.sqrt(vhatb + self.eps)
        
        return dw, db

Eve Es ist eine verbesserte Version von Adam. Es beschleunigt das Lernen in flachen Bereichen (Bereiche mit einem Gefälle von nahezu Null). Der Name ist in Mode, nicht wahr? Adam und Eva

\begin{align}
  g_t &= \nabla_{w_t}\mathcal{L}(w_t) \\
  m_t &= \beta_1 m_{t-1} + (1-\beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \\
  \hat{m}_t &= \cfrac{m_t}{1-\beta_1^t} \\
  \hat{v}_t &= \cfrac{v_t}{1-\beta_2^t} \\
  \textrm{if} \quad &t \gt 1 \\
  & d_t = \cfrac{\left| f_t - f_{t-1} \right|}{\textrm{min} \left\{ f_t, f_{t-1} \right\} - f^{\star}} \\
  & \hat{d}_t = \textrm{clip}\left( d_t, \left[ \frac{1}{c}, c \right] \right) \\
  & \tilde{d}_t = \beta_3 \tilde{d}_{t-1} + (1-\beta_3)\hat{d}_t \\
  \textrm{else} \\
  & \tilde{d}_t = 1 \\
  \textrm{end} &\textrm{if} \\
  \Delta w_t &= - \cfrac{\alpha}{\tilde{d}_t} \cfrac{\hat{m}_t}{\sqrt{\hat{v}_t} + \varepsilon} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Es ist zu schwer ... Ich werde ein paar Anmerkungen zu den Symbolen in der Formel hinzufügen. $ f_t $ ist der Wert der Zielfunktion $ f $ zum Zeitschritt $ t $. $ f ^ {\ star} $ ist der Mindestwert der Zielfunktion. Laut Originalarbeit ist jedoch $ 0 $ ausreichend, wenn der quadratische Fehler oder der Kreuzentropiefehler für die Verlustfunktion verwendet wird. $ \ textrm {clip} $ ist eine Funktion, die den Minimal- und Maximalwert festlegt und die Werte darunter / darüber auf den eingestellten Wert rundet. </ font>

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \mu m_{t-1} + (1-\mu)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\mu)(g_t - m_{t-1}) \\
  v_t &= \nu v_{t-1} + (1-\nu)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\nu)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert.

Hyperparameter

Symbol Notation im Code Wert
\alpha alpha 1e-3=10^{-3}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
\beta_3 beta3 0.999
c c 10
\varepsilon eps 1e-8=10^{-8}
f^{\star} fstar 0
m_0 mw, mb 0
v_0 vw, vb 0
f_t fw, fb \infty
\tilde{d}_0 dtilde_w, dtilde_b 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class Eve(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, beta3=0.999,
                 c=10, eps=1e-8, fstar=0, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta
        self.beta3 = beta3
        self.c = c
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.fw = np.inf
        self.fb = np.inf
        self.fstar = fstar
        self.dtilde_w = 0
        self.dtilde_b = 0
    
    
    def update(self, grad_w, grad_b, t=1, fw=1, fb=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        mhatw = self.mw/(1 - self.beta1**t)
        mhatb = self.mb/(1 - self.beta1**t)
        vhatw = self.vw/(1 - self.beta2**t)
        vhatb = self.vb/(1 - self.beta2**t)
        
        if t > 1:
            d_w = (np.abs(fw-self.fstar)
                    /(np.minimum(fw, self.fw) - self.fstar))
            d_b = (np.abs(fb-self.fstar)
                    /(np.minimum(fb, self.fb) - self.fstar))
            dhat_w = np.clip(d_w, 1/self.c, self.c)
            dhat_b = np.clip(d_b, 1/self.c, self.c)
            self.dtilde_w += (1 - self.beta3)*(dhat_w - self.dtilde_w)
            self.dtilde_b += (1 - self.beta3)*(dhat_b - self.dtilde_b)
        else:
            self.dtilde_w = 1
            self.dtilde_b = 1
        
        self.fw = fw
        self.fb = fb
        
        dw = -(self.alpha*mhatw
               /(self.dtilde_w*(np.sqrt(vhatw) + self.eps)))
        db = -(self.alpha*mhatb
               /(self.dtilde_b*(np.sqrt(vhatb) + self.eps)))
        
        return dw, db

Santa-E Es enthält die Markov-Ketten-Monte-Carlo-Methode (MCMC) in Adam und RMSprop. Unter ihnen scheint Santa-E den Eulertyp zu verwenden? Originalarbeit wurde zum Zeitpunkt der Implementierung überflogen, aber ehrlich gesagt verstehe ich das überhaupt nicht ... lol Aber selbst wenn Sie nicht verstehen, können Sie den Code schreiben, wenn Sie nach der Formel entwerfen! Also habe ich mein Bestes gegeben. Oder besser gesagt, ich war in Schwierigkeiten, weil ich mir nicht die Anfangswerte des Originalpapiers gegeben habe ... Ich habe einen Blick auf die Implementierungen von Pytorch, Keras, Chainer usw. geworfen (alle waren unzusammenhängend ...) Ich verwende nur die Hyperparameter, die ich für notwendig halte.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L}(w_t) \\
  v_t &= \sigma v_{t-1} + \cfrac{1-\sigma}{N^2} g_t^2 \\
  \mathcal{G}_t &= \cfrac{1}{\sqrt{\lambda + \sqrt{v_t}}} \\
  \textrm{if} \quad &t \lt burnin \\
  & \alpha_t = \alpha_{t-1} + (u_t^2 - \cfrac{\eta}{\beta_t}) \\
  & u_t = \cfrac{\eta}{\beta_t}\cfrac{1-\frac{\mathcal{G}_{t-1}}{\mathcal{G}_t}}{u_{t-1}} + \sqrt{\cfrac{2\eta}{\beta_t}\mathcal{G}_{t-1}} \otimes \zeta_t \\
  \textrm{else} \\
  & \alpha_t = \alpha_{t-1} \\
  & u_t = 0 \\
  \textrm{end} &\textrm{if} \\
  u_t &= u_t + (1-\alpha_t) \otimes u_{t-1} - \eta \mathcal{G}_t \otimes g_t \\
  \Delta w_t &= \mathcal{G}_t \otimes u_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Es gibt viele Elementprodukte $ \ otimes $, aber machen Sie sich darüber keine Sorgen. Es ist das Matrixprodukt, bei dem Sie bei der normalen Implementierung vorsichtig sein sollten.

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}
\sigma sigma 0.95
\lambda lambda_ 1e-8=10^{-8}
burnin burnin 100
C C 5
\alpha alpha_w, alpha_b \sqrt{\eta}C
\zeta_t Generieren Sie Zufallszahlen direkt, ohne sie in Variablen zu speichern \mathcal{N} (0, 1)
v_0 vw, vb 0
\mathcal{G}_t gw, gb 0
u_0 uw, ub \sqrt{\eta} \mathcal{N} (0, 1)

Es ist geworden. $ \ mathcal {N} (0, 1) $ ist eine Zufallszahl gemäß der Standardnormalverteilung. Außerdem wird $ \ beta $ im Code durch "anne_func" und "anne_rate" bestimmt. Das Originalpapier gibt dies nicht an, aber als Bedingung muss $ t \ to \ infty $ $ \ beta_t \ to \ infty $ sein. Anscheinend Keras, [Pytorch](https://github.com/ReyhaneAskari/santa/blob/master/santa. py), Chainer für $ \ beta_t = \ sqrt {t} $ Sie rechnen. Aber ist es in Ordnung, wenn die Bedingungen erfüllt sind? </ font>

Implementierungsbeispiel

optimizers.py


class SantaE(Optimizer):
    def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
                 anne_func=lambda t, n: t**n, anne_rate=0.5,
                 burnin=100, C=5,
                 *args, **kwds):
        """
        Args:
            eta: Learning rate
            sigma: Maybe in other cases;
                    'rho' in RMSprop, AdaDelta, RMSpropGraves.
                    'rhow' or 'rhob' in SMORMS3.
                    'beta2' in Adam, Eve.
                    'nu' in Nadam.
                   To use calculation 'v'.
            lambda_: Named 'eps'(ε) in other cases.
            anne_func: Annealing function.
                       To use calculation 'beta' at each timestep.
                       Default is 'timestep'**'annealing rate'.
                       The calculated value should be towards infinity
                       as 't' increases.
            anne_rate: Annealing rate.
                       To use calculation 'beta' at each timestep.
                       The second Argument of 'anne_func'.
            burnin: Swith exploration and refinement.
                    This should be specified by users.
            C: To calculate first 'alpha'.
        """
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.sigma = sigma
        self.lambda_ = lambda_
        self.anne_func = anne_func
        self.anne_rate = anne_rate
        self.burnin = burnin
        
        # Keep one step before and Initialize.
        self.alpha_w = np.sqrt(eta)*C
        self.alpha_b = np.sqrt(eta)*C
        self.vw = 0
        self.vb = 0
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, t=1, N=200, *args, **kwds):
        try:
            shape_w = grad_w.shape
        except:
            shape_w = (1, )
        try:
            shape_b = grad_b.shape
        except:
            shape_b = (1, )
        
        if t == 1:
            # Initialize uw, ub.
            self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
            self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
        
        self.vw = (self.sigma*self.vw 
                   + grad_w*grad_w * (1 - self.sigma) / N**2)
        self.vb = (self.sigma*self.vb 
                   + grad_b*grad_b * (1 - self.sigma) / N**2)
        
        gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
        gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
        
        beta = self.anne_func(t, self.anne_rate)
        if t < self.burnin:
            # Exploration.
            self.alpha_w += self.uw*self.uw - self.eta/beta
            self.alpha_b += self.ub*self.ub - self.eta/beta
            
            uw = (self.eta/beta * (1 - self.gw/gw)/self.uw
                  + np.sqrt(2*self.eta/beta * self.gw) 
                  * np.random.randn(*shape_w))
            ub = (self.eta/beta * (1 - self.gb/gb)/self.ub
                  + np.sqrt(2*self.eta/beta * self.gb) 
                  * np.random.randn(*shape_b))
        else:
            # Refinement.
            uw = 0
            ub = 0
        
        uw += (1 - self.alpha_w)*self.uw - self.eta*gw*grad_w
        ub += (1 - self.alpha_b)*self.ub - self.eta*gb*grad_b
        
        # Update values.
        self.uw = uw
        self.ub = ub
        self.gw = gw
        self.gb = gb
        
        dw = gw*uw
        db = gb*ub
        
        return dw, db

Santa-SSS Dies ist auch der im selben Artikel beschriebene Algorithmus. Ich verstehe überhaupt nicht, aber ich kann es implementieren. Was ist Lehman Informationsgeometrie ...

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L}(w_t) \\
  v_t &= \sigma v_{t-1} + \cfrac{1-\sigma}{N^2} g_t^2 \\
  \mathcal{G}_t &= \cfrac{1}{\sqrt{\lambda + \sqrt{v_t}}} \\
  \Delta w_t &= \mathcal{G}_t \otimes u_{t-1} \times 0.5 \\
  \textrm{if} \quad &t \lt burnin \\
  & \alpha_t = \alpha_{t-1} + (u_t^2 - \cfrac{\eta}{\beta_t}) \times 0.5 \\
  & u_t = e^{- 0.5\alpha_t} \otimes u_{t-1} \\
  & u_t = u_t - \mathcal{G}_t \otimes g_t \eta + \sqrt{2\mathcal{G}_t \cfrac{\eta}{\beta_t}} \otimes \zeta_t + \cfrac{\eta}{\beta_t}\cfrac{ 
1 - \frac{\mathcal{G}_{t-1}}{\mathcal{G}_t}}{u_{t-1}} \\
  & u_t = e^{-0.5\alpha_t} \otimes u_t \\
  & \alpha_t = \alpha_t + \left( u_t^2 - \cfrac{\eta}{\beta_t} \right) \times 0.5 \\
  \textrm{else} \\
  & \alpha_t = \alpha_{t-1} \\
  & u_t = e^{-0.5\alpha_t} \otimes u_{t-1} \\
  & u_t = u_t - \mathcal{G}_t \otimes g_t \eta \\
  & u_t = e^{-0.5\alpha_t} \otimes u_t \\
  \textrm{end} &\textrm{if} \\
  \Delta w_t &= \Delta w_t + \mathcal{G}_t \otimes u_t \times 0.5 \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Hyperparameter

Symbol Notation im Code Wert
\eta eta 1e-2=10^{-2}
\sigma sigma 0.95
\lambda lambda_ 1e-8=10^{-8}
burnin burnin 100
C C 5
\alpha alpha_w, alpha_b \sqrt{\eta}C
\zeta_t Generieren Sie Zufallszahlen direkt, ohne sie in Variablen zu speichern \mathcal{N} (0, 1)
v_0 vw, vb 0
\mathcal{G}_t gw, gb 0
u_0 uw, ub \sqrt{\eta} \mathcal{N} (0, 1)

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class SantaSSS(Optimizer):
    def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
                 anne_func=lambda t, n: t**n, anne_rate=0.5,
                 burnin=100, C=5,
                 *args, **kwds):
        """
        Args:
            eta: Learning rate
            sigma: Maybe in other cases;
                    'rho' in RMSprop, AdaDelta, RMSpropGraves.
                    'rhow' or 'rhob' in SMORMS3.
                    'beta2' in Adam, Eve.
                    'nu' in Nadam.
                   To use calculation 'v'.
            lambda_: Named 'eps'(ε) in other cases.
            anne_func: Annealing function.
                       To use calculation 'beta' at each timestep.
                       Default is 'timestep'**'annealing rate'.
                       The calculated value should be towards infinity
                       as 't' increases.
            anne_rate: Annealing rate.
                       To use calculation 'beta' at each timestep.
                       The second Argument of 'anne_func'.
            burnin: Swith exploration and refinement.
                    This should be specified by users.
            C: To calculate first 'alpha'.
        """
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.sigma = sigma
        self.lambda_ = lambda_
        self.anne_func = anne_func
        self.anne_rate = anne_rate
        self.burnin = burnin
        
        # Keep one step before and Initialize.
        self.alpha_w = np.sqrt(eta)*C
        self.alpha_b = np.sqrt(eta)*C
        self.vw = 0
        self.vb = 0
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, t=1, N=200, *args, **kwds):
        try:
            shape_w = grad_w.shape
        except:
            shape_w = (1, )
        try:
            shape_b = grad_b.shape
        except:
            shape_b = (1, )
        
        if t == 1:
            # Initialize uw, ub.
            self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
            self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
        
        self.vw = (self.sigma*self.vw 
                   + grad_w*grad_w * (1 - self.sigma) / N**2)
        self.vb = (self.sigma*self.vb 
                   + grad_b*grad_b * (1 - self.sigma) / N**2)
        
        gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
        gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
        
        dw = 0.5*gw*self.uw
        db = 0.5*gb*self.ub
        
        beta = self.anne_func(t, self.anne_rate)
        if t < self.burnin:
            # Exploration.
            self.alpha_w += (self.uw*self.uw - self.eta/beta)*0.5
            self.alpha_b += (self.ub*self.ub - self.eta/beta)*0.5
            
            uw = np.exp(-0.5*self.alpha_w)*self.uw
            ub = np.exp(-0.5*self.alpha_b)*self.ub
            uw += (-gw*grad_w*self.eta 
                        + np.sqrt(2*self.gw*self.eta/beta)
                        * np.random.randn(*shape_w)
                        + self.eta/beta*(1-self.gw/gw)/self.uw)
            ub += (-gb*grad_b*self.eta 
                        + np.sqrt(2*self.gb*self.eta/beta)
                        * np.random.randn(*shape_b)
                        + self.eta/beta*(1-self.gb/gb)/self.ub)
            uw *= np.exp(-0.5*self.alpha_w)
            ub *= np.exp(-0.5*self.alpha_b)
            
            self.alpha_w += (uw*uw - self.eta/beta)*0.5
            self.alpha_b += (ub*ub - self.eta/beta)*0.5
        else:
            # Refinement.
            uw = np.exp(-0.5*self.alpha_w)*self.uw
            ub = np.exp(-0.5*self.alpha_b)*self.ub
            
            uw -= gw*grad_w*self.eta
            ub -= gb*grad_b*self.eta
            
            uw *= np.exp(-0.5*self.alpha_w)
            ub *= np.exp(-0.5*self.alpha_b)
        
        # Update values.
        self.uw = uw
        self.ub = ub
        self.gw = gw
        self.gb = gb
        
        dw = gw*uw*0.5
        db = gb*ub*0.5
        
        return dw, db

GD by GD Papier Ich habe gelesen ... ich weiß es überhaupt nicht ...

AdaSecant Artikel Lesen ... Ich verstehe die Beziehung zum gleitenden Durchschnitt nicht ...

AMSGrad In RMSprop, AdaDelta, Adam und Nadam wurde der exponentielle Abfall durch den exponentiellen gleitenden Durchschnitt durchgeführt, aber dies ist der Grund. Selbst wenn wichtige Gradienteninformationen fließen, kann diese möglicherweise nicht zur optimalen Lösung konvergieren, da sie sofort vergessen werden. AMS Grad hat dieses Problem gelöst.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \beta_1 m_{t-1} + (1-\beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \\
  \hat{v}_t &= \textrm{max}\{ \hat{v}_{t-1}, v_t \} \\
  \Delta w_t &= - \alpha_t \cfrac{m_t}{\sqrt{\hat{v}_t + \varepsilon}} \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \beta_1 m_{t-1} + (1-\beta_1)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\beta_1)(g_t - m_{t-1}) \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\beta_2)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert. Ist es auch besser, mit $ \ alpha_t = \ frac {\ alpha} {\ sqrt {t}} $ zu rechnen? Es gab eine solche Beschreibung in der Zeitung.

Hyperparameter

Symbol Notation im Code Wert
\alpha_0 alpha 1e-3=10^{-3}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
\varepsilon eps 1e-8=10^{-8}
m_0 mw, mb 0
v_0 vw, vb 0
\hat{v}_0 vhatw, vhatb 0

Es ist geworden. Für den Anfangswert $ \ alpha_0 $ gab es in dem Papier keinen bestimmten Wert, daher Chainer ) Ich habe es mitgebracht.

Implementierungsbeispiel

optimizers.py


class AMSGrad(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.vhatw = 0
        self.vhatb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        self.vhatw = np.maximum(self.vhatw, self.vw)
        self.vhatb = np.maximum(self.vhatb, self.vb)
        
        alpha_t = self.alpha / np.sqrt(t)
        
        dw = - alpha_t * self.mw/np.sqrt(self.vhatw + self.eps)
        db = - alpha_t * self.mb/np.sqrt(self.vhatb + self.eps)
        
        return dw, db

AdaBound AMSGrad unterdrückte das Problem einer unnötig großen Lernrate, löste jedoch nicht das entgegengesetzte Problem einer unnötig kleinen Lernrate. AdaBound und AMSBound haben das "schnelle Lernen von Adam, können aber den Generalisierungsfehler nicht gut unterdrücken" und den "endgültigen Generalisierungsfehler von SGD können reduziert werden, aber bis dahin" Die Kombination der "langsamen" Punkte wurde als Optimierungsmethode vorgeschlagen, die sich am Anfang wie Adam und am Ende wie SGD verhält. AdaBound ist eine verbesserte Version von Adam.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \beta_1 m_{t-1} + (1-\beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \\
  \hat{\eta}_t &= \textrm{clip}\left( \cfrac{\alpha}{\sqrt{v_t}}, \left[ \eta_l(t), \eta_h(t) \right] \right) \\
  \eta_t &= \cfrac{\hat{\eta}_t}{\sqrt{t}} \\
  \Delta w_t &= - \eta_t m_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \beta_1 m_{t-1} + (1-\beta_1)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\beta_1)(g_t - m_{t-1}) \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\beta_2)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert. Wenn die Lernrate als SGD (Lernrate am Ende) $ \ eta $ beträgt,

\begin{align}
  \eta_l(t) &= \eta \left( 1 - \cfrac{1}{(1-\beta_2)t+1} \right) \\
  \eta_u(t) &= \eta \left( 1 - \cfrac{1}{(1-\beta_2)t} \right)
\end{align}

Es scheint, dass es in der Zeitung berechnet wird, also werde ich es (wahrscheinlich) übernehmen. tatsächlich,

  • $ \ eta_l $ ist $ 0 $ bei $ t = 0 $, $ \ eta _- $ bei $ t \ bis \ infty $ </ font>
  • $ \ eta_u $ ist $ \ infty $ für $ t = 0 $, $ \ eta_ + $ für $ t \ bis \ infty $ </ font>
  • $ \ eta_ {\ pm} $ ist die rechte und die linke Grenze </ font>.

Es scheint, dass es sein sollte.

Hyperparameter

Symbol Notation im Code Wert
\alpha_0 alpha 1e-3=10^{-3}
\eta eta 1e-1=10^{-1}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
\varepsilon eps 1e-8=10^{-8}
m_0 mw, mb 0
v_0 vw, vb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class AdaBound(Optimizer):
    def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
                 eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.eta = eta
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        etal = self.eta*(1 - 1/((1-self.beta2)**(t+1)))
        etau = self.eta*(1 + 1/((1-self.beta2)*t))
        
        etahatw_t = np.clip(self.alpha/np.sqrt(self.vw), etal, etau)
        etahatb_t = np.clip(self.alpha/np.sqrt(self.vb), etal, etau)
        
        etaw_t = etahatw_t/np.sqrt(t)
        etab_t = etahatb_t/np.sqrt(t)
        
        dw = - etaw_t*self.mw
        db = - etab_t*self.mb
        
        return dw, db

AMSBound AMSGrad unterdrückte das Problem einer unnötig großen Lernrate, löste jedoch nicht das entgegengesetzte Problem einer unnötig kleinen Lernrate. AdaBound und AMSBound haben das "schnelle Lernen von Adam, können aber den Generalisierungsfehler nicht gut unterdrücken" und den "endgültigen Generalisierungsfehler von SGD können reduziert werden, aber bis dahin" Die Kombination der "langsamen" Punkte wurde als Optimierungsmethode vorgeschlagen, die sich am Anfang wie Adam und am Ende wie SGD verhält. AdaBound ist eine verbesserte Version von Adam.

\begin{align}
  g_t &= \nabla_{w_t} \mathcal{L} (w_t) \\
  m_t &= \beta_1 m_{t-1} + (1-\beta_1) g_t \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \\
  \hat{v}_t &= \textrm{max}(\hat{v}_{t-1}, v_t) \\
  \hat{\eta}_t &= \textrm{clip}\left( \cfrac{\alpha}{\sqrt{v_t}}, \left[ \eta_l(t), \eta_h(t) \right] \right) \\
  \eta_t &= \cfrac{\hat{\eta}_t}{\sqrt{t}} \\
  \Delta w_t &= - \eta_t m_t \\
  w_{t+1} &= w_t + \Delta w_t
\end{align}

Bei der Montage Formel 2 und Formel 3

\begin{align}
  m_t &= \beta_1 m_{t-1} + (1-\beta_1)g_t \\
\Leftrightarrow
  m_t &= m_{t-1} + (1-\beta_1)(g_t - m_{t-1}) \\
  v_t &= \beta_2 v_{t-1} + (1-\beta_2)g_t^2 \\
\Leftrightarrow
  v_t &= v_{t-1} + (1-\beta_2)(g_t^2 - v_{t-1})
\end{align}

Die Formel wird wie folgt transformiert. $ \ eta_l $ und $ \ eta_u $ werden auf die gleiche Weise wie AdaBound berechnet. </ font>

Hyperparameter

Symbol Notation im Code Wert
\alpha_0 alpha 1e-3=10^{-3}
\eta eta 1e-1=10^{-1}
\beta_1 beta1 0.9
\beta_2 beta2 0.999
\varepsilon eps 1e-8=10^{-8}
m_0 mw, mb 0
v_0 vw, vb 0
\hat{v}_0 vhatw, vhatb 0

Es ist geworden.

Implementierungsbeispiel

optimizers.py


class AMSBound(Optimizer):
    def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
                 eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.eta = eta
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.vhatw = 0
        self.vhatb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        self.vhatw = np.maximum(self.vhatw, self.vw)
        self.vhatb = np.maximum(self.vhatb, self.vb)
        
        etal = self.eta*(1 - 1/((1-self.beta2)*t + 1))
        etau = self.eta*(1 + 1/((1-self.beta2)*t + self.eps))
        
        etahatw_t = np.clip(self.alpha/np.sqrt(self.vhatw), etal, etau)
        etahatb_t = np.clip(self.alpha/np.sqrt(self.vhatb), etal, etau)
        
        etaw_t = etahatw_t/np.sqrt(t)
        etab_t = etahatb_t/np.sqrt(t)
        
        dw = - etaw_t*self.mw
        db = - etab_t*self.mb
        
        return dw, db

Beispiel für einen Implementierungscode

Hier ist eine Liste von Codebeispielen. Ich werde auch ein einfaches experimentelles Ergebnis veröffentlichen. In der Zwischenzeit können wir einen Vergleich am Sattelpunkt hinzufügen.

optimizers.py

optimizers.py


import numpy as np


class Optimizer():
    """
Eine Superklasse, die von der Optimierungsmethode geerbt wurde.
    """
    def __init__(self, *args, **kwds):
        pass
    
    
    def update(self, *args, **kwds):
        pass


class SGD(Optimizer):
    def __init__(self, eta=1e-2, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        dw = -self.eta*grad_w
        db = -self.eta*grad_b
        return dw, db


class MSGD(Optimizer):
    def __init__(self, eta=1e-2, mu=0.9, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.mu = mu
        
        #Halten Sie den Wert des vorherigen Schritts
        self.dw = 0
        self.db = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
        db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
        
        #Das Zuweisen in der Ansicht anstelle des Kopierens liegt daran, dass diese Werte verwendet werden können
        #Dies liegt daran, dass es nicht geändert wird.
        self.dw = dw
        self.db = db
        
        return dw, db


class NAG(Optimizer):
    def __init__(self, eta=1e-2, mu=0.9, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.mu = mu
        
        #Enthält den Wert des vorherigen Schritts
        self.dw = 0
        self.db = 0
    
    
    def update(self, grad_w, grad_b, w=0, b=0, df=None, *args, **kwds):
        grad_w = df(w + self.mu*self.dw)
        grad_b = df(b + self.mu*self.db)
        
        dw = self.mu*self.dw - (1-self.mu)*self.eta*grad_w
        db = self.mu*self.db - (1-self.mu)*self.eta*grad_b
        
        #Das Zuweisen in der Ansicht anstelle des Kopierens liegt daran, dass diese Werte verwendet werden können
        #Dies liegt daran, dass es nicht geändert wird.
        self.dw = dw
        self.db = db
        
        return dw, db


class AdaGrad(Optimizer):
    def __init__(self, eta=1e-3, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        
        #Halten Sie den Wert des vorherigen Schritts
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.gw += grad_w*grad_w
        self.gb += grad_b*grad_b
        
        dw = -self.eta*grad_w/np.sqrt(self.gw)
        db = -self.eta*grad_b/np.sqrt(self.gb)

        return dw, db


class RMSprop(Optimizer):
    def __init__(self, eta=1e-2, rho=0.99, eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.rho = rho
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -self.eta*grad_w/np.sqrt(self.vw+self.eps)
        db = -self.eta*grad_b/np.sqrt(self.vb+self.eps)
        
        return dw, db


class AdaDelta(Optimizer):
    def __init__(self, rho=0.95, eps=1e-6, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.rho = rho
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.vw = 0
        self.vb = 0
        self.uw = 0
        self.ub = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -grad_w*np.sqrt(self.uw+self.eps)/np.sqrt(self.vw+self.eps)
        db = -grad_b*np.sqrt(self.ub+self.eps)/np.sqrt(self.vb+self.eps)
        
        self.uw += (1-self.rho)*(dw**2 - self.uw)
        self.ub += (1-self.rho)*(db**2 - self.ub)
        
        return dw, db


class Adam(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        alpha_t = self.alpha*np.sqrt(1-self.beta2**t)/(1-self.beta1**t)
        
        dw = -alpha_t*self.mw/(np.sqrt(self.vw+self.eps))
        db = -alpha_t*self.mb/(np.sqrt(self.vb+self.eps))
        
        return dw, db


class RMSpropGraves(Optimizer):
    def __init__(self, eta=1e-4, rho=0.95, eps=1e-4, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.rho = rho
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self,grad_w, grad_b, *args, **kwds):
        self.mw += (1-self.rho)*(grad_w - self.mw)
        self.mb += (1-self.rho)*(grad_b - self.mb)
        self.vw += (1-self.rho)*(grad_w**2 - self.vw)
        self.vb += (1-self.rho)*(grad_b**2 - self.vb)
        
        dw = -self.eta*grad_w/np.sqrt(self.vw - self.mw**2 + self.eps)
        db = -self.eta*grad_b/np.sqrt(self.vb - self.mb**2 + self.eps)
        
        return dw, db


class SMORMS3(Optimizer):
    def __init__(self, eta=1e-3, eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.zetaw = 0
        self.zetab = 0
        self.sw = 0
        self.sb = 0
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, *args, **kwds):
        self.sw = 1 + (1 - self.zetaw*self.sw)
        self.sb = 1 + (1 - self.zetab*self.sb)
        rhow = 1/(1+self.sw)
        rhob = 1/(1+self.sb)
        
        self.mw += (1-rhow)*(grad_w - self.mw)
        self.mb += (1-rhob)*(grad_b - self.mb)
        self.vw += (1-rhow)*(grad_w**2 - self.vw)
        self.vb += (1-rhob)*(grad_b**2 - self.vb)
        
        self.zetaw = self.mw**2 / (self.vw + self.eps)
        self.zetaw = self.mb**2 / (self.vb + self.eps)
        
        dw = -grad_w*(np.minimum(self.eta, self.zetaw)
                      /np.sqrt(self.vw + self.eps))
        db = -grad_b*(np.minimum(self.eta, self.zetab)
                      /np.sqrt(self.vb + self.eps))
        
        return dw, db


class AdaMax(Optimizer):
    def __init__(self, alpha=2e-3, beta1=0.9, beta2=0.999,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.uw = 0
        self.ub = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.uw = np.maximum(self.beta2*self.uw, np.abs(grad_w))
        self.ub = np.maximum(self.beta2*self.ub, np.abs(grad_b))
        
        alpha_t = self.alpha/(1 - self.beta1**t)
        
        dw = -alpha_t*self.mw/self.uw
        db = -alpha_t*self.mb/self.ub
        
        return dw, db


class Nadam(Optimizer):
    def __init__(self, alpha=2e-3, mu=0.975, nu=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.mu = mu
        self.nu = nu
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.mu)*(grad_w - self.mw)
        self.mb += (1-self.mu)*(grad_b - self.mb)
        self.vw += (1-self.nu)*(grad_w**2 - self.vw)
        self.vb += (1-self.nu)*(grad_b**2 - self.vb)
        
        mhatw = (self.mu*self.mw/(1-self.mu**(t+1)) 
                 + (1-self.mu)*grad_w/(1-self.mu**t))
        mhatb = (self.mu*self.mb/(1-self.mu**(t+1)) 
                 + (1-self.mu)*grad_b/(1-self.mu**t))
        vhatw = self.nu*self.vw/(1-self.nu**t)
        vhatb = self.nu*self.vb/(1-self.nu**t)
        
        dw = -self.alpha*mhatw/np.sqrt(vhatw + self.eps)
        db = -self.alpha*mhatb/np.sqrt(vhatb + self.eps)
        
        return dw, db


class Eve(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, beta3=0.999,
                 c=10, eps=1e-8, fstar=0, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        self.beta3 = beta3
        self.c = c
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.fw = np.inf
        self.fb = np.inf
        self.fstar = fstar
        self.dtilde_w = 0
        self.dtilde_b = 0
    
    
    def update(self, grad_w, grad_b, t=1, fw=1, fb=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        mhatw = self.mw/(1 - self.beta1**t)
        mhatb = self.mb/(1 - self.beta1**t)
        vhatw = self.vw/(1 - self.beta2**t)
        vhatb = self.vb/(1 - self.beta2**t)
        
        if t > 1:
            d_w = (np.abs(fw-self.fstar)
                    /(np.minimum(fw, self.fw) - self.fstar))
            d_b = (np.abs(fb-self.fstar)
                    /(np.minimum(fb, self.fb) - self.fstar))
            dhat_w = np.clip(d_w, 1/self.c, self.c)
            dhat_b = np.clip(d_b, 1/self.c, self.c)
            self.dtilde_w += (1 - self.beta3)*(dhat_w - self.dtilde_w)
            self.dtilde_b += (1 - self.beta3)*(dhat_b - self.dtilde_b)
        else:
            self.dtilde_w = 1
            self.dtilde_b = 1
        
        self.fw = fw
        self.fb = fb
        
        dw = -(self.alpha*mhatw
               /(self.dtilde_w*(np.sqrt(vhatw) + self.eps)))
        db = -(self.alpha*mhatb
               /(self.dtilde_b*(np.sqrt(vhatb) + self.eps)))
        
        return dw, db


class SantaE(Optimizer):
    def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
                 anne_func=lambda t, n: t**n, anne_rate=0.5,
                 burnin=100, C=5,
                 *args, **kwds):
        """
        Args:
            eta: Learning rate
            sigma: Maybe in other cases;
                    'rho' in RMSprop, AdaDelta, RMSpropGraves.
                    'rhow' or 'rhob' in SMORMS3.
                    'beta2' in Adam, Eve.
                    'nu' in Nadam.
                   To use calculation 'v'.
            lambda_: Named 'eps'(ε) in other cases.
            anne_func: Annealing function.
                       To use calculation 'beta' at each timestep.
                       Default is 'timestep'**'annealing rate'.
                       The calculated value should be towards infinity
                       as 't' increases.
            anne_rate: Annealing rate.
                       To use calculation 'beta' at each timestep.
                       The second Argument of 'anne_func'.
            burnin: Swith exploration and refinement.
                    This should be specified by users.
            C: To calculate first 'alpha'.
        """
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.sigma = sigma
        self.lambda_ = lambda_
        self.anne_func = anne_func
        self.anne_rate = anne_rate
        self.burnin = burnin
        
        # Keep one step before and Initialize.
        self.alpha_w = np.sqrt(eta)*C
        self.alpha_b = np.sqrt(eta)*C
        self.vw = 0
        self.vb = 0
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, t=1, N=200, *args, **kwds):
        try:
            shape_w = grad_w.shape
        except:
            shape_w = (1, )
        try:
            shape_b = grad_b.shape
        except:
            shape_b = (1, )
        
        if t == 1:
            # Initialize uw, ub.
            self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
            self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
        
        self.vw = (self.sigma*self.vw 
                   + grad_w*grad_w * (1 - self.sigma) / N**2)
        self.vb = (self.sigma*self.vb 
                   + grad_b*grad_b * (1 - self.sigma) / N**2)
        
        gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
        gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
        
        beta = self.anne_func(t, self.anne_rate)
        if t < self.burnin:
            # Exploration.
            self.alpha_w += self.uw*self.uw - self.eta/beta
            self.alpha_b += self.ub*self.ub - self.eta/beta
            
            uw = (self.eta/beta * (1 - self.gw/gw)/self.uw
                  + np.sqrt(2*self.eta/beta * self.gw) 
                  * np.random.randn(*shape_w))
            ub = (self.eta/beta * (1 - self.gb/gb)/self.ub
                  + np.sqrt(2*self.eta/beta * self.gb) 
                  * np.random.randn(*shape_b))
        else:
            # Refinement.
            uw = 0
            ub = 0
        
        uw += (1 - self.alpha_w)*self.uw - self.eta*gw*grad_w
        ub += (1 - self.alpha_b)*self.ub - self.eta*gb*grad_b
        
        # Update values.
        self.uw = uw
        self.ub = ub
        self.gw = gw
        self.gb = gb
        
        dw = gw*uw
        db = gb*ub
        
        return dw, db


class SantaSSS(Optimizer):
    def __init__(self, eta=1e-2, sigma=0.95, lambda_=1e-8,
                 anne_func=lambda t, n: t**n, anne_rate=0.5,
                 burnin=100, C=5,
                 *args, **kwds):
        """
        Args:
            eta: Learning rate
            sigma: Maybe in other cases;
                    'rho' in RMSprop, AdaDelta, RMSpropGraves.
                    'rhow' or 'rhob' in SMORMS3.
                    'beta2' in Adam, Eve.
                    'nu' in Nadam.
                   To use calculation 'v'.
            lambda_: Named 'eps'(ε) in other cases.
            anne_func: Annealing function.
                       To use calculation 'beta' at each timestep.
                       Default is 'timestep'**'annealing rate'.
                       The calculated value should be towards infinity
                       as 't' increases.
            anne_rate: Annealing rate.
                       To use calculation 'beta' at each timestep.
                       The second Argument of 'anne_func'.
            burnin: Swith exploration and refinement.
                    This should be specified by users.
            C: To calculate first 'alpha'.
        """
        super().__init__(*args, **kwds)
        
        self.eta = eta
        self.sigma = sigma
        self.lambda_ = lambda_
        self.anne_func = anne_func
        self.anne_rate = anne_rate
        self.burnin = burnin
        
        # Keep one step before and Initialize.
        self.alpha_w = np.sqrt(eta)*C
        self.alpha_b = np.sqrt(eta)*C
        self.vw = 0
        self.vb = 0
        self.gw = 0
        self.gb = 0
    
    
    def update(self, grad_w, grad_b, t=1, N=200, *args, **kwds):
        try:
            shape_w = grad_w.shape
        except:
            shape_w = (1, )
        try:
            shape_b = grad_b.shape
        except:
            shape_b = (1, )
        
        if t == 1:
            # Initialize uw, ub.
            self.uw = np.sqrt(self.eta)*np.random.randn(*shape_w)
            self.ub = np.sqrt(self.eta)*np.random.randn(*shape_b)
        
        self.vw = (self.sigma*self.vw 
                   + grad_w*grad_w * (1 - self.sigma) / N**2)
        self.vb = (self.sigma*self.vb 
                   + grad_b*grad_b * (1 - self.sigma) / N**2)
        
        gw = 1/np.sqrt(self.lambda_ + np.sqrt(self.vw))
        gb = 1/np.sqrt(self.lambda_ + np.sqrt(self.vb))
        
        dw = 0.5*gw*self.uw
        db = 0.5*gb*self.ub
        
        beta = self.anne_func(t, self.anne_rate)
        if t < self.burnin:
            # Exploration.
            self.alpha_w += (self.uw*self.uw - self.eta/beta)*0.5
            self.alpha_b += (self.ub*self.ub - self.eta/beta)*0.5
            
            uw = np.exp(-0.5*self.alpha_w)*self.uw
            ub = np.exp(-0.5*self.alpha_b)*self.ub
            uw += (-gw*grad_w*self.eta 
                        + np.sqrt(2*self.gw*self.eta/beta)
                        * np.random.randn(*shape_w)
                        + self.eta/beta*(1-self.gw/gw)/self.uw)
            ub += (-gb*grad_b*self.eta 
                        + np.sqrt(2*self.gb*self.eta/beta)
                        * np.random.randn(*shape_b)
                        + self.eta/beta*(1-self.gb/gb)/self.ub)
            uw *= np.exp(-0.5*self.alpha_w)
            ub *= np.exp(-0.5*self.alpha_b)
            
            self.alpha_w += (uw*uw - self.eta/beta)*0.5
            self.alpha_b += (ub*ub - self.eta/beta)*0.5
        else:
            # Refinement.
            uw = np.exp(-0.5*self.alpha_w)*self.uw
            ub = np.exp(-0.5*self.alpha_b)*self.ub
            
            uw -= gw*grad_w*self.eta
            ub -= gb*grad_b*self.eta
            
            uw *= np.exp(-0.5*self.alpha_w)
            ub *= np.exp(-0.5*self.alpha_b)
        
        # Update values.
        self.uw = uw
        self.ub = ub
        self.gw = gw
        self.gb = gb
        
        dw = gw*uw*0.5
        db = gb*ub*0.5
        
        return dw, db


class AMSGrad(Optimizer):
    def __init__(self, alpha=1e-3, beta1=0.9, beta2=0.999, eps=1e-8,
                 *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.vhatw = 0
        self.vhatb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        self.vhatw = np.maximum(self.vhatw, self.vw)
        self.vhatb = np.maximum(self.vhatb, self.vb)
        
        alpha_t = self.alpha / np.sqrt(t)
        
        dw = - alpha_t * self.mw/np.sqrt(self.vhatw + self.eps)
        db = - alpha_t * self.mb/np.sqrt(self.vhatb + self.eps)
        
        return dw, db


class AdaBound(Optimizer):
    def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
                 eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.eta = eta
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        
        etal = self.eta*(1 - 1/((1-self.beta2)*t + 1))
        etau = self.eta*(1 + 1/((1-self.beta2)*t + self.eps))
        
        etahatw_t = np.clip(self.alpha/np.sqrt(self.vw), etal, etau)
        etahatb_t = np.clip(self.alpha/np.sqrt(self.vb), etal, etau)
        
        etaw_t = etahatw_t/np.sqrt(t)
        etab_t = etahatb_t/np.sqrt(t)
        
        dw = - etaw_t*self.mw
        db = - etab_t*self.mb
        
        return dw, db


class AMSBound(Optimizer):
    def __init__(self, alpha=1e-3, eta=1e-1, beta1=0.9, beta2=0.999,
                 eps=1e-8, *args, **kwds):
        super().__init__(*args, **kwds)
        
        self.alpha = alpha
        self.eta = eta
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        
        #Halten Sie den Wert des vorherigen Schritts
        self.mw = 0
        self.mb = 0
        self.vw = 0
        self.vb = 0
        self.vhatw = 0
        self.vhatb = 0
    
    
    def update(self, grad_w, grad_b, t=1, *args, **kwds):
        self.mw += (1-self.beta1)*(grad_w - self.mw)
        self.mb += (1-self.beta1)*(grad_b - self.mb)
        self.vw += (1-self.beta2)*(grad_w**2 - self.vw)
        self.vb += (1-self.beta2)*(grad_b**2 - self.vb)
        self.vhatw = np.maximum(self.vhatw, self.vw)
        self.vhatb = np.maximum(self.vhatb, self.vb)
        
        etal = self.eta*(1 - 1/((1-self.beta2)*t + 1))
        etau = self.eta*(1 + 1/((1-self.beta2)*t + self.eps))
        
        etahatw_t = np.clip(self.alpha/np.sqrt(self.vhatw), etal, etau)
        etahatb_t = np.clip(self.alpha/np.sqrt(self.vhatb), etal, etau)
        
        etaw_t = etahatw_t/np.sqrt(t)
        etab_t = etahatb_t/np.sqrt(t)
        
        dw = - etaw_t*self.mw
        db = - etab_t*self.mb
        
        return dw, db
Experimenteller Code

opt_example.py


%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation


def f(x):
    return ((1/4) * x**4 - (1/6) * x**3 - (17/8) * x**2 - (15/8) * x) * 4


def df(x):
    return (x**3 - (1/2) * x**2 - (17/4) * x - 15/8) * 4


x = np.arange(-3, 3, 1e-2)
start_x = -3
start_y = f(start_x)
exact = 2.5
epoch = 100
seed=32
np.random.seed(seed=seed)
# epoch=100 ist Samen=32


opt_dict = {
    "SDG": SGD(),
    "MSGD": MSGD(),
    "NAG": NAG(mu=0.95),
    "AdaGrad": AdaGrad(eta=4e-1),
    "RMSprop": RMSprop(eta=4e-2),
    "AdaDelta": AdaDelta(eps=1e-2),
    "Adam": Adam(alpha=4e-1),
    "RMSpropGraves": RMSpropGraves(eta=4e-2),
    "SMORMS3": SMORMS3(eta=4e-2),
    "AdaMax": AdaMax(),
    "Nadam": Nadam(),
    "Eve": Eve(alpha=4e-1),
    "SantaE": SantaE(eta=4e-4, burnin=0.5*epoch),
    "SantaSSS": SantaSSS(eta=4e-4, burnin=0.5*epoch),
    "AMSGrad": AMSGrad(alpha=4e-1),
    "AdaBound": AdaBound(alpha=4e-1),
    "AMSBound": AMSBound(alpha=4e-1),
}
current_x = np.full(len(opt_dict), start_x, dtype=float)
current_y = np.full(len(opt_dict), start_y, dtype=float)
err_list = np.zeros((epoch, len(opt_dict)), dtype=float)

cmap = plt.get_cmap("rainbow")
coloring = [cmap(i) for i in np.linspace(0, 1, len(opt_dict))]

fig, ax = plt.subplots(1)
ax.set_position([0.1, 0.1, 0.6, 0.8])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.grid()
images = []
fixed_im, = ax.plot(x, f(x))
for i in range(1, epoch+1):
    im_buf = []
    for j, opt in enumerate(opt_dict):
        im, = ax.plot(current_x[j], current_y[j], 
                      marker="o", color=coloring[j])
        err_list[i-1, j] = 0.5 * (current_x[j] - exact)**2
        
        dx, _ = opt_dict[opt].update(df(current_x[j]), 1,
                                     t=i, N=epoch, 
                                     w=current_x[j],
                                     df=df, fw=f(current_x[j]))
        current_x[j] += dx
        current_y[j] = f(current_x[j])
        im_buf.append(im)
    images.append([fixed_im] + im_buf)
for j, opt in enumerate(opt_dict):
    im, = ax.plot(current_x[j], current_y[j], 
                  marker="o", color=coloring[j],
                  label=opt)
    im_buf.append(im)
images.append([fixed_im] + im_buf)

fig.suptitle("Optimiser comparison")
fig.legend(bbox_to_anchor=(0.96, 0.9))
ani = animation.ArtistAnimation(fig, images, interval=100)

fig2, ax2 = plt.subplots(1)
fig2.suptitle("Optimiser comparison of errors")
ax2.set_position([0.1, 0.1, 0.6, 0.8])
ax2.set_xlabel("x")
ax2.set_ylabel("errors")
ax2.set_yscale("log")
ax2.grid()
for j, opt in enumerate(opt_dict):
    ax2.plot(err_list[:, j], color=coloring[j], label=opt)
fig2.legend(bbox_to_anchor=(0.96, 0.9))

opt_comparison.gif opt_comparison_of_error.png

abschließend

Es hat viel Zeit in Anspruch genommen ... Wenn Sie Fehler finden, lassen Sie es mich bitte wissen.

Referenz

Deep Learning-Serie