[PYTHON] Deep Learning von Grund auf neu 4.4.2 Gradient für neuronale Netze Die Frage nach der Funktion numerischer Gradient wurde gelöst.

Inhalt dieses Artikels

Der Inhalt ist ["Deep Learning von Grund auf neu"](https://www.amazon.co.jp/%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4% BD% 9C% E3% 82% 8BDeep-Learning-% E2% 80% 95Python% E3% 81% A7% E5% AD% A6% E3% 81% B6% E3% 83% 87% E3% 82% A3% E3 % 83% BC% E3% 83% 97% E3% 83% A9% E3% 83% BC% E3% 83% 8B% E3% 83% B3% E3% 82% B0% E3% 81% AE% E7% 90 % 86% E8% AB% 96% E3% 81% A8% E5% AE% 9F% E8% A3% 85-% E6% 96% 8E% E8% 97% A4-% E5% BA% B7% E6% AF % 85 / dp / 4873117585) 4.4.2 Gradient für neuronales Netzwerk (gemäß S.110). Da die Frage gelöst wurde, werde ich einen Artikel schreiben.

Frage

Was ich mich gefragt habe, war der Code am Ende von S.111. (folgende)

>>> def f(W):
... return net.loss(x, t)
...
>>> dW = numerical_gradient(f, net.W)
>>> print(dW)
[[ 0.21924763 0.14356247 -0.36281009]
[ 0.32887144 0.2153437 -0.54421514]]

Ich habe die Funktion "f" definiert und als Argument an die Funktion "numerischer Gradient" übergeben, die kurz vor diesem Buch definiert wurde. Als ich das zweite Argument dieser Funktion "numerischer Gradient" auf einen geeigneten Wert änderte, änderte sich der Wert von "dW". (folgende)

#Net als zweites Argument.Geben Sie W. an.(net.Für W, p in diesem Buch.Siehe den Kommentar von 110.)
>>> dW = numerical_gradient(f, net.W) 
>>> print(dW)
[[ 0.06281915  0.46086202 -0.52368118]
 [ 0.09422873  0.69129304 -0.78552177]]

#Speichern Sie das numpy-Array in a und geben Sie es als zweites Argument an.
>>> a = np.array([[0.2, 0.1, -0.3],
         [0.12, -0.17, 0.088]])
>>> dW = numerical_gradient(f, a)
>>> print(dW)
[[0. 0. 0.]
 [0. 0. 0.]]

Ich habe nicht verstanden, warum sich der Wert von "dW" geändert hat. Dieser Artikel ist die Antwort auf diese Frage.

Warum wunderte ich mich?

Ich werde erklären, warum ich mich gefragt habe, warum sich der Wert von "dW" geändert hat.

Punkt 1

Erstens hat der Rückgabewert dieser f -Funktion nichts mit dem Wert des Arguments W zu tun. Dies liegt daran, dass "W" nicht nach "return" in der Funktion "f" erscheint. Unabhängig davon, welchen Wert Sie für das Funktionsargument f`` W ändern, ändert sich der Rückgabewert überhaupt nicht. (Siehe unten)

#Geben Sie 3 als Argument der Funktion f an.
>>> f(3) 
2.0620146712373737


#net to f Funktion.Geben Sie W. an.
>>> f(net.W) 
2.0620146712373737


#Definieren Sie ein Numpy-Array und weisen Sie es einem zu. Vergleichen Sie, wann a an die Funktion f übergeben wird und wann 3 übergeben wird.
>>> a = np.array([[0.2, 0.1, -0.3], 
         [0.12, -0.17, 0.088]]) 
>>> f(a) == f(3) 
True

Punkt 2

Als weiteren Punkt werde ich die Funktion "numerischer Gradient" vorstellen. (folgende)

def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val #Stellen Sie den Wert wieder her
        it.iternext()   
        
    return grad

Diese Funktion gibt den in der Funktion definierten Grad zurück. Wenn Sie am Ende des Codes folgen, wie dieser "Grad" abgeleitet wird, Sie finden den Code grad [idx] = (fxh1 --fxh2) / (2 * h). Was sind also "fxh1" und "fxh2"? Sie finden den Code fxh1 = f (x) fxh2 = f (x).

Zusammenfassung der Punkte 1 und 2

Ab Punkt 2 kann der Rückgabewert der Funktion "numerischer Gradient" grad "dem Wert von" f (x) "zugeordnet werden. Ab Punkt 1 gibt die Funktion f unabhängig vom Wert des Arguments einen konstanten Wert zurück. Von den Punkten 1 und 2 aus wird unabhängig davon, welcher Wert dem zweiten Argument "x" der Funktion "numerischer Gradient" zugewiesen wird Ich fand es seltsam, dass sich der Rückgabewert der Funktion numerischer_gradient ändern würde.

Lösungen

Schauen wir uns zunächst die Funktion numerischer_gradient genauer an. Und ich werde die Funktion f etwas genauer erklären.

Erfahren Sie mehr über die Funktion numerischer_gradient

Eine kleine Änderung am Code "numerischer_gradient". Insbesondere unter "fxh1 = f (x)" fxh2 = f (x) " Geben Sie print (fxh1) print (fxh2) ein. (folgende)

def numerical_gradient(f, x):
    h = 1e-4 
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        print('idx:', idx)
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) # f(x+h)
        print('fxh1:', fxh1) # print(fxh1)Eingeben
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        print('fxh2:', fxh2) # print(fxh2)Eingeben
        
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val 
        it.iternext()   
        
    return grad

