[PYTHON] Deep Learning / Deep Learning von Grund auf neu Kapitel 6 Memo

1. Zuallererst

Ich lese ein Meisterwerk, ** "Deep Learning from Zero" **. Diesmal ist ein Memo von Kapitel 6. Um den Code auszuführen, laden Sie den gesamten Code von Github herunter und verwenden Sie das Jupiter-Notizbuch in Kapitel 06.

2. Code zum Ausprobieren der Optimierungsmethode

Um die Optimierungsmethode tatsächlich auszuprobieren, verwenden wir ch06 / optimizer_compare_mnist.py mit einigen Modifikationen / Ergänzungen. Das Netzwerk ist eine 100x4-Schicht, die MNIST klassifiziert. Mit der "Optimierungsschlüsseleinstellung" im folgenden Code können Sie nur den Optimierer auskommentieren und ausführen, den Sie nicht verwenden.

import os
import sys
sys.path.append(os.pardir)  #Einstellungen zum Importieren von Dateien in das übergeordnete Verzeichnis
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.util import smooth_curve  # smooth_curve (Eine Funktion, die den Übergang des Verlustwerts glättet)importieren
from common.multi_layer_net import MultiLayerNet  #MultiLayerNet-Import
from common.optimizer import *  #Optimiererimport

#MNIST-Daten lesen
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

#Grundeinstellung
train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2001

#Optimierungstasteneinstellung
optimizers = {}
optimizers['SGD'] = SGD()
optimizers['Momentum'] = Momentum()
optimizers['Nesterov'] = Nesterov()
optimizers['AdaGrad'] = AdaGrad()
optimizers['RMSprop'] = RMSprop() 
optimizers['Adam'] = Adam()

#Netzwerk und Zug_Stellen Sie den Verlust für jeden Optimierungsschlüssel ein
networks = {}
train_loss = {}
for key in optimizers.keys():
    networks[key] = MultiLayerNet(
        input_size=784, hidden_size_list=[100, 100, 100, 100],
        output_size=10)
    train_loss[key] = []    


#Lernschleife
for i in range(max_iterations):
    
    #Extrahieren Sie Mini-Batch-Daten
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    #Aktualisieren Sie den Farbverlauf und zeichnen Sie den Verlust für jeden Optimierungsschlüssel auf
    for key in optimizers.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizers[key].update(networks[key].params, grads)
    
        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)
    
    #Verlustanzeige(Alle 500 Iter)
    if i % 500 == 0:
        print( "===========" + "iteration:" + str(i) + "===========")
        for key in optimizers.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))


#Zeichnen eines Diagramms
fig = plt.figure(figsize=(8,6))  #Spezifikation der Diagrammgröße
markers = {"SGD": "o", "Momentum": "x", "Nesterov": "^", "AdaGrad": "s", "RMSprop":"*", "Adam": "D", } 
x = np.arange(max_iterations)
for key in optimizers.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
plt.show()

3.SGD Das Basismodell der Optimierungsmethode ist "SGD", das bis zu Kapitel 5 verwendet wurde. スクリーンショット 2020-05-06 10.20.23.png

Betrachtet man die Implementierung von SGD in common / optimizer.py,

class SGD:

    def __init__(self, lr=0.01):
        self.lr = lr
        
    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key] 

4.Momentum SGD braucht Zeit, um zu optimieren, insbesondere in den frühen Stadien, da Gradientenaktualisierungen immer konstant sind. Hier kommt "Momentum" ins Spiel.

スクリーンショット 2020-05-06 10.22.35.png

"Momentum" ist eine Methode, um den Grad der Gradientenaktualisierung schrittweise zu erhöhen, während sich die Richtung des Gradienten nicht ändert. Es ist nur ein Bild des Balls, der entsprechend der Neigung des Bodens rollt, und $ \ alpha = 0,9 $ kann als Reibung und Luftwiderstand des Bodens angesehen werden.

Um das Bild etwas konkreter auszudrücken, beispielsweise unter der Annahme, dass die Ergebnisse der vier Gradientenberechnungen für $ \ frac {\ partielles L} {\ partielles W} $ gleich sind, ist v スクリーンショット 2020-05-04 14.24.01.png Sie können sehen, dass der Grad der Aktualisierung des Verlaufs allmählich auf -1,0, -1,9, -2,71, -3,439 ansteigt. Betrachtet man die Implementierung von Momentum in common / optimizer.py,

