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!
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.
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,
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.
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 |
ist.
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 | ||
mu | ||
dw, db |
Es ist geworden.
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 | ||
mu | ||
dw, db |
Es wird gesagt.
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 | ||
gw, gb |
Es ist geworden.
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 | ||
rho | ||
eps | ||
vw, vb |
Es ist geworden.
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 | ||
eps | ||
vw, vb | ||
uw, ub |
Es ist geworden.
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 | ||
beta1 | ||
beta2 | ||
eps | ||
mw, mb | ||
vw, vb |
Es ist geworden.
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 | ||
rho | ||
eps | ||
mw, mb | ||
vw, vb |
Es ist geworden.
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 | ||
eps | ||
zetaw, zetab | ||
sw, sb | ||
mw, mb | ||
vw, vb |
Es ist geworden.
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 | ||
beta1 | ||
beta2 | ||
mw, mb | ||
uw, ub |
Es ist geworden.
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
\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 | ||
mu | ||
nu | ||
eps | ||
mw, mb | ||
vw, vb |
Es ist geworden.
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 | ||
beta1 | ||
beta2 | ||
beta3 | ||
c | ||
eps | ||
fstar | ||
mw, mb | ||
vw, vb | ||
fw, fb | ||
dtilde_w, dtilde_b |
Es ist geworden.
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 | ||
sigma | ||
lambda_ | ||
burnin | ||
C | ||
alpha_w, alpha_b | ||
Generieren Sie Zufallszahlen direkt, ohne sie in Variablen zu speichern | ||
vw, vb | ||
gw, gb | ||
uw, ub |
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>
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 | ||
sigma | ||
lambda_ | ||
burnin | ||
C | ||
alpha_w, alpha_b | ||
Generieren Sie Zufallszahlen direkt, ohne sie in Variablen zu speichern | ||
vw, vb | ||
gw, gb | ||
uw, ub |
Es ist geworden.
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 | ||
beta1 | ||
beta2 | ||
eps | ||
mw, mb | ||
vw, vb | ||
vhatw, vhatb |
Es ist geworden. Für den Anfangswert $ \ alpha_0 $ gab es in dem Papier keinen bestimmten Wert, daher Chainer ) Ich habe es mitgebracht.
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,
Es scheint, dass es sein sollte.
Hyperparameter
Symbol | Notation im Code | Wert |
---|---|---|
alpha | ||
eta | ||
beta1 | ||
beta2 | ||
eps | ||
mw, mb | ||
vw, vb |
Es ist geworden.
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 | ||
eta | ||
beta1 | ||
beta2 | ||
eps | ||
mw, mb | ||
vw, vb | ||
vhatw, vhatb |
Es ist geworden.
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
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
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
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))
Es hat viel Zeit in Anspruch genommen ... Wenn Sie Fehler finden, lassen Sie es mich bitte wissen.
Recommended Posts