Verschieben wir nun den Code, indem wir das zweite Argument ändern.

Ersetzen Sie das zweite Argument durch "net.W"

>>> dW = numerical_gradient(f, net.W)
fxh1: 2.062020953321506
fxh2: 2.0620083894906935
fxh1: 2.062060757760379
fxh2: 2.061968585355599
fxh1: 2.061962303319411
fxh2: 2.062067039554999
fxh1: 2.062024094490122
fxh2: 2.062005248743893
fxh1: 2.062083801262337
fxh2: 2.0619455426551796
fxh1: 2.061936119510309
fxh2: 2.06209322386368

Ersetzen Sie das zweite Argument durch Ihr eigenes numpy-Array "a"

>>> a = np.array([[0.2, 0.1, -0.3],
         [0.12, -0.17, 0.088]])
>>> dW = numerical_gradient(f, a)
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737
fxh1: 2.0620146712373737
fxh2: 2.0620146712373737

Für diejenigen, die dem zweiten Argument "net.W" zugewiesen haben, unterscheiden sich die Werte geringfügig zwischen "fxh1" und "fxh2". Wenn Sie dagegen Ihr eigenes numpy-Array "a" ersetzen, haben "fxh1" und "fxh2" den gleichen Wert. Warum? Von nun an werde ich erklären, indem ich den Fall betrachte, in dem "net.W" im zweiten Argument eingegeben wird.

Schauen wir uns die Funktion "numerischer Gradient" genauer an. In der Mitte befindet sich der folgende Code. Dieser Code ändert die Indexnummer von "idx", ruft das "x" dieser Indexnummer ab und Ein kleines "h" wird zu dem extrahierten "x" hinzugefügt und der "f" -Funktion zugewiesen.

it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index #Ändern Sie die Indexnummer von idx
        tmp_val = x[idx]     #Nehmen Sie das x dieser Indexnummer heraus
        x[idx] = tmp_val + h #Fügen Sie dem extrahierten x ein kleines h hinzu
        fxh1 = f(x) # f(x+h) #Der Funktion f zugeordnet.