class Momentum:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
            params[key] += self.v[key]

5.Nesterov Das Momentum neigt zu Überschwingen, wenn die Richtung des Gradienten nach Erhöhen des Gradienten der Gradientenaktualisierung umgekehrt wird. Hier wird der "Nesterov" (auch bekannt als Nestrovs Impuls) eingeführt, der eine teilweise Modifikation des Impulses darstellt.

Nesterov ändert die Position, an der der Gradient berechnet wird, auf die Position nach der Gradientenaktualisierung, einen Schritt voraus, anstelle der aktuellen Position. Natürlich kenne ich die genaue Position nach dem Aktualisieren des Verlaufs nicht, aber ich werde sie ersetzen, indem ich das ungefähre v unter Verwendung des aktuellen Verlaufs finde. Es ist zu erwarten, dass dies ein Überschwingen unterdrückt. スクリーンショット 2020-05-04 19.34.07.png Betrachtet man die Implementierung in common / optimizer.py,

class Nesterov:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key]
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]

Vergleichen wir nun "SGD", "Momentum", "Nesterov".

スクリーンショット 2020-05-05 19.14.14.png

Im Vergleich zu "SGD" haben "Momentum" und "Nesterov" die Geschwindigkeit der anfänglichen Verlustreduzierung und die endgültige Verlustrate überwältigend verbessert. "Nesterov" ist einen Schritt besser als "Momentum", und es scheint, dass die Variation des Verlusts etwas geringer ist.

6.AdaGrad AdaGrad stellt zwei wichtige Ideen vor.

Das erste ist die Idee der "adaptiven Lernrate" (Adaptive), die besagt, dass eine große Anzahl von Parametern gemäß den Parametern optimiert werden sollte, anstatt auf einmal optimiert zu werden.

Die zweite ist die Idee, den Lernkoeffizienten zu verringern, um die Lernrate zu Beginn des Lernens zu erhöhen und die Lernrate im Verlauf des Lernens zu verringern, um das Lernen effizient zu fördern.

スクリーンショット 2020-05-06 10.25.44.png

Die Lernrate wird korrigiert, indem die Summe der Quadrate des Gradienten auf h akkumuliert und beim Aktualisieren des Gradienten mit $ \ frac {1} {\ sqrt {h} + \ epsilon} $ multipliziert wird. Mit anderen Worten wird die Lernrate der stark aktualisierten Parameter allmählich verringert. $ \ Epsilon $ ist übrigens eine sehr kleine Zahl (um eine Nullteilung zu verhindern). Betrachtet man die Implementierung von common / optimizer.py,

class AdaGrad:

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

7.RMSprop RMSprop ist eine verbesserte Version von AdaGrad, die den ** exponentiellen gleitenden Durchschnitt ** des Quadrats des Gradienten in h speichert (vergangene Berechnungsergebnisse nach und nach vergessen und neue Berechnungsergebnisse einbeziehen) und den Gradienten aktualisiert. Die Lernrate wird korrigiert, indem dabei $ \ frac {1} {\ sqrt {h} + \ epsilon} $ multipliziert wird.

スクリーンショット 2020-05-06 10.28.25.png

Betrachtet man die Implementierung von common / optimizer.py,

class RMSprop:

    def __init__(self, lr=0.01, decay_rate = 0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

8.Adam "Adam" ist das Ergebnis der Idee, das Beste aus "Momentum" und "AdaGrad" herauszuholen.

スクリーンショット 2020-05-06 10.31.16.png

m ist wie der exponentielle gleitende Durchschnitt von Momentum und v ist AdaGrad selbst. Danach werden "m" und "v" in großem Umfang verwendet, während der Iter klein ist, und der Nutzungsgrad wird mit zunehmendem Iter geschwächt. Die Implementierung erfolgt durch geringfügiges Ändern der Formel wie unten gezeigt.

スクリーンショット 2020-05-06 10.02.53.png

Betrachtet man die Implementierung von common / optimizer.py,

class Adam:

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)         
        
        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])            
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

Vergleichen wir nun "AdaGrad", "RMSprop", "Adam".

![Screenshot 2020-05-05 19.06.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/4ba770be-3cfe-15a1-16bb- 030899994296.png)

Es gibt keine Optimierungsmethode, die für jede Aufgabe die besten Ergebnisse liefert. Diesmal lieferte "AdaGrad" die besten Ergebnisse.

