Der vorherige Artikel war hier. In diesem Artikel werden wir uns mit der Implementierung von Lernregeln befassen. Der Hauptteil wurde jedoch bereits unter [hier] installiert (https://qiita.com/kuroitu/items/36a58b37690d570dc618).
Der nächste Artikel ist hier
Zunächst werde ich wie gewohnt mit Skalar denken. Neuronenobjekte haben die Variablen $ w und b $.
y = \sigma(xw + b)
Hier, wenn die Eingabe $ x $ als Konstante betrachtet wird
y = f(w, b) = \sigma(xw + b)
Kann geschrieben werden als Mit anderen Worten, das Ziel der Lernregel besteht darin, die Werte von $ w und b $ entsprechend zu ändern, um sie näher an den Zielwert $ y ^ {\ star} $ zu bringen. </ font>
Schauen wir es uns theoretisch an.
y = f(w, b) = \sigma(wx + b)
Im
\begin{align}
y &= y^{\star} = 0.5 \\
x &= x_0 = 1
\end{align}
Wenn dann die Aktivierungsfunktion die Sigmoidfunktion ist und der Parameterraum von $ w und b $ dargestellt ist, ist dies wie folgt.
show_loss_space.py
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x_0 = 1
y_star = 0.5
sigma = lambda x: 1/(1+np.exp(-x))
w = np.arange(-2, 2, 1e-2)
b = np.arange(-2, 2, 1e-2)
W, B = np.meshgrid(w, b)
y = 0.5*(sigma(x_0*W + B) - y_star)**2
elevation = np.arange(np.min(y), np.max(y), 1/2**8)
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.set_xlabel("w")
ax.set_ylabel("b")
ax.set_zlabel("loss")
ax.view_init(60)
ax.grid()
ax.plot_surface(W, B, y, cmap="autumn", alpha=0.8)
ax.contour(W, B, y, cmap="autumn", levels=elevation, alpha=0.8)
fig.suptitle("Loss space")
fig.show()
fig.savefig("Loss_space.png ")
Die Abbildung zeigt den Verlustraum, wenn der quadratische Fehler für die Verlustfunktion verwendet wird. Die Lernregel lautet, dass sich der zufällige Anfangswert $ w_0, b_0 $ allmählich $ w ^ {\ star}, b ^ {\ star} $ nähert, was den optimalen Wert $ y ^ {\ star} $ ergibt. Das ist der Zweck von. </ font> Zu diesem Zeitpunkt ist die Lernregel die ** Gradientenabstiegsmethode **, die eine Weiterentwicklung der ** steilsten Abstiegsmethode ** darstellt. Die Gradientenabstiegsmethode ist eine Methode zum Abfahren einer Steigung unter Verwendung eines ** Gradienten (partielle Differenzierung) an einem bestimmten Punkt jedes Parameters. Hier enthält Formeln und Codes für jede Methode. Außerdem zeigt hier den Abstieg in einigen Erkundungsräumen. <img src=https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F640911%2F6f55cfbc-4a9f-d4fe-c70e-49dca7cbf683.gif?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=02b37020417dbead312cc8c82f5eac7e>
Dieser Artikel befasst sich mit der einfachsten SGD. Die Formel für SGD lautet wie folgt.
\begin{align}
g_t &= \nabla_{w_t}\mathcal{L}(w_t, b_t) \\
\Delta w_t &= - \eta g_t \\
w_{t+1} &= w_t + \Delta w_t
\end{align}
Diese Formel listet nur $ w $ auf, aber Sie können sehen, dass die Lernregel für Bias dieselbe ist, wenn Sie sie durch $ b $ ersetzen. Stellen Sie sich $ \ mathcal {L} (w_t, b_t) $ als Verlustfunktion und $ \ nabla_ {w_t} $ als partielles Differential für $ w_t $ vor (die obige Formel ist eine Matrixdarstellung). </ font> Die obige Formel auf Japanisch ausdrücken
--Finden Sie den Gradienten durch partielle Differenzierung --Berechnen Sie den Bewegungsumfang
Es sieht aus wie. Es ist einfach. Lass uns genauer hinschauen.
Für das erste "Finden des Gradienten durch partielle Differenzierung" wird die in Back Propagation eingeführte Methode der Fehlerrückausbreitung verwendet. Das ist gut. "Bewegen" ist auch wörtlich.
In Bezug auf den Teil "Berechnung des Bewegungsumfangs"
Ich möchte über zwei Punkte sprechen.
In Bezug auf 1. denke ich, dass dies leicht zu verstehen ist, wenn Sie tatsächlich konkret darüber nachdenken. Zum Beispiel beträgt die Steigung am Punkt von $ (x, y) = (1, 1) $ $ 2 $, aber die Richtung, in die Sie sich bewegen möchten, ist die Minusrichtung, nicht wahr? Natürlich ist auch das Gegenteil der Fall. Daher sind die Richtung und der Gradient, die Sie bewegen möchten, immer entgegengesetzt, sodass sie negativ sind. Wie Sie aus der Grafik ersehen können, ist 2., wenn Sie die Steigung $ 2 $ unverändert verwenden und $ \ Delta x = -2 $ usw. setzen, $ x = -1 $ und es wird der optimale Wert übergeben. .. </ font> Daher multiplizieren wir den Koeffizienten $ \ eta \ ll 1 $, der als Lernrate bezeichnet wird, um den Bewegungsumfang so zu begrenzen, dass er allmählich auf den optimalen Wert abfällt. Diese Lernrate ist ein Wert, der als ** Hyperparameter ** bezeichnet wird, und es gibt viele Lernregeln, die Menschen für diesen Teil entwerfen müssen. In den meisten Fällen funktioniert die Verwendung der in den Papieren angegebenen Standardwerte. Abhängig von dem Problem, das Sie lösen möchten, müssen Sie möglicherweise experimentieren.
Nun, abgesehen von den Details, lassen Sie es uns vorerst implementieren. Das Implementierungsziel ist wie gewohnt [baselayer.py](https://qiita.com/kuroitu/items/884c62c48c2daa3def08#%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC % E3% 83% A2% E3% 82% B8% E3% 83% A5% E3% 83% BC% E3% 83% AB% E3% 81% AE% E3% 82% B3% E3% 83% BC% E3 % 83% 89% E6% BA% 96% E5% 82% 99).
baselayer.py
def update(self, **kwds):
"""
Implementierung des Parameterlernens
"""
dw, db = self.opt.update(self.grad_w, self.grad_b, **kwds)
self.w += dw
self.b += db
Der Teil von "self.opt.update (self.grad_w, self.grad_b, ** kwds)" wird nach [hier] geworfen (https://qiita.com/kuroitu/items/36a58b37690d570dc618). Hier ist der SGD-Code als Beispiel.
optimziers.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
Der Inhalt des Codes entspricht der oben eingeführten Formel. Empfängt den Gradienten in Bezug auf $ w und b $ von außen, multipliziert ihn gemäß der Lernregel mit $ - \ eta $, bestimmt den Bewegungsumfang und wirft ihn zurück. </ font> Das Ebenenobjekt empfängt diese Bewegungsmenge und aktualisiert seine eigenen Parameter.
Nun, das ist alles für diese Zeit. Sie fragen sich vielleicht: "Was? Im Falle einer Warteschlange?" Tatsächlich ist der Code für Matrizen genau der gleiche. [optimizers.py](https://qiita.com/kuroitu/items/36a58b37690d570dc618#%E5%AE%9F%E8%A3%85%E3%82%B3%E3%83%BC%E3%83%89 Selbst wenn Sie sich% E4% BE% 8B) ansehen, können Sie die Berechnung des Matrixprodukts nicht finden. Der Grund ist natürlich, wenn Sie darüber nachdenken, aber selbst wenn Sie mit einem Mini-Batch lernen, sollte der Gradient, der fließt, für jeden Parameter eindeutig sein, und es ist notwendig, ihn unter Einbeziehung der Gradienten anderer Parameter zu berechnen. Weil es kein ... gibt. Wenn Sie diesmal mit einem Skalar darüber nachdenken und es implementieren, können Sie es auf die gleiche Weise mit einer Matrix berechnen.
Lassen Sie das Layer-Objekt schließlich den Optimierer "opt" mit der Methode "__ init__" verwenden.
__init__.py
def __init__(self, *, prev=1, n=1,
name="", wb_width=1,
act="ReLU", err_func="square", opt="Adam",
act_dict={}, opt_dict={}, **kwds):
self.prev = prev #Anzahl der Ausgänge der vorherigen Ebene=Anzahl der Eingaben in diese Ebene
self.n = n #Anzahl der Ausgänge in dieser Ebene=Anzahl der Eingaben in die nächste Ebene
self.name = name #Der Name dieser Ebene
#Stellen Sie Gewicht und Vorspannung ein
self.w = wb_width*np.random.randn(prev, n)
self.b = wb_width*np.random.randn(n)
#Aktivierungsfunktion(Klasse)Erhalten
self.act = get_act(act, **act_dict)
#Verlustfunktion(Klasse)Erhalten
self.errfunc = get_errfunc(err_func)
#Optimierer(Klasse)Erhalten
self.opt = get_opt(opt, **opt_dict)
Nächstes Mal werde ich die Lokalisierung der Aktivierungsfunktion und des Optimierers sowie die Verlustfunktion vorstellen.
Recommended Posts