Hat sich der Rückgabewert der Funktion "f" aufgrund der Hinzufügung eines kleinen "h" zu "x" geändert? Die Tatsache, dass sich der Rückgabewert der Funktion "f" aufgrund von Änderungen in den Argumenten nicht ändert, wird in Punkt 1 von "Warum haben Sie sich gefragt?"

Tatsächlich gibt es einen Teil, der sich durch Hinzufügen eines kleinen "h" zu "x" geändert hat. Das "x" hier ist das "net.W", das dem zweiten Argument der Funktion "numerischer_gradient" zugewiesen ist. Nach dem Hinzufügen eines kleinen "h" zu "net.W" wird es an das Argument der "f" -Funktion übergeben. Der folgende Teil der zuvor gezeigten Funktion "numerischer Gradient".

x[idx] = tmp_val + h #Fügen Sie dem extrahierten x ein kleines h hinzu
fxh1 = f(x)          #Der Funktion f zugeordnet.

Wichtig ist hier die Reihenfolge, in der die Funktion "f" aufgerufen wird, nachdem sich "net.W" geändert hat. Wie wirkt sich die Änderung in "net.W" auf die "f" -Funktion aus?

Erklären Sie die Funktion "f" etwas genauer

Mal sehen, wie sich die Änderung in "net.W" auf die "f" -Funktion auswirkt. Die f Funktion ist unten gezeigt.

def f(W):
    return net.loss(x, t)

Die "Verlust" -Funktion, die in der "f" -Funktion angezeigt wird, ist in der auf S.110 dieses Handbuchs definierten "simpleNet" -Klasse definiert. Die simpleNet Klasse ist unten gezeigt.

import sys, os
sys.path.append(os.pardir)  
import numpy as np
from common.functions import softmax, cross_entropy_error
from common.gradient import numerical_gradient


class simpleNet:
    def __init__(self):
        self.W = np.random.randn(2,3)

    def predict(self, x):
        return np.dot(x, self.W)

    def loss(self, x, t):
        z = self.predict(x)
        y = softmax(z)
        loss = cross_entropy_error(y, t)

        return loss

Sie sehen die "Verlust" -Funktion am unteren Rand von "simpleNet". Innerhalb der "Verlust" -Funktion gibt es eine "Vorhersage" -Funktion. Die "Vorhersage" -Funktion wird direkt über der "Verlust" -Funktion definiert. Wenn Sie sich die Funktion "Vorhersagen" genauer ansehen, sehen Sie den Gewichtungsparameter "W".

Wie wirkt sich die Änderung in "net.W" am Ende von "Erfahren Sie mehr über die Funktion" numerischer Gradient "auf die Funktion" f "aus? Die Antwort ist hier. Durch Ändern von "net.W" hat sich der Gewichtungsparameter "W" der "Vorhersage" -Funktion geändert, die von der "Verlust" -Funktion in der "f" -Funktion aufgerufen wird. Dann ändert sich natürlich der Rückgabewert der "Verlust" -Funktion.

Zusammenfassung

Die Erklärung ist endlich vorbei.

Kehren wir zur Funktion numerischer_gradient zurück. Die Funktion numerischer_gradient wird unten noch einmal gezeigt.

def numerical_gradient(f, x):
    h = 1e-4 
    grad = np.zeros_like(x)

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        print('idx:', idx)
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x) # f(x+h)
    
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        print('fxh2:', fxh2)

        grad[idx] = (fxh1 - fxh2) / (2*h)

        x[idx] = tmp_val 
        it.iternext()   

    return grad

Wie oben erwähnt, ändert sich der Rückgabewert der "Verlust" -Funktion in der "f" -Funktion aufgrund der Änderung von "net.W". In diesem Code wurde durch Hinzufügen eines kleinen "h" zu "x" ("net.W") die Funktion "f" und der Wert von "fxh1" geändert. Gleiches gilt für das nachfolgende "fxh2". Dann an den nachfolgenden Code übergeben, druckt die Funktion numerischer_gradient den Rückgabewert. Dies löste die erste Frage, die ich stellte.