Die Lernratenkorrektur durch den exponentiellen gleitenden Durchschnitt des Quadrats des Gradienten von "RMSprop" war für diese Aufgabe übertrieben, und die Verlustamplitude wurde sehr groß, und die endgültige Verlustrate wurde ebenfalls hoch.

Es wird gesagt, dass "Adam" eine Optimierungsmethode ist, die vorerst zuerst verwendet werden sollte und immer eine stabile Leistung zeigt. Diese Aufgabe ist nicht die beste, zeigt aber einen Übergang zur Reduzierung des Verlusts von Ehrenschülern.

9. Gewichtsinitialisierung

Wenn der Anfangswert des Gewichts Null ist, werden alle Gewichtswerte einheitlich aktualisiert und die Ausdruckskraft geht verloren. Welche Art der Initialisierung gut ist, hängt von der Art der Aktivierungsfunktion ab.

Wenn die Sigmoidfunktion, die Tanh-Funktion usw. symmetrisch sind und der mittlere Bereich als lineare Funktion betrachtet werden kann, wird die Gaußsche Verteilung mit $ \ sqrt {\ frac {1} {n}} $ als Standardabweichung, die als "Xavier-Initialisierung" bezeichnet wird, als optimal bezeichnet. Es ist

Bei Verwendung von ReUL wird gesagt, dass die Gaußsche Verteilung mit $ \ sqrt {\ frac {2} {n}} $ als Standardabweichung, die als "He-Initialisierung" bezeichnet wird, optimal ist.

10.Dropout "Dropout" ist eine Methode, um das Überlernen selbst in einem sehr ausdrucksstarken Netzwerk zu unterdrücken, indem die für jeden Iter zufällig ausgewählten Neuronen während des Lernens getrennt werden. Mit Blick auf den Implementierungscode,

class Dropout:

    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    #Vorwärtsausbreitung
    def forward(self, x, train_flg=True):
        #Erstellen Sie zum Zeitpunkt des Lernens eine Maske, die bestimmt, ob eine Verbindung hergestellt werden soll, und multiplizieren Sie das sich vorwärts ausbreitende Signal mit der Maske.
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask

        #Wenden Sie beim Ableiten keine Maske an und decken Sie das gesamte Signal ab(1 - dropout_ratio)Multiplizieren
        else:
            return x * (1.0 - self.dropout_ratio)
    
    #Multiplizieren Sie das Backpropagating-Signal mit der Maske
    def backward(self, dout):
        return dout * self.mask

Bei jeder Lernsitzung werden eine einheitliche Zufallszahl und ein Schwellenwert (dropout_ratio) verwendet, um eine Maske (True für verbindbar, False für nicht verbindbar) zu erstellen, die bestimmt, ob eine Verbindung hergestellt werden soll oder nicht. Maskieren Sie dann das sich vorwärts ausbreitende Signal (x * self.mask). In ähnlicher Weise maskieren Sie das Signal während der Rückausbreitung.

In einem konkreten Bild sieht es so aus:

スクリーンショット 2020-05-06 18.49.05.png

Zum Zeitpunkt der Inferenz wird das gesamte Signal ohne Maskierung mit (1 --dropout_ratio) multipliziert, und nur die Größe des gesamten Signals wird angepasst.

11.Batch Normalization Batch Normalization ist eine 2015 angekündigte Methode, die die Lernkonvergenzgeschwindigkeit verbessert, den Bedarf an Dropout verringert und anfänglich gewichtet, indem jeder Mini-Batch so normalisiert wird, dass er einen Durchschnitt von 0 und eine Verteilung von 1 hat. Sie können Effekte wie die Reduzierung des Konvertierungsbedarfs erzielen (robust für die Gewichtsinitialisierung). Unten ist der Algorithmus. スクリーンショット 2020-05-06 14.05.00.png

12.Weight decay Der Gewichtsabfall ist eine Methode zur Unterdrückung des Überlernens, indem eine Strafe für ein großes Gewicht im Lernprozess verhängt wird.

Wenn das Gewicht W ist, ist es durch Hinzufügen von $ \ frac {1} {2} \ lambda W ^ 2 $ zur Verlustfunktion möglich, die Zunahme des Gewichts W zu unterdrücken, und diese Methode wird ** L2 reguliert ** Wird genannt. Wenn Sie hier $ \ lambda $ erhöhen, können Sie die Strafe erhöhen. ![Screenshot 2020-05-06 14.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/ddbf678e-4b99-e2f8-a182- 76a1329147c5.png)

