[PYTHON] Einführung in Deep Learning ~ Dropout Edition ~

Überblick

Der vorherige Artikel war hier In diesem Artikel erklären wir Dropout, eine der typischen Methoden zur Unterdrückung von Überlernen. Obwohl es sich um eine einfache Methode handelt, kann ihre Wirkung aus der Tatsache abgeleitet werden, dass sie seit ihrem Vorschlag verwendet wurde. Übrigens scheint es keine theoretische Erklärung zu geben, die das Überlernen unterdrücken könnte. (Vielleicht unzureichende Forschung ...) Es gibt jedoch viele mögliche Gründe ~

Inhaltsverzeichnis

Was ist ein Schulabbrecher?

Dropout: Dropout wurde 2012 vorgeschlagen, um das Überlernen zu kontrollieren, und wurde auch vom bekannten ** AlexNet ** übernommen. Der Umriss ist nur "die Ausgabe jeder Schicht der vollständig verbundenen Schicht mit einer bestimmten Wahrscheinlichkeit $ ratio $ während des Lernens ausschließen". Ich bin überrascht, dass Überlernen damit unterdrückt werden kann. neural_net.png neural_net_dropout.png Ich glaube nicht, dass es eine theoretische Erklärung dafür gibt, warum Überlernen unterdrückt wird, aber es gibt verschiedene Theorien. Eines davon ist das Lernen von Ensembles.

Beziehung zum Ensemble-Lernen