Der wichtige Punkt ist ● Unterdrücken Sie, dass das zweite Argument der Funktion "numerischer Gradient", "x", "net.W" ist. ● Der Rückgabewert der Funktion f hat sich geändert, als sich net.W geändert hat.

Lesen wir weiter "Deep Learning von Grund auf neu"!

Recommended Posts

Deep Learning von Grund auf neu 4.4.2 Gradient für neuronale Netze Die Frage nach der Funktion numerischer Gradient wurde gelöst.
[Deep Learning von Grund auf neu] Methoden zur Aktualisierung der wichtigsten Parameter für neuronale Netze
[Deep Learning von Grund auf neu] Informationen zu den Ebenen, die für die Implementierung der Backpropagation-Verarbeitung in einem neuronalen Netzwerk erforderlich sind
[Deep Learning von Grund auf neu] Über die Optimierung von Hyperparametern
Bereiten Sie die Umgebung für das O'Reilly-Buch "Deep Learning from Grund neu" mit apt-get vor (Debian 8).
[Deep Learning von Grund auf neu] Beschleunigen des neuronalen Netzwerks Ich erklärte die Verarbeitung der Rückausbreitung
[Deep Learning von Grund auf neu] Anfangswert des Gewichts des neuronalen Netzwerks unter Verwendung der Sigmoid-Funktion
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
Deep Learning von Grund auf neu
Deep Learning von Grund auf neu 4.3.3 Zeichnen Sie einen Gradientenvektor Ihrer eigenen Funktion basierend auf dem Beispielcode der partiellen Differenzierung.
[Deep Learning von Grund auf neu] Anfangsgewicht des neuronalen Netzwerks bei Verwendung der Relu-Funktion
Kapitel 3 Neuronales Netz Schneiden Sie nur die guten Punkte des Deeplearning aus, die von Grund auf neu erstellt wurden
[Deep Learning von Grund auf neu] Ich habe versucht, die Gradientenbestätigung auf leicht verständliche Weise zu erklären.
Erstellen Sie mit Docker eine Umgebung für "Deep Learning von Grund auf neu"
Deep Learning von Grund auf 1-3 Kapitel
Lua-Version Deep Learning von Grund auf neu Teil 6 [Inferenzverarbeitung für neuronale Netze]
Tiefes Lernen von Grund auf neu (Kostenberechnung)
Deep Learning Memo von Grund auf neu gemacht
Realisieren Sie mit Docker und Vagrant die Umgebungskonstruktion für "Deep Learning von Grund auf neu"
[Deep Learning von Grund auf neu] Layer-Implementierung von der Softmax-Funktion zum Überkreuzen von Entropiefehlern
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 4 Implementierung der Verlustfunktion
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 7]
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
Tiefes Lernen / Tiefes Lernen von Grund auf 2-Versuchen Sie, GRU zu bewegen
Deep Learning / Deep Learning von Grund auf neu Kapitel 6 Memo
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 6]
"Deep Learning von Grund auf neu" mit Haskell (unvollendet)
Deep Learning / Deep Learning von Grund auf neu Kapitel 7 Memo
[Windows 10] Aufbau einer "Deep Learning from Scratch" -Umgebung
Lernbericht über das Lesen von "Deep Learning von Grund auf neu"
Informationen zur Datenerweiterungsverarbeitung für tiefes Lernen
"Deep Learning from Grund" Memo zum Selbststudium (Teil 12) Deep Learning
[Lernnotiz] Deep Learning von Grund auf neu gemacht [~ Kapitel 4]
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 3 Implementierung eines dreischichtigen neuronalen Netzwerks
Deep Learning von Grund auf neu Die Theorie und Implementierung des mit Python erlernten Deep Learning Kapitel 3
[Für Anfänger] Was ist in Deep Learning von Grund auf neu geschrieben?