Übrigens in der Verlustfunktion\lambda |W|Was wurde hinzugefügtL1-RegularisierungWird genannt.

Recommended Posts

[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 7]
Deep Learning / Deep Learning von Grund auf neu Kapitel 6 Memo
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 5]
[Lernnotiz] Deep Learning von Grund auf neu gemacht [Kapitel 6]
Deep Learning / Deep Learning von Grund auf neu Kapitel 7 Memo
[Lernnotiz] Deep Learning von Grund auf neu gemacht [~ Kapitel 4]
Deep Learning von Grund auf neu Kapitel 2 Perceptron (Memo lesen)
Deep Learning / Deep Learning von Grund auf neu 2 Kapitel 4 Memo
Deep Learning / Deep Learning von Null 2 Kapitel 5 Memo
Deep Learning / Deep Learning von Null 2 Kapitel 7 Memo
Deep Learning / Deep Learning von Null 2 Kapitel 8 Memo
Deep Learning / Deep Learning von Grund auf neu Kapitel 5 Memo
Deep Learning / Deep Learning von Grund auf neu Kapitel 4 Memo
Deep Learning / Deep Learning von Grund auf neu 2 Kapitel 3 Memo
Deep Learning Memo von Grund auf neu gemacht
Deep Learning / Deep Learning von Null 2 Kapitel 6 Memo
Deep Learning von Grund auf neu
"Deep Learning from Grund" Memo zum Selbststudium (Teil 12) Deep Learning
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 9) MultiLayerNet-Klasse
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
[Lernnotiz] Deep Learning von Grund auf ~ Implementierung von Dropout ~
"Deep Learning from Grund" Memo zum Selbststudium (10) MultiLayerNet-Klasse
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 11) CNN
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 19) Datenerweiterung
Anwendung von Deep Learning 2 von Grund auf neu Spam-Filter
Tiefes Lernen von Grund auf neu (Kostenberechnung)
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 1
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 5
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 2
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 3
Ein Amateur stolperte in Deep Learning von Grund auf neu. Hinweis: Kapitel 7
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 5
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 1
Ein Amateur stolperte über Deep Learning ❷ von Grund auf neu Hinweis: Kapitel 4
Selbststudien-Memo "Deep Learning from Grund" (Nr. 18) Eins! Miau! Grad-CAM!
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 4
Ein Amateur stolperte in Deep Learning von Grund auf neu Hinweis: Kapitel 2
Selbststudien-Memo "Deep Learning from Grund" (Nr. 15) TensorFlow-Anfänger-Tutorial
Tiefes Lernen / Tiefes Lernen von Grund auf 2-Versuchen Sie, GRU zu bewegen
"Deep Learning von Grund auf neu" mit Haskell (unvollendet)
[Windows 10] Aufbau einer "Deep Learning from Scratch" -Umgebung
Lernbericht über das Lesen von "Deep Learning von Grund auf neu"
[Deep Learning von Grund auf neu] Über die Optimierung von Hyperparametern
Schreiben Sie Ihre Eindrücke von der Deep Learning 3 Framework Edition, die von Grund auf neu erstellt wurde
Selbststudien-Memo "Deep Learning from Grund" (Nr. 13) Verwenden Sie Google Colaboratory
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 10-2) Anfangswert des Gewichts
Django Memo # 1 von Grund auf neu
"Deep Learning from Grund" Memo zum Selbststudium (Nr. 14) Führen Sie das Programm in Kapitel 4 in Google Colaboratory aus
"Deep Learning from Grund" Memo zum Selbststudium (Teil 8) Ich habe die Grafik in Kapitel 6 mit matplotlib gezeichnet
Kapitel 2 Implementierung von Perceptron Schneiden Sie nur die guten Punkte des Deeplearning aus, die von Grund auf neu erstellt wurden
GitHub des guten Buches "Deep Learning von Grund auf neu"
Python vs Ruby "Deep Learning von Grund auf neu" Zusammenfassung
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 2 Logikschaltung von Perceptron
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 4 Implementierung der Verlustfunktion
Kapitel 1 Einführung in Python Schneiden Sie nur die guten Punkte des Deeplearning aus, die von Grund auf neu erstellt wurden
[Deep Learning von Grund auf neu] Ich habe die Affine-Ebene implementiert
Python vs Ruby "Deep Learning von Grund auf neu" Kapitel 3 Implementierung eines dreischichtigen neuronalen Netzwerks