Erstens ist das Lernen von Ensembles eine Technologie, die durch die Integration mehrerer schwacher Lernender eine hohe Genauigkeit erzielt. Aussetzer sind von besonderer Bedeutung für die Absacktechnik. Weitere Informationen [hier](https://qiita.com/kuroitu/items/57425380546f7b9ed91c#%E3%82%A2%E3%83%B3%E3%82%B5%E3%83%B3%E3%83%96 % E3% 83% AB% E5% AD% A6% E7% BF% 92), beziehen Sie sich bitte darauf. In jedem Fall trainieren Aussetzer mehrere Modelle gleichzeitig, es ist also eine Art Absackung. bagging_dropout.png Die Tatsache, dass die deaktivierenden Neuronen bei jedem Training unterschiedlich sind, bedeutet, dass sie für jedes Muster unterschiedliche Modelle lernen, was bedeutet, dass sie unterschiedliche Modelle lernen.

Daraus ergibt sich, dass das Lernen mit mehreren Lernenden = Ensemble-Lernen auf simulierte Weise durchgeführt wird. Eines der Merkmale des Absackens ist, dass das Trainingsergebnis eine hohe Verzerrung und eine geringe Varianz aufweist, sodass es nicht perfekt zu den Trainingsdaten passt, obwohl es in gewissem Maße organisiert ist. Daher wird angenommen, dass Überlernen unterdrückt wird.

Siehe die Implementierung aus der Theorie

Lassen Sie uns nun einen kurzen Blick auf die Implementierung aus der Theorie werfen. Wie bereits erwähnt, ist die Dropout-Schicht einfach zu implementieren, da sie nur "die Ausgabe jeder Schicht der vollständig verbundenen Schicht mit einer bestimmten Wahrscheinlichkeit $ ratio $ während des Trainings ausschaltet". Wie Sie vielleicht bemerkt haben, liegt der Fokus jedoch auf "** beim Lernen **". Was passiert also, wenn Sie mit dem Lernen fertig sind und mit dem Denken beginnen?

Es fällt während der Inferenz nicht aus, so dass alle Neuronen aktiv bleiben. Wie Sie sich vorstellen können, ist die Ausgabe "** Dichte **" zum Zeitpunkt des Lernens und zum Zeitpunkt der Inferenz unterschiedlich. train_vs_predict.png Um dies zu lösen, gibt es eine Methode zum Multiplizieren der Ausgabe mit $ (1 - Verhältnis) $ zum Zeitpunkt der Inferenz.

Werfen wir einen Blick auf die Formel. Unter der Annahme, dass die Ausgabe vor dem Anwenden des Dropouts $ y $ und die Ausgabe nach dem Anwenden $ \ hat {y} $ ist, ist der erwartete Wert der Ausgabe zum Zeitpunkt des Lernens

\mathbb{E}[\hat{y}] = \underbrace{(1 - ratio) y}_{Erwarteter Wert aktiver Neuronen} + \underbrace{ratio \times 0}_{非Erwarteter Wert aktiver Neuronen} = (1 - ratio)y

Es sieht aus wie. Andererseits ist zum Zeitpunkt der Inferenz die Kürzungsrate $ ratio $ 0 und der erwartete Wert der Ausgabe ist

\mathbb{E}[\hat{y}] = \underbrace{(1 - 0) y}_{Erwarteter Wert aktiver Neuronen} + \underbrace{0 \times 0}_{非Erwarteter Wert aktiver Neuronen} = y

Und die Ausgabe von $ \ frac {1} {1 --ratio} $ times ist "dunkel". (Beachten Sie, dass $ ratio $ hier $ 0 \ le ratio \ lt 1 $ ist.) Die Idee ist, diese Nichtübereinstimmung zu beseitigen, indem die Ausgabe zum Zeitpunkt der Inferenz mit $ (1 - Verhältnis) $ multipliziert wird, um diese "Dunkelheit" anzupassen.

(1 - ratio) \mathbb{E}[\hat{y}] = (1 - ratio) \left\{ \underbrace{(1 - 0) y}_{Erwarteter Wert aktiver Neuronen} + \underbrace{0 \times 0}_{非Erwarteter Wert aktiver Neuronen} \right\}= (1 - ratio)y

Dies ist jedoch eine einfache und gefährliche Methode. Natürlich können Sie ohne Probleme lernen und ohne Probleme Rückschlüsse ziehen. Diese Methode birgt jedoch das Risiko, "die Inferenzausgabe zu ändern". Ich denke nicht, dass dies in vielen Fällen ein Problem ist, aber die Ausgabe der Inferenzphase wird verwendet, um die Genauigkeit des Modells zu bewerten. Berühren Sie es daher am besten.

Dann gibt es eine Methode zum "Ausrichten der Ausgabe zum Zeitpunkt des Lernens mit der zum Zeitpunkt der Inferenz". Mit anderen Worten, die Dichte wird "verdunkelt", indem die Lernleistung durch $ (1 - Verhältnis) $ geteilt wird.

\cfrac{1}{1 - ratio}\mathbb{E}[\hat{y}] = \cfrac{1}{1 - ratio} \left\{ \underbrace{(1 - ratio) y}_{Erwarteter Wert aktiver Neuronen} + \underbrace{ratio \times 0}_{非Erwarteter Wert aktiver Neuronen} \right\} = y

Auf diese Weise bleibt der erwartete Wert der Ausgabe zum Zeitpunkt des Lernens und zum Zeitpunkt der Inferenz gleich, sodass die Ausgabe zum Zeitpunkt der Inferenz nicht berührt werden muss. Diese Methode des Spielens mit der Ausgabe während des Lernens wird im Gegensatz zur normalen Dropout-Methode als ** inverse Dropout-Methode ** bezeichnet.

Implementierung der Dropout-Ebene

Implementieren wir nun die Dropout-Ebene mithilfe der inversen Dropout-Methode.

dropout.py


class Dropout(BaseLayer):
    def __init__(self, *args,
                 mode="cpu", ratio=0.25,
                 prev=1, n=None, **kwds):
        if not n is None:
            raise KeyError("'n' must not be specified.")
        super().__init__(*args, mode=mode, **kwds)

        self.ratio = ratio
        self.mask = self.calculator.zeros(prev)
        self.prev = prev
        self.n = prev
    

    def forward(self, x, *args, train_flag=True, **kwds):
        if train_flag:
            self.mask = self.calculator.random.randn(self.prev)
            self.mask = self.calculator.where(self.mask >= self.ratio, 1, 0)
            return x*self.mask/(1- self.ratio)
        else:
            return x
    

    def backward(self, grad, *args, **kwds):
        return grad*self.mask/(1 - self.ratio)
    

    def update(self, *args, **kwds):
        pass

Die Implementierung ist einfach, nicht wahr? Die Anzahl der Neuronen in der Ausgabe muss mit der vorherigen Schicht übereinstimmen, damit sie während der Initialisierungsphase abgestoßen wird.

Für die Vorwärtsausbreitung wird während des Lernens eine Variable namens "Maske" verwendet, um zufällig ausfallende Neuronen auszuwählen. Darüber hinaus wird der umgekehrte Ausfall durch Teilen durch $ (1-Verhältnis) $ zum Zeitpunkt der Ausgabe realisiert. Daher handelt es sich um eine Implementierung, die zum Zeitpunkt der Inferenz durchlaufen wird.

Da die Rückausbreitung nur während des Lernens verwendet wird, besteht keine Notwendigkeit, die Verarbeitung wie die Vorwärtsausbreitung zu trennen. Die gleiche "Maske" wie bei der Vorwärtsausbreitung wird mit dem Elementprodukt multipliziert, so dass sich nur aktive Neuronen rückwärts ausbreiten, und sie wird auch durch $ (1-Verhältnis) $ geteilt.

In der Dropout-Ebene sind keine Parameter zu lernen, daher ist die Implementierung erfolgreich.

Wenn Sie eine Dropout-Ebene hinzufügen, fügen Sie der _TypeManager-Klasse eine Dropout-Ebene hinzu und berechnen Sie den Fehler in der Training-Funktion bei der Implementierung der Trainer-Klasse und der Forward-Funktion, die in der Predict -Funktion verwendet wird. Fügen wir train_flag hinzu.

type_manager.py und manager.py

type_manager.py


class _TypeManager():
    """
Manager-Klasse für Ebenentypen
    """
    N_TYPE = 5  #Anzahl der Ebenentypen

    BASE = -1
    MIDDLE = 0  #Nummerierung der mittleren Schicht
    OUTPUT = 1  #Nummerierung der Ausgabeebene
    DROPOUT = 2    #Nummerierung der Dropout-Ebene
    CONV = 3    #Nummerierung der Faltschicht
    POOL = 4    #Nummerierung der Pooling-Schicht
    
    REGULATED_DIC = {"Middle": MiddleLayer,
                     "Output": OutputLayer,
                     "Dropout": Dropout,
                     "Conv": ConvLayer,
                     "Pool": PoolingLayer,
                     "BaseLayer": None}
    
    
    @property
    def reg_keys(self):
        return list(self.REGULATED_DIC.keys())
    
    
    def name_rule(self, name):
        name = name.lower()
        if "middle" in name or name == "mid" or name == "m":
            name = self.reg_keys[self.MIDDLE]
        elif "output" in name or name == "out" or name == "o":
            name = self.reg_keys[self.OUTPUT]
        elif "dropout" in name or name == "drop" or name == "d":
            name = self.reg_keys[self.DROPOUT]
        elif "conv" in name or name == "c":
            name = self.reg_keys[self.CONV]
        elif "pool" in name or name == "p":
            name = self.reg_keys[self.POOL]
        else:
            raise UndefinedLayerError(name)
        
        return name

trainer.py


import time


import matplotlib.pyplot as plt
import matplotlib.animation as animation


softmax = type(get_act("softmax"))
sigmoid = type(get_act("sigmoid"))


class Trainer(Switch):
    def __init__(self, x, y, *args, mode="cpu", **kwds):
        #Ob GPU verfügbar ist
        if not mode in ["cpu", "gpu"]:
            raise KeyError("'mode' must select in {}".format(["cpu", "gpu"])
                         + "but you specify '{}'.".format(mode))
        self.mode = mode.lower()

        super().__init__(*args, mode=self.mode, **kwds)

        self.x_train, self.x_test = x
        self.y_train, self.y_test = y
        self.x_train = self.calculator.asarray(self.x_train)
        self.x_test = self.calculator.asarray(self.x_test)
        self.y_train = self.calculator.asarray(self.y_train)
        self.y_test = self.calculator.asarray(self.y_test)
    
        self.make_anim = False
    

    def forward(self, x, train_flag=True, lim_memory=10):
        def propagate(x, train_flag=True):
            x_in = x
            n_batch = x.shape[0]
            switch = True
            for ll in self.layer_list:
                if switch and not self.is_CNN(ll.name):
                    x_in = x_in.reshape(n_batch, -1)
                    switch = False
                x_in = ll.forward(x_in, train_flag=train_flag)
        
        #Weil die Vorwärtsausbreitungsmethode auch zur Fehlerberechnung und Vorhersage unbekannter Daten verwendet wird
        #Die Speicherkapazität kann groß sein
        if self.calculator.prod(
            self.calculator.asarray(x.shape))*8/2**20 >= lim_memory:
            #Gleitkommazahl mit doppelter Genauigkeit(8byte)Bei 10 MB(=30*2**20)Mehr als
            #Wenn Sie Speicher verwenden, teilen Sie ihn in 5 MB oder weniger und führen Sie ihn aus
            n_batch = int(5*2**20/(8*self.calculator.prod(
                                     self.calculator.asarray(x.shape[1:]))))
            if self.mode == "cpu":
                y = self.calculator.zeros((x.shape[0], lm[-1].n))
            elif self.mode == "gpu":
                y = self.calculator.zeros((x.shape[0], lm[-1].n))
            n_loop = int(self.calculator.ceil(x.shape[0]/n_batch))
            for i in range(n_loop):
                propagate(x[i*n_batch : (i+1)*n_batch], train_flag=train_flag)
                y[i*n_batch : (i+1)*n_batch] = lm[-1].y.copy()
            lm[-1].y = y
        else:
            #Ansonsten normal laufen
            propagate(x, train_flag=train_flag)

・
・
・
    
    def training(self, epoch, n_batch=16, threshold=1e-8,
                 show_error=True, show_train_error=False, **kwds):
        if show_error:
            self.error_list = []
        if show_train_error:
            self.train_error_list = []
        if self.make_anim:
            self.images = []
        self.n_batch = n_batch
        
        n_train = self.x_train.shape[0]//n_batch
        n_test = self.x_test.shape[0]
        
        #Fang an zu lernen
        if self.mode == "gpu":
            cp.cuda.Stream.null.synchronize()
        start_time = time.time()
        lap_time = -1
        error = 0
        error_prev = 0
        rand_index = self.calculator.arange(self.x_train.shape[0])
        for t in range(1, epoch+1):
            #Szenenerstellung
            if self.make_anim:
                self.make_scene(t, epoch)
            
            #Trainingsfehlerberechnung
            if show_train_error:
                self.forward(self.x_train[rand_index[:n_test]],
                             train_flag=False)
                error = lm[-1].get_error(self.y_train[rand_index[:n_test]])
                self.train_error_list.append(error)
            
            #Fehlerberechnung
            self.forward(self.x_test, train_flag=False)
            error = lm[-1].get_error(self.y_test)
            if show_error:
                self.error_list.append(error)

・
・
・
    
    def predict(self, x=None, y=None, threshold=0.5):
        if x is None:
            x = self.x_test
        if y is None:
            y = self.y_test
        
        self.forward(x, train_flag=False)
        self.y_pred = self.pred_func(self[-1].y, threshold=threshold)
        y = self.pred_func(y, threshold=threshold)
        print("correct:", y[:min(16, int(y.shape[0]*0.1))])
        print("predict:", self.y_pred[:min(16, int(y.shape[0]*0.1))])
        print("accuracy rate:",
              100*self.calculator.sum(self.y_pred == y, 
                                      dtype=int)/y.shape[0], "%",
              "({}/{})".format(self.calculator.sum(self.y_pred == y, dtype=int),
                               y.shape[0]))
        if self.mode == "cpu":
            return self.y_pred
        elif self.mode == "gpu":
            return self.y_pred.get()

Experiment

Lass uns experimentieren. Das Lernen mit dem MNIST-Datensatz verursacht jedoch nicht viel Übertraining, sodass der Effekt möglicherweise schwach erscheint. Das Experiment wird in Google Colaboratory durchgeführt. Ich arbeite im GPU-Modus, weil ich das MNIST-Dataset von Keras verwende, aber für 200 Epochen dauert es immer noch etwa 20 Minuten. Der Code kann unverändert ausgeführt werden, indem Sie von github zu Google Colaboratory springen.

test.py


%matplotlib inline
#Erstellen Sie eine Faltungsschicht und eine Ausgabeebene
M, F_h, F_w = 10, 3, 3
lm = LayerManager((x_train, x_test), (t_train, t_test), mode="gpu")
lm.append(name="c", I_shape=(C, I_h, I_w), F_shape=(M, F_h, F_w), pad=1)
lm.append(name="p", I_shape=lm[-1].O_shape, pool=2)
lm.append(name="m", n=100, opt="eve")
lm.append(name="d", ratio=0.5)
lm.append(name="o", n=n_class, act="softmax", err_func="Cross")

#Lernen
epoch = 200
threshold = 1e-8
n_batch = 128
lm.training(epoch, threshold=threshold, n_batch=n_batch, show_train_error=True)

#Vorhersagen
print("training dataset")
_ = lm.predict(x=lm.x_train, y=lm.y_train)
print("test dataset")
y_pred = lm.predict()

dropout_normal_comparison.png Zur Veranschaulichung der experimentellen Ergebnisse ist ein wenig mühsame Arbeit erforderlich. Führen Sie zuerst die Testcodezelle ohne die Dropout-Schicht aus und führen Sie dann den folgenden Code aus, der in einer anderen Zelle vorbereitet wurde.

get_error.py


err_list = lm.error_list

Führen Sie als Nächstes die Testcodezelle mit der Dropout-Schicht aus und führen Sie den folgenden Code aus, der in einer anderen Zelle vorbereitet wurde.

get_drop_error.py


drop_error_list = lm.error_list

Bereiten Sie nach dem obigen Setup den folgenden Code in einer anderen Zelle vor und führen Sie ihn aus.

plot.py


fig, ax = plt.subplots(1)
fig.suptitle("error comparison")
ax.set_xlabel("epoch")
ax.set_ylabel("error")
ax.set_yscale("log")
ax.grid()
ax.plot(drop_error_list, label="dropout error")
ax.plot(err_list, label="normal error")
ax.legend(loc="best")

Sie können es jetzt anzeigen.

abschließend

Es ist etwas nervig, also denken wir über eine Implementierung nach, die es einfacher macht, diese Art der vergleichenden Überprüfung zu veranschaulichen ...

Recommended Posts

Einführung in Deep Learning ~ Dropout Edition ~
Einführung in Deep Learning ~ Lernregeln ~
Tiefe Stärkung des Lernens 1 Einführung in die Stärkung des Lernens
Einführung in Deep Learning ~ Backpropagation ~
Einführung in das tiefe Lernen ~ Funktionsnäherung ~
Einführung in Deep Learning ~ Forward Propagation ~
Einführung in Deep Learning ~ CNN Experiment ~
Einführung in Deep Learning ~ Falten und Pooling ~
Einführung in das maschinelle Lernen
Einführung in Deep Learning ~ Lokalisierungs- und Verlustfunktion ~
[Lernmemorandum] Einführung in vim
Eine Einführung in das maschinelle Lernen
Super Einführung in das maschinelle Lernen
Ich habe versucht, Dropout zu erklären
Einführung in das maschinelle Lernen Schreiben von Notizen
Tiefes Lernen, um ohne GPU zu beginnen
Einführung in die Bibliothek für maschinelles Lernen SHOGUN
Deep Strengthing Learning 3 Praktische Ausgabe: Block Breaking
Tiefes Lernen
Einführung in Python Django (2) Mac Edition
Einführung in Deep Learning (2) - Versuchen Sie Ihre eigene nichtlineare Regression mit Chainer-
Einführung in das maschinelle Lernen: Funktionsweise des Modells
Tiefes Lernen von Grund auf neu (Vorwärtsausbreitung)
Eine Einführung in OpenCV für maschinelles Lernen
So studieren Sie den Deep Learning G-Test
Bildausrichtung: von SIFT bis Deep Learning
Eine Einführung in Python für maschinelles Lernen
Einführung in das maschinelle Lernen - Hard Margin SVM Edition-
Einführung in TensorFlow - Erläuterung der Begriffe und Konzepte des maschinellen Lernens
Einführung in MQTT (Einführung)
Einführung in Scrapy (1)
Einführung in Scrapy (3)
[Einführung] Stärkung des Lernens
Erste Schritte mit Supervisor
Einführung in Tkinter 1: Einführung
Deep Learning Memorandum
Einführung in PyQt
Einführung in Scrapy (2)
Starten Sie Deep Learning
[Linux] Einführung in Linux
Python Deep Learning
Einführung in Scrapy (4)
Deep Learning × Python
Einführung in discord.py (2)
[Python] Einfache Einführung in das maschinelle Lernen mit Python (SVM)
[Super Einführung in das maschinelle Lernen] Lernen Sie Pytorch-Tutorials
Ein Amateur versuchte Deep Learning mit Caffe (Einführung)
Deep Learning von Grund auf neu ① Kapitel 6 "Lerntechniken"
Eine Einführung in Cython, ohne tief zu gehen
[Einführung in StyleGAN2] Unabhängiges Lernen mit 10 Anime-Gesichtern ♬
[Lernnotiz] Deep Learning von Grund auf ~ Implementierung von Dropout ~
[Super Einführung in das maschinelle Lernen] Lernen Sie Pytorch-Tutorials
[Für Anfänger] Einführung in die Vektorisierung beim maschinellen Lernen
Einführung in Cython ohne tief zu gehen -2-
Einführung in Deep Learning (1) --Chainer wird Anfängern leicht verständlich erklärt.
Ich habe versucht, Deep Learning mit Spark × Keras × Docker 2 Multi-Host-Edition skalierbar zu machen
Warum was? Deep Learning Scientific Calculation Library Numpy Edition
Python: Deep Learning-Praxis
Deep Learning / Aktivierungsfunktionen